Compare commits
	
		
			2 Commits
		
	
	
		
			gnome-3-10
			...
			3.7.3.1-br
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					7d598b7725 | ||
| 
						 | 
					9f890982b2 | 
							
								
								
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						@@ -19,8 +19,6 @@ configure
 | 
			
		||||
data/50-gnome-shell-*.xml
 | 
			
		||||
data/gnome-shell.desktop
 | 
			
		||||
data/gnome-shell.desktop.in
 | 
			
		||||
data/gnome-shell-wayland.desktop
 | 
			
		||||
data/gnome-shell-wayland.desktop.in
 | 
			
		||||
data/gnome-shell-extension-prefs.desktop
 | 
			
		||||
data/gnome-shell-extension-prefs.desktop.in
 | 
			
		||||
data/gschemas.compiled
 | 
			
		||||
@@ -73,14 +71,13 @@ src/calendar-server/evolution-calendar.desktop.in
 | 
			
		||||
src/calendar-server/org.gnome.Shell.CalendarServer.service
 | 
			
		||||
src/gnome-shell
 | 
			
		||||
src/gnome-shell-calendar-server
 | 
			
		||||
src/gnome-shell-extension-prefs
 | 
			
		||||
src/gnome-shell-extension-tool
 | 
			
		||||
src/gnome-shell-extension-prefs
 | 
			
		||||
src/gnome-shell-hotplug-sniffer
 | 
			
		||||
src/gnome-shell-jhbuild
 | 
			
		||||
src/gnome-shell-perf-helper
 | 
			
		||||
src/gnome-shell-perf-tool
 | 
			
		||||
src/gnome-shell-real
 | 
			
		||||
src/gnome-shell-wayland
 | 
			
		||||
src/hotplug-sniffer/org.gnome.Shell.HotplugSniffer.service
 | 
			
		||||
src/run-js-test
 | 
			
		||||
src/test-recorder
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								HACKING
									
									
									
									
									
								
							
							
						
						@@ -138,8 +138,8 @@ GObjects, although this feature isn't used very often in the Shell itself.
 | 
			
		||||
 | 
			
		||||
        _init: function(icon, label) {
 | 
			
		||||
            this.parent({ reactive: false });
 | 
			
		||||
            this.actor.add_child(icon);
 | 
			
		||||
            this.actor.add_child(label);
 | 
			
		||||
            this.addActor(icon);
 | 
			
		||||
            this.addActor(label);
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
        open: function() {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										719
									
								
								NEWS
									
									
									
									
									
								
							
							
						
						@@ -1,721 +1,6 @@
 | 
			
		||||
3.10.4
 | 
			
		||||
======
 | 
			
		||||
* Fix keyboard activation of legacy tray icons [Giovanni; #721267]
 | 
			
		||||
* Fix regressions from window-tracking changes in 3.10.3 [Florian; #722434,
 | 
			
		||||
  #722690, #722840, #723308, #722928]
 | 
			
		||||
* Scale UI on high-resolution displays [Adel; #705410]
 | 
			
		||||
* Misc. bug fixes [Giovanni, Jasper; #722547, #723197, #724256]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Giovanni Campagna, Adel Gadllah, Florian Müllner, Jasper St. Pierre
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Nilamdyuti Goswami [as], Stas Solovey [ru], A S Alam [pa]
 | 
			
		||||
 | 
			
		||||
3.10.3
 | 
			
		||||
======
 | 
			
		||||
* Fix reentrancy issue in message tray [Jasper; #711694]
 | 
			
		||||
* Fix resetting prompt on user switch [Ray; #710456]
 | 
			
		||||
* Fix screen shield on login screen [Florian; #719378]
 | 
			
		||||
* Catch more extension errors on enable/disable [Sebastien; #688331]
 | 
			
		||||
* Fix entry focus of chat notifications [Jasper; #709853]
 | 
			
		||||
* Fix app switcher order with dialog windows [Florian; #719824]
 | 
			
		||||
* Allow remote search providers without icons [Debarshi; #719965]
 | 
			
		||||
* Fix ref-counting issue in ShellApp [Giovanni; #721439]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Giovanni Campagna, Sebastien Lafargue, Florian Müllner, Debarshi Ray,
 | 
			
		||||
  Jasper St. Pierre, Ray Strode
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Emin Tufan Çetin [tr], Muhammet Kara [tr], A S Alam [pa], Khaled Hosny [ar],
 | 
			
		||||
  Dušan Kazik [sk], Wylmer Wang [zh_CN], Benjamin Steinwender [de],
 | 
			
		||||
  Matej Urbančič [sl], Aurimas Černius [lt], Rafael Ferreira [pt_BR],
 | 
			
		||||
  Andika Triwidada [id], Marek Černocký [cs], Tong Hui [zh_CN],
 | 
			
		||||
  Adorilson Bezerra [pt_BR], Christian Kirbach [de],
 | 
			
		||||
  Мирослав Николић [sr, sr@latin]
 | 
			
		||||
 | 
			
		||||
3.10.2.1
 | 
			
		||||
========
 | 
			
		||||
* Lower mutter requirement to not end up without wayland support
 | 
			
		||||
 | 
			
		||||
3.10.2
 | 
			
		||||
======
 | 
			
		||||
* gdm: Don't allow user-list to fill up the entire screen [Florian; #710555]
 | 
			
		||||
* Restore support for 'disable-restart-buttons' [Florian; #711244]
 | 
			
		||||
* alidate parameters of exposed DBus methods [Florian; #699752]
 | 
			
		||||
* Misc. bug fixes [Florian; #709806]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Florian Müllner
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Ihar Hrachyshka [be], Stas Solovey [ru], Kjartan Maraas [nb],
 | 
			
		||||
  Dimitris Spingos [el], Rafael Ferreira [pt_BR], Yuri Myasoedov [ru],
 | 
			
		||||
  Sphinx Jiang [zh_CN], Shantha kumar [ta]
 | 
			
		||||
 | 
			
		||||
3.10.1
 | 
			
		||||
======
 | 
			
		||||
* Make sure lock screen is drawn once before switching user [Giovanni; #708051]
 | 
			
		||||
* Fix signal strength indicators in network selector [Jasper; #708442]
 | 
			
		||||
* Scroll search results when focusing provider icons [Jasper; #708868]
 | 
			
		||||
* Add separate hover/active states to page indicators [Carlos; #708852]
 | 
			
		||||
* Tweak appearance of user name and avatar [Yash; #702309]
 | 
			
		||||
* Hide "Turn On" in network menu when disabled by hardware [Giovanni; #709635]
 | 
			
		||||
* Cancel open keyring prompts when the screen is locked [Florian; #708910]
 | 
			
		||||
* Differentiate "Not Connected" and "Off" in network menu [Giovanni; #709043]
 | 
			
		||||
* Make network settings items point to the right device [Giovanni; #709246]
 | 
			
		||||
* Remove animation of window preview titles [Sebastien; #709392]
 | 
			
		||||
* Add 'Notifications' switch to tray menu [Florian; #707073]
 | 
			
		||||
* Make dropdown arrows consistent [Carlos; #709564]
 | 
			
		||||
* power: Use icon from primary device for status [Jasper; #709925]
 | 
			
		||||
* Fix XDND drags to overview [Adel; #708887]
 | 
			
		||||
* Fix workspace switcher disappearing with too many workspaces [Jasper; #694881]
 | 
			
		||||
* Handle search results with 'special:' prefix specially [Giovanni; #707055]
 | 
			
		||||
* gdm: Support pre-authenticated logins from oVirt [Vinzenz; #702162]
 | 
			
		||||
* Use ARROW role for labels representing arrows [Alejandro; #710120]
 | 
			
		||||
* Make selected view in app picker persistent [Florian; #710042]
 | 
			
		||||
* Make network selector navigable by keyboard [Alejandro; #710144]
 | 
			
		||||
* Misc bug fixes [Florian, Adel, Jasper, Aleksander, Giovanni, Dan, Michael,
 | 
			
		||||
  Tim; #709034, #709263, #698486, #709286, #709248, #709543, #696564, #703265,
 | 
			
		||||
  #709638, #709866, #709998, #710019, #710104, #710115]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Giovanni Campagna, Michael Catanzaro, Vinzenz Feenstra, Adel Gadllah,
 | 
			
		||||
  Yash Girdhar, Sebastien Lafargue, Tim Lunn, Aleksander Morgado,
 | 
			
		||||
  Florian Müllner, Alejandro Piñeiro, Carlos Soriano, Jasper St. Pierre,
 | 
			
		||||
  Dieter Verfaillie, Dan Winship
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Inaki Larranaga Murgoitio [eu], Christian Kirbach [de], Muhammet Kara [tr],
 | 
			
		||||
  Aurimas Černius [lt], Ryan Lortie [eo], Rūdolfs Mazurs [lv],
 | 
			
		||||
  Dušan Kazik [sk], Fran Diéguez [gl], Enrico Nicoletto [pt_BR],
 | 
			
		||||
  Kjartan Maraas [nb], Victor Ibragimov [tg], Matej Urbančič [sl],
 | 
			
		||||
  A S Alam [pa], Nilamdyuti Goswami [as], Daniel Mustieles [es],
 | 
			
		||||
  Cheng-Chia Tseng [zh_HK, zh_TW], Mattias Põldaru [et], Kenneth Nielsen [da],
 | 
			
		||||
  Milo Casagrande [it], Marek Černocký [cs], Ihar Hrachyshka [be],
 | 
			
		||||
  Мирослав Николић [sr, sr@latin], Arash Mousavi [fa], Yuri Myasoedov [ru],
 | 
			
		||||
  Gil Forcada [ca], Carles Ferrando [ca@valencia], Andika Triwidada [id],
 | 
			
		||||
  Timo Jyrinki [fi], Piotr Drąg [pl], Rafael Ferreira [pt_BR],
 | 
			
		||||
  Gabor Kelemen [hu], Yosef Or Boczko [he], Daniel Korostil [uk],
 | 
			
		||||
  Wouter Bolsterlee [nl], António Lima [pt]
 | 
			
		||||
 | 
			
		||||
3.10.0.1
 | 
			
		||||
=========
 | 
			
		||||
* Fix login screen [Ray; #708691]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Ray Strode, Giovanni Campagna, Jasper St. Pierree
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Kjartan Maraas [nb], Marek Černocký [cs], A S Alam [pa], Daniel Mustieles [es],
 | 
			
		||||
  Ihar Hrachyshka [be], Chao-Hsiung Liao [zh_HK], Nilamdyuti Goswami [as],
 | 
			
		||||
  Yuri Myasoedov [ru], Baurzhan Muftakhidinov [kk]
 | 
			
		||||
 | 
			
		||||
3.10.0
 | 
			
		||||
======
 | 
			
		||||
* Fix fade effect in ScrollViews [Carlos; #708256]
 | 
			
		||||
* network: Resync when activating connection changes [Jasper; #708322]
 | 
			
		||||
* Close run dialog when the screen locks [Florian; #708218]
 | 
			
		||||
* Fix entry growing out of password dialogs [Florian; #708324, #703833]
 | 
			
		||||
* Vertically center labels in submenu items [Jasper; #708330]
 | 
			
		||||
* https://bugzilla.gnome.org/show_bug.cgi?id=708387 [Mike; #708387]
 | 
			
		||||
* Fix bluetooth icon not being added to status menu [Jasper; #708541]
 | 
			
		||||
* Fix GNOME 2 keyring dialogs appearing on lock screen [Florian; #708187]
 | 
			
		||||
* Fix passwords being cleared twice when authentication fails [Florian; #708186]
 | 
			
		||||
* Fix message tray appearing in a11y popup on login screen [Florian; #708380]
 | 
			
		||||
* Increase width of aggregate menu popup [Adel; #708472]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Adel Gadllah, Mike Gorse, Ryan Lortie, Florian Müllner, Frédéric Péters,
 | 
			
		||||
  Carlos Soriano, Jasper St. Pierre, Rico Tzschichholz
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Daniel Șerbănescu [ro], Ryan Lortie [eo], Ihar Hrachyshka [be],
 | 
			
		||||
  A S Alam [pa], Jiro Matsuzawa [ja], Chao-Hsiung Liao [zh_HK, zh_TW],
 | 
			
		||||
  Piotr Drąg [pl], Kristjan SCHMIDT [eo], Daniel Korostil [uk],
 | 
			
		||||
  Rūdolfs Mazurs [lv], Reinout van Schouwen [nl], Yosef Or Boczko [he],
 | 
			
		||||
  Fran Diéguez [gl], António Lima [pt], Andika Triwidada [id],
 | 
			
		||||
  Alexandre Franke [fr], Rafael Ferreira [pt_BR], Milo Casagrande [it],
 | 
			
		||||
  Kenneth Nielsen [da], Matej Urbančič [sl]
 | 
			
		||||
 | 
			
		||||
3.9.92
 | 
			
		||||
======
 | 
			
		||||
* Don't show page indicators if there's only one page [Florian; #707363]
 | 
			
		||||
* Make :active style of app and non-app results consistent [Jakub; #704714]
 | 
			
		||||
* Fade app pages when scrolled [Florian; #707409]
 | 
			
		||||
* Don't block scrolling on page indicators [Carlos; #707609]
 | 
			
		||||
* Tweak visual appearance of folder views [Florian; #707662]
 | 
			
		||||
* Don't put minimized apps at the end of the app switcher [Florian; #707663]
 | 
			
		||||
* Merge the wayland branch [Giovanni, Neil; #707467]
 | 
			
		||||
* Make search entry behave better in RTL locales [Matthias, Florian; #705779]
 | 
			
		||||
* Allow to change app pages with pageUp/pageDown keys [Carlos; #707979]
 | 
			
		||||
* Set approriate a11y states on expandable menu items [Alejandro; #708038]
 | 
			
		||||
* Improve page indicator animation [Carlos; #707565]
 | 
			
		||||
* Misc bug fixes and cleanups [Florian, Olivier, Jasper, Giovanni, Magdalen,
 | 
			
		||||
  Adel, Carlos, Rico, Joanmarie; #707308, #707430, #707508, #707557, #707600,
 | 
			
		||||
  #707614, #707666, #707814, #707806, #707801, #707889, #707892, #707935,
 | 
			
		||||
  #707842, #707940, #707996, #708007, #708009, #708020, #707580, #708080]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Magdalen Berns, Olivier Blin, Giovanni Campagna, Matthias Clasen,
 | 
			
		||||
  Joanmarie Diggs, Adel Gadllah, Florian Müllner, Alejandro Piñeiro,
 | 
			
		||||
  Neil Roberts, Carlos Soriano, Jasper St. Pierre, Jakub Steiner,
 | 
			
		||||
  Rico Tzschichholz
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Rafael Ferreira [pt_BR], Fran Diéguez [gl], Daniel Mustieles [es],
 | 
			
		||||
  Aurimas Černius [lt], Luca Ferretti [it], Piotr Drąg [pl],
 | 
			
		||||
  Chao-Hsiung Liao [zh_HK, zh_TW], Timo Jyrinki [fi], Daniel Korostil [uk],
 | 
			
		||||
  Dušan Kazik [sk], Adam Matoušek [cs], Marek Černocký [cs],
 | 
			
		||||
  Jiro Matsuzawa [ja], Yuri Myasoedov [ru], Tobias Endrigkeit [de],
 | 
			
		||||
  Kjartan Maraas [nb], Victor Ibragimov [tg], Мирослав Николић [sr, sr@latin],
 | 
			
		||||
  A S Alam [pa], Khaled Hosny [ar], Andika Triwidada [id],
 | 
			
		||||
  Nilamdyuti Goswami [as], Ihar Hrachyshka [be], Rūdolfs Mazurs [lv],
 | 
			
		||||
  Mattias Põldaru [et], Gabor Kelemen [hu], Bruce Cowan [en_GB],
 | 
			
		||||
  Matej Urbančič [sl], Enrico Nicoletto [pt_BR], Benjamin Steinwender [de],
 | 
			
		||||
  Changwoo Ryu [ko], Kris Thomsen [da], Alexandre Franke [fr],
 | 
			
		||||
  Evgeny Bobkin [ru], Baurzhan Muftakhidinov [kk], Peter Mráz [sk],
 | 
			
		||||
  Inaki Larranaga Murgoitio [eu], Yosef Or Boczko [he]
 | 
			
		||||
 | 
			
		||||
3.9.91
 | 
			
		||||
======
 | 
			
		||||
* Improve submenu styling [Jakub; #706037]
 | 
			
		||||
* Fix changing slider values via keyboard [Alejandro; #706386]
 | 
			
		||||
* Fix accessibility of sliders [Alejandro; #706391]
 | 
			
		||||
* Tweak system actions style [Jakub; #706638]
 | 
			
		||||
* Add support for auth without username / fix Not Listed? [Ray; #706607]
 | 
			
		||||
* Dash: Don't show tooltips for apps with open popups [Giovanni; #705611]
 | 
			
		||||
* Implement new end-session/power-off dialog design [Jasper, Matthias; #706612]
 | 
			
		||||
* Implement building separate binaries for x11 and wayland [Giovanni; #705497]
 | 
			
		||||
* authPrompt: Fix controls moving when showing messages [Ray; #706670]
 | 
			
		||||
* Tweak padding between system status icons [Allan; #706796]
 | 
			
		||||
* Add a generic accessible usable by JS code [Alejandro; #648623]
 | 
			
		||||
* Improve keynav and accessibility of the calendar [Alejandro; #706903]
 | 
			
		||||
* Update to new NetworkManager APIs [Jasper; #706098]
 | 
			
		||||
* Hide system actions section in the lock screen [Jasper; #706852]
 | 
			
		||||
* Don't show other logged in users at log out [Giovanni; #707124]
 | 
			
		||||
* Remove "Session" subtitle heading in login dialog [Jasper; #707072]
 | 
			
		||||
* dash: Reload favorites when installed apps change [Giovanni; #706878]
 | 
			
		||||
* Don't open overview after closing last window on workspace [Florian; #662581]
 | 
			
		||||
* Add FocusApp DBus method [Giovanni; #654086]
 | 
			
		||||
* Add ShowApplications DBus method [Giovanni; #698743]
 | 
			
		||||
* Implement new app picker design [Carlos, Florian; #706081]
 | 
			
		||||
* Improve frequent apps being empty by default [Carlos, Florian; #694710]
 | 
			
		||||
* Extend clickable area of page indicators [Giovanni; #707314]
 | 
			
		||||
 | 
			
		||||
* Misc bug fixes [Ray, Giovanni, Jasper, Emmanuele; #706542, #706654, #706005,
 | 
			
		||||
  #706681, #706841, #706843, #707064, #706262, #707197, #707269]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Emmanuele Bassi, Giovanni Campagna, Matthias Clasen, Allan Day, Adel Gadllah,
 | 
			
		||||
  Florian Müllner, Alejandro Piñeiro, Carlos Soriano, Jasper St. Pierre,
 | 
			
		||||
  Jakub Steiner, Ray Strode, Seán de Búrca
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Piotr Drąg [pl], Kjartan Maraas [nb], Victor Ibragimov [tg],
 | 
			
		||||
  Enrico Nicoletto [pt_BR], Benjamin Steinwender [de],
 | 
			
		||||
  Baurzhan Muftakhidinov [kk], Aurimas Černius [lt], Seán de Búrca [ga],
 | 
			
		||||
  Fran Diéguez [gl], Daniel Mustieles [es], Dušan Kazik [sk],
 | 
			
		||||
  Matej Urbančič [sl], Andika Triwidada [id], Jordi Mas [ca],
 | 
			
		||||
  Ihar Hrachyshka [be]
 | 
			
		||||
 | 
			
		||||
3.9.90
 | 
			
		||||
======
 | 
			
		||||
* workspaceThumbnails: Exclude transient windows when shifting workspaces
 | 
			
		||||
  [Bradley; #705174]
 | 
			
		||||
* Never show a horizontal scrollbar on lock screen [Jasper; #704327]
 | 
			
		||||
* authPrompt: Fix disable-user-list / Not Listed? [Ray; #705370]
 | 
			
		||||
* Animate the lock screen notification transitions [Giovanni; #687660]
 | 
			
		||||
* Wake up the screen when new notifications appear [Giovanni; #703084]
 | 
			
		||||
* Use StartupWMClass for application matching [Giovanni; #673657, #705801]
 | 
			
		||||
* dateMenu: Add style class for the clock label [Jonh; #705634]
 | 
			
		||||
* keyboard: Translate IBus IME name if possible [Daiki; #695673]
 | 
			
		||||
* power: Display single digit minutes correctly [Sebastian; #705803]
 | 
			
		||||
* Implement new aggregate status menu [Jasper; #705845]
 | 
			
		||||
* Improve triangle animation when expanding sub-menus [Tarun; #703109]
 | 
			
		||||
* Fix alignment of search provider icons [Tarun; #695760]
 | 
			
		||||
* Slide dash and workspace switcher on overview transitions [Tarun; #694262]
 | 
			
		||||
* Respect always-show-universal-access-status setting [Tanner; #705733]
 | 
			
		||||
* Handle .desktop files with capital letters [Giovanni; #706252]
 | 
			
		||||
* authPrompt: Add smartcard support [Ray; #683437]
 | 
			
		||||
* Fix call notifications in busy mode [Emilio; #666221]
 | 
			
		||||
* Improve triangle animation when expanding sub-menus [Tarun; #703109]
 | 
			
		||||
* Move message tray menu to a tray button [Jasper; #699272]
 | 
			
		||||
* Wi-fi dialog improvements [Jasper, Allan; #705916, #706136]
 | 
			
		||||
* Work towards running as wayland compositor [Giovanni]
 | 
			
		||||
 - Switch to Mutter abstraction layer for cursor tracking [#705911]
 | 
			
		||||
 - Add confirmation dialog for display changes [#706208]
 | 
			
		||||
* Use a different background in screen shield [Giovanni; #688210]
 | 
			
		||||
* Add fade animation before blanking the screen [Giovanni; #699112]
 | 
			
		||||
* Misc. bugfixes and cleanups [Jasper, Giovanni, Adel, Colin, Ray, Florian,
 | 
			
		||||
  Magdalen; #704448, #702536, #686855, #695581, #700901, #701761, #701495,
 | 
			
		||||
  #701848, #697833, #701731, #705664, #705840, #705898, #706089, #706153,
 | 
			
		||||
  #704646, #706262, #706324, #703810, #703811, #704015, #706232, #705917,
 | 
			
		||||
  #706536]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Magdalen Berns, Giovanni Campagna, Allan Day, Tanner Doshier, Adel Gadllah,
 | 
			
		||||
  Sebastian Keller, Tarun Kumar Joshi, Florian Müllner, Bradley Pankow,
 | 
			
		||||
  Emilio Pozuelo Monfort, Jasper St. Pierre, Ray Strode, Rico Tzschichholz,
 | 
			
		||||
  Daiki Ueno, Colin Walters, Jonh Wendell
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Kjartan Maraas [nb], Aurimas Černius [lt], Yaron Shahrabani [he],
 | 
			
		||||
  Fran Diéguez [gl], Gabor Kelemen [hu],
 | 
			
		||||
  Juan Diego Martins da Costa Cruz [pt_BR], Inaki Larranaga Murgoitio [eu],
 | 
			
		||||
  Yuri Myasoedov [ru], Daniel Mustieles [es], Seán de Búrca [ga],
 | 
			
		||||
  Khaled Hosny [ar], Victor Ibragimov [tg], Friedel Wolff [af],
 | 
			
		||||
  Marek Černocký [cs], Matej Urbančič [sl], A S Alam [pa],
 | 
			
		||||
  Rafael Ferreira [pt_BR], Andika Triwidada [id], Dušan Kazik [sk]
 | 
			
		||||
 | 
			
		||||
3.9.5
 | 
			
		||||
=====
 | 
			
		||||
* Fix width changes of the calendar popup [Florian; #704200]
 | 
			
		||||
* Work towards aggregate status menu [Jasper; #702539, #704336, #704368,
 | 
			
		||||
  #704670]
 | 
			
		||||
* Update design of lock screen notifications [Allan; #702305]
 | 
			
		||||
* Don't show empty backgroundMenu [Michael; #703868]
 | 
			
		||||
* Add option to limit app switcher to current workspace [Adel; #703538]
 | 
			
		||||
* Consolidate design of login screen and unlock dialog [Ray; #702308, #704795]
 | 
			
		||||
* Respect hasWorkspace property of session mode [Jasper; #698593]
 | 
			
		||||
* Fix fade of app menu icon in RTL locales [Jasper; #704583]
 | 
			
		||||
* Destroy notifications when the close button is clicked [Adel; #687016]
 | 
			
		||||
* Fix clicks on legacy tray icons in the message tray [Florian; #704095]
 | 
			
		||||
* authPrompt: Fade out message if users start to type [Ray; #704817]
 | 
			
		||||
* Export timestamps of global shortcuts on DBus [Bastien; #704859]
 | 
			
		||||
* Fix duplicate search provider results [Jasper; #700283]
 | 
			
		||||
* Misc bug fixes and cleanups [Lionel, Florian, Emilio, Ray, Jasper; #703859,
 | 
			
		||||
  #703540, #704077, #703997, #704318, #704347, #704265, #704411, #704430,
 | 
			
		||||
  #704347, #704453, #704471, #704542, #704707, #703905, #705037]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Allan Day, Adel Gadllah, Lionel Landwerlin, Florian Müllner, Bastien Nocera,
 | 
			
		||||
  Emilio Pozuelo Monfort, Jasper St. Pierre, Ray Strode, Colin Walters,
 | 
			
		||||
  Michael Wood
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  eternalhui [zh_CN], Victor Ibragimov [tg], Dušan Kazik [sk],
 | 
			
		||||
  Jiro Matsuzawa [ja], Kjartan Maraas [nb], Milo Casagrande [it],
 | 
			
		||||
  Marek Černocký [cs], Daniel Mustieles [es], Benjamin Steinwender [de]
 | 
			
		||||
 | 
			
		||||
3.9.4
 | 
			
		||||
=====
 | 
			
		||||
* Fix chat entries not being focused when expanded [Jasper; #698778]
 | 
			
		||||
* Fix alignment of "Not Listed?" label [Mathieu; #702307]
 | 
			
		||||
* Fix alignment of time stamps in chat notifications [Carlos; #687809]
 | 
			
		||||
* Round the ends of slider trough [Jasper; #702825]
 | 
			
		||||
* Add support for "box-shadow: none" [Cosimo; #702782]
 | 
			
		||||
* Keep chrome below popup windows [Florian; #702338]
 | 
			
		||||
* Move the session list to a popup menu [Ray; #702818]
 | 
			
		||||
* Fix autorun notifications for "non-native" volumes [Matthias; #703418]
 | 
			
		||||
* dnd: Speed up by not picking on each motion event [Jasper; #703443]
 | 
			
		||||
* Fix management of asynchronous background loading [Lionel; #703001]
 | 
			
		||||
* Rework focus handling [Jasper; #700735]
 | 
			
		||||
* Optimize box-shadow rendering [Lionel; #689858]
 | 
			
		||||
* Remove support for fixed positioning in BoxLayouts [Florian; #703808]
 | 
			
		||||
* Misc bug fixes and cleanups [Adel, Jasper, Florian, Ray, Lionel, Emilio;
 | 
			
		||||
  #702849, #610279, #703132, #703105, #703160, #703126, #703304, #703403,
 | 
			
		||||
  #698593, #703442, #703565, #700901, #703874, #703807, #703893, #703909]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Mathieu Bridon, Giovanni Campagna, Cosimo Cecchi, Matthias Clasen,
 | 
			
		||||
  Fran Diéguez, Adel Gadllah, Lionel Landwerlin, Florian Müllner,
 | 
			
		||||
  Emilio Pozuelo Monfort, Carlos Soriano, Jasper St. Pierre, Ray Strode
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Baurzhan Muftakhidinov [kk], Marek Černocký [cs], Daniel Mustieles [es],
 | 
			
		||||
  Fran Diéguez [gl], Kjartan Maraas [nb], Andika Triwidada [id],
 | 
			
		||||
  Benjamin Steinwender [de], Nguyễn Thái Ngọc Duy [vi], Trần Ngọc Quân [vi]
 | 
			
		||||
 | 
			
		||||
3.9.3
 | 
			
		||||
=====
 | 
			
		||||
* Don't push window thumbs when workspace switcher is hidden [Jasper; #701167]
 | 
			
		||||
* Tweak timeout for activating windows during XDND [Adel; #700150]
 | 
			
		||||
* Fix ellipsization in control buttons in app picker [Carlos; #696307]
 | 
			
		||||
* Fix DND to empty dash [Florian; #684618]
 | 
			
		||||
* Fix OSD window appearing below system modal dialogs [Rui; #701269]
 | 
			
		||||
* Clear clipboard on screen lock to prevent information leak [Florian; #698922]
 | 
			
		||||
* Allow session mode specific overrides schema [Florian; #701717]
 | 
			
		||||
* window-switcher: Only show windows from current workspace by default
 | 
			
		||||
  [Florian; #701214]
 | 
			
		||||
* logout dialog: Show the correct text right away [Matthias; #702056]
 | 
			
		||||
* bluetooth: Port to bluez 5 [Emilio; #700891]
 | 
			
		||||
* dateMenu: Allow events to span multiple lines [Giovanni; #701231]
 | 
			
		||||
* gdm: Clear message queue when no more messages are pending [Jonh; #702458]
 | 
			
		||||
* Misc bug fixes and cleanups [Jasper, Florian, Adel, Giovanni; #693836,
 | 
			
		||||
  #700972, #701386, #700877, #701755, #698918, #701224, #702125, #701954,
 | 
			
		||||
  #701849, #702121]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Giovanni Campagna, Matthias Clasen, Fran Diéguez, Adel Gadllah, Rui Matos,
 | 
			
		||||
  Florian Müllner, Emilio Pozuelo Monfort, Carlos Soriano, Jasper St. Pierre,
 | 
			
		||||
  Jonh Wendell
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Marek Černocký [cs], Victor Ibragimov [tg], Fran Diéguez [gl],
 | 
			
		||||
  Benjamin Steinwender [de], Cheng-Chia Tseng [zh_HK, zh_TW],
 | 
			
		||||
  eternalhui [zh_CN], Ivaylo Valkov [bg], Kjartan Maraas [nb],
 | 
			
		||||
  Daniel Mustieles [es]
 | 
			
		||||
 | 
			
		||||
3.9.2
 | 
			
		||||
=====
 | 
			
		||||
* Use a symbolic icon for DESKTOP windows [Matthias; #697914]
 | 
			
		||||
* Move paint state cache into StWidget [Jasper; #697274]
 | 
			
		||||
* gdm: Fix regression where domain login hint not shown [Stef; #698200]
 | 
			
		||||
* Make calendar keyboard navigable [Tanner; #667434]
 | 
			
		||||
* Hide "Open Calendar" item when no calendar app is installed [Lionel; #697725]
 | 
			
		||||
* Update how branding appears on login screen [Florian; #694912, #699877]
 | 
			
		||||
* Allow OSD popups to grow if necessary [Marta; #696523]
 | 
			
		||||
* Fix offset of shadow offscreen rendering [Lionel; #698301]
 | 
			
		||||
* Fix insensitive button preventing empty keyring password [Stef; #696304]
 | 
			
		||||
* Allow cancelling keyring dialog between prompts [Stef; #682830]
 | 
			
		||||
* modalDialog: Show spinner while working [Stef; #684438]
 | 
			
		||||
* overview: Only show close buttons for windows that may close [Jasper; #699269]
 | 
			
		||||
* Add input purpose and hints to StEntry and StIMText [Daiki; #691392]
 | 
			
		||||
* Set input-purpose property for password entries [Rui; #700043]
 | 
			
		||||
* Provide a DBus API for screencasting [Florian; #696247]
 | 
			
		||||
* overview: Disable hotcorner during DND [Jasper; #698484]
 | 
			
		||||
* polkitAgent: Allow retrying after mistyped passwords [Stef; #684431]
 | 
			
		||||
* Add a way to get backtraces from criticals and warnings [Giovanni; #700262]
 | 
			
		||||
* Allow switch-to-workspace-n keybindings in overview [Florian; #649977]
 | 
			
		||||
* Update man page [Matthias; #700339]
 | 
			
		||||
* Add FocusSearch DBus method [Florian; #700536]
 | 
			
		||||
* Hide frequent view when app monitoring is disabled [Florian; #699714]
 | 
			
		||||
* Show switcher popup for switch-to-workspace-n keybindings [Elad; #659288]
 | 
			
		||||
* gdm: Update the session chooser style [Allan; #695742]
 | 
			
		||||
* Fix some app folders getting truncated at the top [Florian; #694371]
 | 
			
		||||
* Don't block the message tray while a notification is showing [Jasper; #700639]
 | 
			
		||||
* popupMenu: Allow for an optional border for slider handle [Florian; #697917]
 | 
			
		||||
* Re-lock screen when restarted after a crash [Colin; #691987]
 | 
			
		||||
* Synchronize input source switching with key events [Rui; #697007]
 | 
			
		||||
* Switch input source on modifiers-only accelerator [Rui; #697008]
 | 
			
		||||
* Allow input source switching in message tray [Rui; #697009]
 | 
			
		||||
* Misc bug fixes and cleanups [Alban, Jasper, Giovanni, Florian, Rui, Tomeu,
 | 
			
		||||
  Stef, Gustavo; #698863, #699799, #699800, #676285, #699975, #700097, #698812,
 | 
			
		||||
  #698486, #700194, #695314, #700257, #699678, #700356, #700322, #700394,
 | 
			
		||||
  #700409, #700595, #700625, #691746, #700620, #700807, #659288, #700784,
 | 
			
		||||
  #700842, #700847, #700488, #700735, #696159, #700900, #700853, #700923,
 | 
			
		||||
  #700944, #697661, #700854, #700190, #699189, #701097]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Elad Alfassa, Alban Browaeys, Giovanni Campagna, Matthias Clasen, Allan Day,
 | 
			
		||||
  Tanner Doshier, Lionel Landwerlin, Rui Matos, Simon McVittie,
 | 
			
		||||
  Marta Milakovic, Florian Müllner, Gustavo Padovan, Jasper St. Pierre,
 | 
			
		||||
  Daiki Ueno, Tomeu Vizoso, Stef Walter, Colin Walters
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Matej Urbančič [sl], Kjartan Maraas [nb], Victor Ibragimov [tg],
 | 
			
		||||
  Dušan Kazik [sk], Gil Forcada [ca], Daniel Mustieles [es]
 | 
			
		||||
 | 
			
		||||
3.9.1
 | 
			
		||||
=====
 | 
			
		||||
* Add additional toggle-overview keybinding [Matthias; #698251]
 | 
			
		||||
* Disable <super> shortcut when sticky keys are enabled [Matthias; #685974]
 | 
			
		||||
* Disable tray context menu while a notification displays [Jasper; #695800]
 | 
			
		||||
* Watch GApplication busy state [Cosimo; #697207]
 | 
			
		||||
* Disable style transitions if animations are disabled [Jasper; #698391]
 | 
			
		||||
* Filter out hidden applications from "Frequent" view [Giovanni; #696949]
 | 
			
		||||
* Fix window previews swapping place randomly [Jasper; #694469, #698776]
 | 
			
		||||
* Add support for serialized GIcons in remote search providers [Cosimo; #698761]
 | 
			
		||||
* Fix hotcorner regression in RTL locales [Jasper; #698884]
 | 
			
		||||
* Allow some keybindings to work while a top bar menu is open [Florian; #698938]
 | 
			
		||||
* Make open-app-menu keybinding a toggle action [Florian; #686756]
 | 
			
		||||
* Only recognize common URL schemes in notification messages [Monica; #661225]
 | 
			
		||||
* Misc fixes and cleanups [Tim, Jasper, Florian, Giovanni, Owen; #698531,
 | 
			
		||||
  #698622, #698427, #698483, #698513, #697203, #698959, #698918, #699029,
 | 
			
		||||
  #699075, #696720, #649748]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Giovanni Campagna, Cosimo Cecchi, Monica Chelliah, Matthias Clasen, Tim Lunn,
 | 
			
		||||
  Florian Müllner, Jasper St. Pierre, Michael Wood, Owen W. Taylor
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Fran Diéguez [gl], Muhammet Kara [tr], Daniel Mustieles [es],
 | 
			
		||||
  Gil Forcada [ia], Anish A [ml], Dimitris Spingos [el], Marek Černocký [cs],
 | 
			
		||||
  Žygimantas Beručka [lt]
 | 
			
		||||
 | 
			
		||||
3.8.1
 | 
			
		||||
=====
 | 
			
		||||
* Clip window group during startup animation [Jasper; #696323]
 | 
			
		||||
* Check for logind rather than systemd [Martin; #696252]
 | 
			
		||||
* Don't special-case last remote search provider position [Giovanni; #694974]
 | 
			
		||||
* Fix memory leaks [Ray, Jasper; ##697119, #697295, #697300, #697395]
 | 
			
		||||
* AppSwitcherPopup: Activate only the selected window if any [Rui; #697480]
 | 
			
		||||
* Enable screen recorder keybinding in all modes [Florian; #696200]
 | 
			
		||||
* Remove box-shadow from screen shield for performance reasons [Adel; #697274]
 | 
			
		||||
* Add support for -st-natural-width/height CSS properties [Giovanni; #664411]
 | 
			
		||||
* Remove excessive padding from notification buttons [Allan; #664411]
 | 
			
		||||
* Fix thumbnail dragging in overview [Jasper; #697504]
 | 
			
		||||
* theme-node: Add get_url()/lookup_url() methods [Florian; #693688]
 | 
			
		||||
* Misc bug fixes and cleanups [Jasper, Rui, Colin, David, Ray, Matthias:
 | 
			
		||||
  #695859, #696259, #696585, #696436, #697432, #697435, #697560, #697722,
 | 
			
		||||
  #697709]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Giovanni Campagna, Matthias Clasen, Allan Day, Adel Gadllah, David Gumberg,
 | 
			
		||||
  Rui Matos, Florian Müllner, Martin Pitt, Jasper St. Pierre, Ray Strode,
 | 
			
		||||
  Colin Walters
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Daniel Martinez [an], Bruce Cowan [en_GB], Khaled Hosny [ar],
 | 
			
		||||
  Ihar Hrachyshka [be], Aron Xu [zh_CN], Wojciech Szczęsny [pl],
 | 
			
		||||
  Inaki Larranaga Murgoitio [eu], Kjartan Maraas [nb],
 | 
			
		||||
  Милош Поповић [sr, sr@latin], Trần Ngọc Quân [vi]
 | 
			
		||||
 | 
			
		||||
3.8.0.1
 | 
			
		||||
3.7.3.1
 | 
			
		||||
=======
 | 
			
		||||
* Background bug fixes [Ray; #696712]
 | 
			
		||||
 | 
			
		||||
3.8.0
 | 
			
		||||
=====
 | 
			
		||||
* Remove blur and desaturation from lock screen [Jasper; #696322]
 | 
			
		||||
* Remove scroll view fade near edges [Adel; #696404]
 | 
			
		||||
* dateMenu: Open calendar component when using Evolution [Florian; #696432]
 | 
			
		||||
* Fix unlocking on fast user switch [Cosimo; #696287]
 | 
			
		||||
* Tweak screen shield animation [Rui; #696380]
 | 
			
		||||
* Fix major memory leak when changing backgrounds [Ray; #696157]
 | 
			
		||||
* Miscellaneous bug fixes [Jasper, Adel, Florian; #696199, #696212, #696422,
 | 
			
		||||
  #696447, #696235]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Giovanni Campagna, Cosimo Cecchi, Adel Gadllah, Rui Matos, Florian Müllner,
 | 
			
		||||
  Jasper St. Pierre, Ray Strode
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Alexandre Franke [fr], Victor Ibragimov [tg], Arash Mousavi [fa],
 | 
			
		||||
  Gabor Kelemen [hu], Sandeep Sheshrao Shedmake [mr], ManojKumar Giri [or],
 | 
			
		||||
  Shantha kumar [ta], Rajesh Ranjan [hi], Stas Solovey [ru],
 | 
			
		||||
  Shankar Prasad [kn], Dušan Kazik [sk], Ihar Hrachyshka [be],
 | 
			
		||||
  Wouter Bolsterlee [nl], Kris Thomsen [da], Jiro Matsuzawa [ja],
 | 
			
		||||
  Daniel Korostil [uk], Ani Peter [ml], Krishnababu Krothapalli [te],
 | 
			
		||||
  Mantas Kriaučiūnas [lt], Praveen Illa [te]
 | 
			
		||||
 | 
			
		||||
3.7.92
 | 
			
		||||
======
 | 
			
		||||
* Drop fallback lock implementation [Florian; #693403]
 | 
			
		||||
* Don't let the user trigger message-tray when in fullscreen [Jasper; #694997]
 | 
			
		||||
* Scroll search results when using keynav [Jasper; #689681]
 | 
			
		||||
* Allow raising the shield by starting to type the password [Jasper; #686740]
 | 
			
		||||
* Improve tracking of fullscreen windows [Owen; #649748]
 | 
			
		||||
* Improve animation of new windows in overview [Giovanni; #695582]
 | 
			
		||||
* workspace switcher: Animate new workspaces created by DND [Giovanni; #685285]
 | 
			
		||||
* Give user time to read messages on login screen [Ray; #694688]
 | 
			
		||||
* Misc bug fixes and cleanups [Jasper, Ray, Florian, Cosimo, Giovanni, Adel,
 | 
			
		||||
  Stef, Takao, Rui, Neil; #695154, #694993, #695272, #691578, #694321, #695338,
 | 
			
		||||
  #695409, #695458, #695526, #695601, #695471, #695324, #695650, #695656,
 | 
			
		||||
  #695659, #695485, #695395, #694951, #695824, #695841, #695801, #694321,
 | 
			
		||||
  #693708, #695800, #695607, #695882, #691578, #685851, #694371, #690857,
 | 
			
		||||
  #694092, #695747, #696007, #693438, #696064, #696102
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Giovanni Campagna, Cosimo Cecchi, Allan Day, Takao Fujiwara, Adel Gadllah,
 | 
			
		||||
  Tim Lunn, Rui Matos, Florian Müllner, Neil Roberts, Jasper St. Pierre,
 | 
			
		||||
  Ray Strode, Stef Walter, Colin Walters, Owen W. Taylor
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Nilamdyuti Goswami [as], Chao-Hsiung Liao [zh_HK, zh_TW],
 | 
			
		||||
  Yuri Myasoedov [ru], Gheyret Kenji [ug], Baurzhan Muftakhidinov [kk],
 | 
			
		||||
  Ville-Pekka Vainio [fi], Matej Urbančič [sl],
 | 
			
		||||
  Мирослав Николић [sr, sr@latin], Rūdolfs Mazurs [lv], Christian Kirbach [de],
 | 
			
		||||
  Andika Triwidada [id], Gil Forcada [ca], Mattias Põldaru [et],
 | 
			
		||||
  Duarte Loreto [pt], Adam Matoušek [cs], Changwoo Ryu [ko],
 | 
			
		||||
  Ihar Hrachyshka [be], Carles Ferrando [ca@valencia], Sweta Kothari [gu]
 | 
			
		||||
 | 
			
		||||
3.7.91
 | 
			
		||||
======
 | 
			
		||||
* overview: Fade out controls during DND that are not targets [Cosimo; #686984]
 | 
			
		||||
* overview: Keep open when a Control key is held [Florian; #686984]
 | 
			
		||||
* Improve login screen => session transition [Ray; #694321]
 | 
			
		||||
* Center application grid horizontally [Florian; #694261]
 | 
			
		||||
* Fix hiding panel when fullscreen windows span multiple monitors [Adel; 646861]
 | 
			
		||||
* Tweak thresholds of pressure barrier [Jasper; #694467]
 | 
			
		||||
* Tweak window picker layout [Jasper; #694902]
 | 
			
		||||
* Expose key grab DBus API to gnome-settings-daemon [Florian; #643111]
 | 
			
		||||
* Don't always show message tray in overview, add message indicator
 | 
			
		||||
  [Cosimo; #687787]
 | 
			
		||||
* Tweak startup animation [Ray; #694326]
 | 
			
		||||
* Add OSD popups and expose them to gnome-settings-daemon [Florian; #613543]
 | 
			
		||||
* Move loupe icon to the start of the search entry [Jasper; #695069]
 | 
			
		||||
* Don't show the input switcher with less than 2 items [Rui; 695000]
 | 
			
		||||
* Fix auto-completion of system modals immediately upon display [Stef; #692937]
 | 
			
		||||
* Ignore workspaces in alt-tab [Florian; #661156]
 | 
			
		||||
* Disable copying text from password entries [Florian; #695104]
 | 
			
		||||
* Use standard styling for ibus candidate popups [Allan; #694796]
 | 
			
		||||
* Fix calendar changing height on month changes [Giovanni; #641383]
 | 
			
		||||
* Port the hot corner to use pressure barriers [Jasper; #663661]
 | 
			
		||||
* Misc bug fixes and cleanups: [Hashem, Giovanni, Alban, Jasper, Cosimo,
 | 
			
		||||
  Florian, Adel, Daniel, Matthias, Ray, Rui, Guillaume, Stef; #685849, #690643,
 | 
			
		||||
  #694292, #693814, #694234, #694365, #694287, #694336, #694256, #694261,
 | 
			
		||||
  #663601, #694441, #694284, #694463, #694475, #687248, #694394, #694320,
 | 
			
		||||
  #694701, #694784, #694858, #694906, #694327, #694876, #694905, #694969,
 | 
			
		||||
  #694970, #694988, #695006, #695001, #694998, #695023, #695002, #695073,
 | 
			
		||||
  #695126, #687748, #694837, #693907, #679851, #694988]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Giovanni Campagna, Cosimo Cecchi, Matthias Clasen, Alban Crequy, Allan Day,
 | 
			
		||||
  Guillaume Desmottes, Adel Gadllah, Rui Matos, Daniel Mustieles,
 | 
			
		||||
  Hashem Nasarat, Jasper St. Pierre, Ray Strode, Stef Walter
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Yuri Myasoedov [ru], Adam Matoušek [cs], Piotr Drąg [pl], Matej Urbančič [sl],
 | 
			
		||||
  Sweta Kothari [gu], Kjartan Maraas [nb], Nguyễn Thái Ngọc Duy [vi],
 | 
			
		||||
  Chao-Hsiung Liao [zh_HK, zh_TW], Dimitris Spingos [el],
 | 
			
		||||
  Inaki Larranaga Murgoitio [eu], Luca Ferretti [it], A S Alam [pa],
 | 
			
		||||
  Gheyret Kenji [ug], Stas Solovey [ru], Enrico Nicoletto [pt_BR],
 | 
			
		||||
  Fran Diéguez [gl], Daniel Mustieles [es], Aurimas Černius [lt]
 | 
			
		||||
 | 
			
		||||
3.7.90
 | 
			
		||||
======
 | 
			
		||||
* Let GNOME Shell work on EGL and GLES2 [Neil; #693225, #693438, #693339]
 | 
			
		||||
* Implement middle-click paste [Jasper; #645019]
 | 
			
		||||
* Fix top bar transition between session modes [Rui; #692966]
 | 
			
		||||
* Trigger the message tray with downward pressure [Jasper; #677215]
 | 
			
		||||
* Don't ask for a password on shutdown [Adel; #693385]
 | 
			
		||||
* Add a context menu to the message tray [Adel; #691035, #693887]
 | 
			
		||||
* Use unicode formatting in the date menu [Matthias; #689251]
 | 
			
		||||
* Use proper ellipsis instead of three dots [Jeremy; #689542]
 | 
			
		||||
* Tweak screen shield animation [Giovanni; #691964]
 | 
			
		||||
* Always hide the OSK when showing the message tray [Florian; #662687]
 | 
			
		||||
* Support sound in notifications [Giovanni; #642831]
 | 
			
		||||
* Place application popup menus above chrome [Jasper; #633620]
 | 
			
		||||
* Hide overview elements while searching [Cosimo; #682050]
 | 
			
		||||
* Implement updated IBus candidate popup designs [Rui; #691902]
 | 
			
		||||
* Add support for enable-animations preference [Cosimo; #655746]
 | 
			
		||||
* Don't always show the message tray in the overview [Cosimo; #693987]
 | 
			
		||||
* Improve arrangement of window previews [Adel; #690313]
 | 
			
		||||
* Remove builtin settings provider [Giovanni; #690824]
 | 
			
		||||
* Minimize fullscreen windows when they end up in the background [Adel; #693991]
 | 
			
		||||
* Add context menu to the background actor [Jasper; #681540]
 | 
			
		||||
* Handle backgrounds in the shell, improve startup animation [Ray; #682429]
 | 
			
		||||
* Hide universal access menu when not needed [Giovanni; #681528]
 | 
			
		||||
* Implement updated app picker designs [Florian; #694192]
 | 
			
		||||
* Improve login manager -> session transition [Ray; #694062]
 | 
			
		||||
* Don't use a grid layout in window picker [Adel; #694210]
 | 
			
		||||
* Use scroll wheel for workspace switching rather than zoom [Florian; #686639]
 | 
			
		||||
* Misc bug fixes and cleanups: [Jasper, Florian, Debarshi, Adel, Matthias,
 | 
			
		||||
  Giovanni, Daiki, Rico, Bastien, Cosimo, Ray, Allan, Antonio; #693284,
 | 
			
		||||
  #692680, #691746, #693303, #693162, #693161, #693522, #693385, #691715,
 | 
			
		||||
  #688915, #689106, #682429, #693570, #693737, #693458, #692845, 693836,
 | 
			
		||||
  #681540, #679925, #688227, #692773, #693909, #683288, #693854, #693746,
 | 
			
		||||
  #693931, #693924, #693940, #693970, #693935, #693937, #693974, #693936,
 | 
			
		||||
  #693975, #693822, #694030, #685849, #694052, #694035, #694038, #694079,
 | 
			
		||||
  #694064, #681735, #694100, #694057, #694070, #693572, #693896, #686984,
 | 
			
		||||
  #694123, #694125, #693756, #693757, #687556, #694215, 694062, #694227,
 | 
			
		||||
  #694240, #694234, #694264, 694276, 694282, #694241, #689394, #694202,
 | 
			
		||||
  #694265, #694289, #691806, #694290, #694296]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Jeremy Bicha, Giovanni Campagna, Cosimo Cecchi, Matthias Clasen, Allan Day,
 | 
			
		||||
  António Fernandes, Adel Gadllah, Rui Matos, Florian Müllner, Bastien Nocera,
 | 
			
		||||
  Debarshi Ray, Neil Roberts, Jasper St. Pierre, Ray Strode, Rico Tzschichholz,
 | 
			
		||||
  Daiki Ueno
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Yasumichi Akahoshi [ja], Yoji TOYODA [ja], Dušan Kazik [sk],
 | 
			
		||||
  Wouter Bolsterlee [nl], Matej Urbančič [sl], Gheyret Kenji [ug],
 | 
			
		||||
  Ivaylo Valkov [bg], Daniel Korostil [uk], Gheyret Kenji [ug],
 | 
			
		||||
  Daniel Mustieles [es], Anish A [ml], Gil Forcada [ca],
 | 
			
		||||
  Carles Ferrando [ca@valencia], Мирослав Николић [sr, sr@latin],
 | 
			
		||||
  Aurimas Černius [lt], Rafael Ferreira [pt_BR], Fran Diéguez [gl],
 | 
			
		||||
  Piotr Drąg [pl], Luca Ferretti [it], A S Alam [pa]
 | 
			
		||||
 | 
			
		||||
3.7.5
 | 
			
		||||
=====
 | 
			
		||||
* MessageTray: pass keyboard events to tray icons [Giovanni; #687425]
 | 
			
		||||
* network: add support for virtual devices (vlan, bond, bridge) [Dan; #677144]
 | 
			
		||||
* gdm: Allow right-clicking buttons for left-handed users [Jasper; #688748]
 | 
			
		||||
* Make list search results span all available horizontal space [Tanner; #691967]
 | 
			
		||||
* Make Show-Applications button depress when held down [Hashem; #692319]
 | 
			
		||||
* Set a max width on search results [Cosimo; #692453]
 | 
			
		||||
* Reserve scrollbar allocation for automatic policy [Cosimo; #686881]
 | 
			
		||||
* Improve scaling algorithm for window thumbnails [Jasper; #686944]
 | 
			
		||||
* Fix launching settings panels after g-c-c changes [Jasper; #692483]
 | 
			
		||||
* Stop launching applications from empty searches [Hashem; #692391]
 | 
			
		||||
* Implement per-source notification filtering [Giovanni; #685926]
 | 
			
		||||
* ScreenShield: Omit ActiveChanged() signal at end of fade [Giovanni; #691964]
 | 
			
		||||
* ScreenShield: Lower the shield on idle before locking [Giovanni; #692560]
 | 
			
		||||
* Make previews of minimized windows translucent in overview [Florian; #692999]
 | 
			
		||||
* windowManager: Respect icon geometry when minimizing [Florian; #692997]
 | 
			
		||||
* ScreenShield: Only show lock icon when actually locked [Giovanni; #693007]
 | 
			
		||||
* general: Use & instead of 'and' for Settings panels [Jeremy; #689590]
 | 
			
		||||
* network: Add support for new ModemManager1 interface [Aleksander; #687359]
 | 
			
		||||
* network: Handle LTE-only modems as GSM ones [Aleksander; #688144]
 | 
			
		||||
* mobile-providers: Port to libnm-gtk [Aleksander; #688943]
 | 
			
		||||
* general: Consistently use Title Case in top bar [Jeremy; #689589]
 | 
			
		||||
* panel: Add :overview pseudo class while in overview [Florian; #693218]
 | 
			
		||||
* sessionMode: Add support for mode-specific styling [Florian; #693219]
 | 
			
		||||
* loginManager: Make suspend a NOP in the ConsoleKit patch [Florian; #693162]
 | 
			
		||||
* screenShield: Inhibit suspend until the screen is locked [Florian; #686482]
 | 
			
		||||
* Misc bug fixes and cleanups [Jasper, Giovanni, Rui, Cosimo, Florian, Stefano,
 | 
			
		||||
  Adel, Yanko; #691745, #691731, #690171, #689091, #691976, #691963, #684279,
 | 
			
		||||
  #692052, #692091, #642831, #692454, #692715, #692678, #692723, #692677,
 | 
			
		||||
  #683986, #692693, #692749, #692948, #692995, #692996, #692994, #677215,
 | 
			
		||||
  #692586, #693067, #693031, #693049, #643111, #693161, #693220]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Jeremy Bicha, Giovanni Campagna, Cosimo Cecchi, Tanner Doshier,
 | 
			
		||||
  Stefano Facchini, Adel Gadllah, Yanko Kaneti, Rui Matos, Aleksander Morgado,
 | 
			
		||||
  Florian Müllner, Hashem Nasarat, Jasper St. Pierre, Dan Winship
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Duarte Loreto [pt], Daniel Mustieles [es], Kjartan Maraas [nb],
 | 
			
		||||
  Nilamdyuti Goswami [as], Мирослав Николић [sr,sr@latin],
 | 
			
		||||
  Tobias Endrigkeit [de], Fabio Tomat [fur], Matej Urbančič [sl], A S Alam [pa],
 | 
			
		||||
  Inaki Larranaga Murgoitio [eu], Piotr Drąg [pl], Wouter Bolsterlee [nl],
 | 
			
		||||
  Gheyret Kenji [ug], Yaron Shahrabani [he], Chao-Hsiung Liao [zh_HK,zh_TW],
 | 
			
		||||
  Milo Casagrande [it], Benjamin Steinwender [de]
 | 
			
		||||
 | 
			
		||||
3.7.4.1
 | 
			
		||||
=======
 | 
			
		||||
* userMenu: Use show-full-name-in-top-bar setting [Bastien; #689561]
 | 
			
		||||
* dateMenu: Add "Open Clocks" entry [Mathieu; #644390]
 | 
			
		||||
* screenshot: Immediately show the flash spot [Jasper; #691875]
 | 
			
		||||
* Misc. bug fixes [Rico, Jeremy]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Jeremy Bicha, Mathieu Bridon, Bastien Nocera, Jasper St. Pierre,
 | 
			
		||||
  Rico Tzschichholz
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Ihar Hrachyshka [be]
 | 
			
		||||
 | 
			
		||||
3.7.4
 | 
			
		||||
=====
 | 
			
		||||
* Make menu separators crisp [Giovanni, Allan; #641745]
 | 
			
		||||
* power: Update for new D-Bus name [Bastien; #690506]
 | 
			
		||||
* Add smooth scrolling support [Jasper; #687573]
 | 
			
		||||
* Tweak notification layout [Allan; #688506]
 | 
			
		||||
* Ping the active window when using the app menu [Giovanni; #684340]
 | 
			
		||||
* Make password entries insensitive after submission [Jasper; #690594, #690895]
 | 
			
		||||
* Honor lock-delay GSettings key [Giovanni, Matthias; #690766, #691170]
 | 
			
		||||
* Use text/calendar preferred app as the calendar app [Giovanni; #690767]
 | 
			
		||||
* lookingGlass: Move to an inspect() function [Jasper; #690726]
 | 
			
		||||
* Make OSK animation quicker, snappier [Rui; #688642]
 | 
			
		||||
* Allow to close chat notifications with Escape [Jasper; #690897]
 | 
			
		||||
* Honor org.gnome.desktop.screensaver.user-switch-enabled [Giovanni; #691042]
 | 
			
		||||
* Add a SelectArea() DBus method [Cosimo; #687954]
 | 
			
		||||
* Support non-absolute paths when saving screenshots [Cosimo; #688004]
 | 
			
		||||
* OSK: Fix extended keys popups [Rui; #674955]
 | 
			
		||||
* Don't hide or show the keyboard immediately [Rui; #688646]
 | 
			
		||||
* Improve padding in power menu [Giovanni; #689297]
 | 
			
		||||
* Add per-window input source switching [Rui; #691414]
 | 
			
		||||
* Misc bug fixes and cleanups [Rico, Jasper, Giovanni, Rui, Florian, Dan;
 | 
			
		||||
  #690608, #690589, #690539, #687081, #690667, #690665, #690666, #685856,
 | 
			
		||||
  #690858, #690895, #680414, #690965, #691019, #690590, #681376, #690180,
 | 
			
		||||
  #685513, #689263, #691553, #691720, #691743, #691750]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Giovanni Campagna, Cosimo Cecchi, Matthias Clasen, Allan Day, Rui Matos,
 | 
			
		||||
  Florian Müllner, Bastien Nocera, Jasper St. Pierre, Rico Tzschichholz,
 | 
			
		||||
  Dan Winship
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Matej Urbančič [sl], Kjartan Maraas [nb], Mattias Põldaru [et],
 | 
			
		||||
  Yaron Shahrabani [he], Aurimas Černius [lt], Khaled Hosny [ar],
 | 
			
		||||
  Fran Diéguez [gl], Daniel Mustieles [es], Piotr Drąg [pl], Balázs Úr [hu],
 | 
			
		||||
  Baurzhan Muftakhidinov [kk], Tobias Endrigkeit [de], Dušan Kazik [sk],
 | 
			
		||||
  Aron Xu [zh_CN], Gheyret Kenji [ug]
 | 
			
		||||
* Revert 490206b to not depend on NMGTK-0.9.7, which hasn't been released yet
 | 
			
		||||
 | 
			
		||||
3.7.3
 | 
			
		||||
=====
 | 
			
		||||
 
 | 
			
		||||
@@ -17,4 +17,5 @@ libgnome_shell_browser_plugin_la_SOURCES = 	\
 | 
			
		||||
 | 
			
		||||
libgnome_shell_browser_plugin_la_CFLAGS = 	\
 | 
			
		||||
	$(BROWSER_PLUGIN_CFLAGS)		\
 | 
			
		||||
	-DG_DISABLE_DEPRECATED			\
 | 
			
		||||
	-DG_LOG_DOMAIN=\"GnomeShellBrowserPlugin\"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										170
									
								
								configure.ac
									
									
									
									
									
								
							
							
						
						@@ -1,5 +1,5 @@
 | 
			
		||||
AC_PREREQ(2.63)
 | 
			
		||||
AC_INIT([gnome-shell],[3.10.4],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
 | 
			
		||||
AC_INIT([gnome-shell],[3.7.3.1],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
 | 
			
		||||
 | 
			
		||||
AC_CONFIG_HEADERS([config.h])
 | 
			
		||||
AC_CONFIG_SRCDIR([src/shell-global.c])
 | 
			
		||||
@@ -16,6 +16,8 @@ m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
 | 
			
		||||
 | 
			
		||||
# Checks for programs.
 | 
			
		||||
AC_PROG_CC
 | 
			
		||||
# Needed for per-target cflags, like in gnomeshell-taskpanel
 | 
			
		||||
AM_PROG_CC_C_O
 | 
			
		||||
 | 
			
		||||
# Initialize libtool
 | 
			
		||||
LT_PREREQ([2.2.6])
 | 
			
		||||
@@ -24,6 +26,9 @@ LT_INIT([disable-static])
 | 
			
		||||
# i18n
 | 
			
		||||
IT_PROG_INTLTOOL([0.40])
 | 
			
		||||
 | 
			
		||||
AM_GNU_GETTEXT([external])
 | 
			
		||||
AM_GNU_GETTEXT_VERSION([0.17])
 | 
			
		||||
 | 
			
		||||
GETTEXT_PACKAGE=gnome-shell
 | 
			
		||||
AC_SUBST(GETTEXT_PACKAGE)
 | 
			
		||||
AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE",
 | 
			
		||||
@@ -50,73 +55,80 @@ if $PKG_CONFIG --exists gstreamer-1.0 '>=' $GSTREAMER_MIN_VERSION ; then
 | 
			
		||||
   AC_MSG_RESULT(yes)
 | 
			
		||||
   build_recorder=true
 | 
			
		||||
   recorder_modules="gstreamer-1.0 gstreamer-base-1.0 x11 gtk+-3.0"
 | 
			
		||||
   PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-1.0)
 | 
			
		||||
   PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-1.0 xfixes gl)
 | 
			
		||||
else
 | 
			
		||||
   AC_MSG_RESULT(no)
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
 | 
			
		||||
 | 
			
		||||
CLUTTER_MIN_VERSION=1.13.4
 | 
			
		||||
CLUTTER_MIN_VERSION=1.11.11
 | 
			
		||||
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
 | 
			
		||||
GJS_MIN_VERSION=1.38.1
 | 
			
		||||
MUTTER_MIN_VERSION=3.10.4
 | 
			
		||||
GTK_MIN_VERSION=3.7.9
 | 
			
		||||
GIO_MIN_VERSION=2.37.0
 | 
			
		||||
GJS_MIN_VERSION=1.33.2
 | 
			
		||||
MUTTER_MIN_VERSION=3.7.3
 | 
			
		||||
GTK_MIN_VERSION=3.3.9
 | 
			
		||||
GIO_MIN_VERSION=2.35.0
 | 
			
		||||
LIBECAL_MIN_VERSION=3.5.3
 | 
			
		||||
LIBEDATASERVER_MIN_VERSION=3.5.3
 | 
			
		||||
LIBEDATASERVERUI_MIN_VERSION=3.5.3
 | 
			
		||||
TELEPATHY_GLIB_MIN_VERSION=0.17.5
 | 
			
		||||
TELEPATHY_LOGGER_MIN_VERSION=0.2.4
 | 
			
		||||
POLKIT_MIN_VERSION=0.100
 | 
			
		||||
STARTUP_NOTIFICATION_MIN_VERSION=0.11
 | 
			
		||||
GCR_MIN_VERSION=3.7.5
 | 
			
		||||
GNOME_DESKTOP_REQUIRED_VERSION=3.7.90
 | 
			
		||||
GCR_MIN_VERSION=3.3.90
 | 
			
		||||
GNOME_DESKTOP_REQUIRED_VERSION=3.7.1
 | 
			
		||||
GNOME_MENUS_REQUIRED_VERSION=3.5.3
 | 
			
		||||
NETWORKMANAGER_MIN_VERSION=0.9.8
 | 
			
		||||
PULSE_MIN_VERS=2.0
 | 
			
		||||
 | 
			
		||||
# Collect more than 20 libraries for a prize!
 | 
			
		||||
SHARED_PCS="gio-unix-2.0 >= $GIO_MIN_VERSION
 | 
			
		||||
            libxml-2.0
 | 
			
		||||
            gtk+-3.0 >= $GTK_MIN_VERSION
 | 
			
		||||
            atk-bridge-2.0
 | 
			
		||||
            gjs-internals-1.0 >= $GJS_MIN_VERSION
 | 
			
		||||
            libgnome-menu-3.0 >= $GNOME_MENUS_REQUIRED_VERSION
 | 
			
		||||
            $recorder_modules
 | 
			
		||||
            gdk-x11-3.0 libsoup-2.4
 | 
			
		||||
            xtst
 | 
			
		||||
            clutter-x11-1.0 >= $CLUTTER_MIN_VERSION
 | 
			
		||||
            clutter-glx-1.0 >= $CLUTTER_MIN_VERSION
 | 
			
		||||
            libstartup-notification-1.0 >= $STARTUP_NOTIFICATION_MIN_VERSION
 | 
			
		||||
            gobject-introspection-1.0 >= $GOBJECT_INTROSPECTION_MIN_VERSION
 | 
			
		||||
            libcanberra libcanberra-gtk3
 | 
			
		||||
            telepathy-glib >= $TELEPATHY_GLIB_MIN_VERSION
 | 
			
		||||
            polkit-agent-1 >= $POLKIT_MIN_VERSION
 | 
			
		||||
            libnm-glib libnm-util >= $NETWORKMANAGER_MIN_VERSION
 | 
			
		||||
            libnm-gtk >= $NETWORKMANAGER_MIN_VERSION
 | 
			
		||||
            libsecret-unstable gcr-base-3 >= $GCR_MIN_VERSION"
 | 
			
		||||
PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
 | 
			
		||||
			       libxml-2.0
 | 
			
		||||
                               gtk+-3.0 >= $GTK_MIN_VERSION
 | 
			
		||||
                               atk-bridge-2.0
 | 
			
		||||
                               libmutter >= $MUTTER_MIN_VERSION
 | 
			
		||||
                               gjs-internals-1.0 >= $GJS_MIN_VERSION
 | 
			
		||||
			       libgnome-menu-3.0 >= $GNOME_MENUS_REQUIRED_VERSION
 | 
			
		||||
                               $recorder_modules
 | 
			
		||||
                               gdk-x11-3.0 libsoup-2.4
 | 
			
		||||
                               gl
 | 
			
		||||
			       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
 | 
			
		||||
                               telepathy-glib >= $TELEPATHY_GLIB_MIN_VERSION
 | 
			
		||||
                               telepathy-logger-0.2 >= $TELEPATHY_LOGGER_MIN_VERSION
 | 
			
		||||
                               polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes
 | 
			
		||||
                               libnm-glib libnm-util gnome-keyring-1
 | 
			
		||||
                               gcr-3 >= $GCR_MIN_VERSION)
 | 
			
		||||
 | 
			
		||||
PKG_CHECK_MODULES(GNOME_SHELL, $SHARED_PCS)
 | 
			
		||||
PKG_CHECK_MODULES(MUTTER, libmutter >= $MUTTER_MIN_VERSION)
 | 
			
		||||
PKG_CHECK_MODULES(MUTTER_WAYLAND, [libmutter-wayland >= $MUTTER_MIN_VERSION],
 | 
			
		||||
                 [MUTTER_WAYLAND_TYPELIB_DIR=`$PKG_CONFIG --variable=typelibdir libmutter-wayland`
 | 
			
		||||
                  AC_SUBST(MUTTER_WAYLAND_TYPELIB_DIR)
 | 
			
		||||
                  have_mutter_wayland=yes],
 | 
			
		||||
                 [have_mutter_wayland=no])
 | 
			
		||||
PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0)
 | 
			
		||||
 | 
			
		||||
AM_CONDITIONAL(HAVE_MUTTER_WAYLAND, test $have_mutter_wayland != no)
 | 
			
		||||
PKG_CHECK_MODULES(SHELL_HOTPLUG_SNIFFER, gio-2.0 gdk-pixbuf-2.0)
 | 
			
		||||
 | 
			
		||||
PKG_CHECK_MODULES(BROWSER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION json-glib-1.0 >= 0.13.2)
 | 
			
		||||
 | 
			
		||||
GNOME_KEYBINDINGS_KEYSDIR=`$PKG_CONFIG --variable keysdir gnome-keybindings`
 | 
			
		||||
AC_SUBST([GNOME_KEYBINDINGS_KEYSDIR])
 | 
			
		||||
 | 
			
		||||
GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION])
 | 
			
		||||
 | 
			
		||||
saved_CFLAGS=$CFLAGS
 | 
			
		||||
saved_LIBS=$LIBS
 | 
			
		||||
CFLAGS=$GNOME_SHELL_CFLAGS
 | 
			
		||||
LIBS=$GNOME_SHELL_LIBS
 | 
			
		||||
AC_CHECK_FUNCS(JS_NewGlobalObject XFixesCreatePointerBarrier)
 | 
			
		||||
CFLAGS=$saved_CFLAGS
 | 
			
		||||
LIBS=$saved_LIBS
 | 
			
		||||
 | 
			
		||||
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(BROWSER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION json-glib-1.0 >= 0.13.2)
 | 
			
		||||
PKG_CHECK_MODULES(TRAY, 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.7.4)
 | 
			
		||||
PKG_CHECK_MODULES(CARIBOU, caribou-1.0 >= 0.4.8)
 | 
			
		||||
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.7.2.2)
 | 
			
		||||
 | 
			
		||||
AC_MSG_CHECKING([for bluetooth support])
 | 
			
		||||
PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.9.0],
 | 
			
		||||
PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.1.0],
 | 
			
		||||
        [BLUETOOTH_DIR=`$PKG_CONFIG --variable=applet_libdir gnome-bluetooth-1.0`
 | 
			
		||||
	 BLUETOOTH_LIBS=`$PKG_CONFIG --variable=applet_libs gnome-bluetooth-1.0`
 | 
			
		||||
	 AC_SUBST([BLUETOOTH_LIBS],["$BLUETOOTH_LIBS"])
 | 
			
		||||
@@ -133,15 +145,36 @@ PKG_CHECK_MODULES(CALENDAR_SERVER, libecal-1.2 >= $LIBECAL_MIN_VERSION libedatas
 | 
			
		||||
AC_SUBST(CALENDAR_SERVER_CFLAGS)
 | 
			
		||||
AC_SUBST(CALENDAR_SERVER_LIBS)
 | 
			
		||||
 | 
			
		||||
GNOME_KEYBINDINGS_KEYSDIR=`$PKG_CONFIG --variable keysdir gnome-keybindings`
 | 
			
		||||
AC_SUBST([GNOME_KEYBINDINGS_KEYSDIR])
 | 
			
		||||
AC_ARG_WITH(systemd,
 | 
			
		||||
            AS_HELP_STRING([--with-systemd],
 | 
			
		||||
                           [Add systemd support]),
 | 
			
		||||
            [with_systemd=$withval], [with_systemd=auto])
 | 
			
		||||
 | 
			
		||||
GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION])
 | 
			
		||||
PKG_CHECK_MODULES(SYSTEMD,
 | 
			
		||||
                  [libsystemd-login libsystemd-daemon],
 | 
			
		||||
                  [have_systemd=yes], [have_systemd=no])
 | 
			
		||||
 | 
			
		||||
if test "x$with_systemd" = "xauto" ; then
 | 
			
		||||
        if test x$have_systemd = xno ; then
 | 
			
		||||
                use_systemd=no
 | 
			
		||||
        else
 | 
			
		||||
                use_systemd=yes
 | 
			
		||||
        fi
 | 
			
		||||
else
 | 
			
		||||
        use_systemd=$with_systemd
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if test "x$use_systemd" = "xyes"; then
 | 
			
		||||
        if test "x$have_systemd" = "xno"; then
 | 
			
		||||
                AC_MSG_ERROR([Systemd support explicitly required, but systemd not found])
 | 
			
		||||
        fi
 | 
			
		||||
 | 
			
		||||
        AC_DEFINE(WITH_SYSTEMD, 1, [systemd support])
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
MUTTER_GIR_DIR=`$PKG_CONFIG --variable=girdir libmutter`
 | 
			
		||||
AC_SUBST(MUTTER_GIR_DIR)
 | 
			
		||||
 | 
			
		||||
MUTTER_TYPELIB_DIR=`$PKG_CONFIG --variable=typelibdir libmutter`
 | 
			
		||||
AC_SUBST(MUTTER_GIR_DIR)
 | 
			
		||||
AC_SUBST(MUTTER_TYPELIB_DIR)
 | 
			
		||||
 | 
			
		||||
GJS_CONSOLE=`$PKG_CONFIG --variable=gjs_console gjs-1.0`
 | 
			
		||||
@@ -164,6 +197,16 @@ fi
 | 
			
		||||
 | 
			
		||||
# Sets GLIB_GENMARSHAL and GLIB_MKENUMS
 | 
			
		||||
AM_PATH_GLIB_2_0()
 | 
			
		||||
G_IR_SCANNER=`$PKG_CONFIG --variable=g_ir_scanner gobject-introspection-1.0`
 | 
			
		||||
AC_SUBST(G_IR_SCANNER)
 | 
			
		||||
G_IR_COMPILER=`$PKG_CONFIG --variable=g_ir_compiler gobject-introspection-1.0`
 | 
			
		||||
AC_SUBST(G_IR_COMPILER)
 | 
			
		||||
G_IR_GENERATE=`$PKG_CONFIG --variable=g_ir_generate gobject-introspection-1.0`
 | 
			
		||||
AC_SUBST(G_IR_GENERATE)
 | 
			
		||||
GIRDIR=`$PKG_CONFIG --variable=girdir gobject-introspection-1.0`
 | 
			
		||||
AC_SUBST(GIRDIR)
 | 
			
		||||
TYPELIBDIR="$($PKG_CONFIG --variable=typelibdir gobject-introspection-1.0)"
 | 
			
		||||
AC_SUBST(TYPELIBDIR)
 | 
			
		||||
 | 
			
		||||
GTK_DOC_CHECK([1.15], [--flavour no-tmpl])
 | 
			
		||||
 | 
			
		||||
@@ -179,7 +222,36 @@ if test "$enable_man" != no; then
 | 
			
		||||
fi
 | 
			
		||||
AM_CONDITIONAL(ENABLE_MAN, test "$enable_man" != no)
 | 
			
		||||
 | 
			
		||||
GNOME_COMPILE_WARNINGS([error])
 | 
			
		||||
# Stay command-line compatible with the gnome-common configure option. Here
 | 
			
		||||
# minimum/yes/maximum are the same, however.
 | 
			
		||||
AC_ARG_ENABLE(compile_warnings,
 | 
			
		||||
  AS_HELP_STRING([--enable-compile-warnings=@<:@no/minimum/yes/maximum/error@:>@],[Turn on compiler warnings]),,
 | 
			
		||||
  enable_compile_warnings=error)
 | 
			
		||||
 | 
			
		||||
changequote(,)dnl
 | 
			
		||||
if test "$enable_compile_warnings" != no ; then
 | 
			
		||||
  if test "x$GCC" = "xyes"; then
 | 
			
		||||
    case " $CFLAGS " in
 | 
			
		||||
    *[\ \	]-Wall[\ \	]*) ;;
 | 
			
		||||
    *) CFLAGS="$CFLAGS -Wall" ;;
 | 
			
		||||
    esac
 | 
			
		||||
    case " $CFLAGS " in
 | 
			
		||||
    *[\ \	]-Wmissing-prototypes[\ \	]*) ;;
 | 
			
		||||
    *) CFLAGS="$CFLAGS -Wmissing-prototypes" ;;
 | 
			
		||||
    esac
 | 
			
		||||
    if test "$enable_compile_warnings" = error ; then
 | 
			
		||||
      case " $CFLAGS " in
 | 
			
		||||
      *[\ \	]-Werror[\ \	]*) ;;
 | 
			
		||||
      *) CFLAGS="$CFLAGS -Werror -Wno-error=deprecated-declarations" ;;
 | 
			
		||||
      esac
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
fi
 | 
			
		||||
changequote([,])dnl
 | 
			
		||||
 | 
			
		||||
AC_ARG_ENABLE(jhbuild-wrapper-script,
 | 
			
		||||
  AS_HELP_STRING([--enable-jhbuild-wrapper-script],[Make "gnome-shell" script work for jhbuild]),,enable_jhbuild_wrapper_script=no)
 | 
			
		||||
AM_CONDITIONAL(USE_JHBUILD_WRAPPER_SCRIPT, test "x$enable_jhbuild_wrapper_script" = xyes)
 | 
			
		||||
 | 
			
		||||
BROWSER_PLUGIN_DIR="${BROWSER_PLUGIN_DIR:-"\${libdir}/mozilla/plugins"}"
 | 
			
		||||
AC_ARG_VAR([BROWSER_PLUGIN_DIR],[Where to install the plugin to])
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12
									
								
								data/50-gnome-shell-screenshot.xml.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,12 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" ?>
 | 
			
		||||
<KeyListEntries schema="org.gnome.shell.keybindings"
 | 
			
		||||
                group="system"
 | 
			
		||||
                _name="Screenshots"
 | 
			
		||||
                wm_name="GNOME Shell"
 | 
			
		||||
                package="gnome-shell">
 | 
			
		||||
 | 
			
		||||
	<KeyListEntry name="toggle-recording"
 | 
			
		||||
                      _description="Record a screencast"/>
 | 
			
		||||
 | 
			
		||||
</KeyListEntries>
 | 
			
		||||
 | 
			
		||||
@@ -11,9 +11,6 @@
 | 
			
		||||
	<KeyListEntry name="focus-active-notification"
 | 
			
		||||
                      _description="Focus the active notification"/>
 | 
			
		||||
 | 
			
		||||
	<KeyListEntry name="toggle-overview"
 | 
			
		||||
                      _description="Show the overview"/>
 | 
			
		||||
 | 
			
		||||
	<KeyListEntry name="toggle-application-view"
 | 
			
		||||
                      _description="Show all applications"/>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +1,5 @@
 | 
			
		||||
wandadir = $(pkgdatadir)
 | 
			
		||||
dist_wanda_DATA = wanda.png
 | 
			
		||||
 | 
			
		||||
desktopdir=$(datadir)/applications
 | 
			
		||||
desktop_DATA = gnome-shell.desktop gnome-shell-extension-prefs.desktop
 | 
			
		||||
if HAVE_MUTTER_WAYLAND
 | 
			
		||||
desktop_DATA += gnome-shell-wayland.desktop
 | 
			
		||||
endif HAVE_MUTTER_WAYLAND
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# We substitute in bindir so it works as an autostart
 | 
			
		||||
# file when built in a non-system prefix
 | 
			
		||||
@@ -19,8 +12,6 @@ endif HAVE_MUTTER_WAYLAND
 | 
			
		||||
 | 
			
		||||
introspectiondir = $(datadir)/dbus-1/interfaces
 | 
			
		||||
introspection_DATA =				\
 | 
			
		||||
	org.gnome.Shell.Screencast.xml		\
 | 
			
		||||
	org.gnome.Shell.Screenshot.xml		\
 | 
			
		||||
	org.gnome.ShellSearchProvider.xml	\
 | 
			
		||||
	org.gnome.ShellSearchProvider2.xml
 | 
			
		||||
 | 
			
		||||
@@ -45,10 +36,6 @@ dist_theme_DATA =				\
 | 
			
		||||
	theme/message-tray-background.png	\
 | 
			
		||||
	theme/more-results.svg			\
 | 
			
		||||
	theme/noise-texture.png			\
 | 
			
		||||
	theme/page-indicator-active.svg		\
 | 
			
		||||
	theme/page-indicator-inactive.svg	\
 | 
			
		||||
	theme/page-indicator-checked.svg	\
 | 
			
		||||
	theme/page-indicator-hover.svg		\
 | 
			
		||||
	theme/panel-button-border.svg		\
 | 
			
		||||
	theme/panel-button-highlight-narrow.svg	\
 | 
			
		||||
	theme/panel-button-highlight-wide.svg	\
 | 
			
		||||
@@ -64,7 +51,10 @@ dist_theme_DATA =				\
 | 
			
		||||
	theme/ws-switch-arrow-down.png
 | 
			
		||||
 | 
			
		||||
keysdir = @GNOME_KEYBINDINGS_KEYSDIR@
 | 
			
		||||
keys_in_files = 50-gnome-shell-system.xml.in
 | 
			
		||||
keys_in_files =					\
 | 
			
		||||
	50-gnome-shell-screenshot.xml.in	\
 | 
			
		||||
	50-gnome-shell-system.xml.in		\
 | 
			
		||||
	$(NULL)
 | 
			
		||||
keys_DATA = $(keys_in_files:.xml.in=.xml)
 | 
			
		||||
 | 
			
		||||
gsettings_SCHEMAS = org.gnome.shell.gschema.xml
 | 
			
		||||
@@ -89,7 +79,6 @@ convert_DATA = gnome-shell-overrides.convert
 | 
			
		||||
 | 
			
		||||
EXTRA_DIST =						\
 | 
			
		||||
	gnome-shell.desktop.in.in			\
 | 
			
		||||
	gnome-shell-wayland.desktop.in.in		\
 | 
			
		||||
	gnome-shell-extension-prefs.desktop.in.in	\
 | 
			
		||||
	$(introspection_DATA)				\
 | 
			
		||||
	$(menu_DATA)					\
 | 
			
		||||
@@ -99,7 +88,6 @@ EXTRA_DIST =						\
 | 
			
		||||
 | 
			
		||||
CLEANFILES =						\
 | 
			
		||||
	gnome-shell.desktop.in				\
 | 
			
		||||
	gnome-shell-wayland.desktop.in			\
 | 
			
		||||
	gnome-shell-extension-prefs.in			\
 | 
			
		||||
	$(desktop_DATA)					\
 | 
			
		||||
	$(keys_DATA)					\
 | 
			
		||||
 
 | 
			
		||||
@@ -1,15 +0,0 @@
 | 
			
		||||
[Desktop Entry]
 | 
			
		||||
Type=Application
 | 
			
		||||
_Name=GNOME Shell (wayland compositor)
 | 
			
		||||
_Comment=Window management and application launching
 | 
			
		||||
Exec=@bindir@/mutter-launch -- gnome-shell-wayland --wayland
 | 
			
		||||
X-GNOME-Bugzilla-Bugzilla=GNOME
 | 
			
		||||
X-GNOME-Bugzilla-Product=gnome-shell
 | 
			
		||||
X-GNOME-Bugzilla-Component=general
 | 
			
		||||
X-GNOME-Bugzilla-Version=@VERSION@
 | 
			
		||||
Categories=GNOME;GTK;Core;
 | 
			
		||||
OnlyShowIn=GNOME;
 | 
			
		||||
NoDisplay=true
 | 
			
		||||
X-GNOME-Autostart-Phase=DisplayServer
 | 
			
		||||
X-GNOME-Autostart-Notify=true
 | 
			
		||||
X-GNOME-AutoRestart=false
 | 
			
		||||
@@ -1,96 +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.Screencast:
 | 
			
		||||
      @short_description: Screencast interface
 | 
			
		||||
 | 
			
		||||
      The interface used to record screen contents.
 | 
			
		||||
  -->
 | 
			
		||||
  <interface name="org.gnome.Shell.Screencast">
 | 
			
		||||
 | 
			
		||||
    <!--
 | 
			
		||||
        Screencast:
 | 
			
		||||
        @file_template: the template for the filename to use
 | 
			
		||||
        @options: a dictionary of optional parameters
 | 
			
		||||
        @success: whether the screencast was started successfully
 | 
			
		||||
        @filename_used: the file where the screencast is being saved
 | 
			
		||||
 | 
			
		||||
        Records a screencast of the whole screen and saves it
 | 
			
		||||
        (by default) as webm video under a filename derived from
 | 
			
		||||
        @file_template. The template is either a relative or absolute
 | 
			
		||||
        filename which may contain some escape sequences - %d and %t
 | 
			
		||||
        will be replaced by the start date and time of the recording.
 | 
			
		||||
        If a relative name is used, the screencast will be saved in the
 | 
			
		||||
        $XDG_VIDEOS_DIR if it exists, or the home directory otherwise.
 | 
			
		||||
        The actual filename of the saved video is returned in @filename_used.
 | 
			
		||||
        The set of optional parameters in @options currently consists of:
 | 
			
		||||
            'draw-cursor'(b): whether the cursor should be included in the
 | 
			
		||||
                              recording (true)
 | 
			
		||||
            'framerate'(i): the number of frames per second that should be
 | 
			
		||||
                            recorded if possible (30)
 | 
			
		||||
            'pipeline'(s): the GStreamer pipeline used to encode recordings
 | 
			
		||||
                           in gst-launch format; if not specified, the
 | 
			
		||||
                           recorder will produce vp8 (webm) video (unset)
 | 
			
		||||
    -->
 | 
			
		||||
    <method name="Screencast">
 | 
			
		||||
      <arg type="s" direction="in" name="file_template"/>
 | 
			
		||||
      <arg type="a{sv}" direction="in" name="options"/>
 | 
			
		||||
      <arg type="b" direction="in" name="flash"/>
 | 
			
		||||
      <arg type="b" direction="out" name="success"/>
 | 
			
		||||
      <arg type="s" direction="out" name="filename_used"/>
 | 
			
		||||
    </method>
 | 
			
		||||
 | 
			
		||||
    <!--
 | 
			
		||||
        ScreencastArea:
 | 
			
		||||
        @x: the X coordinate of the area to capture
 | 
			
		||||
        @y: the Y coordinate of the area to capture
 | 
			
		||||
        @width: the width of the area to capture
 | 
			
		||||
        @height: the height of the area to capture
 | 
			
		||||
        @file_template: the template for the filename to use
 | 
			
		||||
        @options: a dictionary of optional parameters
 | 
			
		||||
        @success: whether the screencast was started successfully
 | 
			
		||||
        @filename_used: the file where the screencast is being saved
 | 
			
		||||
 | 
			
		||||
        Records a screencast of the passed in area and saves it
 | 
			
		||||
        (by default) as webm video under a filename derived from
 | 
			
		||||
        @file_template. The template is either a relative or absolute
 | 
			
		||||
        filename which may contain some escape sequences - %d and %t
 | 
			
		||||
        will be replaced by the start date and time of the recording.
 | 
			
		||||
        If a relative name is used, the screencast will be saved in the
 | 
			
		||||
        $XDG_VIDEOS_DIR if it exists, or the home directory otherwise.
 | 
			
		||||
        The actual filename of the saved video is returned in @filename_used.
 | 
			
		||||
        The set of optional parameters in @options currently consists of:
 | 
			
		||||
            'draw-cursor'(b): whether the cursor should be included in the
 | 
			
		||||
                              recording (true)
 | 
			
		||||
            'framerate'(i): the number of frames per second that should be
 | 
			
		||||
                            recorded if possible (30)
 | 
			
		||||
            'pipeline'(s): the GStreamer pipeline used to encode recordings
 | 
			
		||||
                           in gst-launch format; if not specified, the
 | 
			
		||||
                           recorder will produce vp8 (webm) video (unset)
 | 
			
		||||
    -->
 | 
			
		||||
    <method name="ScreencastArea">
 | 
			
		||||
      <arg type="i" direction="in" name="x"/>
 | 
			
		||||
      <arg type="i" direction="in" name="y"/>
 | 
			
		||||
      <arg type="i" direction="in" name="width"/>
 | 
			
		||||
      <arg type="i" direction="in" name="height"/>
 | 
			
		||||
      <arg type="s" direction="in" name="file_template"/>
 | 
			
		||||
      <arg type="a{sv}" direction="in" name="options"/>
 | 
			
		||||
      <arg type="b" direction="out" name="success"/>
 | 
			
		||||
      <arg type="s" direction="out" name="filename_used"/>
 | 
			
		||||
    </method>
 | 
			
		||||
 | 
			
		||||
    <!--
 | 
			
		||||
        StopScreencast:
 | 
			
		||||
        @success: whether stopping the recording was successful
 | 
			
		||||
 | 
			
		||||
        Stop the recording started by either Screencast or ScreencastArea.
 | 
			
		||||
    -->
 | 
			
		||||
    <method name="StopScreencast">
 | 
			
		||||
      <arg type="b" direction="out" name="success"/>
 | 
			
		||||
    </method>
 | 
			
		||||
 | 
			
		||||
  </interface>
 | 
			
		||||
</node>
 | 
			
		||||
@@ -1,128 +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.Screenshot:
 | 
			
		||||
      @short_description: Screenshot interface
 | 
			
		||||
 | 
			
		||||
      The interface used to capture pictures of the screen contents.
 | 
			
		||||
  -->
 | 
			
		||||
  <interface name="org.gnome.Shell.Screenshot">
 | 
			
		||||
 | 
			
		||||
    <!--
 | 
			
		||||
        Screenshot:
 | 
			
		||||
        @filename: The filename for the screenshot
 | 
			
		||||
        @include_cursor: Whether to include the cursor image or not
 | 
			
		||||
        @flash: Whether to flash the screen or not
 | 
			
		||||
        @success: whether the screenshot was captured
 | 
			
		||||
        @filename_used: the file where the screenshot was saved
 | 
			
		||||
 | 
			
		||||
        Takes a screenshot of the whole screen and saves it
 | 
			
		||||
        in @filename as png image, it returns a boolean
 | 
			
		||||
        indicating whether the operation was successful or not.
 | 
			
		||||
        @filename can either be an absolute path or a basename, in
 | 
			
		||||
        which case the screenshot will be saved in the $XDG_PICTURES_DIR
 | 
			
		||||
        or the home directory if it doesn't exist. The filename used
 | 
			
		||||
        to save the screenshot will be returned in @filename_used.
 | 
			
		||||
    -->
 | 
			
		||||
    <method name="Screenshot">
 | 
			
		||||
      <arg type="b" direction="in" name="include_cursor"/>
 | 
			
		||||
      <arg type="b" direction="in" name="flash"/>
 | 
			
		||||
      <arg type="s" direction="in" name="filename"/>
 | 
			
		||||
      <arg type="b" direction="out" name="success"/>
 | 
			
		||||
      <arg type="s" direction="out" name="filename_used"/>
 | 
			
		||||
    </method>
 | 
			
		||||
 | 
			
		||||
    <!--
 | 
			
		||||
        ScreenshotWindow:
 | 
			
		||||
        @include_frame: Whether to include the frame or not
 | 
			
		||||
        @include_cursor: Whether to include the cursor image or not
 | 
			
		||||
        @flash: Whether to flash the window area or not
 | 
			
		||||
        @filename: The filename for the screenshot
 | 
			
		||||
        @success: whether the screenshot was captured
 | 
			
		||||
        @filename_used: the file where the screenshot was saved
 | 
			
		||||
 | 
			
		||||
        Takes a screenshot of the focused window (optionally omitting the frame)
 | 
			
		||||
        and saves it in @filename as png image, it returns a boolean
 | 
			
		||||
        indicating whether the operation was successful or not.
 | 
			
		||||
        @filename can either be an absolute path or a basename, in
 | 
			
		||||
        which case the screenshot will be saved in the $XDG_PICTURES_DIR
 | 
			
		||||
        or the home directory if it doesn't exist. The filename used
 | 
			
		||||
        to save the screenshot will be returned in @filename_used.
 | 
			
		||||
    -->
 | 
			
		||||
    <method name="ScreenshotWindow">
 | 
			
		||||
      <arg type="b" direction="in" name="include_frame"/>
 | 
			
		||||
      <arg type="b" direction="in" name="include_cursor"/>
 | 
			
		||||
      <arg type="b" direction="in" name="flash"/>
 | 
			
		||||
      <arg type="s" direction="in" name="filename"/>
 | 
			
		||||
      <arg type="b" direction="out" name="success"/>
 | 
			
		||||
      <arg type="s" direction="out" name="filename_used"/>
 | 
			
		||||
    </method>
 | 
			
		||||
 | 
			
		||||
    <!--
 | 
			
		||||
        ScreenshotArea:
 | 
			
		||||
        @x: the X coordinate of the area to capture
 | 
			
		||||
        @y: the Y coordinate of the area to capture
 | 
			
		||||
        @width: the width of the area to capture
 | 
			
		||||
        @height: the height of the area to capture
 | 
			
		||||
        @flash: whether to flash the area or not
 | 
			
		||||
        @filename: the filename for the screenshot
 | 
			
		||||
        @success: whether the screenshot was captured
 | 
			
		||||
        @filename_used: the file where the screenshot was saved
 | 
			
		||||
 | 
			
		||||
        Takes a screenshot of the passed in area and saves it
 | 
			
		||||
        in @filename as png image, it returns a boolean
 | 
			
		||||
        indicating whether the operation was successful or not.
 | 
			
		||||
        @filename can either be an absolute path or a basename, in
 | 
			
		||||
        which case the screenshot will be saved in the $XDG_PICTURES_DIR
 | 
			
		||||
        or the home directory if it doesn't exist. The filename used
 | 
			
		||||
        to save the screenshot will be returned in @filename_used.
 | 
			
		||||
    -->
 | 
			
		||||
    <method name="ScreenshotArea">
 | 
			
		||||
      <arg type="i" direction="in" name="x"/>
 | 
			
		||||
      <arg type="i" direction="in" name="y"/>
 | 
			
		||||
      <arg type="i" direction="in" name="width"/>
 | 
			
		||||
      <arg type="i" direction="in" name="height"/>
 | 
			
		||||
      <arg type="b" direction="in" name="flash"/>
 | 
			
		||||
      <arg type="s" direction="in" name="filename"/>
 | 
			
		||||
      <arg type="b" direction="out" name="success"/>
 | 
			
		||||
      <arg type="s" direction="out" name="filename_used"/>
 | 
			
		||||
    </method>
 | 
			
		||||
 | 
			
		||||
    <!--
 | 
			
		||||
        FlashArea:
 | 
			
		||||
        @x: the X coordinate of the area to flash
 | 
			
		||||
        @y: the Y coordinate of the area to flash
 | 
			
		||||
        @width: the width of the area to flash
 | 
			
		||||
        @height: the height of the area to flash
 | 
			
		||||
 | 
			
		||||
        Renders a flash spot effect in the specified rectangle of the screen.
 | 
			
		||||
    -->
 | 
			
		||||
    <method name="FlashArea">
 | 
			
		||||
      <arg type="i" direction="in" name="x"/>
 | 
			
		||||
      <arg type="i" direction="in" name="y"/>
 | 
			
		||||
      <arg type="i" direction="in" name="width"/>
 | 
			
		||||
      <arg type="i" direction="in" name="height"/>
 | 
			
		||||
    </method>
 | 
			
		||||
 | 
			
		||||
    <!--
 | 
			
		||||
        SelectArea:
 | 
			
		||||
        @x: the X coordinate of the selected area
 | 
			
		||||
        @y: the Y coordinate of the selected area
 | 
			
		||||
        @width: the width of the selected area
 | 
			
		||||
        @height: the height of the selected area
 | 
			
		||||
 | 
			
		||||
        Interactively allows the user to select a rectangular area of
 | 
			
		||||
        the screen, and returns its coordinates.
 | 
			
		||||
    -->
 | 
			
		||||
    <method name="SelectArea">
 | 
			
		||||
      <arg type="i" direction="out" name="x"/>
 | 
			
		||||
      <arg type="i" direction="out" name="y"/>
 | 
			
		||||
      <arg type="i" direction="out" name="width"/>
 | 
			
		||||
      <arg type="i" direction="out" name="height"/>
 | 
			
		||||
    </method>
 | 
			
		||||
 | 
			
		||||
  </interface>
 | 
			
		||||
</node>
 | 
			
		||||
@@ -46,7 +46,7 @@
 | 
			
		||||
    <!--
 | 
			
		||||
        GetResultMetas:
 | 
			
		||||
        @identifiers: An array of result identifiers as returned by GetInitialResultSet() or GetSubsearchResultSet()
 | 
			
		||||
        @metas: A dictionary describing the given search result, containing a human-readable 'name' (string), along with the result identifier this meta is for, 'id' (string). Optionally, either 'gicon' (a serialized GIcon) or 'icon-data' (raw image data as (iiibiiay) - width, height, rowstride, has-alpha, bits per sample, channels, data) can be specified if the result can be better served with a thumbnail of the content (such as with images). A 'description' field (string) may also be specified if more context would help the user find the desired result.
 | 
			
		||||
        @metas: A dictionary describing the given search result, containing 'id' and 'name' (both strings). Optionally, either 'gicon' (a serialized GIcon) or 'icon-data' (raw image data as (iiibiiay) - width, height, rowstride, has-alpha, bits per sample, channels, data) can be specified if the result can be better served with a thumbnail of the content (such as with images).
 | 
			
		||||
 | 
			
		||||
        Return an array of meta data used to display each given result
 | 
			
		||||
    -->
 | 
			
		||||
 
 | 
			
		||||
@@ -46,7 +46,7 @@
 | 
			
		||||
    <!--
 | 
			
		||||
        GetResultMetas:
 | 
			
		||||
        @identifiers: An array of result identifiers as returned by GetInitialResultSet() or GetSubsearchResultSet()
 | 
			
		||||
        @metas: A dictionary describing the given search result, containing a human-readable 'name' (string), along with the result identifier this meta is for, 'id' (string). Optionally, 'icon' (a serialized GIcon as obtained by g_icon_serialize) can be specified if the result can be better served with a thumbnail of the content (such as with images). 'gicon' (a serialized GIcon as obtained by g_icon_to_string) or 'icon-data' (raw image data as (iiibiiay) - width, height, rowstride, has-alpha, bits per sample, channels, data) are deprecated values that can also be used for that purpose. A 'description' field (string) may also be specified if more context would help the user find the desired result.
 | 
			
		||||
        @metas: A dictionary describing the given search result, containing 'id' and 'name' (both strings). Optionally, either 'gicon' (a serialized GIcon) or 'icon-data' (raw image data as (iiibiiay) - width, height, rowstride, has-alpha, bits per sample, channels, data) can be specified if the result can be better served with a thumbnail of the content (such as with images). A 'description' field (string) may also be specified if more context would help the user find the desired result.
 | 
			
		||||
 | 
			
		||||
        Return an array of meta data used to display each given result
 | 
			
		||||
    -->
 | 
			
		||||
 
 | 
			
		||||
@@ -21,6 +21,16 @@
 | 
			
		||||
        EnableExtension and DisableExtension DBus methods on org.gnome.Shell.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="enable-app-monitoring" type="b">
 | 
			
		||||
      <default>true</default>
 | 
			
		||||
      <_summary>Whether to collect stats about applications usage</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        The shell normally monitors active applications in order to present
 | 
			
		||||
        the most used ones (e.g. in launchers). While this data will be
 | 
			
		||||
        kept private, you may want to disable this for privacy reasons.
 | 
			
		||||
        Please note that doing so won't remove already saved data.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="favorite-apps" type="as">
 | 
			
		||||
      <default>[ 'epiphany.desktop', 'evolution.desktop', 'empathy.desktop', 'rhythmbox.desktop', 'shotwell.desktop', 'libreoffice-writer.desktop', 'nautilus.desktop', 'gnome-documents.desktop' ]</default>
 | 
			
		||||
      <_summary>List of desktop file IDs for favorite applications</_summary>
 | 
			
		||||
@@ -29,21 +39,6 @@
 | 
			
		||||
        will be displayed in the favorites area.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="app-folder-categories" type="as">
 | 
			
		||||
      <default>[ 'Utilities', 'Sundry' ]</default>
 | 
			
		||||
      <_summary>List of categories that should be displayed as folders</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        Each category name in this list will be represented as folder in the
 | 
			
		||||
        application view, rather than being displayed inline in the main view.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="app-picker-view" type="u">
 | 
			
		||||
      <default>0</default>
 | 
			
		||||
      <summary>App Picker View</summary>
 | 
			
		||||
      <description>
 | 
			
		||||
        Index of the currently selected view in the application picker.
 | 
			
		||||
      </description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="command-history" type="as">
 | 
			
		||||
      <default>[]</default>
 | 
			
		||||
      <_summary>History for command (Alt-F2) dialog</_summary>
 | 
			
		||||
@@ -52,6 +47,16 @@
 | 
			
		||||
      <default>[]</default>
 | 
			
		||||
      <_summary>History for the looking glass dialog</_summary>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="saved-im-presence" type="i">
 | 
			
		||||
      <default>1</default>
 | 
			
		||||
      <_summary>Internally used to store the last IM presence explicitly set by the user. The
 | 
			
		||||
value here is from the TpConnectionPresenceType enumeration.</_summary>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="saved-session-presence" type="i">
 | 
			
		||||
      <default>0</default>
 | 
			
		||||
      <_summary>Internally used to store the last session presence status for the user. The
 | 
			
		||||
value here is from the GsmPresenceStatus enumeration.</_summary>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="always-show-log-out" type="b">
 | 
			
		||||
      <default>false</default>
 | 
			
		||||
      <_summary>Always show the 'Log out' menuitem in the user menu.</_summary>
 | 
			
		||||
@@ -60,6 +65,11 @@
 | 
			
		||||
        menuitem in single-user, single-session situations.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="show-full-name" type="b">
 | 
			
		||||
      <default>true</default>
 | 
			
		||||
      <_summary>Show full name in the user menu</_summary>
 | 
			
		||||
      <_description>Whether the users full name is shown in the user menu or not.</_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="remember-mount-password" type="b">
 | 
			
		||||
      <default>false</default>
 | 
			
		||||
      <_summary>Whether to remember password for mounting encrypted or remote filesystems</_summary>
 | 
			
		||||
@@ -71,6 +81,7 @@
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <child name="calendar" schema="org.gnome.shell.calendar"/>
 | 
			
		||||
    <child name="recorder" schema="org.gnome.shell.recorder"/>
 | 
			
		||||
    <child name="keybindings" schema="org.gnome.shell.keybindings"/>
 | 
			
		||||
    <child name="keyboard" schema="org.gnome.shell.keyboard"/>
 | 
			
		||||
  </schema>
 | 
			
		||||
@@ -103,13 +114,6 @@
 | 
			
		||||
        Overview.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="toggle-overview" type="as">
 | 
			
		||||
      <default>["<Super>s"]</default>
 | 
			
		||||
      <_summary>Keybinding to open the overview</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        Keybinding to open the Activities Overview.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="toggle-message-tray" type="as">
 | 
			
		||||
      <default>["<Super>m"]</default>
 | 
			
		||||
      <_summary>Keybinding to toggle the visibility of the message tray</_summary>
 | 
			
		||||
@@ -124,6 +128,13 @@
 | 
			
		||||
        Keybinding to focus the active notification.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="toggle-recording" type="as">
 | 
			
		||||
      <default><![CDATA[['<Control><Shift><Alt>r']]]></default>
 | 
			
		||||
      <_summary>Keybinding to toggle the screen recorder</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        Keybinding to start/stop the builtin screen recorder.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
  </schema>
 | 
			
		||||
 | 
			
		||||
  <schema id="org.gnome.shell.keyboard" path="/org/gnome/shell/keyboard/"
 | 
			
		||||
@@ -137,16 +148,41 @@
 | 
			
		||||
    </key>
 | 
			
		||||
  </schema>
 | 
			
		||||
 | 
			
		||||
  <schema id="org.gnome.shell.app-switcher"
 | 
			
		||||
          path="/org/gnome/shell/app-switcher/"
 | 
			
		||||
  <schema id="org.gnome.shell.recorder" path="/org/gnome/shell/recorder/"
 | 
			
		||||
          gettext-domain="@GETTEXT_PACKAGE@">
 | 
			
		||||
    <key type="b" name="current-workspace-only">
 | 
			
		||||
      <default>false</default>
 | 
			
		||||
      <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>
 | 
			
		||||
    <key name="framerate" type="i">
 | 
			
		||||
      <default>30</default>
 | 
			
		||||
      <_summary>Framerate used for recording screencasts.</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        The framerate of the resulting screencast recordered
 | 
			
		||||
        by GNOME Shell's screencast recorder in frames-per-second.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="pipeline" type="s">
 | 
			
		||||
      <default>''</default>
 | 
			
		||||
      <_summary>The gstreamer pipeline used to encode the screencast</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        Sets the GStreamer pipeline used to encode recordings.
 | 
			
		||||
        It follows the syntax used for gst-launch. The pipeline should have
 | 
			
		||||
        an unconnected sink pad where the recorded video is recorded. It will
 | 
			
		||||
        normally have a unconnected source pad; output from that pad
 | 
			
		||||
        will be written into the output file. However the pipeline can also
 | 
			
		||||
        take care of its own output - this might be used to send the output
 | 
			
		||||
        to an icecast server via shout2send or similar. When unset or set
 | 
			
		||||
        to an empty value, the default pipeline will be used. This is currently
 | 
			
		||||
        'vp8enc min_quantizer=13 max_quantizer=13 cpu-used=5 deadline=1000000 threads=%T ! queue ! webmmux'
 | 
			
		||||
        and records to WEBM using the VP8 codec. %T is used as a placeholder
 | 
			
		||||
        for a guess at the optimal thread count on the system.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="file-extension" type="s">
 | 
			
		||||
      <default>'webm'</default>
 | 
			
		||||
      <_summary>File extension used for storing the screencast</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        The filename for recorded screencasts will be a unique filename
 | 
			
		||||
        based on the current date, and use this extension. It should be
 | 
			
		||||
        changed when recording to a different container format.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
  </schema>
 | 
			
		||||
 | 
			
		||||
@@ -168,7 +204,7 @@
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key type="b" name="current-workspace-only">
 | 
			
		||||
      <default>true</default>
 | 
			
		||||
      <default>false</default>
 | 
			
		||||
      <summary>Limit switcher to current workspace.</summary>
 | 
			
		||||
      <description>
 | 
			
		||||
	If true, only windows from the current workspace are shown in the switcher.
 | 
			
		||||
@@ -223,10 +259,10 @@
 | 
			
		||||
 | 
			
		||||
    <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>
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,7 @@
 | 
			
		||||
   height="16"
 | 
			
		||||
   id="svg12430"
 | 
			
		||||
   version="1.1"
 | 
			
		||||
   inkscape:version="0.48.4 r9939"
 | 
			
		||||
   inkscape:version="0.48.3.1 r9886"
 | 
			
		||||
   sodipodi:docname="more-results.svg">
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs12432" />
 | 
			
		||||
@@ -25,18 +25,18 @@
 | 
			
		||||
     borderopacity="1.0"
 | 
			
		||||
     inkscape:pageopacity="1"
 | 
			
		||||
     inkscape:pageshadow="2"
 | 
			
		||||
     inkscape:zoom="90.509668"
 | 
			
		||||
     inkscape:cx="6.5009792"
 | 
			
		||||
     inkscape:cy="8.3589595"
 | 
			
		||||
     inkscape:zoom="1"
 | 
			
		||||
     inkscape:cx="8.3155237"
 | 
			
		||||
     inkscape:cy="0.89548874"
 | 
			
		||||
     inkscape:document-units="px"
 | 
			
		||||
     inkscape:current-layer="g14642-3-0"
 | 
			
		||||
     showgrid="false"
 | 
			
		||||
     borderlayer="true"
 | 
			
		||||
     inkscape:showpageshadow="false"
 | 
			
		||||
     inkscape:window-width="1440"
 | 
			
		||||
     inkscape:window-height="840"
 | 
			
		||||
     inkscape:window-x="0"
 | 
			
		||||
     inkscape:window-y="27"
 | 
			
		||||
     inkscape:window-width="2560"
 | 
			
		||||
     inkscape:window-height="1376"
 | 
			
		||||
     inkscape:window-x="1200"
 | 
			
		||||
     inkscape:window-y="187"
 | 
			
		||||
     inkscape:window-maximized="1">
 | 
			
		||||
    <inkscape:grid
 | 
			
		||||
       type="xygrid"
 | 
			
		||||
@@ -90,11 +90,6 @@
 | 
			
		||||
         transform="translate(-2,0)"
 | 
			
		||||
         width="16"
 | 
			
		||||
         height="16" />
 | 
			
		||||
      <path
 | 
			
		||||
         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
 | 
			
		||||
         d="M 7 5 L 7 7 L 5 7 L 5 9 L 7 9 L 7 11 L 9 11 L 9 9 L 11 9 L 11 7 L 9 7 L 9 5 L 7 5 z "
 | 
			
		||||
         transform="translate(141.99984,397.99107)"
 | 
			
		||||
         id="rect3757" />
 | 
			
		||||
      <path
 | 
			
		||||
         inkscape:connector-curvature="0"
 | 
			
		||||
         style="color:#bebebe;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible"
 | 
			
		||||
 
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.1 KiB  | 
@@ -1,71 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
 | 
			
		||||
 | 
			
		||||
<svg
 | 
			
		||||
   xmlns:dc="http://purl.org/dc/elements/1.1/"
 | 
			
		||||
   xmlns:cc="http://creativecommons.org/ns#"
 | 
			
		||||
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 | 
			
		||||
   xmlns:svg="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 | 
			
		||||
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
 | 
			
		||||
   width="18"
 | 
			
		||||
   height="18"
 | 
			
		||||
   id="svg4703"
 | 
			
		||||
   version="1.1"
 | 
			
		||||
   inkscape:version="0.48.4 r9939"
 | 
			
		||||
   sodipodi:docname="page-indicator-pushed.svg">
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs4705" />
 | 
			
		||||
  <sodipodi:namedview
 | 
			
		||||
     id="base"
 | 
			
		||||
     pagecolor="#ffffff"
 | 
			
		||||
     bordercolor="#666666"
 | 
			
		||||
     borderopacity="1.0"
 | 
			
		||||
     inkscape:pageopacity="0.0"
 | 
			
		||||
     inkscape:pageshadow="2"
 | 
			
		||||
     inkscape:zoom="31.392433"
 | 
			
		||||
     inkscape:cx="1.0245308"
 | 
			
		||||
     inkscape:cy="13.3715"
 | 
			
		||||
     inkscape:current-layer="layer1"
 | 
			
		||||
     showgrid="true"
 | 
			
		||||
     inkscape:grid-bbox="true"
 | 
			
		||||
     inkscape:document-units="px"
 | 
			
		||||
     inkscape:window-width="2560"
 | 
			
		||||
     inkscape:window-height="1374"
 | 
			
		||||
     inkscape:window-x="0"
 | 
			
		||||
     inkscape:window-y="27"
 | 
			
		||||
     inkscape:window-maximized="1">
 | 
			
		||||
    <inkscape:grid
 | 
			
		||||
       type="xygrid"
 | 
			
		||||
       id="grid6140" />
 | 
			
		||||
  </sodipodi:namedview>
 | 
			
		||||
  <metadata
 | 
			
		||||
     id="metadata4708">
 | 
			
		||||
    <rdf:RDF>
 | 
			
		||||
      <cc:Work
 | 
			
		||||
         rdf:about="">
 | 
			
		||||
        <dc:format>image/svg+xml</dc:format>
 | 
			
		||||
        <dc:type
 | 
			
		||||
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
 | 
			
		||||
        <dc:title></dc:title>
 | 
			
		||||
      </cc:Work>
 | 
			
		||||
    </rdf:RDF>
 | 
			
		||||
  </metadata>
 | 
			
		||||
  <g
 | 
			
		||||
     id="layer1"
 | 
			
		||||
     inkscape:label="Layer 1"
 | 
			
		||||
     inkscape:groupmode="layer"
 | 
			
		||||
     transform="translate(0,2)">
 | 
			
		||||
    <path
 | 
			
		||||
       transform="matrix(0.54617904,0,0,0.62523128,-1131.9904,-392.39214)"
 | 
			
		||||
       d="m 2099.9808,638.83099 a 10.985409,9.5964489 0 1 1 -21.9708,0 10.985409,9.5964489 0 1 1 21.9708,0 z"
 | 
			
		||||
       sodipodi:ry="9.5964489"
 | 
			
		||||
       sodipodi:rx="10.985409"
 | 
			
		||||
       sodipodi:cy="638.83099"
 | 
			
		||||
       sodipodi:cx="2088.9954"
 | 
			
		||||
       id="path4711"
 | 
			
		||||
       style="fill:#fdffff;fill-opacity:1;stroke:none"
 | 
			
		||||
       sodipodi:type="arc" />
 | 
			
		||||
  </g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 2.1 KiB  | 
@@ -1,67 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
 | 
			
		||||
 | 
			
		||||
<svg
 | 
			
		||||
   xmlns:dc="http://purl.org/dc/elements/1.1/"
 | 
			
		||||
   xmlns:cc="http://creativecommons.org/ns#"
 | 
			
		||||
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 | 
			
		||||
   xmlns:svg="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 | 
			
		||||
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
 | 
			
		||||
   width="18"
 | 
			
		||||
   height="18"
 | 
			
		||||
   id="svg4703"
 | 
			
		||||
   version="1.1"
 | 
			
		||||
   inkscape:version="0.48.4 r9939"
 | 
			
		||||
   sodipodi:docname="page-indicator-active.svg">
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs4705" />
 | 
			
		||||
  <sodipodi:namedview
 | 
			
		||||
     id="base"
 | 
			
		||||
     pagecolor="#ffffff"
 | 
			
		||||
     bordercolor="#666666"
 | 
			
		||||
     borderopacity="1.0"
 | 
			
		||||
     inkscape:pageopacity="0.0"
 | 
			
		||||
     inkscape:pageshadow="2"
 | 
			
		||||
     inkscape:zoom="22.197802"
 | 
			
		||||
     inkscape:cx="2.1522887"
 | 
			
		||||
     inkscape:cy="16.782904"
 | 
			
		||||
     inkscape:current-layer="layer1"
 | 
			
		||||
     showgrid="true"
 | 
			
		||||
     inkscape:grid-bbox="true"
 | 
			
		||||
     inkscape:document-units="px"
 | 
			
		||||
     inkscape:window-width="1920"
 | 
			
		||||
     inkscape:window-height="1021"
 | 
			
		||||
     inkscape:window-x="0"
 | 
			
		||||
     inkscape:window-y="27"
 | 
			
		||||
     inkscape:window-maximized="1" />
 | 
			
		||||
  <metadata
 | 
			
		||||
     id="metadata4708">
 | 
			
		||||
    <rdf:RDF>
 | 
			
		||||
      <cc:Work
 | 
			
		||||
         rdf:about="">
 | 
			
		||||
        <dc:format>image/svg+xml</dc:format>
 | 
			
		||||
        <dc:type
 | 
			
		||||
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
 | 
			
		||||
        <dc:title />
 | 
			
		||||
      </cc:Work>
 | 
			
		||||
    </rdf:RDF>
 | 
			
		||||
  </metadata>
 | 
			
		||||
  <g
 | 
			
		||||
     id="layer1"
 | 
			
		||||
     inkscape:label="Layer 1"
 | 
			
		||||
     inkscape:groupmode="layer"
 | 
			
		||||
     transform="translate(0,2)">
 | 
			
		||||
    <path
 | 
			
		||||
       transform="matrix(0.72823872,0,0,0.8336417,-1512.2872,-525.55618)"
 | 
			
		||||
       d="m 2099.9808,638.83099 c 0,5.29998 -4.9184,9.59645 -10.9854,9.59645 -6.0671,0 -10.9854,-4.29647 -10.9854,-9.59645 0,-5.29997 4.9183,-9.59645 10.9854,-9.59645 6.067,0 10.9854,4.29648 10.9854,9.59645 z"
 | 
			
		||||
       sodipodi:ry="9.5964489"
 | 
			
		||||
       sodipodi:rx="10.985409"
 | 
			
		||||
       sodipodi:cy="638.83099"
 | 
			
		||||
       sodipodi:cx="2088.9954"
 | 
			
		||||
       id="path4711"
 | 
			
		||||
       style="fill:#fdffff;fill-opacity:0.94117647;stroke:none"
 | 
			
		||||
       sodipodi:type="arc" />
 | 
			
		||||
  </g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 2.1 KiB  | 
@@ -1,67 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
 | 
			
		||||
 | 
			
		||||
<svg
 | 
			
		||||
   xmlns:dc="http://purl.org/dc/elements/1.1/"
 | 
			
		||||
   xmlns:cc="http://creativecommons.org/ns#"
 | 
			
		||||
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 | 
			
		||||
   xmlns:svg="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 | 
			
		||||
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
 | 
			
		||||
   width="18"
 | 
			
		||||
   height="18"
 | 
			
		||||
   id="svg5266"
 | 
			
		||||
   version="1.1"
 | 
			
		||||
   inkscape:version="0.48.4 r9939"
 | 
			
		||||
   sodipodi:docname="page-indicator-inactive.svg">
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs5268" />
 | 
			
		||||
  <sodipodi:namedview
 | 
			
		||||
     id="base"
 | 
			
		||||
     pagecolor="#ffffff"
 | 
			
		||||
     bordercolor="#666666"
 | 
			
		||||
     borderopacity="1.0"
 | 
			
		||||
     inkscape:pageopacity="0"
 | 
			
		||||
     inkscape:pageshadow="2"
 | 
			
		||||
     inkscape:zoom="11.313709"
 | 
			
		||||
     inkscape:cx="-2.307566"
 | 
			
		||||
     inkscape:cy="17.859535"
 | 
			
		||||
     inkscape:current-layer="layer1"
 | 
			
		||||
     showgrid="true"
 | 
			
		||||
     inkscape:grid-bbox="true"
 | 
			
		||||
     inkscape:document-units="px"
 | 
			
		||||
     inkscape:window-width="2560"
 | 
			
		||||
     inkscape:window-height="1374"
 | 
			
		||||
     inkscape:window-x="0"
 | 
			
		||||
     inkscape:window-y="27"
 | 
			
		||||
     inkscape:window-maximized="1" />
 | 
			
		||||
  <metadata
 | 
			
		||||
     id="metadata5271">
 | 
			
		||||
    <rdf:RDF>
 | 
			
		||||
      <cc:Work
 | 
			
		||||
         rdf:about="">
 | 
			
		||||
        <dc:format>image/svg+xml</dc:format>
 | 
			
		||||
        <dc:type
 | 
			
		||||
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
 | 
			
		||||
        <dc:title></dc:title>
 | 
			
		||||
      </cc:Work>
 | 
			
		||||
    </rdf:RDF>
 | 
			
		||||
  </metadata>
 | 
			
		||||
  <g
 | 
			
		||||
     id="layer1"
 | 
			
		||||
     inkscape:label="Layer 1"
 | 
			
		||||
     inkscape:groupmode="layer"
 | 
			
		||||
     transform="translate(0,2)">
 | 
			
		||||
    <path
 | 
			
		||||
       sodipodi:type="arc"
 | 
			
		||||
       style="fill:none;fill-opacity:0;stroke:#ffffff;stroke-width:2.93356276000000005;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
 | 
			
		||||
       id="path5274"
 | 
			
		||||
       sodipodi:cx="2088.9954"
 | 
			
		||||
       sodipodi:cy="638.83099"
 | 
			
		||||
       sodipodi:rx="10.985409"
 | 
			
		||||
       sodipodi:ry="9.5964489"
 | 
			
		||||
       d="m 2099.9808,638.83099 c 0,5.29998 -4.9184,9.59645 -10.9854,9.59645 -6.0671,0 -10.9854,-4.29647 -10.9854,-9.59645 0,-5.29997 4.9183,-9.59645 10.9854,-9.59645 6.067,0 10.9854,4.29648 10.9854,9.59645 z"
 | 
			
		||||
       transform="matrix(0.63720887,0,0,0.72943648,-1322.1264,-458.98661)" />
 | 
			
		||||
  </g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 2.2 KiB  | 
@@ -1,67 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
 | 
			
		||||
 | 
			
		||||
<svg
 | 
			
		||||
   xmlns:dc="http://purl.org/dc/elements/1.1/"
 | 
			
		||||
   xmlns:cc="http://creativecommons.org/ns#"
 | 
			
		||||
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 | 
			
		||||
   xmlns:svg="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 | 
			
		||||
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
 | 
			
		||||
   width="18"
 | 
			
		||||
   height="18"
 | 
			
		||||
   id="svg5266"
 | 
			
		||||
   version="1.1"
 | 
			
		||||
   inkscape:version="0.48.4 r9939"
 | 
			
		||||
   sodipodi:docname="page-indicator-inactive.svg">
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs5268" />
 | 
			
		||||
  <sodipodi:namedview
 | 
			
		||||
     id="base"
 | 
			
		||||
     pagecolor="#ffffff"
 | 
			
		||||
     bordercolor="#666666"
 | 
			
		||||
     borderopacity="1.0"
 | 
			
		||||
     inkscape:pageopacity="0"
 | 
			
		||||
     inkscape:pageshadow="2"
 | 
			
		||||
     inkscape:zoom="11.313709"
 | 
			
		||||
     inkscape:cx="-2.307566"
 | 
			
		||||
     inkscape:cy="17.859535"
 | 
			
		||||
     inkscape:current-layer="layer1"
 | 
			
		||||
     showgrid="true"
 | 
			
		||||
     inkscape:grid-bbox="true"
 | 
			
		||||
     inkscape:document-units="px"
 | 
			
		||||
     inkscape:window-width="2560"
 | 
			
		||||
     inkscape:window-height="1374"
 | 
			
		||||
     inkscape:window-x="0"
 | 
			
		||||
     inkscape:window-y="27"
 | 
			
		||||
     inkscape:window-maximized="1" />
 | 
			
		||||
  <metadata
 | 
			
		||||
     id="metadata5271">
 | 
			
		||||
    <rdf:RDF>
 | 
			
		||||
      <cc:Work
 | 
			
		||||
         rdf:about="">
 | 
			
		||||
        <dc:format>image/svg+xml</dc:format>
 | 
			
		||||
        <dc:type
 | 
			
		||||
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
 | 
			
		||||
        <dc:title />
 | 
			
		||||
      </cc:Work>
 | 
			
		||||
    </rdf:RDF>
 | 
			
		||||
  </metadata>
 | 
			
		||||
  <g
 | 
			
		||||
     id="layer1"
 | 
			
		||||
     inkscape:label="Layer 1"
 | 
			
		||||
     inkscape:groupmode="layer"
 | 
			
		||||
     transform="translate(0,2)">
 | 
			
		||||
    <path
 | 
			
		||||
       sodipodi:type="arc"
 | 
			
		||||
       style="fill:none;fill-opacity:0;stroke:#ffffff;stroke-width:2.93356276000000005;stroke-miterlimit:4;stroke-opacity:0.39215686000000000;stroke-dasharray:none"
 | 
			
		||||
       id="path5274"
 | 
			
		||||
       sodipodi:cx="2088.9954"
 | 
			
		||||
       sodipodi:cy="638.83099"
 | 
			
		||||
       sodipodi:rx="10.985409"
 | 
			
		||||
       sodipodi:ry="9.5964489"
 | 
			
		||||
       d="m 2099.9808,638.83099 c 0,5.29998 -4.9184,9.59645 -10.9854,9.59645 -6.0671,0 -10.9854,-4.29647 -10.9854,-9.59645 0,-5.29997 4.9183,-9.59645 10.9854,-9.59645 6.067,0 10.9854,4.29648 10.9854,9.59645 z"
 | 
			
		||||
       transform="matrix(0.63720887,0,0,0.72943648,-1322.1264,-458.98661)" />
 | 
			
		||||
  </g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 2.2 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								data/wanda.png
									
									
									
									
									
								
							
							
						
						| 
		 Before Width: | Height: | Size: 13 KiB  | 
@@ -88,18 +88,11 @@ doc-gen-org.gnome.Shell.SearchProvider2.xml: $(top_srcdir)/data/org.gnome.ShellS
 | 
			
		||||
	--generate-docbook doc-gen				\
 | 
			
		||||
	$(top_srcdir)/data/org.gnome.ShellSearchProvider2.xml
 | 
			
		||||
 | 
			
		||||
doc-gen-org.gnome.Shell.Screenshot.xml: $(top_srcdir)/data/org.gnome.Shell.Screenshot.xml
 | 
			
		||||
	gdbus-codegen 						\
 | 
			
		||||
	--interface-prefix org.gnome.Shell.Screenshot.		\
 | 
			
		||||
	--generate-docbook doc-gen				\
 | 
			
		||||
	$(top_srcdir)/data/org.gnome.Shell.Screenshot.xml
 | 
			
		||||
 | 
			
		||||
# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE).
 | 
			
		||||
# e.g. content_files=running.sgml building.sgml changes-2.0.sgml
 | 
			
		||||
content_files= \
 | 
			
		||||
	doc-gen-org.gnome.Shell.SearchProvider.xml	\
 | 
			
		||||
	doc-gen-org.gnome.Shell.SearchProvider2.xml	\
 | 
			
		||||
	doc-gen-org.gnome.Shell.Screenshot.xml
 | 
			
		||||
	doc-gen-org.gnome.Shell.SearchProvider2.xml
 | 
			
		||||
 | 
			
		||||
# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded
 | 
			
		||||
# These files must be listed here *and* in content_files
 | 
			
		||||
@@ -112,7 +105,7 @@ expand_content_files=
 | 
			
		||||
# e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS)
 | 
			
		||||
# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib)
 | 
			
		||||
GTKDOC_CFLAGS=$(GNOME_SHELL_CFLAGS)
 | 
			
		||||
GTKDOC_LIBS=$(GNOME_SHELL_LIBS) $(BLUETOOTH_LIBS) $(top_builddir)/src/libgnome-shell-menu.la $(top_builddir)/src/libgnome-shell-base.la $(top_builddir)/src/libgnome-shell.la
 | 
			
		||||
GTKDOC_LIBS=$(GNOME_SHELL_LIBS) $(BLUETOOTH_LIBS) $(top_builddir)/src/libgnome-shell.la
 | 
			
		||||
 | 
			
		||||
# This includes the standard gtk-doc make rules, copied by gtkdocize.
 | 
			
		||||
include $(top_srcdir)/gtk-doc.make
 | 
			
		||||
 
 | 
			
		||||
@@ -46,10 +46,11 @@
 | 
			
		||||
    <xi:include href="doc-gen-org.gnome.Shell.SearchProvider.xml"/>
 | 
			
		||||
    <xi:include href="doc-gen-org.gnome.Shell.SearchProvider2.xml"/>
 | 
			
		||||
    <xi:include href="xml/shell-global.xml"/>
 | 
			
		||||
    <xi:include href="xml/shell-keybinding-modes.xml"/>
 | 
			
		||||
    <xi:include href="xml/shell-wm.xml"/>
 | 
			
		||||
    <xi:include href="xml/shell-xfixes-cursor.xml"/>
 | 
			
		||||
    <xi:include href="xml/shell-util.xml"/>
 | 
			
		||||
    <xi:include href="xml/shell-mount-operation.xml"/>
 | 
			
		||||
    <xi:include href="xml/shell-mobile-providers.xml"/>
 | 
			
		||||
    <xi:include href="xml/shell-network-agent.xml"/>
 | 
			
		||||
    <xi:include href="xml/shell-polkit-authentication-agent.xml"/>
 | 
			
		||||
    <xi:include href="xml/shell-tp-client.xml"/>
 | 
			
		||||
 
 | 
			
		||||
@@ -17,11 +17,10 @@ misc/config.js: misc/config.js.in Makefile
 | 
			
		||||
jsdir = $(pkgdatadir)/js
 | 
			
		||||
 | 
			
		||||
nobase_dist_js_DATA = 	\
 | 
			
		||||
	gdm/authPrompt.js	\
 | 
			
		||||
	gdm/batch.js		\
 | 
			
		||||
	gdm/fingerprint.js	\
 | 
			
		||||
	gdm/loginDialog.js	\
 | 
			
		||||
	gdm/oVirt.js		\
 | 
			
		||||
	gdm/powerMenu.js	\
 | 
			
		||||
	gdm/realmd.js		\
 | 
			
		||||
	gdm/util.js		\
 | 
			
		||||
	extensionPrefs/main.js	\
 | 
			
		||||
@@ -29,22 +28,16 @@ nobase_dist_js_DATA = 	\
 | 
			
		||||
	misc/extensionUtils.js	\
 | 
			
		||||
	misc/fileUtils.js	\
 | 
			
		||||
	misc/gnomeSession.js	\
 | 
			
		||||
	misc/hash.js		\
 | 
			
		||||
	misc/history.js		\
 | 
			
		||||
	misc/jsParse.js		\
 | 
			
		||||
	misc/loginManager.js	\
 | 
			
		||||
	misc/modemManager.js	\
 | 
			
		||||
	misc/objectManager.js	\
 | 
			
		||||
	misc/params.js		\
 | 
			
		||||
	misc/smartcardManager.js \
 | 
			
		||||
	misc/util.js		\
 | 
			
		||||
	perf/core.js		\
 | 
			
		||||
	ui/altTab.js		\
 | 
			
		||||
	ui/animation.js		\
 | 
			
		||||
	ui/appDisplay.js	\
 | 
			
		||||
	ui/appFavorites.js	\
 | 
			
		||||
	ui/backgroundMenu.js	\
 | 
			
		||||
	ui/background.js	\
 | 
			
		||||
	ui/boxpointer.js	\
 | 
			
		||||
	ui/calendar.js		\
 | 
			
		||||
	ui/checkBox.js		\
 | 
			
		||||
@@ -56,7 +49,7 @@ nobase_dist_js_DATA = 	\
 | 
			
		||||
	ui/extensionSystem.js	\
 | 
			
		||||
	ui/extensionDownloader.js \
 | 
			
		||||
	ui/environment.js	\
 | 
			
		||||
	ui/focusCaretTracker.js\
 | 
			
		||||
	ui/flashspot.js		\
 | 
			
		||||
	ui/ibusCandidatePopup.js\
 | 
			
		||||
	ui/grabHelper.js	\
 | 
			
		||||
	ui/iconGrid.js		\
 | 
			
		||||
@@ -73,39 +66,30 @@ nobase_dist_js_DATA = 	\
 | 
			
		||||
	ui/sessionMode.js	\
 | 
			
		||||
	ui/shellEntry.js	\
 | 
			
		||||
	ui/shellMountOperation.js \
 | 
			
		||||
	ui/slider.js		\
 | 
			
		||||
	ui/notificationDaemon.js \
 | 
			
		||||
	ui/osdWindow.js		\
 | 
			
		||||
	ui/overview.js		\
 | 
			
		||||
	ui/overviewControls.js	\
 | 
			
		||||
	ui/panel.js		\
 | 
			
		||||
	ui/panelMenu.js		\
 | 
			
		||||
	ui/pointerWatcher.js    \
 | 
			
		||||
	ui/popupMenu.js		\
 | 
			
		||||
	ui/remoteSearch.js	\
 | 
			
		||||
	ui/remoteMenu.js	\
 | 
			
		||||
	ui/runDialog.js		\
 | 
			
		||||
	ui/screencast.js	\
 | 
			
		||||
	ui/screenshot.js	\
 | 
			
		||||
        ui/screenShield.js	\
 | 
			
		||||
	ui/scripting.js		\
 | 
			
		||||
	ui/search.js		\
 | 
			
		||||
	ui/searchDisplay.js	\
 | 
			
		||||
	ui/shellDBus.js		\
 | 
			
		||||
	ui/status/accessibility.js	\
 | 
			
		||||
	ui/status/brightness.js	\
 | 
			
		||||
	ui/status/keyboard.js	\
 | 
			
		||||
	ui/status/lockScreenMenu.js	\
 | 
			
		||||
	ui/status/network.js	\
 | 
			
		||||
	ui/status/power.js	\
 | 
			
		||||
	ui/status/rfkill.js	\
 | 
			
		||||
	ui/status/volume.js	\
 | 
			
		||||
	ui/status/bluetooth.js	\
 | 
			
		||||
	ui/status/screencast.js	\
 | 
			
		||||
	ui/status/system.js	\
 | 
			
		||||
	ui/switcherPopup.js	\
 | 
			
		||||
	ui/tweener.js		\
 | 
			
		||||
	ui/unlockDialog.js	\
 | 
			
		||||
	ui/userWidget.js	\
 | 
			
		||||
	ui/userMenu.js		\
 | 
			
		||||
	ui/viewSelector.js	\
 | 
			
		||||
	ui/wanda.js		\
 | 
			
		||||
	ui/windowAttentionHandler.js	\
 | 
			
		||||
@@ -120,6 +104,7 @@ nobase_dist_js_DATA = 	\
 | 
			
		||||
	ui/components/automountManager.js	\
 | 
			
		||||
	ui/components/networkAgent.js		\
 | 
			
		||||
	ui/components/polkitAgent.js		\
 | 
			
		||||
	ui/components/recorder.js		\
 | 
			
		||||
	ui/components/telepathyClient.js	\
 | 
			
		||||
	ui/components/keyring.js		\
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 
 | 
			
		||||
@@ -45,7 +45,6 @@ const Application = new Lang.Class({
 | 
			
		||||
        this._extensionPrefsModules = {};
 | 
			
		||||
 | 
			
		||||
        this._extensionIters = {};
 | 
			
		||||
        this._startupUuid = null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _buildModel: function() {
 | 
			
		||||
@@ -204,7 +203,6 @@ const Application = new Lang.Class({
 | 
			
		||||
    _scanExtensions: function() {
 | 
			
		||||
        let finder = new ExtensionUtils.ExtensionFinder();
 | 
			
		||||
        finder.connect('extension-found', Lang.bind(this, this._extensionFound));
 | 
			
		||||
        finder.connect('extensions-loaded', Lang.bind(this, this._extensionsLoaded));
 | 
			
		||||
        finder.scanExtensions();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -214,11 +212,6 @@ const Application = new Lang.Class({
 | 
			
		||||
        this._extensionIters[extension.uuid] = iter;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _extensionsLoaded: function() {
 | 
			
		||||
        if (this._startupUuid && this._extensionAvailable(this._startupUuid))
 | 
			
		||||
            this._selectExtension(this._startupUuid);
 | 
			
		||||
        this._startupUuid = null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onActivate: function() {
 | 
			
		||||
        this._window.present();
 | 
			
		||||
@@ -239,10 +232,10 @@ const Application = new Lang.Class({
 | 
			
		||||
            // Strip off "extension:///" prefix which fakes a URI, if it exists
 | 
			
		||||
            uuid = stripPrefix(uuid, "extension:///");
 | 
			
		||||
 | 
			
		||||
            if (this._extensionAvailable(uuid))
 | 
			
		||||
                this._selectExtension(uuid);
 | 
			
		||||
            else
 | 
			
		||||
                this._startupUuid = uuid;
 | 
			
		||||
            if (!this._extensionAvailable(uuid))
 | 
			
		||||
                return 1;
 | 
			
		||||
 | 
			
		||||
            this._selectExtension(uuid);
 | 
			
		||||
        }
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,505 +0,0 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
const Animation = imports.ui.animation;
 | 
			
		||||
const Batch = imports.gdm.batch;
 | 
			
		||||
const GdmUtil = imports.gdm.util;
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
const ShellEntry = imports.ui.shellEntry;
 | 
			
		||||
const Tweener = imports.ui.tweener;
 | 
			
		||||
const UserWidget = imports.ui.userWidget;
 | 
			
		||||
 | 
			
		||||
const DEFAULT_BUTTON_WELL_ICON_SIZE = 24;
 | 
			
		||||
const DEFAULT_BUTTON_WELL_ANIMATION_DELAY = 1.0;
 | 
			
		||||
const DEFAULT_BUTTON_WELL_ANIMATION_TIME = 0.3;
 | 
			
		||||
 | 
			
		||||
const MESSAGE_FADE_OUT_ANIMATION_TIME = 0.5;
 | 
			
		||||
 | 
			
		||||
const AuthPromptMode = {
 | 
			
		||||
    UNLOCK_ONLY: 0,
 | 
			
		||||
    UNLOCK_OR_LOG_IN: 1
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const AuthPromptStatus = {
 | 
			
		||||
    NOT_VERIFYING: 0,
 | 
			
		||||
    VERIFYING: 1,
 | 
			
		||||
    VERIFICATION_FAILED: 2,
 | 
			
		||||
    VERIFICATION_SUCCEEDED: 3
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const BeginRequestType = {
 | 
			
		||||
    PROVIDE_USERNAME: 0,
 | 
			
		||||
    DONT_PROVIDE_USERNAME: 1
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const AuthPrompt = new Lang.Class({
 | 
			
		||||
    Name: 'AuthPrompt',
 | 
			
		||||
 | 
			
		||||
    _init: function(gdmClient, mode) {
 | 
			
		||||
        this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
 | 
			
		||||
 | 
			
		||||
        this._gdmClient = gdmClient;
 | 
			
		||||
        this._mode = mode;
 | 
			
		||||
 | 
			
		||||
        let reauthenticationOnly;
 | 
			
		||||
        if (this._mode == AuthPromptMode.UNLOCK_ONLY)
 | 
			
		||||
            reauthenticationOnly = true;
 | 
			
		||||
        else if (this._mode == AuthPromptMode.UNLOCK_OR_LOG_IN)
 | 
			
		||||
            reauthenticationOnly = false;
 | 
			
		||||
 | 
			
		||||
        this._userVerifier = new GdmUtil.ShellUserVerifier(this._gdmClient, { reauthenticationOnly: reauthenticationOnly });
 | 
			
		||||
 | 
			
		||||
        this._userVerifier.connect('ask-question', Lang.bind(this, this._onAskQuestion));
 | 
			
		||||
        this._userVerifier.connect('show-message', Lang.bind(this, this._onShowMessage));
 | 
			
		||||
        this._userVerifier.connect('verification-failed', Lang.bind(this, this._onVerificationFailed));
 | 
			
		||||
        this._userVerifier.connect('verification-complete', Lang.bind(this, this._onVerificationComplete));
 | 
			
		||||
        this._userVerifier.connect('reset', Lang.bind(this, this._onReset));
 | 
			
		||||
        this._userVerifier.connect('smartcard-status-changed', Lang.bind(this, this._onSmartcardStatusChanged));
 | 
			
		||||
        this._userVerifier.connect('ovirt-user-authenticated', Lang.bind(this, this._onOVirtUserAuthenticated));
 | 
			
		||||
        this.smartcardDetected = this._userVerifier.smartcardDetected;
 | 
			
		||||
 | 
			
		||||
        this.connect('next', Lang.bind(this, function() {
 | 
			
		||||
                         this.updateSensitivity(false);
 | 
			
		||||
                         this.startSpinning();
 | 
			
		||||
                         if (this._queryingService) {
 | 
			
		||||
                             this._userVerifier.answerQuery(this._queryingService, this._entry.text);
 | 
			
		||||
                         } else {
 | 
			
		||||
                             this._preemptiveAnswer = this._entry.text;
 | 
			
		||||
                         }
 | 
			
		||||
                     }));
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.BoxLayout({ style_class: 'login-dialog-prompt-layout',
 | 
			
		||||
                                        vertical: true });
 | 
			
		||||
        this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
 | 
			
		||||
        this.actor.connect('key-press-event',
 | 
			
		||||
                           Lang.bind(this, function(actor, event) {
 | 
			
		||||
                               if (event.get_key_symbol() == Clutter.KEY_Escape) {
 | 
			
		||||
                                   this.cancel();
 | 
			
		||||
                               }
 | 
			
		||||
                           }));
 | 
			
		||||
 | 
			
		||||
        this._userWell = new St.Bin({ x_fill: true,
 | 
			
		||||
                                      x_align: St.Align.START });
 | 
			
		||||
        this.actor.add(this._userWell,
 | 
			
		||||
                       { x_align: St.Align.START,
 | 
			
		||||
                         x_fill: true,
 | 
			
		||||
                         y_fill: true,
 | 
			
		||||
                         expand: true });
 | 
			
		||||
        this._label = new St.Label({ style_class: 'login-dialog-prompt-label' });
 | 
			
		||||
 | 
			
		||||
        this.actor.add(this._label,
 | 
			
		||||
                       { expand: true,
 | 
			
		||||
                         x_fill: true,
 | 
			
		||||
                         y_fill: true,
 | 
			
		||||
                         x_align: St.Align.START });
 | 
			
		||||
        this._entry = new St.Entry({ style_class: 'login-dialog-prompt-entry',
 | 
			
		||||
                                     can_focus: true });
 | 
			
		||||
        ShellEntry.addContextMenu(this._entry, { isPassword: true });
 | 
			
		||||
 | 
			
		||||
        this.actor.add(this._entry,
 | 
			
		||||
                       { expand: true,
 | 
			
		||||
                         x_fill: true,
 | 
			
		||||
                         y_fill: false,
 | 
			
		||||
                         x_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        this._entry.grab_key_focus();
 | 
			
		||||
 | 
			
		||||
        this._message = new St.Label({ opacity: 0,
 | 
			
		||||
                                       styleClass: 'login-dialog-message' });
 | 
			
		||||
        this._message.clutter_text.line_wrap = true;
 | 
			
		||||
        this.actor.add(this._message, { x_fill: true, y_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        this._buttonBox = new St.BoxLayout({ style_class: 'login-dialog-button-box',
 | 
			
		||||
                                             vertical: false });
 | 
			
		||||
        this.actor.add(this._buttonBox,
 | 
			
		||||
                       { expand:  true,
 | 
			
		||||
                         x_align: St.Align.MIDDLE,
 | 
			
		||||
                         y_align: St.Align.END });
 | 
			
		||||
 | 
			
		||||
        this._defaultButtonWell = new St.Widget({ layout_manager: new Clutter.BinLayout() });
 | 
			
		||||
        this._defaultButtonWellActor = null;
 | 
			
		||||
 | 
			
		||||
        this._initButtons();
 | 
			
		||||
 | 
			
		||||
        let spinnerIcon = global.datadir + '/theme/process-working.svg';
 | 
			
		||||
        this._spinner = new Animation.AnimatedIcon(spinnerIcon, DEFAULT_BUTTON_WELL_ICON_SIZE);
 | 
			
		||||
        this._spinner.actor.opacity = 0;
 | 
			
		||||
        this._spinner.actor.show();
 | 
			
		||||
        this._defaultButtonWell.add_child(this._spinner.actor);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDestroy: function() {
 | 
			
		||||
        this._userVerifier.clear();
 | 
			
		||||
        this._userVerifier.disconnectAll();
 | 
			
		||||
        this._userVerifier = null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _initButtons: function() {
 | 
			
		||||
        this.cancelButton = new St.Button({ style_class: 'modal-dialog-button',
 | 
			
		||||
                                            button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
 | 
			
		||||
                                            reactive: true,
 | 
			
		||||
                                            can_focus: true,
 | 
			
		||||
                                            label: _("Cancel") });
 | 
			
		||||
        this.cancelButton.connect('clicked',
 | 
			
		||||
                                   Lang.bind(this, function() {
 | 
			
		||||
                                       this.cancel();
 | 
			
		||||
                                   }));
 | 
			
		||||
        this._buttonBox.add(this.cancelButton,
 | 
			
		||||
                            { expand: false,
 | 
			
		||||
                              x_fill: false,
 | 
			
		||||
                              y_fill: false,
 | 
			
		||||
                              x_align: St.Align.START,
 | 
			
		||||
                              y_align: St.Align.END });
 | 
			
		||||
 | 
			
		||||
        this._buttonBox.add(this._defaultButtonWell,
 | 
			
		||||
                            { expand: true,
 | 
			
		||||
                              x_fill: false,
 | 
			
		||||
                              y_fill: false,
 | 
			
		||||
                              x_align: St.Align.END,
 | 
			
		||||
                              y_align: St.Align.MIDDLE });
 | 
			
		||||
        this.nextButton = new St.Button({ style_class: 'modal-dialog-button',
 | 
			
		||||
                                          button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
 | 
			
		||||
                                          reactive: true,
 | 
			
		||||
                                          can_focus: true,
 | 
			
		||||
                                          label: _("Next") });
 | 
			
		||||
        this.nextButton.connect('clicked',
 | 
			
		||||
                                 Lang.bind(this, function() {
 | 
			
		||||
                                     this.emit('next');
 | 
			
		||||
                                 }));
 | 
			
		||||
        this.nextButton.add_style_pseudo_class('default');
 | 
			
		||||
        this._buttonBox.add(this.nextButton,
 | 
			
		||||
                            { expand: false,
 | 
			
		||||
                              x_fill: false,
 | 
			
		||||
                              y_fill: false,
 | 
			
		||||
                              x_align: St.Align.END,
 | 
			
		||||
                              y_align: St.Align.END });
 | 
			
		||||
 | 
			
		||||
        this._updateNextButtonSensitivity(this._entry.text.length > 0);
 | 
			
		||||
 | 
			
		||||
        this._entry.clutter_text.connect('text-changed',
 | 
			
		||||
                                         Lang.bind(this, function() {
 | 
			
		||||
                                             if (!this._userVerifier.hasPendingMessages)
 | 
			
		||||
                                                 this._fadeOutMessage();
 | 
			
		||||
 | 
			
		||||
                                             this._updateNextButtonSensitivity(this._entry.text.length > 0);
 | 
			
		||||
                                         }));
 | 
			
		||||
        this._entry.clutter_text.connect('activate', Lang.bind(this, function() {
 | 
			
		||||
            this.emit('next');
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onAskQuestion: function(verifier, serviceName, question, passwordChar) {
 | 
			
		||||
        if (this._preemptiveAnswer) {
 | 
			
		||||
            if (this._queryingService)
 | 
			
		||||
                this._userVerifier.answerQuery(this._queryingService, this._preemptiveAnswer);
 | 
			
		||||
            this._preemptiveAnswer = null;
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this._queryingService)
 | 
			
		||||
            this.clear();
 | 
			
		||||
 | 
			
		||||
        this._queryingService = serviceName;
 | 
			
		||||
        this.setPasswordChar(passwordChar);
 | 
			
		||||
        this.setQuestion(question);
 | 
			
		||||
 | 
			
		||||
        if (passwordChar) {
 | 
			
		||||
            if (this._userVerifier.reauthenticating)
 | 
			
		||||
                this.nextButton.label = _("Unlock");
 | 
			
		||||
            else
 | 
			
		||||
                this.nextButton.label = C_("button", "Sign In");
 | 
			
		||||
        } else {
 | 
			
		||||
            this.nextButton.label = _("Next");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.updateSensitivity(true);
 | 
			
		||||
        this.emit('prompted');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onOVirtUserAuthenticated: function() {
 | 
			
		||||
        if (this.verificationStatus != AuthPromptStatus.VERIFICATION_SUCCEEDED)
 | 
			
		||||
            this.reset();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onSmartcardStatusChanged: function() {
 | 
			
		||||
        this.smartcardDetected = this._userVerifier.smartcardDetected;
 | 
			
		||||
 | 
			
		||||
        // Most of the time we want to reset if the user inserts or removes
 | 
			
		||||
        // a smartcard. Smartcard insertion "preempts" what the user was
 | 
			
		||||
        // doing, and smartcard removal aborts the preemption.
 | 
			
		||||
        // The exceptions are: 1) Don't reset on smartcard insertion if we're already verifying
 | 
			
		||||
        //                        with a smartcard
 | 
			
		||||
        //                     2) Don't reset if we've already succeeded at verification and
 | 
			
		||||
        //                        the user is getting logged in.
 | 
			
		||||
        if (this._userVerifier.serviceIsDefault(GdmUtil.SMARTCARD_SERVICE_NAME) &&
 | 
			
		||||
            this.verificationStatus == AuthPromptStatus.VERIFYING &&
 | 
			
		||||
            this.smartcardDetected)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (this.verificationStatus != AuthPromptStatus.VERIFICATION_SUCCEEDED)
 | 
			
		||||
            this.reset();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onShowMessage: function(userVerifier, message, type) {
 | 
			
		||||
        this.setMessage(message, type);
 | 
			
		||||
        this.emit('prompted');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onVerificationFailed: function() {
 | 
			
		||||
        this._queryingService = null;
 | 
			
		||||
        this.clear();
 | 
			
		||||
 | 
			
		||||
        this.updateSensitivity(true);
 | 
			
		||||
        this.setActorInDefaultButtonWell(null);
 | 
			
		||||
        this.verificationStatus = AuthPromptStatus.VERIFICATION_FAILED;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onVerificationComplete: function() {
 | 
			
		||||
        this.verificationStatus = AuthPromptStatus.VERIFICATION_SUCCEEDED;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onReset: function() {
 | 
			
		||||
        this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
 | 
			
		||||
        this.reset();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addActorToDefaultButtonWell: function(actor) {
 | 
			
		||||
        this._defaultButtonWell.add_child(actor);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setActorInDefaultButtonWell: function(actor, animate) {
 | 
			
		||||
        if (!this._defaultButtonWellActor &&
 | 
			
		||||
            !actor)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let oldActor = this._defaultButtonWellActor;
 | 
			
		||||
 | 
			
		||||
        if (oldActor)
 | 
			
		||||
            Tweener.removeTweens(oldActor);
 | 
			
		||||
 | 
			
		||||
        let isSpinner;
 | 
			
		||||
        if (actor == this._spinner.actor)
 | 
			
		||||
            isSpinner = true;
 | 
			
		||||
        else
 | 
			
		||||
            isSpinner = false;
 | 
			
		||||
 | 
			
		||||
        if (this._defaultButtonWellActor != actor && oldActor) {
 | 
			
		||||
            if (!animate) {
 | 
			
		||||
                oldActor.opacity = 0;
 | 
			
		||||
            } else {
 | 
			
		||||
                Tweener.addTween(oldActor,
 | 
			
		||||
                                 { opacity: 0,
 | 
			
		||||
                                   time: DEFAULT_BUTTON_WELL_ANIMATION_TIME,
 | 
			
		||||
                                   delay: DEFAULT_BUTTON_WELL_ANIMATION_DELAY,
 | 
			
		||||
                                   transition: 'linear',
 | 
			
		||||
                                   onCompleteScope: this,
 | 
			
		||||
                                   onComplete: function() {
 | 
			
		||||
                                      if (isSpinner) {
 | 
			
		||||
                                          if (this._spinner)
 | 
			
		||||
                                              this._spinner.stop();
 | 
			
		||||
                                      }
 | 
			
		||||
                                   }
 | 
			
		||||
                                 });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (actor) {
 | 
			
		||||
            if (isSpinner)
 | 
			
		||||
                this._spinner.play();
 | 
			
		||||
 | 
			
		||||
            if (!animate)
 | 
			
		||||
                actor.opacity = 255;
 | 
			
		||||
            else
 | 
			
		||||
                Tweener.addTween(actor,
 | 
			
		||||
                                 { opacity: 255,
 | 
			
		||||
                                   time: DEFAULT_BUTTON_WELL_ANIMATION_TIME,
 | 
			
		||||
                                   delay: DEFAULT_BUTTON_WELL_ANIMATION_DELAY,
 | 
			
		||||
                                   transition: 'linear' });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._defaultButtonWellActor = actor;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    startSpinning: function() {
 | 
			
		||||
        this.setActorInDefaultButtonWell(this._spinner.actor, true);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    stopSpinning: function() {
 | 
			
		||||
        this.setActorInDefaultButtonWell(null, false);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    clear: function() {
 | 
			
		||||
        this._entry.text = '';
 | 
			
		||||
        this.stopSpinning();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setPasswordChar: function(passwordChar) {
 | 
			
		||||
        this._entry.clutter_text.set_password_char(passwordChar);
 | 
			
		||||
        this._entry.menu.isPassword = passwordChar != '';
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setQuestion: function(question) {
 | 
			
		||||
        this._label.set_text(question);
 | 
			
		||||
 | 
			
		||||
        this._label.show();
 | 
			
		||||
        this._entry.show();
 | 
			
		||||
 | 
			
		||||
        this._entry.grab_key_focus();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getAnswer: function() {
 | 
			
		||||
        let text;
 | 
			
		||||
 | 
			
		||||
        if (this._preemptiveAnswer) {
 | 
			
		||||
            text = this._preemptiveAnswer;
 | 
			
		||||
            this._preemptiveAnswer = null;
 | 
			
		||||
        } else {
 | 
			
		||||
            text = this._entry.get_text();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return text;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _fadeOutMessage: function() {
 | 
			
		||||
        if (this._message.opacity == 0)
 | 
			
		||||
            return;
 | 
			
		||||
        Tweener.removeTweens(this._message);
 | 
			
		||||
        Tweener.addTween(this._message,
 | 
			
		||||
                         { opacity: 0,
 | 
			
		||||
                           time: MESSAGE_FADE_OUT_ANIMATION_TIME,
 | 
			
		||||
                           transition: 'easeOutQuad'
 | 
			
		||||
                         });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setMessage: function(message, type) {
 | 
			
		||||
        if (type == GdmUtil.MessageType.ERROR)
 | 
			
		||||
            this._message.add_style_class_name('login-dialog-message-warning');
 | 
			
		||||
        else
 | 
			
		||||
            this._message.remove_style_class_name('login-dialog-message-warning');
 | 
			
		||||
 | 
			
		||||
        if (type == GdmUtil.MessageType.HINT)
 | 
			
		||||
            this._message.add_style_class_name('login-dialog-message-hint');
 | 
			
		||||
        else
 | 
			
		||||
            this._message.remove_style_class_name('login-dialog-message-hint');
 | 
			
		||||
 | 
			
		||||
        if (message) {
 | 
			
		||||
            Tweener.removeTweens(this._message);
 | 
			
		||||
            this._message.text = message;
 | 
			
		||||
            this._message.opacity = 255;
 | 
			
		||||
        } else {
 | 
			
		||||
            this._message.opacity = 0;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateNextButtonSensitivity: function(sensitive) {
 | 
			
		||||
        this.nextButton.reactive = sensitive;
 | 
			
		||||
        this.nextButton.can_focus = sensitive;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    updateSensitivity: function(sensitive) {
 | 
			
		||||
        this._updateNextButtonSensitivity(sensitive);
 | 
			
		||||
        this._entry.reactive = sensitive;
 | 
			
		||||
        this._entry.clutter_text.editable = sensitive;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    hide: function() {
 | 
			
		||||
        this.setActorInDefaultButtonWell(null, true);
 | 
			
		||||
        this.actor.hide();
 | 
			
		||||
        this._message.opacity = 0;
 | 
			
		||||
 | 
			
		||||
        this.setUser(null);
 | 
			
		||||
 | 
			
		||||
        this.updateSensitivity(true);
 | 
			
		||||
        this._entry.set_text('');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setUser: function(user) {
 | 
			
		||||
        if (user) {
 | 
			
		||||
            let userWidget = new UserWidget.UserWidget(user);
 | 
			
		||||
            this._userWell.set_child(userWidget.actor);
 | 
			
		||||
        } else {
 | 
			
		||||
            this._userWell.set_child(null);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    reset: function() {
 | 
			
		||||
        let oldStatus = this.verificationStatus;
 | 
			
		||||
        this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
 | 
			
		||||
 | 
			
		||||
        if (oldStatus == AuthPromptStatus.VERIFYING)
 | 
			
		||||
            this._userVerifier.cancel();
 | 
			
		||||
 | 
			
		||||
        this._queryingService = null;
 | 
			
		||||
        this.clear();
 | 
			
		||||
        this._message.opacity = 0;
 | 
			
		||||
        this.setUser(null);
 | 
			
		||||
        this.stopSpinning();
 | 
			
		||||
 | 
			
		||||
        if (oldStatus == AuthPromptStatus.VERIFICATION_FAILED)
 | 
			
		||||
            this.emit('failed');
 | 
			
		||||
 | 
			
		||||
        let beginRequestType;
 | 
			
		||||
 | 
			
		||||
        if (this._mode == AuthPromptMode.UNLOCK_ONLY) {
 | 
			
		||||
            // The user is constant at the unlock screen, so it will immediately
 | 
			
		||||
            // respond to the request with the username
 | 
			
		||||
            beginRequestType = BeginRequestType.PROVIDE_USERNAME;
 | 
			
		||||
        } else if (this._userVerifier.serviceIsForeground(GdmUtil.OVIRT_SERVICE_NAME) ||
 | 
			
		||||
                   (this.smartcardDetected &&
 | 
			
		||||
                    this._userVerifier.serviceIsForeground(GdmUtil.SMARTCARD_SERVICE_NAME))) {
 | 
			
		||||
            // We don't need to know the username if the user preempted the login screen
 | 
			
		||||
            // with a smartcard or with preauthenticated oVirt credentials
 | 
			
		||||
            beginRequestType = BeginRequestType.DONT_PROVIDE_USERNAME;
 | 
			
		||||
        } else {
 | 
			
		||||
            // In all other cases, we should get the username up front.
 | 
			
		||||
            beginRequestType = BeginRequestType.PROVIDE_USERNAME;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.emit('reset', beginRequestType);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addCharacter: function(unichar) {
 | 
			
		||||
        if (!this._entry.visible)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._entry.grab_key_focus();
 | 
			
		||||
        this._entry.clutter_text.insert_unichar(unichar);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    begin: function(params) {
 | 
			
		||||
        params = Params.parse(params, { userName: null,
 | 
			
		||||
                                        hold: null });
 | 
			
		||||
 | 
			
		||||
        this.updateSensitivity(false);
 | 
			
		||||
 | 
			
		||||
        let hold = params.hold;
 | 
			
		||||
        if (!hold)
 | 
			
		||||
            hold = new Batch.Hold();
 | 
			
		||||
 | 
			
		||||
        this._userVerifier.begin(params.userName, hold);
 | 
			
		||||
        this.verificationStatus = AuthPromptStatus.VERIFYING;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    finish: function(onComplete) {
 | 
			
		||||
        if (!this._userVerifier.hasPendingMessages) {
 | 
			
		||||
            onComplete();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let signalId = this._userVerifier.connect('no-more-messages',
 | 
			
		||||
                                                  Lang.bind(this, function() {
 | 
			
		||||
                                                      this._userVerifier.disconnect(signalId);
 | 
			
		||||
                                                      onComplete();
 | 
			
		||||
                                                  }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    cancel: function() {
 | 
			
		||||
        this.reset();
 | 
			
		||||
        this.emit('cancelled');
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(AuthPrompt.prototype);
 | 
			
		||||
@@ -1,62 +0,0 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
 | 
			
		||||
const OVirtCredentialsIface = <interface name='org.ovirt.vdsm.Credentials'>
 | 
			
		||||
<signal name="UserAuthenticated">
 | 
			
		||||
    <arg type="s" name="token"/>
 | 
			
		||||
</signal>
 | 
			
		||||
</interface>;
 | 
			
		||||
 | 
			
		||||
const OVirtCredentialsInfo = Gio.DBusInterfaceInfo.new_for_xml(OVirtCredentialsIface);
 | 
			
		||||
 | 
			
		||||
let _oVirtCredentialsManager = null;
 | 
			
		||||
 | 
			
		||||
function OVirtCredentials() {
 | 
			
		||||
    var self = new Gio.DBusProxy({ g_connection: Gio.DBus.system,
 | 
			
		||||
                                   g_interface_name: OVirtCredentialsInfo.name,
 | 
			
		||||
                                   g_interface_info: OVirtCredentialsInfo,
 | 
			
		||||
                                   g_name: 'org.ovirt.vdsm.Credentials',
 | 
			
		||||
                                   g_object_path: '/org/ovirt/vdsm/Credentials',
 | 
			
		||||
                                   g_flags: (Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
 | 
			
		||||
    self.init(null);
 | 
			
		||||
    return self;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const OVirtCredentialsManager = new Lang.Class({
 | 
			
		||||
    Name: 'OVirtCredentialsManager',
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._token = null;
 | 
			
		||||
 | 
			
		||||
        this._credentials = new OVirtCredentials();
 | 
			
		||||
        this._credentials.connectSignal('UserAuthenticated',
 | 
			
		||||
                                        Lang.bind(this, this._onUserAuthenticated));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onUserAuthenticated: function(proxy, sender, [token]) {
 | 
			
		||||
        this._token = token;
 | 
			
		||||
        this.emit('user-authenticated', token);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    hasToken: function() {
 | 
			
		||||
        return this._token != null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getToken: function() {
 | 
			
		||||
        return this._token;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    resetToken: function() {
 | 
			
		||||
        this._token = null;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(OVirtCredentialsManager.prototype);
 | 
			
		||||
 | 
			
		||||
function getOVirtCredentialsManager() {
 | 
			
		||||
    if (!_oVirtCredentialsManager)
 | 
			
		||||
        _oVirtCredentialsManager = new OVirtCredentialsManager();
 | 
			
		||||
 | 
			
		||||
    return _oVirtCredentialsManager;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										129
									
								
								js/gdm/powerMenu.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,129 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 2011 Red Hat, Inc
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 2, or (at your option)
 | 
			
		||||
 * any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 | 
			
		||||
 * 02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
 | 
			
		||||
const LoginManager = imports.misc.loginManager;
 | 
			
		||||
 | 
			
		||||
const GdmUtil = imports.gdm.util;
 | 
			
		||||
const PanelMenu = imports.ui.panelMenu;
 | 
			
		||||
const PopupMenu = imports.ui.popupMenu;
 | 
			
		||||
 | 
			
		||||
const PowerMenuButton = new Lang.Class({
 | 
			
		||||
    Name: 'PowerMenuButton',
 | 
			
		||||
    Extends: PanelMenu.SystemStatusButton,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        /* Translators: accessible name of the power menu in the login screen */
 | 
			
		||||
        this.parent('system-shutdown-symbolic', _("Power"));
 | 
			
		||||
 | 
			
		||||
        this._loginManager = LoginManager.getLoginManager();
 | 
			
		||||
 | 
			
		||||
        this._settings = new Gio.Settings({ schema: GdmUtil.LOGIN_SCREEN_SCHEMA });
 | 
			
		||||
        this._settings.connect('changed::disable-restart-buttons',
 | 
			
		||||
                               Lang.bind(this, this._updateVisibility));
 | 
			
		||||
 | 
			
		||||
        this._createSubMenu();
 | 
			
		||||
 | 
			
		||||
        // ConsoleKit doesn't send notifications when shutdown/reboot
 | 
			
		||||
        // are disabled, so we update the menu item each time the menu opens
 | 
			
		||||
        this.menu.connect('open-state-changed', Lang.bind(this,
 | 
			
		||||
            function(menu, open) {
 | 
			
		||||
                if (open) {
 | 
			
		||||
                    this._updateHaveShutdown();
 | 
			
		||||
                    this._updateHaveRestart();
 | 
			
		||||
                    this._updateHaveSuspend();
 | 
			
		||||
                }
 | 
			
		||||
            }));
 | 
			
		||||
        this._updateHaveShutdown();
 | 
			
		||||
        this._updateHaveRestart();
 | 
			
		||||
        this._updateHaveSuspend();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateVisibility: function() {
 | 
			
		||||
        let shouldBeVisible = (this._haveSuspend || this._haveShutdown || this._haveRestart);
 | 
			
		||||
        this.actor.visible = shouldBeVisible && !this._settings.get_boolean('disable-restart-buttons');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateHaveShutdown: function() {
 | 
			
		||||
        this._loginManager.canPowerOff(Lang.bind(this, function(result) {
 | 
			
		||||
            this._haveShutdown = result;
 | 
			
		||||
            this._powerOffItem.actor.visible = this._haveShutdown;
 | 
			
		||||
            this._updateVisibility();
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateHaveRestart: function() {
 | 
			
		||||
        this._loginManager.canReboot(Lang.bind(this, function(result) {
 | 
			
		||||
            this._haveRestart = result;
 | 
			
		||||
            this._restartItem.actor.visible = this._haveRestart;
 | 
			
		||||
            this._updateVisibility();
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateHaveSuspend: function() {
 | 
			
		||||
        this._loginManager.canSuspend(Lang.bind(this, function(result) {
 | 
			
		||||
            this._haveSuspend = result;
 | 
			
		||||
            this._suspendItem.actor.visible = this._haveSuspend;
 | 
			
		||||
            this._updateVisibility();
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createSubMenu: function() {
 | 
			
		||||
        let item;
 | 
			
		||||
 | 
			
		||||
        item = new PopupMenu.PopupMenuItem(_("Suspend"));
 | 
			
		||||
        item.connect('activate', Lang.bind(this, this._onActivateSuspend));
 | 
			
		||||
        this.menu.addMenuItem(item);
 | 
			
		||||
        this._suspendItem = item;
 | 
			
		||||
 | 
			
		||||
        item = new PopupMenu.PopupMenuItem(_("Restart"));
 | 
			
		||||
        item.connect('activate', Lang.bind(this, this._onActivateRestart));
 | 
			
		||||
        this.menu.addMenuItem(item);
 | 
			
		||||
        this._restartItem = item;
 | 
			
		||||
 | 
			
		||||
        item = new PopupMenu.PopupMenuItem(_("Power Off"));
 | 
			
		||||
        item.connect('activate', Lang.bind(this, this._onActivatePowerOff));
 | 
			
		||||
        this.menu.addMenuItem(item);
 | 
			
		||||
        this._powerOffItem = item;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onActivateSuspend: function() {
 | 
			
		||||
        if (!this._haveSuspend)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._loginManager.suspend();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onActivateRestart: function() {
 | 
			
		||||
        if (!this._haveRestart)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._loginManager.reboot();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onActivatePowerOff: function() {
 | 
			
		||||
        if (!this._haveShutdown)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._loginManager.powerOff();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
@@ -63,7 +63,7 @@ const Manager = new Lang.Class({
 | 
			
		||||
                                           Lang.bind(this, this._reloadRealms))
 | 
			
		||||
        this._realms = {};
 | 
			
		||||
 | 
			
		||||
        this._signalId = this._aggregateProvider.connect('g-properties-changed',
 | 
			
		||||
        this._aggregateProvider.connect('g-properties-changed',
 | 
			
		||||
                                        Lang.bind(this, function(proxy, properties) {
 | 
			
		||||
                                            if ('Realms' in properties.deep_unpack())
 | 
			
		||||
                                                this._reloadRealms();
 | 
			
		||||
@@ -106,7 +106,7 @@ const Manager = new Lang.Class({
 | 
			
		||||
        realm.connect('g-properties-changed',
 | 
			
		||||
                      Lang.bind(this, function(proxy, properties) {
 | 
			
		||||
                                if ('Configured' in properties.deep_unpack())
 | 
			
		||||
                                    this._reloadRealm(realm);
 | 
			
		||||
                                    this._reloadRealm();
 | 
			
		||||
                                }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -134,18 +134,6 @@ const Manager = new Lang.Class({
 | 
			
		||||
        this._updateLoginFormat();
 | 
			
		||||
 | 
			
		||||
        return this._loginFormat;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    release: function() {
 | 
			
		||||
        Service(Gio.DBus.system,
 | 
			
		||||
                'org.freedesktop.realmd',
 | 
			
		||||
                '/org/freedesktop/realmd',
 | 
			
		||||
                function(service) {
 | 
			
		||||
                    service.ReleaseRemote();
 | 
			
		||||
                });
 | 
			
		||||
        this._aggregateProvider.disconnect(this._signalId);
 | 
			
		||||
        this._realms = { };
 | 
			
		||||
        this._updateLoginFormat();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(Manager.prototype)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										401
									
								
								js/gdm/util.js
									
									
									
									
									
								
							
							
						
						@@ -1,33 +1,23 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Mainloop = imports.mainloop;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
const Batch = imports.gdm.batch;
 | 
			
		||||
const Fprint = imports.gdm.fingerprint;
 | 
			
		||||
const OVirt = imports.gdm.oVirt;
 | 
			
		||||
const Realmd = imports.gdm.realmd;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
const ShellEntry = imports.ui.shellEntry;
 | 
			
		||||
const SmartcardManager = imports.misc.smartcardManager;
 | 
			
		||||
const Tweener = imports.ui.tweener;
 | 
			
		||||
 | 
			
		||||
const PASSWORD_SERVICE_NAME = 'gdm-password';
 | 
			
		||||
const FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint';
 | 
			
		||||
const SMARTCARD_SERVICE_NAME = 'gdm-smartcard';
 | 
			
		||||
const OVIRT_SERVICE_NAME = 'gdm-ovirtcred';
 | 
			
		||||
const FADE_ANIMATION_TIME = 0.16;
 | 
			
		||||
const CLONE_FADE_ANIMATION_TIME = 0.25;
 | 
			
		||||
 | 
			
		||||
const LOGIN_SCREEN_SCHEMA = 'org.gnome.login-screen';
 | 
			
		||||
const PASSWORD_AUTHENTICATION_KEY = 'enable-password-authentication';
 | 
			
		||||
const FINGERPRINT_AUTHENTICATION_KEY = 'enable-fingerprint-authentication';
 | 
			
		||||
const SMARTCARD_AUTHENTICATION_KEY = 'enable-smartcard-authentication';
 | 
			
		||||
const BANNER_MESSAGE_KEY = 'banner-message-enable';
 | 
			
		||||
const BANNER_MESSAGE_TEXT_KEY = 'banner-message-text';
 | 
			
		||||
const ALLOWED_FAILURES_KEY = 'allowed-failures';
 | 
			
		||||
@@ -35,16 +25,6 @@ const ALLOWED_FAILURES_KEY = 'allowed-failures';
 | 
			
		||||
const LOGO_KEY = 'logo';
 | 
			
		||||
const DISABLE_USER_LIST_KEY = 'disable-user-list';
 | 
			
		||||
 | 
			
		||||
// Give user 16ms to read each character of a PAM message
 | 
			
		||||
const USER_READ_TIME = 16
 | 
			
		||||
 | 
			
		||||
const MessageType = {
 | 
			
		||||
    NONE: 0,
 | 
			
		||||
    ERROR: 1,
 | 
			
		||||
    INFO: 2,
 | 
			
		||||
    HINT: 3
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function fadeInActor(actor) {
 | 
			
		||||
    if (actor.opacity == 255 && actor.visible)
 | 
			
		||||
        return null;
 | 
			
		||||
@@ -91,34 +71,6 @@ function fadeOutActor(actor) {
 | 
			
		||||
    return hold;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function cloneAndFadeOutActor(actor) {
 | 
			
		||||
    // Immediately hide actor so its sibling can have its space
 | 
			
		||||
    // and position, but leave a non-reactive clone on-screen,
 | 
			
		||||
    // so from the user's point of view it smoothly fades away
 | 
			
		||||
    // and reveals its sibling.
 | 
			
		||||
    actor.hide();
 | 
			
		||||
 | 
			
		||||
    let clone = new Clutter.Clone({ source: actor,
 | 
			
		||||
                                    reactive: false });
 | 
			
		||||
 | 
			
		||||
    Main.uiGroup.add_child(clone);
 | 
			
		||||
 | 
			
		||||
    let [x, y] = actor.get_transformed_position();
 | 
			
		||||
    clone.set_position(x, y);
 | 
			
		||||
 | 
			
		||||
    let hold = new Batch.Hold();
 | 
			
		||||
    Tweener.addTween(clone,
 | 
			
		||||
                     { opacity: 0,
 | 
			
		||||
                       time: CLONE_FADE_ANIMATION_TIME,
 | 
			
		||||
                       transition: 'easeOutQuad',
 | 
			
		||||
                       onComplete: function() {
 | 
			
		||||
                           clone.destroy();
 | 
			
		||||
                           hold.release();
 | 
			
		||||
                       }
 | 
			
		||||
                     });
 | 
			
		||||
    return hold;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const ShellUserVerifier = new Lang.Class({
 | 
			
		||||
    Name: 'ShellUserVerifier',
 | 
			
		||||
 | 
			
		||||
@@ -129,45 +81,17 @@ const ShellUserVerifier = new Lang.Class({
 | 
			
		||||
        this._client = client;
 | 
			
		||||
 | 
			
		||||
        this._settings = new Gio.Settings({ schema: LOGIN_SCREEN_SCHEMA });
 | 
			
		||||
        this._settings.connect('changed',
 | 
			
		||||
                               Lang.bind(this, this._updateDefaultService));
 | 
			
		||||
        this._updateDefaultService();
 | 
			
		||||
 | 
			
		||||
        this._fprintManager = new Fprint.FprintManager();
 | 
			
		||||
        this._smartcardManager = SmartcardManager.getSmartcardManager();
 | 
			
		||||
 | 
			
		||||
        // We check for smartcards right away, since an inserted smartcard
 | 
			
		||||
        // at startup should result in immediately initiating authentication.
 | 
			
		||||
        // This is different than fingeprint readers, where we only check them
 | 
			
		||||
        // after a user has been picked.
 | 
			
		||||
        this._checkForSmartcard();
 | 
			
		||||
 | 
			
		||||
        this._smartcardManager.connect('smartcard-inserted',
 | 
			
		||||
                                       Lang.bind(this, this._checkForSmartcard));
 | 
			
		||||
        this._smartcardManager.connect('smartcard-removed',
 | 
			
		||||
                                       Lang.bind(this, this._checkForSmartcard));
 | 
			
		||||
 | 
			
		||||
        this._messageQueue = [];
 | 
			
		||||
        this._messageQueueTimeoutId = 0;
 | 
			
		||||
        this.hasPendingMessages = false;
 | 
			
		||||
        this.reauthenticating = false;
 | 
			
		||||
        this._realmManager = new Realmd.Manager();
 | 
			
		||||
 | 
			
		||||
        this._failCounter = 0;
 | 
			
		||||
 | 
			
		||||
        this._oVirtCredentialsManager = OVirt.getOVirtCredentialsManager();
 | 
			
		||||
 | 
			
		||||
        if (this._oVirtCredentialsManager.hasToken())
 | 
			
		||||
            this._oVirtUserAuthenticated(this._oVirtCredentialsManager.getToken());
 | 
			
		||||
 | 
			
		||||
        this._oVirtCredentialsManager.connect('user-authenticated',
 | 
			
		||||
                                              Lang.bind(this, this._oVirtUserAuthenticated));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    begin: function(userName, hold) {
 | 
			
		||||
        this._cancellable = new Gio.Cancellable();
 | 
			
		||||
        this._hold = hold;
 | 
			
		||||
        this._userName = userName;
 | 
			
		||||
        this.reauthenticating = false;
 | 
			
		||||
 | 
			
		||||
        this._checkForFingerprintReader();
 | 
			
		||||
 | 
			
		||||
@@ -185,10 +109,8 @@ const ShellUserVerifier = new Lang.Class({
 | 
			
		||||
        if (this._cancellable)
 | 
			
		||||
            this._cancellable.cancel();
 | 
			
		||||
 | 
			
		||||
        if (this._userVerifier) {
 | 
			
		||||
        if (this._userVerifier)
 | 
			
		||||
            this._userVerifier.call_cancel_sync(null);
 | 
			
		||||
            this.clear();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    clear: function() {
 | 
			
		||||
@@ -201,124 +123,33 @@ const ShellUserVerifier = new Lang.Class({
 | 
			
		||||
            this._userVerifier.run_dispose();
 | 
			
		||||
            this._userVerifier = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._clearMessageQueue();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    answerQuery: function(serviceName, answer) {
 | 
			
		||||
        if (!this.hasPendingMessages) {
 | 
			
		||||
            this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
 | 
			
		||||
        } else {
 | 
			
		||||
            let signalId = this.connect('no-more-messages',
 | 
			
		||||
                                        Lang.bind(this, function() {
 | 
			
		||||
                                            this.disconnect(signalId);
 | 
			
		||||
                                            this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
 | 
			
		||||
                                        }));
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
        // Clear any previous message
 | 
			
		||||
        this.emit('show-message', null, null);
 | 
			
		||||
 | 
			
		||||
    _getIntervalForMessage: function(message) {
 | 
			
		||||
        // We probably could be smarter here
 | 
			
		||||
        return message.length * USER_READ_TIME;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    finishMessageQueue: function() {
 | 
			
		||||
        if (!this.hasPendingMessages)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._messageQueue = [];
 | 
			
		||||
 | 
			
		||||
        this.hasPendingMessages = false;
 | 
			
		||||
        this.emit('no-more-messages');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _queueMessageTimeout: function() {
 | 
			
		||||
        if (this._messageQueue.length == 0) {
 | 
			
		||||
            this.finishMessageQueue();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this._messageQueueTimeoutId != 0)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let message = this._messageQueue.shift();
 | 
			
		||||
 | 
			
		||||
        this.emit('show-message', message.text, message.type);
 | 
			
		||||
 | 
			
		||||
        this._messageQueueTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
 | 
			
		||||
                                                       message.interval,
 | 
			
		||||
                                                       Lang.bind(this, function() {
 | 
			
		||||
                                                           this._messageQueueTimeoutId = 0;
 | 
			
		||||
                                                           this._queueMessageTimeout();
 | 
			
		||||
                                                       }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _queueMessage: function(message, messageType) {
 | 
			
		||||
        let interval = this._getIntervalForMessage(message);
 | 
			
		||||
 | 
			
		||||
        this.hasPendingMessages = true;
 | 
			
		||||
        this._messageQueue.push({ text: message, type: messageType, interval: interval });
 | 
			
		||||
        this._queueMessageTimeout();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _clearMessageQueue: function() {
 | 
			
		||||
        this.finishMessageQueue();
 | 
			
		||||
 | 
			
		||||
        if (this._messageQueueTimeoutId != 0) {
 | 
			
		||||
            GLib.source_remove(this._messageQueueTimeoutId);
 | 
			
		||||
            this._messageQueueTimeoutId = 0;
 | 
			
		||||
        }
 | 
			
		||||
        this.emit('show-message', null, MessageType.NONE);
 | 
			
		||||
        this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _checkForFingerprintReader: function() {
 | 
			
		||||
        this._haveFingerprintReader = false;
 | 
			
		||||
 | 
			
		||||
        if (!this._settings.get_boolean(FINGERPRINT_AUTHENTICATION_KEY)) {
 | 
			
		||||
            this._updateDefaultService();
 | 
			
		||||
        if (!this._settings.get_boolean(FINGERPRINT_AUTHENTICATION_KEY))
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._fprintManager.GetDefaultDeviceRemote(Gio.DBusCallFlags.NONE, this._cancellable, Lang.bind(this,
 | 
			
		||||
            function(device, error) {
 | 
			
		||||
                if (!error && device)
 | 
			
		||||
                    this._haveFingerprintReader = true;
 | 
			
		||||
                    this._updateDefaultService();
 | 
			
		||||
            }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _oVirtUserAuthenticated: function(token) {
 | 
			
		||||
        this._preemptingService = OVIRT_SERVICE_NAME;
 | 
			
		||||
        this.emit('ovirt-user-authenticated');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _checkForSmartcard: function() {
 | 
			
		||||
        let smartcardDetected;
 | 
			
		||||
 | 
			
		||||
        if (!this._settings.get_boolean(SMARTCARD_AUTHENTICATION_KEY))
 | 
			
		||||
            smartcardDetected = false;
 | 
			
		||||
        else if (this.reauthenticating)
 | 
			
		||||
            smartcardDetected = this._smartcardManager.hasInsertedLoginToken();
 | 
			
		||||
        else
 | 
			
		||||
            smartcardDetected = this._smartcardManager.hasInsertedTokens();
 | 
			
		||||
 | 
			
		||||
        if (smartcardDetected != this.smartcardDetected) {
 | 
			
		||||
            this.smartcardDetected = smartcardDetected;
 | 
			
		||||
 | 
			
		||||
            if (this.smartcardDetected)
 | 
			
		||||
                this._preemptingService = SMARTCARD_SERVICE_NAME;
 | 
			
		||||
            else if (this._preemptingService == SMARTCARD_SERVICE_NAME)
 | 
			
		||||
                this._preemptingService = null;
 | 
			
		||||
 | 
			
		||||
            this.emit('smartcard-status-changed');
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _reportInitError: function(where, error) {
 | 
			
		||||
        logError(error, where);
 | 
			
		||||
        this._hold.release();
 | 
			
		||||
 | 
			
		||||
        this._queueMessage(_("Authentication error"), MessageType.ERROR);
 | 
			
		||||
        this.emit('show-message', _("Authentication error"), 'login-dialog-message-warning');
 | 
			
		||||
        this._verificationFailed(false);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -339,7 +170,6 @@ const ShellUserVerifier = new Lang.Class({
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.reauthenticating = true;
 | 
			
		||||
        this._connectSignals();
 | 
			
		||||
        this._beginVerification();
 | 
			
		||||
        this._hold.release();
 | 
			
		||||
@@ -370,119 +200,126 @@ const ShellUserVerifier = new Lang.Class({
 | 
			
		||||
        this._userVerifier.connect('verification-complete', Lang.bind(this, this._onVerificationComplete));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getForegroundService: function() {
 | 
			
		||||
        if (this._preemptingService)
 | 
			
		||||
            return this._preemptingService;
 | 
			
		||||
 | 
			
		||||
        return this._defaultService;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    serviceIsForeground: function(serviceName) {
 | 
			
		||||
        return serviceName == this._getForegroundService();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    serviceIsDefault: function(serviceName) {
 | 
			
		||||
        return serviceName == this._defaultService;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateDefaultService: function() {
 | 
			
		||||
        if (this._settings.get_boolean(PASSWORD_AUTHENTICATION_KEY))
 | 
			
		||||
            this._defaultService = PASSWORD_SERVICE_NAME;
 | 
			
		||||
        else if (this.smartcardDetected)
 | 
			
		||||
            this._defaultService = SMARTCARD_SERVICE_NAME;
 | 
			
		||||
        else if (this._haveFingerprintReader)
 | 
			
		||||
            this._defaultService = FINGERPRINT_SERVICE_NAME;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _startService: function(serviceName) {
 | 
			
		||||
    _beginVerification: function() {
 | 
			
		||||
        this._hold.acquire();
 | 
			
		||||
 | 
			
		||||
        if (this._userName) {
 | 
			
		||||
           this._userVerifier.call_begin_verification_for_user(serviceName,
 | 
			
		||||
                                                               this._userName,
 | 
			
		||||
                                                               this._cancellable,
 | 
			
		||||
                                                               Lang.bind(this, function(obj, result) {
 | 
			
		||||
               try {
 | 
			
		||||
                   obj.call_begin_verification_for_user_finish(result);
 | 
			
		||||
               } catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
 | 
			
		||||
                   return;
 | 
			
		||||
               } catch(e) {
 | 
			
		||||
                   this._reportInitError('Failed to start verification for user', e);
 | 
			
		||||
                   return;
 | 
			
		||||
               }
 | 
			
		||||
            this._userVerifier.call_begin_verification_for_user(PASSWORD_SERVICE_NAME,
 | 
			
		||||
                                                                this._userName,
 | 
			
		||||
                                                                this._cancellable,
 | 
			
		||||
                                                                Lang.bind(this, function(obj, result) {
 | 
			
		||||
                try {
 | 
			
		||||
                    obj.call_begin_verification_for_user_finish(result);
 | 
			
		||||
                } catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
 | 
			
		||||
                    return;
 | 
			
		||||
                } catch(e) {
 | 
			
		||||
                    this._reportInitError('Failed to start verification for user', e);
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
               this._hold.release();
 | 
			
		||||
           }));
 | 
			
		||||
                this._hold.release();
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
            if (this._haveFingerprintReader) {
 | 
			
		||||
                this._hold.acquire();
 | 
			
		||||
 | 
			
		||||
                this._userVerifier.call_begin_verification_for_user(FINGERPRINT_SERVICE_NAME,
 | 
			
		||||
                                                                    this._userName,
 | 
			
		||||
                                                                    this._cancellable,
 | 
			
		||||
                                                                    Lang.bind(this, function(obj, result) {
 | 
			
		||||
                    try {
 | 
			
		||||
                        obj.call_begin_verification_for_user_finish(result);
 | 
			
		||||
                    } catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
 | 
			
		||||
                        return;
 | 
			
		||||
                    } catch(e) {
 | 
			
		||||
                        this._reportInitError('Failed to start fingerprint verification for user', e);
 | 
			
		||||
                        return;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    this._hold.release();
 | 
			
		||||
                }));
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
           this._userVerifier.call_begin_verification(serviceName,
 | 
			
		||||
                                                      this._cancellable,
 | 
			
		||||
                                                      Lang.bind(this, function(obj, result) {
 | 
			
		||||
               try {
 | 
			
		||||
                   obj.call_begin_verification_finish(result);
 | 
			
		||||
               } catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
 | 
			
		||||
                   return;
 | 
			
		||||
               } catch(e) {
 | 
			
		||||
                   this._reportInitError('Failed to start verification', e);
 | 
			
		||||
                   return;
 | 
			
		||||
               }
 | 
			
		||||
            this._userVerifier.call_begin_verification(PASSWORD_SERVICE_NAME,
 | 
			
		||||
                                                       this._cancellable,
 | 
			
		||||
                                                       Lang.bind(this, function(obj, result) {
 | 
			
		||||
                try {
 | 
			
		||||
                    obj.call_begin_verification_finish(result);
 | 
			
		||||
                } catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
 | 
			
		||||
                    return;
 | 
			
		||||
                } catch(e) {
 | 
			
		||||
                    this._reportInitError('Failed to start verification', e);
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
               this._hold.release();
 | 
			
		||||
           }));
 | 
			
		||||
                this._hold.release();
 | 
			
		||||
            }));
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _beginVerification: function() {
 | 
			
		||||
        this._startService(this._getForegroundService());
 | 
			
		||||
 | 
			
		||||
        if (this._userName && this._haveFingerprintReader && !this.serviceIsForeground(FINGERPRINT_SERVICE_NAME))
 | 
			
		||||
            this._startService(FINGERPRINT_SERVICE_NAME);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onInfo: function(client, serviceName, info) {
 | 
			
		||||
        if (this.serviceIsForeground(serviceName)) {
 | 
			
		||||
            this._queueMessage(info, MessageType.INFO);
 | 
			
		||||
        } else if (serviceName == FINGERPRINT_SERVICE_NAME &&
 | 
			
		||||
        // We don't display fingerprint messages, because they
 | 
			
		||||
        // have words like UPEK in them. Instead we use the messages
 | 
			
		||||
        // as a cue to display our own message.
 | 
			
		||||
        if (serviceName == FINGERPRINT_SERVICE_NAME &&
 | 
			
		||||
            this._haveFingerprintReader) {
 | 
			
		||||
            // We don't show fingerprint messages directly since it's
 | 
			
		||||
            // not the main auth service. Instead we use the messages
 | 
			
		||||
            // as a cue to display our own message.
 | 
			
		||||
 | 
			
		||||
            // Translators: this message is shown below the password entry field
 | 
			
		||||
            // to indicate the user can swipe their finger instead
 | 
			
		||||
            this._queueMessage(_("(or swipe finger)"), MessageType.HINT);
 | 
			
		||||
            this.emit('show-login-hint', _("(or swipe finger)"));
 | 
			
		||||
        } else if (serviceName == PASSWORD_SERVICE_NAME) {
 | 
			
		||||
            this.emit('show-message', info, 'login-dialog-message-info');
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onProblem: function(client, serviceName, problem) {
 | 
			
		||||
        if (!this.serviceIsForeground(serviceName))
 | 
			
		||||
        // we don't want to show auth failed messages to
 | 
			
		||||
        // users who haven't enrolled their fingerprint.
 | 
			
		||||
        if (serviceName != PASSWORD_SERVICE_NAME)
 | 
			
		||||
            return;
 | 
			
		||||
        this.emit('show-message', problem, 'login-dialog-message-warning');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
        this._queueMessage(problem, MessageType.ERROR);
 | 
			
		||||
    _showRealmLoginHint: function() {
 | 
			
		||||
        if (this._realmManager.loginFormat) {
 | 
			
		||||
            let hint = this._realmManager.loginFormat;
 | 
			
		||||
 | 
			
		||||
            hint = hint.replace(/%U/g, 'user');
 | 
			
		||||
            hint = hint.replace(/%D/g, 'DOMAIN');
 | 
			
		||||
            hint = hint.replace(/%[^UD]/g, '');
 | 
			
		||||
 | 
			
		||||
            // Translators: this message is shown below the username entry field
 | 
			
		||||
            // to clue the user in on how to login to the local network realm
 | 
			
		||||
            this.emit('show-login-hint',
 | 
			
		||||
                      _("(e.g., user or %s)").format(hint));
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onInfoQuery: function(client, serviceName, question) {
 | 
			
		||||
        if (!this.serviceIsForeground(serviceName))
 | 
			
		||||
        // We only expect questions to come from the main auth service
 | 
			
		||||
        if (serviceName != PASSWORD_SERVICE_NAME)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._showRealmLoginHint();
 | 
			
		||||
        this._realmLoginHintSignalId = this._realmManager.connect('login-format-changed',
 | 
			
		||||
                                                                  Lang.bind(this, this._showRealmLoginHint));
 | 
			
		||||
 | 
			
		||||
        this.emit('ask-question', serviceName, question, '');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onSecretInfoQuery: function(client, serviceName, secretQuestion) {
 | 
			
		||||
        if (!this.serviceIsForeground(serviceName))
 | 
			
		||||
        // We only expect secret requests to come from the main auth service
 | 
			
		||||
        if (serviceName != PASSWORD_SERVICE_NAME)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (serviceName == OVIRT_SERVICE_NAME) {
 | 
			
		||||
            // The only question asked by this service is "Token?"
 | 
			
		||||
            this.answerQuery(serviceName, this._oVirtCredentialsManager.getToken());
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.emit('ask-question', serviceName, secretQuestion, '\u25cf');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onReset: function() {
 | 
			
		||||
        this.clear();
 | 
			
		||||
 | 
			
		||||
        // Clear previous attempts to authenticate
 | 
			
		||||
        this._failCounter = 0;
 | 
			
		||||
        this._updateDefaultService();
 | 
			
		||||
 | 
			
		||||
        this.emit('reset');
 | 
			
		||||
    },
 | 
			
		||||
@@ -491,15 +328,6 @@ const ShellUserVerifier = new Lang.Class({
 | 
			
		||||
        this.emit('verification-complete');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _cancelAndReset: function() {
 | 
			
		||||
        this.cancel();
 | 
			
		||||
        this._onReset();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _retry: function() {
 | 
			
		||||
        this.begin(this._userName, new Batch.Hold());
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _verificationFailed: function(retry) {
 | 
			
		||||
        // For Not Listed / enterprise logins, immediately reset
 | 
			
		||||
        // the dialog
 | 
			
		||||
@@ -511,47 +339,34 @@ const ShellUserVerifier = new Lang.Class({
 | 
			
		||||
            this._failCounter < this._settings.get_int(ALLOWED_FAILURES_KEY);
 | 
			
		||||
 | 
			
		||||
        if (canRetry) {
 | 
			
		||||
            if (!this.hasPendingMessages) {
 | 
			
		||||
                this._retry();
 | 
			
		||||
            } else {
 | 
			
		||||
                let signalId = this.connect('no-more-messages',
 | 
			
		||||
                                            Lang.bind(this, function() {
 | 
			
		||||
                                                this.disconnect(signalId);
 | 
			
		||||
                                                this._retry();
 | 
			
		||||
                                            }));
 | 
			
		||||
            }
 | 
			
		||||
            this.clear();
 | 
			
		||||
            this.begin(this._userName, new Batch.Hold());
 | 
			
		||||
        } else {
 | 
			
		||||
            if (!this.hasPendingMessages) {
 | 
			
		||||
                this._cancelAndReset();
 | 
			
		||||
            } else {
 | 
			
		||||
                let signalId = this.connect('no-more-messages',
 | 
			
		||||
                                            Lang.bind(this, function() {
 | 
			
		||||
                                                this.disconnect(signalId);
 | 
			
		||||
                                                this._cancelAndReset();
 | 
			
		||||
                                            }));
 | 
			
		||||
            }
 | 
			
		||||
            // Allow some time to see the message, then reset everything
 | 
			
		||||
            Mainloop.timeout_add(3000, Lang.bind(this, function() {
 | 
			
		||||
                this.cancel();
 | 
			
		||||
 | 
			
		||||
                this._onReset();
 | 
			
		||||
            }));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.emit('verification-failed');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onConversationStopped: function(client, serviceName) {
 | 
			
		||||
        // If the login failed with the preauthenticated oVirt credentials
 | 
			
		||||
        // then discard the credentials and revert to default authentication
 | 
			
		||||
        // mechanism.
 | 
			
		||||
        if (this.serviceIsForeground(OVIRT_SERVICE_NAME)) {
 | 
			
		||||
            this._oVirtCredentialsManager.resetToken();
 | 
			
		||||
            this._preemptingService = null;
 | 
			
		||||
            this._verificationFailed(false);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // if the password service fails, then cancel everything.
 | 
			
		||||
        // But if, e.g., fingerprint fails, still give
 | 
			
		||||
        // password authentication a chance to succeed
 | 
			
		||||
        if (this.serviceIsForeground(serviceName)) {
 | 
			
		||||
        if (serviceName == PASSWORD_SERVICE_NAME) {
 | 
			
		||||
            this._verificationFailed(true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.emit('hide-login-hint');
 | 
			
		||||
 | 
			
		||||
        if (this._realmLoginHintSignalId) {
 | 
			
		||||
            this._realmManager.disconnect(this._realmLoginHintSignalId);
 | 
			
		||||
            this._realmLoginHintSignalId = 0;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(ShellUserVerifier.prototype);
 | 
			
		||||
 
 | 
			
		||||
@@ -174,15 +174,10 @@ const ExtensionFinder = new Lang.Class({
 | 
			
		||||
        this.emit('extension-found', extension);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _extensionsLoaded: function() {
 | 
			
		||||
        this.emit('extensions-loaded');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    scanExtensions: function() {
 | 
			
		||||
        let perUserDir = Gio.File.new_for_path(global.userdatadir);
 | 
			
		||||
        FileUtils.collectFromDatadirsAsync('extensions',
 | 
			
		||||
                                           { processFile: Lang.bind(this, this._loadExtension),
 | 
			
		||||
                                             loadedCallback: Lang.bind(this, this._extensionsLoaded),
 | 
			
		||||
                                             includeUserDir: true,
 | 
			
		||||
                                             data: perUserDir });
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -117,6 +117,7 @@ function recursivelyMoveDir(srcDir, destDir) {
 | 
			
		||||
        let type = info.get_file_type();
 | 
			
		||||
        let srcChild = srcDir.get_child(info.get_name());
 | 
			
		||||
        let destChild = destDir.get_child(info.get_name());
 | 
			
		||||
        log([srcChild.get_path(), destChild.get_path()]);
 | 
			
		||||
        if (type == Gio.FileType.REGULAR)
 | 
			
		||||
            srcChild.move(destChild, Gio.FileCopyFlags.NONE, null, null);
 | 
			
		||||
        else if (type == Gio.FileType.DIRECTORY)
 | 
			
		||||
 
 | 
			
		||||
@@ -58,7 +58,6 @@ const SessionManagerIface = <interface name="org.gnome.SessionManager">
 | 
			
		||||
    <arg type="u" direction="in" />
 | 
			
		||||
    <arg type="b" direction="out" />
 | 
			
		||||
</method>
 | 
			
		||||
<property name="SessionIsActive" type="b" access="read"/>
 | 
			
		||||
<signal name="InhibitorAdded">
 | 
			
		||||
    <arg type="o" direction="out"/>
 | 
			
		||||
</signal>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										144
									
								
								js/misc/hash.js
									
									
									
									
									
								
							
							
						
						@@ -1,144 +0,0 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const System = imports.system;
 | 
			
		||||
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
 | 
			
		||||
// This is an implementation of EcmaScript SameValue algorithm,
 | 
			
		||||
// which returns true if two values are not observably distinguishable.
 | 
			
		||||
// It was taken from http://wiki.ecmascript.org/doku.php?id=harmony:egal
 | 
			
		||||
//
 | 
			
		||||
// In the future, we may want to use the 'is' operator instead.
 | 
			
		||||
function _sameValue(x, y) {
 | 
			
		||||
    if (x === y) {
 | 
			
		||||
        // 0 === -0, but they are not identical
 | 
			
		||||
        return x !== 0 || 1 / x === 1 / y;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // NaN !== NaN, but they are identical.
 | 
			
		||||
    // NaNs are the only non-reflexive value, i.e., if x !== x,
 | 
			
		||||
    // then x is a NaN.
 | 
			
		||||
    // isNaN is broken: it converts its argument to number, so
 | 
			
		||||
    // isNaN("foo") => true
 | 
			
		||||
    return x !== x && y !== y;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const _hashers = {
 | 
			
		||||
    object: function(o) { return o ? System.addressOf(o) : 'null'; },
 | 
			
		||||
    function: function(f) { return System.addressOf(f); },
 | 
			
		||||
    string: function(s) { return s; },
 | 
			
		||||
    number: function(n) { return String(n); },
 | 
			
		||||
    undefined: function() { return 'undefined'; },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Map is meant to be similar in usage to ES6 Map, which is
 | 
			
		||||
   described at http://wiki.ecmascript.org/doku.php?id=harmony:simple_maps_and_sets,
 | 
			
		||||
   without requiring more than ES5 + Gjs extensions.
 | 
			
		||||
 | 
			
		||||
   Known differences from other implementations:
 | 
			
		||||
   Polyfills around the web usually implement HashMaps for
 | 
			
		||||
   primitive values and reversed WeakMaps for object keys,
 | 
			
		||||
   but we want real maps with real O(1) semantics in all cases,
 | 
			
		||||
   and the easiest way is to have different hashers for different
 | 
			
		||||
   types.
 | 
			
		||||
 | 
			
		||||
   Known differences from the ES6 specification:
 | 
			
		||||
   - Map is a Lang.Class, not a ES6 class, so inheritance,
 | 
			
		||||
     prototype, sealing, etc. work differently.
 | 
			
		||||
   - items(), keys() and values() don't return iterators,
 | 
			
		||||
     they return actual arrays, so they incur a full copy everytime
 | 
			
		||||
     they're called, and they don't see changes if you mutate
 | 
			
		||||
     the table while iterating
 | 
			
		||||
     (admittedly, the ES6 spec is a bit unclear on this, and
 | 
			
		||||
     the reference code would just blow up)
 | 
			
		||||
*/
 | 
			
		||||
const Map = new Lang.Class({
 | 
			
		||||
    Name: 'Map',
 | 
			
		||||
 | 
			
		||||
    _init: function(iterable) {
 | 
			
		||||
        this._pool = { };
 | 
			
		||||
        this._size = 0;
 | 
			
		||||
 | 
			
		||||
        if (iterable) {
 | 
			
		||||
            for (let i = 0; i < iterable.length; i++) {
 | 
			
		||||
                let [key, value] = iterable[i];
 | 
			
		||||
                this.set(key, value);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _hashKey: function(key) {
 | 
			
		||||
        let type = typeof(key);
 | 
			
		||||
        return type + ':' + _hashers[type](key);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _internalGet: function(key) {
 | 
			
		||||
        let hash = this._hashKey(key);
 | 
			
		||||
        let node = this._pool[hash];
 | 
			
		||||
 | 
			
		||||
        if (node && _sameValue(node.key, key))
 | 
			
		||||
            return [true, node.value];
 | 
			
		||||
        else
 | 
			
		||||
            return [false, null];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get: function(key) {
 | 
			
		||||
        return this._internalGet(key)[1];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    has: function(key) {
 | 
			
		||||
        return this._internalGet(key)[0];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    set: function(key, value) {
 | 
			
		||||
        let hash = this._hashKey(key);
 | 
			
		||||
        let node = this._pool[hash];
 | 
			
		||||
 | 
			
		||||
        if (node) {
 | 
			
		||||
            node.key = key;
 | 
			
		||||
            node.value = value;
 | 
			
		||||
        } else {
 | 
			
		||||
            this._pool[hash] = { key: key, value: value };
 | 
			
		||||
            this._size++;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    delete: function(key) {
 | 
			
		||||
        let hash = this._hashKey(key);
 | 
			
		||||
        let node = this._pool[hash];
 | 
			
		||||
 | 
			
		||||
        if (node && _sameValue(node.key, key)) {
 | 
			
		||||
            delete this._pool[hash];
 | 
			
		||||
            this._size--;
 | 
			
		||||
            return [node.key, node.value];
 | 
			
		||||
        } else {
 | 
			
		||||
            return [null, null];
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    keys: function() {
 | 
			
		||||
        let pool = this._pool;
 | 
			
		||||
        return Object.getOwnPropertyNames(pool).map(function(hash) {
 | 
			
		||||
            return pool[hash].key;
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    values: function() {
 | 
			
		||||
        let pool = this._pool;
 | 
			
		||||
        return Object.getOwnPropertyNames(pool).map(function(hash) {
 | 
			
		||||
            return pool[hash].value;
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    items: function() {
 | 
			
		||||
        let pool = this._pool;
 | 
			
		||||
        return Object.getOwnPropertyNames(pool).map(function(hash) {
 | 
			
		||||
            return [pool[hash].key, pool[hash].value];
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    size: function() {
 | 
			
		||||
        return this._size;
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
@@ -5,32 +5,27 @@ const Gio = imports.gi.Gio;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Mainloop = imports.mainloop;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const UPowerGlib = imports.gi.UPowerGlib;
 | 
			
		||||
 | 
			
		||||
const SystemdLoginManagerIface = <interface name='org.freedesktop.login1.Manager'>
 | 
			
		||||
<method name='PowerOff'>
 | 
			
		||||
    <arg type='b' direction='in'/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name='Reboot'>
 | 
			
		||||
    <arg type='b' direction='in'/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name='Suspend'>
 | 
			
		||||
    <arg type='b' direction='in'/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name='CanPowerOff'>
 | 
			
		||||
    <arg type='s' direction='out'/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name='CanReboot'>
 | 
			
		||||
    <arg type='s' direction='out'/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name='CanSuspend'>
 | 
			
		||||
    <arg type='s' direction='out'/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name='Inhibit'>
 | 
			
		||||
    <arg type='s' direction='in'/>
 | 
			
		||||
    <arg type='s' direction='in'/>
 | 
			
		||||
    <arg type='s' direction='in'/>
 | 
			
		||||
    <arg type='s' direction='in'/>
 | 
			
		||||
    <arg type='h' direction='out'/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name='GetSession'>
 | 
			
		||||
    <arg type='s' direction='in'/>
 | 
			
		||||
    <arg type='o' direction='out'/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name='ListSessions'>
 | 
			
		||||
    <arg name='sessions' type='a(susso)' direction='out'/>
 | 
			
		||||
</method>
 | 
			
		||||
<signal name='PrepareForSleep'>
 | 
			
		||||
    <arg type='b' direction='out'/>
 | 
			
		||||
</signal>
 | 
			
		||||
</interface>;
 | 
			
		||||
 | 
			
		||||
const SystemdLoginSessionIface = <interface name='org.freedesktop.login1.Session'>
 | 
			
		||||
@@ -56,6 +51,12 @@ const ConsoleKitManagerIface = <interface name='org.freedesktop.ConsoleKit.Manag
 | 
			
		||||
</interface>;
 | 
			
		||||
 | 
			
		||||
const ConsoleKitSessionIface = <interface name='org.freedesktop.ConsoleKit.Session'>
 | 
			
		||||
<method name='IsActive'>
 | 
			
		||||
    <arg type='b' direction='out' />
 | 
			
		||||
</method>
 | 
			
		||||
<signal name='ActiveChanged'>
 | 
			
		||||
    <arg type='b' direction='out' />
 | 
			
		||||
</signal>
 | 
			
		||||
<signal name='Lock' />
 | 
			
		||||
<signal name='Unlock' />
 | 
			
		||||
</interface>;
 | 
			
		||||
@@ -64,38 +65,7 @@ const ConsoleKitSession = Gio.DBusProxy.makeProxyWrapper(ConsoleKitSessionIface)
 | 
			
		||||
const ConsoleKitManager = Gio.DBusProxy.makeProxyWrapper(ConsoleKitManagerIface);
 | 
			
		||||
 | 
			
		||||
function haveSystemd() {
 | 
			
		||||
    return GLib.access("/run/systemd/seats", 0) >= 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function versionCompare(required, reference) {
 | 
			
		||||
    required = required.split('.');
 | 
			
		||||
    reference = reference.split('.');
 | 
			
		||||
 | 
			
		||||
    for (let i = 0; i < required.length; i++) {
 | 
			
		||||
        let requiredInt = parseInt(required[i]);
 | 
			
		||||
        let referenceInt = parseInt(reference[i]);
 | 
			
		||||
        if (requiredInt != referenceInt)
 | 
			
		||||
            return requiredInt < referenceInt;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function canLock() {
 | 
			
		||||
    try {
 | 
			
		||||
        let params = GLib.Variant.new('(ss)', ['org.gnome.DisplayManager.Manager', 'Version']);
 | 
			
		||||
        let result = Gio.DBus.system.call_sync('org.gnome.DisplayManager',
 | 
			
		||||
                                               '/org/gnome/DisplayManager/Manager',
 | 
			
		||||
                                               'org.freedesktop.DBus.Properties',
 | 
			
		||||
                                               'Get', params, null,
 | 
			
		||||
                                               Gio.DBusCallFlags.NONE,
 | 
			
		||||
                                               -1, null);
 | 
			
		||||
 | 
			
		||||
        let version = result.deep_unpack()[0].deep_unpack();
 | 
			
		||||
        return versionCompare('3.5.91', version);
 | 
			
		||||
    } catch(e) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    return GLib.access("/sys/fs/cgroup/systemd", 0) >= 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
let _loginManager = null;
 | 
			
		||||
@@ -123,30 +93,42 @@ const LoginManagerSystemd = new Lang.Class({
 | 
			
		||||
        this._proxy = new SystemdLoginManager(Gio.DBus.system,
 | 
			
		||||
                                              'org.freedesktop.login1',
 | 
			
		||||
                                              '/org/freedesktop/login1');
 | 
			
		||||
        this._proxy.connectSignal('PrepareForSleep',
 | 
			
		||||
                                  Lang.bind(this, this._prepareForSleep));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Having this function is a bit of a hack since the Systemd and ConsoleKit
 | 
			
		||||
    // session objects have different interfaces - but in both cases there are
 | 
			
		||||
    // Lock/Unlock signals, and that's all we count upon at the moment.
 | 
			
		||||
    getCurrentSessionProxy: function(callback) {
 | 
			
		||||
        if (this._currentSession) {
 | 
			
		||||
            callback (this._currentSession);
 | 
			
		||||
            return;
 | 
			
		||||
    getCurrentSessionProxy: function() {
 | 
			
		||||
        if (!this._currentSession) {
 | 
			
		||||
            this._currentSession = new SystemdLoginSession(Gio.DBus.system,
 | 
			
		||||
                                                           'org.freedesktop.login1',
 | 
			
		||||
                                                           '/org/freedesktop/login1/session/' +
 | 
			
		||||
                                                           GLib.getenv('XDG_SESSION_ID'));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._proxy.GetSessionRemote(GLib.getenv('XDG_SESSION_ID'), Lang.bind(this,
 | 
			
		||||
            function(result, error) {
 | 
			
		||||
                if (error) {
 | 
			
		||||
                    logError(error, 'Could not get a proxy for the current session');
 | 
			
		||||
                } else {
 | 
			
		||||
                    this._currentSession = new SystemdLoginSession(Gio.DBus.system,
 | 
			
		||||
                                                                   'org.freedesktop.login1',
 | 
			
		||||
                                                                   result[0]);
 | 
			
		||||
                    callback(this._currentSession);
 | 
			
		||||
                }
 | 
			
		||||
            }));
 | 
			
		||||
        return this._currentSession;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get sessionActive() {
 | 
			
		||||
        return Shell.session_is_active_for_systemd();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    canPowerOff: function(asyncCallback) {
 | 
			
		||||
        this._proxy.CanPowerOffRemote(function(result, error) {
 | 
			
		||||
            if (error)
 | 
			
		||||
                asyncCallback(false);
 | 
			
		||||
            else
 | 
			
		||||
                asyncCallback(result[0] != 'no');
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    canReboot: function(asyncCallback) {
 | 
			
		||||
        this._proxy.CanRebootRemote(function(result, error) {
 | 
			
		||||
            if (error)
 | 
			
		||||
                asyncCallback(false);
 | 
			
		||||
            else
 | 
			
		||||
                asyncCallback(result[0] != 'no');
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    canSuspend: function(asyncCallback) {
 | 
			
		||||
@@ -158,44 +140,18 @@ const LoginManagerSystemd = new Lang.Class({
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    listSessions: function(asyncCallback) {
 | 
			
		||||
        this._proxy.ListSessionsRemote(function(result, error) {
 | 
			
		||||
            if (error)
 | 
			
		||||
                asyncCallback([]);
 | 
			
		||||
            else
 | 
			
		||||
                asyncCallback(result[0]);
 | 
			
		||||
        });
 | 
			
		||||
    powerOff: function() {
 | 
			
		||||
        this._proxy.PowerOffRemote(true);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    reboot: function() {
 | 
			
		||||
        this._proxy.RebootRemote(true);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    suspend: function() {
 | 
			
		||||
        this._proxy.SuspendRemote(true);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    inhibit: function(reason, callback) {
 | 
			
		||||
        let inVariant = GLib.Variant.new('(ssss)',
 | 
			
		||||
                                         ['sleep',
 | 
			
		||||
                                          'GNOME Shell',
 | 
			
		||||
                                          reason,
 | 
			
		||||
                                          'delay']);
 | 
			
		||||
        this._proxy.call_with_unix_fd_list('Inhibit', inVariant, 0, -1, null, null,
 | 
			
		||||
            Lang.bind(this, function(proxy, result) {
 | 
			
		||||
                let fd = -1;
 | 
			
		||||
                try {
 | 
			
		||||
                    let [outVariant, fdList] = proxy.call_with_unix_fd_list_finish(result);
 | 
			
		||||
                    fd = fdList.steal_fds(outVariant.deep_unpack())[0];
 | 
			
		||||
                    callback(new Gio.UnixInputStream({ fd: fd }));
 | 
			
		||||
                } catch(e) {
 | 
			
		||||
                    logError(e, "Error getting systemd inhibitor");
 | 
			
		||||
                    callback(null);
 | 
			
		||||
                }
 | 
			
		||||
            }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _prepareForSleep: function(proxy, sender, [aboutToSuspend]) {
 | 
			
		||||
        this.emit('prepare-for-sleep', aboutToSuspend);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(LoginManagerSystemd.prototype);
 | 
			
		||||
 | 
			
		||||
const LoginManagerConsoleKit = new Lang.Class({
 | 
			
		||||
    Name: 'LoginManagerConsoleKit',
 | 
			
		||||
@@ -204,45 +160,70 @@ const LoginManagerConsoleKit = new Lang.Class({
 | 
			
		||||
        this._proxy = new ConsoleKitManager(Gio.DBus.system,
 | 
			
		||||
                                            'org.freedesktop.ConsoleKit',
 | 
			
		||||
                                            '/org/freedesktop/ConsoleKit/Manager');
 | 
			
		||||
        this._upClient = new UPowerGlib.Client();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Having this function is a bit of a hack since the Systemd and ConsoleKit
 | 
			
		||||
    // session objects have different interfaces - but in both cases there are
 | 
			
		||||
    // Lock/Unlock signals, and that's all we count upon at the moment.
 | 
			
		||||
    getCurrentSessionProxy: function(callback) {
 | 
			
		||||
        if (this._currentSession) {
 | 
			
		||||
            callback (this._currentSession);
 | 
			
		||||
            return;
 | 
			
		||||
    getCurrentSessionProxy: function() {
 | 
			
		||||
        if (!this._currentSession) {
 | 
			
		||||
            let [currentSessionId] = this._proxy.GetCurrentSessionSync();
 | 
			
		||||
            this._currentSession = new ConsoleKitSession(Gio.DBus.system,
 | 
			
		||||
                                                         'org.freedesktop.ConsoleKit',
 | 
			
		||||
                                                         currentSessionId);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._proxy.GetCurrentSessionRemote(Lang.bind(this,
 | 
			
		||||
            function(result, error) {
 | 
			
		||||
                if (error) {
 | 
			
		||||
                    logError(error, 'Could not get a proxy for the current session');
 | 
			
		||||
                } else {
 | 
			
		||||
                    this._currentSession = new ConsoleKitSession(Gio.DBus.system,
 | 
			
		||||
                                                                 'org.freedesktop.ConsoleKit',
 | 
			
		||||
                                                                 result[0]);
 | 
			
		||||
                    callback(this._currentSession);
 | 
			
		||||
                }
 | 
			
		||||
            }));
 | 
			
		||||
        return this._currentSession;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get sessionActive() {
 | 
			
		||||
        if (this._sessionActive !== undefined)
 | 
			
		||||
            return this._sessionActive;
 | 
			
		||||
 | 
			
		||||
        let session = this.getCurrentSessionProxy();
 | 
			
		||||
        session.connectSignal('ActiveChanged', Lang.bind(this, function(object, senderName, [isActive]) {
 | 
			
		||||
            this._sessionActive = isActive;
 | 
			
		||||
        }));
 | 
			
		||||
        [this._sessionActive] = session.IsActiveSync();
 | 
			
		||||
 | 
			
		||||
        return this._sessionActive;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    canPowerOff: function(asyncCallback) {
 | 
			
		||||
        this._proxy.CanStopRemote(function(result, error) {
 | 
			
		||||
            if (error)
 | 
			
		||||
                asyncCallback(false);
 | 
			
		||||
            else
 | 
			
		||||
                asyncCallback(result[0]);
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    canReboot: function(asyncCallback) {
 | 
			
		||||
        this._proxy.CanRestartRemote(function(result, error) {
 | 
			
		||||
            if (error)
 | 
			
		||||
                asyncCallback(false);
 | 
			
		||||
            else
 | 
			
		||||
                asyncCallback(result[0]);
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    canSuspend: function(asyncCallback) {
 | 
			
		||||
        asyncCallback(false);
 | 
			
		||||
        Mainloop.idle_add(Lang.bind(this, function() {
 | 
			
		||||
            asyncCallback(this._upClient.get_can_suspend());
 | 
			
		||||
            return false;
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    listSessions: function(asyncCallback) {
 | 
			
		||||
        asyncCallback([]);
 | 
			
		||||
    powerOff: function() {
 | 
			
		||||
        this._proxy.StopRemote();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    reboot: function() {
 | 
			
		||||
        this._proxy.RestartRemote();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    suspend: function() {
 | 
			
		||||
        this.emit('prepare-for-sleep', true);
 | 
			
		||||
        this.emit('prepare-for-sleep', false);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    inhibit: function(reason, callback) {
 | 
			
		||||
        callback(null);
 | 
			
		||||
        this._upClient.suspend_sync(null);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(LoginManagerConsoleKit.prototype);
 | 
			
		||||
 
 | 
			
		||||
@@ -2,93 +2,9 @@
 | 
			
		||||
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const NMGtk = imports.gi.NMGtk;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
 | 
			
		||||
// _getMobileProvidersDatabase:
 | 
			
		||||
//
 | 
			
		||||
// Gets the database of mobile providers, with references between MCCMNC/SID and
 | 
			
		||||
// operator name
 | 
			
		||||
//
 | 
			
		||||
let _mpd;
 | 
			
		||||
function _getMobileProvidersDatabase() {
 | 
			
		||||
    if (_mpd == null) {
 | 
			
		||||
        try {
 | 
			
		||||
            _mpd = new NMGtk.MobileProvidersDatabase();
 | 
			
		||||
            _mpd.init(null);
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
            log(e.message);
 | 
			
		||||
            _mpd = null;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return _mpd;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// _findProviderForMccMnc:
 | 
			
		||||
// @operator_name: operator name
 | 
			
		||||
// @operator_code: operator code
 | 
			
		||||
//
 | 
			
		||||
// Given an operator name string (which may not be a real operator name) and an
 | 
			
		||||
// operator code string, tries to find a proper operator name to display.
 | 
			
		||||
//
 | 
			
		||||
function _findProviderForMccMnc(operator_name, operator_code) {
 | 
			
		||||
    if (operator_name) {
 | 
			
		||||
        if (operator_name.length != 0 &&
 | 
			
		||||
            (operator_name.length > 6 || operator_name.length < 5)) {
 | 
			
		||||
            // this looks like a valid name, i.e. not an MCCMNC (that some
 | 
			
		||||
            // devices return when not yet connected
 | 
			
		||||
            return operator_name;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (isNaN(parseInt(operator_name))) {
 | 
			
		||||
            // name is definitely not a MCCMNC, so it may be a name
 | 
			
		||||
            // after all; return that
 | 
			
		||||
            return operator_name;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let needle;
 | 
			
		||||
    if ((!operator_name || operator_name.length == 0) && operator_code)
 | 
			
		||||
        needle = operator_code;
 | 
			
		||||
    else if (operator_name && (operator_name.length == 6 || operator_name.length == 5))
 | 
			
		||||
        needle = operator_name;
 | 
			
		||||
    else // nothing to search
 | 
			
		||||
        return null;
 | 
			
		||||
 | 
			
		||||
    let mpd = _getMobileProvidersDatabase();
 | 
			
		||||
    if (mpd) {
 | 
			
		||||
        let provider = mpd.lookup_3gpp_mcc_mnc(needle);
 | 
			
		||||
        if (provider)
 | 
			
		||||
            return provider.get_name();
 | 
			
		||||
    }
 | 
			
		||||
    return null;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// _findProviderForSid:
 | 
			
		||||
// @sid: System Identifier of the serving CDMA network
 | 
			
		||||
//
 | 
			
		||||
// Tries to find the operator name corresponding to the given SID
 | 
			
		||||
//
 | 
			
		||||
function _findProviderForSid(sid) {
 | 
			
		||||
    if (sid == 0)
 | 
			
		||||
        return null;
 | 
			
		||||
 | 
			
		||||
    let mpd = _getMobileProvidersDatabase();
 | 
			
		||||
    if (mpd) {
 | 
			
		||||
        let provider = mpd.lookup_cdma_sid(sid);
 | 
			
		||||
        if (provider)
 | 
			
		||||
            return provider.get_name();
 | 
			
		||||
    }
 | 
			
		||||
    return null;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
// Support for the old ModemManager interface (MM < 0.7)
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// The following are not the complete interfaces, just the methods we need
 | 
			
		||||
// (or may need in the future)
 | 
			
		||||
 | 
			
		||||
@@ -126,6 +42,76 @@ const ModemCdmaInterface = <interface name="org.freedesktop.ModemManager.Modem.C
 | 
			
		||||
 | 
			
		||||
const ModemCdmaProxy = Gio.DBusProxy.makeProxyWrapper(ModemCdmaInterface);
 | 
			
		||||
 | 
			
		||||
let _providersTable;
 | 
			
		||||
function _getProvidersTable() {
 | 
			
		||||
    if (_providersTable)
 | 
			
		||||
        return _providersTable;
 | 
			
		||||
    return _providersTable = Shell.mobile_providers_parse(null,null);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function findProviderForMCCMNC(table, needle) {
 | 
			
		||||
    let needlemcc = needle.substring(0, 3);
 | 
			
		||||
    let needlemnc = needle.substring(3, needle.length);
 | 
			
		||||
 | 
			
		||||
    let name2, name3;
 | 
			
		||||
    for (let iter in table) {
 | 
			
		||||
        let country = table[iter];
 | 
			
		||||
        let providers = country.get_providers();
 | 
			
		||||
 | 
			
		||||
        // Search through each country's providers
 | 
			
		||||
        for (let i = 0; i < providers.length; i++) {
 | 
			
		||||
            let provider = providers[i];
 | 
			
		||||
 | 
			
		||||
            // Search through MCC/MNC list
 | 
			
		||||
            let list = provider.get_gsm_mcc_mnc();
 | 
			
		||||
            for (let j = 0; j < list.length; j++) {
 | 
			
		||||
                let mccmnc = list[j];
 | 
			
		||||
 | 
			
		||||
                // Match both 2-digit and 3-digit MNC; prefer a
 | 
			
		||||
                // 3-digit match if found, otherwise a 2-digit one.
 | 
			
		||||
                if (mccmnc.mcc != needlemcc)
 | 
			
		||||
                    continue;  // MCC was wrong
 | 
			
		||||
 | 
			
		||||
                if (!name3 && needle.length == 6 && needlemnc == mccmnc.mnc)
 | 
			
		||||
                    name3 = provider.name;
 | 
			
		||||
 | 
			
		||||
                if (!name2 && needlemnc.substring(0, 2) == mccmnc.mnc.substring(0, 2))
 | 
			
		||||
                    name2 = provider.name;
 | 
			
		||||
 | 
			
		||||
                if (name2 && name3)
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return name3 || name2 || null;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function findProviderForSid(table, sid) {
 | 
			
		||||
    if (sid == 0)
 | 
			
		||||
        return null;
 | 
			
		||||
 | 
			
		||||
    // Search through each country
 | 
			
		||||
    for (let iter in table) {
 | 
			
		||||
        let country = table[iter];
 | 
			
		||||
        let providers = country.get_providers();
 | 
			
		||||
 | 
			
		||||
        // Search through each country's providers
 | 
			
		||||
        for (let i = 0; i < providers.length; i++) {
 | 
			
		||||
            let provider = providers[i];
 | 
			
		||||
            let cdma_sid = provider.get_cdma_sid();
 | 
			
		||||
 | 
			
		||||
            // Search through CDMA SID list
 | 
			
		||||
            for (let j = 0; j < cdma_sid.length; j++) {
 | 
			
		||||
                if (cdma_sid[j] == sid)
 | 
			
		||||
                    return provider.name;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return null;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const ModemGsm = new Lang.Class({
 | 
			
		||||
    Name: 'ModemGsm',
 | 
			
		||||
 | 
			
		||||
@@ -141,7 +127,7 @@ const ModemGsm = new Lang.Class({
 | 
			
		||||
            this.emit('notify::signal-quality');
 | 
			
		||||
        }));
 | 
			
		||||
        this._proxy.connectSignal('RegistrationInfo', Lang.bind(this, function(proxy, sender, [status, code, name]) {
 | 
			
		||||
            this.operator_name = _findProviderForMccMnc(name, code);
 | 
			
		||||
            this.operator_name = this._findOperatorName(name, code);
 | 
			
		||||
            this.emit('notify::operator-name');
 | 
			
		||||
        }));
 | 
			
		||||
        this._proxy.GetRegistrationInfoRemote(Lang.bind(this, function([result], err) {
 | 
			
		||||
@@ -151,7 +137,7 @@ const ModemGsm = new Lang.Class({
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let [status, code, name] = result;
 | 
			
		||||
            this.operator_name = _findProviderForMccMnc(name, code);
 | 
			
		||||
            this.operator_name = this._findOperatorName(name, code);
 | 
			
		||||
            this.emit('notify::operator-name');
 | 
			
		||||
        }));
 | 
			
		||||
        this._proxy.GetSignalQualityRemote(Lang.bind(this, function(result, err) {
 | 
			
		||||
@@ -164,6 +150,32 @@ const ModemGsm = new Lang.Class({
 | 
			
		||||
            }
 | 
			
		||||
            this.emit('notify::signal-quality');
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _findOperatorName: function(name, opCode) {
 | 
			
		||||
        if (name) {
 | 
			
		||||
            if (name && name.length != 0 && (name.length > 6 || name.length < 5)) {
 | 
			
		||||
                // this looks like a valid name, i.e. not an MCCMNC (that some
 | 
			
		||||
                // devices return when not yet connected
 | 
			
		||||
                return name;
 | 
			
		||||
            }
 | 
			
		||||
            if (isNaN(parseInt(name))) {
 | 
			
		||||
                // name is definitely not a MCCMNC, so it may be a name
 | 
			
		||||
                // after all; return that
 | 
			
		||||
                return name;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let needle;
 | 
			
		||||
        if ((name == null || name.length == 0) && opCode)
 | 
			
		||||
            needle = opCode;
 | 
			
		||||
        else if (name.length == 6 || name.length == 5)
 | 
			
		||||
            needle = name;
 | 
			
		||||
        else // nothing to search
 | 
			
		||||
            return null;
 | 
			
		||||
 | 
			
		||||
        let table = _getProvidersTable();
 | 
			
		||||
        return findProviderForMCCMNC(table, needle);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(ModemGsm.prototype);
 | 
			
		||||
@@ -203,99 +215,15 @@ const ModemCdma = new Lang.Class({
 | 
			
		||||
                // it will return an error if the device is not connected
 | 
			
		||||
                this.operator_name = null;
 | 
			
		||||
            } else {
 | 
			
		||||
                let [bandClass, band, sid] = result;
 | 
			
		||||
 | 
			
		||||
                this.operator_name = _findProviderForSid(sid)
 | 
			
		||||
                let [bandClass, band, id] = result;
 | 
			
		||||
                if (name.length > 0) {
 | 
			
		||||
                    let table = _getProvidersTable();
 | 
			
		||||
                    this.operator_name = findProviderForSid(table, id);
 | 
			
		||||
                } else
 | 
			
		||||
                    this.operator_name = null;
 | 
			
		||||
            }
 | 
			
		||||
            this.emit('notify::operator-name');
 | 
			
		||||
        }));
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(ModemCdma.prototype);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
// Support for the new ModemManager1 interface (MM >= 0.7)
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
const BroadbandModemInterface = <interface name="org.freedesktop.ModemManager1.Modem">
 | 
			
		||||
<property name="SignalQuality" type="(ub)" access="read" />
 | 
			
		||||
</interface>;
 | 
			
		||||
const BroadbandModemProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModemInterface);
 | 
			
		||||
 | 
			
		||||
const BroadbandModem3gppInterface = <interface name="org.freedesktop.ModemManager1.Modem.Modem3gpp">
 | 
			
		||||
<property name="OperatorCode" type="s" access="read" />
 | 
			
		||||
<property name="OperatorName" type="s" access="read" />
 | 
			
		||||
</interface>;
 | 
			
		||||
const BroadbandModem3gppProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModem3gppInterface);
 | 
			
		||||
 | 
			
		||||
const BroadbandModemCdmaInterface = <interface name="org.freedesktop.ModemManager1.Modem.ModemCdma">
 | 
			
		||||
<property name="Sid" type="u" access="read" />
 | 
			
		||||
</interface>;
 | 
			
		||||
const BroadbandModemCdmaProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModemCdmaInterface);
 | 
			
		||||
 | 
			
		||||
const BroadbandModem = new Lang.Class({
 | 
			
		||||
    Name: 'BroadbandModem',
 | 
			
		||||
 | 
			
		||||
    _init: function(path, capabilities) {
 | 
			
		||||
        this._proxy = new BroadbandModemProxy(Gio.DBus.system, 'org.freedesktop.ModemManager1', path);
 | 
			
		||||
        this._proxy_3gpp = new BroadbandModem3gppProxy(Gio.DBus.system, 'org.freedesktop.ModemManager1', path);
 | 
			
		||||
        this._proxy_cdma = new BroadbandModemCdmaProxy(Gio.DBus.system, 'org.freedesktop.ModemManager1', path);
 | 
			
		||||
        this._capabilities = capabilities;
 | 
			
		||||
 | 
			
		||||
        this._proxy.connect('g-properties-changed', Lang.bind(this, function(proxy, properties) {
 | 
			
		||||
            if ('SignalQuality' in properties.deep_unpack())
 | 
			
		||||
                this._reloadSignalQuality();
 | 
			
		||||
        }));
 | 
			
		||||
        this._reloadSignalQuality();
 | 
			
		||||
 | 
			
		||||
        this._proxy_3gpp.connect('g-properties-changed', Lang.bind(this, function(proxy, properties) {
 | 
			
		||||
            let unpacked = properties.deep_unpack();
 | 
			
		||||
            if ('OperatorName' in unpacked || 'OperatorCode' in unpacked)
 | 
			
		||||
                this._reload3gppOperatorName();
 | 
			
		||||
        }));
 | 
			
		||||
        this._reload3gppOperatorName();
 | 
			
		||||
 | 
			
		||||
        this._proxy_cdma.connect('g-properties-changed', Lang.bind(this, function(proxy, properties) {
 | 
			
		||||
            let unpacked = properties.deep_unpack();
 | 
			
		||||
            if ('Nid' in unpacked || 'Sid' in unpacked)
 | 
			
		||||
                this._reloadCdmaOperatorName();
 | 
			
		||||
        }));
 | 
			
		||||
        this._reloadCdmaOperatorName();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _reloadSignalQuality: function() {
 | 
			
		||||
        let [quality, recent] = this._proxy.SignalQuality;
 | 
			
		||||
        this.signal_quality = quality;
 | 
			
		||||
        this.emit('notify::signal-quality');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _reloadOperatorName: function() {
 | 
			
		||||
        let new_name = "";
 | 
			
		||||
        if (this.operator_name_3gpp && this.operator_name_3gpp.length > 0)
 | 
			
		||||
            new_name += this.operator_name_3gpp;
 | 
			
		||||
 | 
			
		||||
        if (this.operator_name_cdma && this.operator_name_cdma.length > 0) {
 | 
			
		||||
            if (new_name != "")
 | 
			
		||||
                new_name += ", ";
 | 
			
		||||
            new_name += this.operator_name_cdma;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.operator_name = new_name;
 | 
			
		||||
        this.emit('notify::operator-name');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _reload3gppOperatorName: function() {
 | 
			
		||||
        let name = this._proxy_3gpp.OperatorName;
 | 
			
		||||
        let code = this._proxy_3gpp.OperatorCode;
 | 
			
		||||
        this.operator_name_3gpp = _findProviderForMccMnc(name, code);
 | 
			
		||||
        this._reloadOperatorName();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _reloadCdmaOperatorName: function() {
 | 
			
		||||
        let sid = this._proxy_cdma.Sid;
 | 
			
		||||
        this.operator_name_cdma = _findProviderForSid(sid);
 | 
			
		||||
        this._reloadOperatorName();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(BroadbandModem.prototype);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,257 +0,0 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
 | 
			
		||||
// Specified in the D-Bus specification here:
 | 
			
		||||
// http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager
 | 
			
		||||
const ObjectManagerIface = <interface name="org.freedesktop.DBus.ObjectManager">
 | 
			
		||||
  <method name="GetManagedObjects">
 | 
			
		||||
    <arg name="objects" type="a{oa{sa{sv}}}" direction="out"/>
 | 
			
		||||
  </method>
 | 
			
		||||
  <signal name="InterfacesAdded">
 | 
			
		||||
    <arg name="objectPath" type="o"/>
 | 
			
		||||
    <arg name="interfaces" type="a{sa{sv}}" />
 | 
			
		||||
  </signal>
 | 
			
		||||
  <signal name="InterfacesRemoved">
 | 
			
		||||
    <arg name="objectPath" type="o"/>
 | 
			
		||||
    <arg name="interfaces" type="as" />
 | 
			
		||||
  </signal>
 | 
			
		||||
</interface>;
 | 
			
		||||
 | 
			
		||||
const ObjectManagerInfo = Gio.DBusInterfaceInfo.new_for_xml(ObjectManagerIface);
 | 
			
		||||
 | 
			
		||||
const ObjectManager = new Lang.Class({
 | 
			
		||||
    Name: 'ObjectManager',
 | 
			
		||||
    _init: function(params) {
 | 
			
		||||
        params = Params.parse(params, { connection: null,
 | 
			
		||||
                                        name: null,
 | 
			
		||||
                                        objectPath: null,
 | 
			
		||||
                                        knownInterfaces: null,
 | 
			
		||||
                                        cancellable: null,
 | 
			
		||||
                                        onLoaded: null });
 | 
			
		||||
 | 
			
		||||
        this._connection = params.connection;
 | 
			
		||||
        this._serviceName = params.name;
 | 
			
		||||
        this._managerPath = params.objectPath;
 | 
			
		||||
        this._cancellable = params.cancellable;
 | 
			
		||||
 | 
			
		||||
        this._managerProxy = new Gio.DBusProxy({ g_connection: this._connection,
 | 
			
		||||
                                                 g_interface_name: ObjectManagerInfo.name,
 | 
			
		||||
                                                 g_interface_info: ObjectManagerInfo,
 | 
			
		||||
                                                 g_name: this._serviceName,
 | 
			
		||||
                                                 g_object_path: this._managerPath,
 | 
			
		||||
                                                 g_flags: Gio.DBusProxyFlags.NONE });
 | 
			
		||||
 | 
			
		||||
        this._interfaceInfos = {};
 | 
			
		||||
        this._objects = {};
 | 
			
		||||
        this._interfaces = {};
 | 
			
		||||
        this._onLoaded = params.onLoaded;
 | 
			
		||||
 | 
			
		||||
        if (params.knownInterfaces)
 | 
			
		||||
            this._registerInterfaces(params.knownInterfaces);
 | 
			
		||||
 | 
			
		||||
        // Start out inhibiting load until at least the proxy
 | 
			
		||||
        // manager is loaded and the remote objects are fetched
 | 
			
		||||
        this._numLoadInhibitors = 1;
 | 
			
		||||
        this._managerProxy.init_async(GLib.PRIORITY_DEFAULT,
 | 
			
		||||
                                      this._cancellable,
 | 
			
		||||
                                      Lang.bind(this, this._onManagerProxyLoaded));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _tryToCompleteLoad: function() {
 | 
			
		||||
        this._numLoadInhibitors--;
 | 
			
		||||
        if (this._numLoadInhibitors == 0) {
 | 
			
		||||
            if (this._onLoaded)
 | 
			
		||||
                this._onLoaded();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _addInterface: function(objectPath, interfaceName, onFinished) {
 | 
			
		||||
        let info = this._interfaceInfos[interfaceName];
 | 
			
		||||
 | 
			
		||||
        if (!info) {
 | 
			
		||||
           if (onFinished)
 | 
			
		||||
               onFinished();
 | 
			
		||||
           return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let proxy = new Gio.DBusProxy({ g_connection: this._connection,
 | 
			
		||||
                                       g_name: this._serviceName,
 | 
			
		||||
                                       g_object_path: objectPath,
 | 
			
		||||
                                       g_interface_name: interfaceName,
 | 
			
		||||
                                       g_interface_info: info,
 | 
			
		||||
                                       g_flags: Gio.DBusProxyFlags.NONE });
 | 
			
		||||
 | 
			
		||||
        proxy.init_async(GLib.PRIORITY_DEFAULT,
 | 
			
		||||
                         this._cancellable,
 | 
			
		||||
                         Lang.bind(this, function(initable, result) {
 | 
			
		||||
               let error = null;
 | 
			
		||||
               try {
 | 
			
		||||
                   initable.init_finish(result);
 | 
			
		||||
               } catch(e) {
 | 
			
		||||
                   logError(e, 'could not initialize proxy for interface ' + interfaceName);
 | 
			
		||||
 | 
			
		||||
                   if (onFinished)
 | 
			
		||||
                       onFinished();
 | 
			
		||||
                   return;
 | 
			
		||||
               }
 | 
			
		||||
 | 
			
		||||
               let isNewObject;
 | 
			
		||||
               if (!this._objects[objectPath]) {
 | 
			
		||||
                   this._objects[objectPath] = {};
 | 
			
		||||
                   isNewObject = true;
 | 
			
		||||
               } else {
 | 
			
		||||
                   isNewObject = false;
 | 
			
		||||
               }
 | 
			
		||||
 | 
			
		||||
               this._objects[objectPath][interfaceName] = proxy;
 | 
			
		||||
 | 
			
		||||
               if (!this._interfaces[interfaceName])
 | 
			
		||||
                   this._interfaces[interfaceName] = [];
 | 
			
		||||
 | 
			
		||||
               this._interfaces[interfaceName].push(proxy);
 | 
			
		||||
 | 
			
		||||
               if (isNewObject)
 | 
			
		||||
                   this.emit('object-added', objectPath);
 | 
			
		||||
 | 
			
		||||
               this.emit('interface-added', interfaceName, proxy);
 | 
			
		||||
 | 
			
		||||
               if (onFinished)
 | 
			
		||||
                   onFinished();
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _removeInterface: function(objectPath, interfaceName) {
 | 
			
		||||
        if (!this._objects[objectPath])
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let proxy = this._objects[objectPath][interfaceName];
 | 
			
		||||
 | 
			
		||||
        if (this._interfaces[interfaceName]) {
 | 
			
		||||
            let index = this._interfaces[interfaceName].indexOf(proxy);
 | 
			
		||||
 | 
			
		||||
            if (index >= 0)
 | 
			
		||||
                this._interfaces[interfaceName].splice(index, 1);
 | 
			
		||||
 | 
			
		||||
            if (this._interfaces[interfaceName].length == 0)
 | 
			
		||||
                delete this._interfaces[interfaceName];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.emit('interface-removed', interfaceName, proxy);
 | 
			
		||||
 | 
			
		||||
        this._objects[objectPath][interfaceName] = null;
 | 
			
		||||
 | 
			
		||||
        if (Object.keys(this._objects[objectPath]).length == 0) {
 | 
			
		||||
            delete this._objects[objectPath];
 | 
			
		||||
            this.emit('object-removed', objectPath);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onManagerProxyLoaded: function(initable, result) {
 | 
			
		||||
        let error = null;
 | 
			
		||||
        try {
 | 
			
		||||
            initable.init_finish(result);
 | 
			
		||||
        } catch(e) {
 | 
			
		||||
            logError(e, 'could not initialize object manager for object ' + params.name);
 | 
			
		||||
 | 
			
		||||
            this._tryToCompleteLoad();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._managerProxy.connectSignal('InterfacesAdded',
 | 
			
		||||
                                         Lang.bind(this, function(objectManager, sender, [objectPath, interfaces]) {
 | 
			
		||||
                                             let interfaceNames = Object.keys(interfaces);
 | 
			
		||||
                                             for (let i = 0; i < interfaceNames.length; i++)
 | 
			
		||||
                                                 this._addInterface(objectPath, interfaceNames[i]);
 | 
			
		||||
                                         }));
 | 
			
		||||
        this._managerProxy.connectSignal('InterfacesRemoved',
 | 
			
		||||
                                         Lang.bind(this, function(objectManager, sender, [objectPath, interfaceNames]) {
 | 
			
		||||
                                             for (let i = 0; i < interfaceNames.length; i++)
 | 
			
		||||
                                                 this._removeInterface(objectPath, interfaceNames[i]);
 | 
			
		||||
                                         }));
 | 
			
		||||
 | 
			
		||||
        if (Object.keys(this._interfaceInfos).length == 0) {
 | 
			
		||||
            this._tryToCompleteLoad();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._managerProxy.GetManagedObjectsRemote(Lang.bind(this, function(result, error) {
 | 
			
		||||
            if (!result) {
 | 
			
		||||
                if (error) {
 | 
			
		||||
                   logError(error, 'could not get remote objects for service ' + this._serviceName + ' path ' + this._managerPath);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                this._tryToCompleteLoad();
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let [objects] = result;
 | 
			
		||||
 | 
			
		||||
            let objectPaths = Object.keys(objects);
 | 
			
		||||
            for (let i = 0; i < objectPaths.length; i++) {
 | 
			
		||||
                let objectPath = objectPaths[i];
 | 
			
		||||
                let object = objects[objectPath];
 | 
			
		||||
 | 
			
		||||
                let interfaceNames = Object.getOwnPropertyNames(object);
 | 
			
		||||
                for (let j = 0; j < interfaceNames.length; j++) {
 | 
			
		||||
                    let interfaceName = interfaceNames[j];
 | 
			
		||||
 | 
			
		||||
                    // Prevent load from completing until the interface is loaded
 | 
			
		||||
                    this._numLoadInhibitors++;
 | 
			
		||||
                    this._addInterface(objectPath,
 | 
			
		||||
                                       interfaceName,
 | 
			
		||||
                                       Lang.bind(this, this._tryToCompleteLoad));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            this._tryToCompleteLoad();
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _registerInterfaces: function(interfaces) {
 | 
			
		||||
        for (let i = 0; i < interfaces.length; i++) {
 | 
			
		||||
            let info = Gio.DBusInterfaceInfo.new_for_xml(interfaces[i]);
 | 
			
		||||
            this._interfaceInfos[info.name] = info;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getProxy: function(objectPath, interfaceName) {
 | 
			
		||||
        let object = this._objects[objectPath];
 | 
			
		||||
 | 
			
		||||
        if (!object)
 | 
			
		||||
            return null;
 | 
			
		||||
 | 
			
		||||
        return object[interfaceName];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getProxiesForInterface: function(interfaceName) {
 | 
			
		||||
        let proxyList = this._interfaces[interfaceName];
 | 
			
		||||
 | 
			
		||||
        if (!proxyList)
 | 
			
		||||
            return [];
 | 
			
		||||
 | 
			
		||||
        return proxyList;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getAllProxies: function() {
 | 
			
		||||
        let proxies = [];
 | 
			
		||||
 | 
			
		||||
        let objectPaths = Object.keys(this._objects);
 | 
			
		||||
        for (let i = 0; i < objectPaths.length; i++) {
 | 
			
		||||
            let object = this._objects[objectPaths];
 | 
			
		||||
 | 
			
		||||
            let interfaceNames = Object.keys(object);
 | 
			
		||||
            for (let j = 0; i < interfaceNames.length; i++) {
 | 
			
		||||
                let interfaceName = interfaceNames[i];
 | 
			
		||||
                if (object[interfaceName])
 | 
			
		||||
                    proxies.push(object(interfaceName));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return proxies;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(ObjectManager.prototype);
 | 
			
		||||
@@ -1,117 +0,0 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
 | 
			
		||||
const ObjectManager = imports.misc.objectManager;
 | 
			
		||||
 | 
			
		||||
const SmartcardTokenIface = <interface name="org.gnome.SettingsDaemon.Smartcard.Token">
 | 
			
		||||
  <property name="Name" type="s" access="read"/>
 | 
			
		||||
  <property name="Driver" type="o" access="read"/>
 | 
			
		||||
  <property name="IsInserted" type="b" access="read"/>
 | 
			
		||||
  <property name="UsedToLogin" type="b" access="read"/>
 | 
			
		||||
</interface>;
 | 
			
		||||
 | 
			
		||||
let _smartcardManager = null;
 | 
			
		||||
 | 
			
		||||
function getSmartcardManager() {
 | 
			
		||||
    if (_smartcardManager == null)
 | 
			
		||||
        _smartcardManager = new SmartcardManager();
 | 
			
		||||
 | 
			
		||||
    return _smartcardManager;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const SmartcardManager = new Lang.Class({
 | 
			
		||||
    Name: 'SmartcardManager',
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._objectManager = new ObjectManager.ObjectManager({ connection: Gio.DBus.session,
 | 
			
		||||
                                                                name: "org.gnome.SettingsDaemon.Smartcard",
 | 
			
		||||
                                                                objectPath: '/org/gnome/SettingsDaemon/Smartcard',
 | 
			
		||||
                                                                knownInterfaces: [ SmartcardTokenIface ],
 | 
			
		||||
                                                                onLoaded: Lang.bind(this, this._onLoaded) });
 | 
			
		||||
        this._insertedTokens = {};
 | 
			
		||||
        this._loginToken = null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onLoaded: function() {
 | 
			
		||||
        let tokens = this._objectManager.getProxiesForInterface('org.gnome.SettingsDaemon.Smartcard.Token');
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < tokens.length; i++)
 | 
			
		||||
            this._addToken(tokens[i]);
 | 
			
		||||
 | 
			
		||||
        this._objectManager.connect('interface-added', Lang.bind(this, function(objectManager, interfaceName, proxy) {
 | 
			
		||||
            if (interfaceName == 'org.gnome.SettingsDaemon.Smartcard.Token')
 | 
			
		||||
                this._addToken(proxy);
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        this._objectManager.connect('interface-removed', Lang.bind(this, function(objectManager, interfaceName, proxy) {
 | 
			
		||||
            if (interfaceName == 'org.gnome.SettingsDaemon.Smartcard.Token')
 | 
			
		||||
                this._removeToken(proxy);
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateToken: function(token) {
 | 
			
		||||
        let objectPath = token.get_object_path();
 | 
			
		||||
 | 
			
		||||
        delete this._insertedTokens[objectPath];
 | 
			
		||||
 | 
			
		||||
        if (token.IsInserted)
 | 
			
		||||
            this._insertedTokens[objectPath] = token;
 | 
			
		||||
 | 
			
		||||
        if (token.UsedToLogin)
 | 
			
		||||
            this._loginToken = token;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _addToken: function(token) {
 | 
			
		||||
        this._updateToken(token);
 | 
			
		||||
 | 
			
		||||
        token.connect('g-properties-changed',
 | 
			
		||||
                      Lang.bind(this, function(proxy, properties) {
 | 
			
		||||
                          if ('IsInserted' in properties.deep_unpack()) {
 | 
			
		||||
                              this._updateToken(token);
 | 
			
		||||
 | 
			
		||||
                              if (token.IsInserted) {
 | 
			
		||||
                                  this.emit('smartcard-inserted', token);
 | 
			
		||||
                              } else {
 | 
			
		||||
                                  this.emit('smartcard-removed', token);
 | 
			
		||||
                              }
 | 
			
		||||
                          }
 | 
			
		||||
                      }));
 | 
			
		||||
 | 
			
		||||
        // Emit a smartcard-inserted at startup if it's already plugged in
 | 
			
		||||
        if (token.IsInserted)
 | 
			
		||||
            this.emit('smartcard-inserted', token);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _removeToken: function(token) {
 | 
			
		||||
        let objectPath = token.get_object_path();
 | 
			
		||||
 | 
			
		||||
        if (this._insertedTokens[objectPath] == token) {
 | 
			
		||||
            delete this._insertedTokens[objectPath];
 | 
			
		||||
            this.emit('smartcard-removed', token);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this._loginToken == token)
 | 
			
		||||
            this._loginToken = null;
 | 
			
		||||
 | 
			
		||||
        token.disconnectAll();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    hasInsertedTokens: function() {
 | 
			
		||||
        return Object.keys(this._insertedTokens).length > 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    hasInsertedLoginToken: function() {
 | 
			
		||||
        if (!this._loginToken)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        if (!this._loginToken.IsInserted)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(SmartcardManager.prototype);
 | 
			
		||||
							
								
								
									
										219
									
								
								js/misc/util.js
									
									
									
									
									
								
							
							
						
						@@ -1,15 +1,8 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const Tweener = imports.ui.tweener;
 | 
			
		||||
 | 
			
		||||
const SCROLL_TIME = 0.1;
 | 
			
		||||
 | 
			
		||||
// http://daringfireball.net/2010/07/improved_regex_for_matching_urls
 | 
			
		||||
const _balancedParens = '\\((?:[^\\s()<>]+|(?:\\(?:[^\\s()<>]+\\)))*\\)';
 | 
			
		||||
@@ -20,7 +13,7 @@ const _urlRegexp = new RegExp(
 | 
			
		||||
    '(^|' + _leadingJunk + ')' +
 | 
			
		||||
    '(' +
 | 
			
		||||
        '(?:' +
 | 
			
		||||
            '(?:http|https|ftp)://' +             // scheme://
 | 
			
		||||
            '[a-z][\\w-]+://' +                   // scheme://
 | 
			
		||||
            '|' +
 | 
			
		||||
            'www\\d{0,3}[.]' +                    // www.
 | 
			
		||||
            '|' +
 | 
			
		||||
@@ -80,22 +73,6 @@ function spawnCommandLine(command_line) {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// spawnApp:
 | 
			
		||||
// @argv: an argv array
 | 
			
		||||
//
 | 
			
		||||
// Runs @argv as if it was an application, handling startup notification
 | 
			
		||||
function spawnApp(argv) {
 | 
			
		||||
    try {
 | 
			
		||||
        let app = Gio.AppInfo.create_from_commandline(argv.join(' '), null,
 | 
			
		||||
                                                      Gio.AppInfoCreateFlags.SUPPORTS_STARTUP_NOTIFICATION);
 | 
			
		||||
 | 
			
		||||
        let context = global.create_app_launch_context();
 | 
			
		||||
        app.launch([], context);
 | 
			
		||||
    } catch(err) {
 | 
			
		||||
        _handleSpawnError(argv[0], err);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// trySpawn:
 | 
			
		||||
// @argv: an argv array
 | 
			
		||||
//
 | 
			
		||||
@@ -157,6 +134,111 @@ function _handleSpawnError(command, err) {
 | 
			
		||||
    Main.notifyError(title, err.message);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// killall:
 | 
			
		||||
// @processName: a process name
 | 
			
		||||
//
 | 
			
		||||
// Kills @processName. If no process with the given name is found,
 | 
			
		||||
// this will fail silently.
 | 
			
		||||
function killall(processName) {
 | 
			
		||||
    try {
 | 
			
		||||
        // pkill is more portable than killall, but on Linux at least
 | 
			
		||||
        // it won't match if you pass more than 15 characters of the
 | 
			
		||||
        // process name... However, if you use the '-f' flag to match
 | 
			
		||||
        // the entire command line, it will work, but we have to be
 | 
			
		||||
        // careful in that case that we can match
 | 
			
		||||
        // '/usr/bin/processName' but not 'gedit processName.c' or
 | 
			
		||||
        // whatever...
 | 
			
		||||
 | 
			
		||||
        let argv = ['pkill', '-f', '^([^ ]*/)?' + processName + '($| )'];
 | 
			
		||||
        GLib.spawn_sync(null, argv, null, GLib.SpawnFlags.SEARCH_PATH, null);
 | 
			
		||||
        // It might be useful to return success/failure, but we'd need
 | 
			
		||||
        // a wrapper around WIFEXITED and WEXITSTATUS. Since none of
 | 
			
		||||
        // the current callers care, we don't bother.
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
        logError(e, 'Failed to kill ' + processName);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// This was ported from network-manager-applet
 | 
			
		||||
// Copyright 2007 - 2011 Red Hat, Inc.
 | 
			
		||||
// Author: Dan Williams <dcbw@redhat.com>
 | 
			
		||||
 | 
			
		||||
const _IGNORED_WORDS = [
 | 
			
		||||
        'Semiconductor',
 | 
			
		||||
        'Components',
 | 
			
		||||
        'Corporation',
 | 
			
		||||
        'Communications',
 | 
			
		||||
        'Company',
 | 
			
		||||
        'Corp.',
 | 
			
		||||
        'Corp',
 | 
			
		||||
        'Co.',
 | 
			
		||||
        'Inc.',
 | 
			
		||||
        'Inc',
 | 
			
		||||
        'Incorporated',
 | 
			
		||||
        'Ltd.',
 | 
			
		||||
        'Limited.',
 | 
			
		||||
        'Intel',
 | 
			
		||||
        'chipset',
 | 
			
		||||
        'adapter',
 | 
			
		||||
        '[hex]',
 | 
			
		||||
        'NDIS',
 | 
			
		||||
        'Module'
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
const _IGNORED_PHRASES = [
 | 
			
		||||
        'Multiprotocol MAC/baseband processor',
 | 
			
		||||
        'Wireless LAN Controller',
 | 
			
		||||
        'Wireless LAN Adapter',
 | 
			
		||||
        'Wireless Adapter',
 | 
			
		||||
        'Network Connection',
 | 
			
		||||
        'Wireless Cardbus Adapter',
 | 
			
		||||
        'Wireless CardBus Adapter',
 | 
			
		||||
        '54 Mbps Wireless PC Card',
 | 
			
		||||
        'Wireless PC Card',
 | 
			
		||||
        'Wireless PC',
 | 
			
		||||
        'PC Card with XJACK(r) Antenna',
 | 
			
		||||
        'Wireless cardbus',
 | 
			
		||||
        'Wireless LAN PC Card',
 | 
			
		||||
        'Technology Group Ltd.',
 | 
			
		||||
        'Communication S.p.A.',
 | 
			
		||||
        'Business Mobile Networks BV',
 | 
			
		||||
        'Mobile Broadband Minicard Composite Device',
 | 
			
		||||
        'Mobile Communications AB',
 | 
			
		||||
        '(PC-Suite Mode)'
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
function fixupPCIDescription(desc) {
 | 
			
		||||
    desc = desc.replace(/[_,]/, ' ');
 | 
			
		||||
 | 
			
		||||
    /* Attempt to shorten ID by ignoring certain phrases */
 | 
			
		||||
    for (let i = 0; i < _IGNORED_PHRASES.length; i++) {
 | 
			
		||||
        let item = _IGNORED_PHRASES[i];
 | 
			
		||||
        let pos = desc.indexOf(item);
 | 
			
		||||
        if (pos != -1) {
 | 
			
		||||
            let before = desc.substring(0, pos);
 | 
			
		||||
            let after = desc.substring(pos + item.length, desc.length);
 | 
			
		||||
            desc = before + after;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Attmept to shorten ID by ignoring certain individual words */
 | 
			
		||||
    let words = desc.split(' ');
 | 
			
		||||
    let out = [ ];
 | 
			
		||||
    for (let i = 0; i < words.length; i++) {
 | 
			
		||||
        let item = words[i];
 | 
			
		||||
 | 
			
		||||
        // skip empty items (that come out from consecutive spaces)
 | 
			
		||||
        if (item.length == 0)
 | 
			
		||||
            continue;
 | 
			
		||||
 | 
			
		||||
        if (_IGNORED_WORDS.indexOf(item) == -1) {
 | 
			
		||||
            out.push(item);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return out.join(' ');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// lowerBound:
 | 
			
		||||
// @array: an array or array-like object, already sorted
 | 
			
		||||
//         according to @cmp
 | 
			
		||||
@@ -206,92 +288,3 @@ function insertSorted(array, val, cmp) {
 | 
			
		||||
 | 
			
		||||
    return pos;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const CloseButton = new Lang.Class({
 | 
			
		||||
    Name: 'CloseButton',
 | 
			
		||||
    Extends: St.Button,
 | 
			
		||||
 | 
			
		||||
    _init: function(boxpointer) {
 | 
			
		||||
        this.parent({ style_class: 'notification-close'});
 | 
			
		||||
 | 
			
		||||
        // This is a bit tricky. St.Bin has its own x-align/y-align properties
 | 
			
		||||
        // that compete with Clutter's properties. This should be fixed for
 | 
			
		||||
        // Clutter 2.0. Since St.Bin doesn't define its own setters, the
 | 
			
		||||
        // setters are a workaround to get Clutter's version.
 | 
			
		||||
        this.set_x_align(Clutter.ActorAlign.END);
 | 
			
		||||
        this.set_y_align(Clutter.ActorAlign.START);
 | 
			
		||||
 | 
			
		||||
        // XXX Clutter 2.0 workaround: ClutterBinLayout needs expand
 | 
			
		||||
        // to respect the alignments.
 | 
			
		||||
        this.set_x_expand(true);
 | 
			
		||||
        this.set_y_expand(true);
 | 
			
		||||
 | 
			
		||||
        this._boxPointer = boxpointer;
 | 
			
		||||
        if (boxpointer)
 | 
			
		||||
            this._boxPointer.connect('arrow-side-changed', Lang.bind(this, this._sync));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _computeBoxPointerOffset: function() {
 | 
			
		||||
        if (!this._boxPointer || !this._boxPointer.actor.get_stage())
 | 
			
		||||
            return 0;
 | 
			
		||||
 | 
			
		||||
        let side = this._boxPointer.arrowSide;
 | 
			
		||||
        if (side == St.Side.TOP)
 | 
			
		||||
            return this._boxPointer.getArrowHeight();
 | 
			
		||||
        else
 | 
			
		||||
            return 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _sync: function() {
 | 
			
		||||
        let themeNode = this.get_theme_node();
 | 
			
		||||
 | 
			
		||||
        let offY = this._computeBoxPointerOffset();
 | 
			
		||||
        this.translation_x = themeNode.get_length('-shell-close-overlap-x')
 | 
			
		||||
        this.translation_y = themeNode.get_length('-shell-close-overlap-y') + offY;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    vfunc_style_changed: function() {
 | 
			
		||||
        this._sync();
 | 
			
		||||
        this.parent();
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
function makeCloseButton(boxpointer) {
 | 
			
		||||
    return new CloseButton(boxpointer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function ensureActorVisibleInScrollView(scrollView, actor) {
 | 
			
		||||
    let adjustment = scrollView.vscroll.adjustment;
 | 
			
		||||
    let [value, lower, upper, stepIncrement, pageIncrement, pageSize] = adjustment.get_values();
 | 
			
		||||
 | 
			
		||||
    let offset = 0;
 | 
			
		||||
    let vfade = scrollView.get_effect("fade");
 | 
			
		||||
    if (vfade)
 | 
			
		||||
        offset = vfade.vfade_offset;
 | 
			
		||||
 | 
			
		||||
    let box = actor.get_allocation_box();
 | 
			
		||||
    let y1 = box.y1, y2 = box.y2;
 | 
			
		||||
 | 
			
		||||
    let parent = actor.get_parent();
 | 
			
		||||
    while (parent != scrollView) {
 | 
			
		||||
        if (!parent)
 | 
			
		||||
            throw new Error("actor not in scroll view");
 | 
			
		||||
 | 
			
		||||
        let box = parent.get_allocation_box();
 | 
			
		||||
        y1 += box.y1;
 | 
			
		||||
        y2 += box.y1;
 | 
			
		||||
        parent = parent.get_parent();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (y1 < value + offset)
 | 
			
		||||
        value = Math.max(0, y1 - offset);
 | 
			
		||||
    else if (y2 > value + pageSize - offset)
 | 
			
		||||
        value = Math.min(upper, y2 + offset - pageSize);
 | 
			
		||||
    else
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    Tweener.addTween(adjustment,
 | 
			
		||||
                     { value: value,
 | 
			
		||||
                       time: SCROLL_TIME,
 | 
			
		||||
                       transition: 'easeOutQuad' });
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										137
									
								
								js/ui/altTab.js
									
									
									
									
									
								
							
							
						
						@@ -98,13 +98,43 @@ const AppSwitcherPopup = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createSwitcher: function() {
 | 
			
		||||
        let apps = Shell.AppSystem.get_default().get_running ();
 | 
			
		||||
    _getAppLists: function() {
 | 
			
		||||
        let tracker = Shell.WindowTracker.get_default();
 | 
			
		||||
        let appSys = Shell.AppSystem.get_default();
 | 
			
		||||
        let allApps = appSys.get_running ();
 | 
			
		||||
 | 
			
		||||
        if (apps.length == 0)
 | 
			
		||||
        let screen = global.screen;
 | 
			
		||||
        let display = screen.get_display();
 | 
			
		||||
        let windows = display.get_tab_list(Meta.TabList.NORMAL_ALL, screen,
 | 
			
		||||
                                           screen.get_active_workspace());
 | 
			
		||||
 | 
			
		||||
        // windows is only the windows on the current workspace. For
 | 
			
		||||
        // each one, if it corresponds to an app we know, move that
 | 
			
		||||
        // app from allApps to apps.
 | 
			
		||||
        let apps = [];
 | 
			
		||||
        for (let i = 0; i < windows.length && allApps.length != 0; i++) {
 | 
			
		||||
            let app = tracker.get_window_app(windows[i]);
 | 
			
		||||
            let index = allApps.indexOf(app);
 | 
			
		||||
            if (index != -1) {
 | 
			
		||||
                apps.push(app);
 | 
			
		||||
                allApps.splice(index, 1);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Now @apps is a list of apps on the current workspace, in
 | 
			
		||||
        // standard Alt+Tab order (MRU except for minimized windows),
 | 
			
		||||
        // and allApps is a list of apps that only appear on other
 | 
			
		||||
        // workspaces, sorted by user_time, which is good enough.
 | 
			
		||||
        return [apps, allApps];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createSwitcher: function() {
 | 
			
		||||
        let [localApps, otherApps] = this._getAppLists();
 | 
			
		||||
 | 
			
		||||
        if (localApps.length == 0 && otherApps.length == 0)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        this._switcherList = new AppSwitcher(apps, this);
 | 
			
		||||
        this._switcherList = new AppSwitcher(localApps, otherApps, this);
 | 
			
		||||
        this._items = this._switcherList.icons;
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
@@ -232,13 +262,15 @@ const AppSwitcherPopup = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _finish : function(timestamp) {
 | 
			
		||||
        let appIcon = this._items[this._selectedIndex];
 | 
			
		||||
        if (this._currentWindow < 0)
 | 
			
		||||
            appIcon.app.activate_window(appIcon.cachedWindows[0], timestamp);
 | 
			
		||||
        else
 | 
			
		||||
            Main.activateWindow(appIcon.cachedWindows[this._currentWindow], timestamp);
 | 
			
		||||
 | 
			
		||||
        this.parent();
 | 
			
		||||
 | 
			
		||||
        let appIcon = this._items[this._selectedIndex];
 | 
			
		||||
        let window;
 | 
			
		||||
        if (this._currentWindow >= 0)
 | 
			
		||||
            window = appIcon.cachedWindows[this._currentWindow];
 | 
			
		||||
        else
 | 
			
		||||
            window = null;
 | 
			
		||||
        appIcon.app.activate_window(window, timestamp);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDestroy : function() {
 | 
			
		||||
@@ -355,13 +387,10 @@ const WindowSwitcherPopup = new Lang.Class({
 | 
			
		||||
    Name: 'WindowSwitcherPopup',
 | 
			
		||||
    Extends: SwitcherPopup.SwitcherPopup,
 | 
			
		||||
 | 
			
		||||
    _init: function(items) {
 | 
			
		||||
        this.parent(items);
 | 
			
		||||
        this._settings = new Gio.Settings({ schema: 'org.gnome.shell.window-switcher' });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getWindowList: function() {
 | 
			
		||||
        let workspace = this._settings.get_boolean('current-workspace-only') ? global.screen.get_active_workspace() : null;
 | 
			
		||||
        let settings = new Gio.Settings({ schema: 'org.gnome.shell.window-switcher' });
 | 
			
		||||
        let workspace = settings.get_boolean('current-workspace-only') ? global.screen.get_active_workspace()
 | 
			
		||||
                                                                       : null;
 | 
			
		||||
        return global.display.get_tab_list(Meta.TabList.NORMAL, global.screen, workspace);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -371,8 +400,7 @@ const WindowSwitcherPopup = new Lang.Class({
 | 
			
		||||
        if (windows.length == 0)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        let mode = this._settings.get_enum('app-icon-mode');
 | 
			
		||||
        this._switcherList = new WindowList(windows, mode);
 | 
			
		||||
        this._switcherList = new WindowList(windows);
 | 
			
		||||
        this._items = this._switcherList.icons;
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
@@ -401,9 +429,9 @@ const WindowSwitcherPopup = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _finish: function() {
 | 
			
		||||
        Main.activateWindow(this._items[this._selectedIndex].window);
 | 
			
		||||
 | 
			
		||||
        this.parent();
 | 
			
		||||
 | 
			
		||||
        Main.activateWindow(this._items[this._selectedIndex].window);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@@ -433,32 +461,34 @@ const AppSwitcher = new Lang.Class({
 | 
			
		||||
    Name: 'AppSwitcher',
 | 
			
		||||
    Extends: SwitcherPopup.SwitcherList,
 | 
			
		||||
 | 
			
		||||
    _init : function(apps, altTabPopup) {
 | 
			
		||||
    _init : function(localApps, otherApps, altTabPopup) {
 | 
			
		||||
        this.parent(true);
 | 
			
		||||
 | 
			
		||||
        // Construct the AppIcons, add to the popup
 | 
			
		||||
        let activeWorkspace = global.screen.get_active_workspace();
 | 
			
		||||
        let workspaceIcons = [];
 | 
			
		||||
        let otherIcons = [];
 | 
			
		||||
        for (let i = 0; i < localApps.length; i++) {
 | 
			
		||||
            let appIcon = new AppIcon(localApps[i]);
 | 
			
		||||
            // Cache the window list now; we don't handle dynamic changes here,
 | 
			
		||||
            // and we don't want to be continually retrieving it
 | 
			
		||||
            appIcon.cachedWindows = appIcon.app.get_windows();
 | 
			
		||||
            workspaceIcons.push(appIcon);
 | 
			
		||||
        }
 | 
			
		||||
        for (let i = 0; i < otherApps.length; i++) {
 | 
			
		||||
            let appIcon = new AppIcon(otherApps[i]);
 | 
			
		||||
            appIcon.cachedWindows = appIcon.app.get_windows();
 | 
			
		||||
            otherIcons.push(appIcon);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.icons = [];
 | 
			
		||||
        this._arrows = [];
 | 
			
		||||
 | 
			
		||||
        let windowTracker = Shell.WindowTracker.get_default();
 | 
			
		||||
        let settings = new Gio.Settings({ schema: 'org.gnome.shell.app-switcher' });
 | 
			
		||||
        let workspace = settings.get_boolean('current-workspace-only') ? global.screen.get_active_workspace()
 | 
			
		||||
                                                                       : null;
 | 
			
		||||
        let allWindows = global.display.get_tab_list(Meta.TabList.NORMAL,
 | 
			
		||||
                                                     global.screen, workspace);
 | 
			
		||||
 | 
			
		||||
        // Construct the AppIcons, add to the popup
 | 
			
		||||
        for (let i = 0; i < apps.length; i++) {
 | 
			
		||||
            let appIcon = new AppIcon(apps[i]);
 | 
			
		||||
            // Cache the window list now; we don't handle dynamic changes here,
 | 
			
		||||
            // and we don't want to be continually retrieving it
 | 
			
		||||
            appIcon.cachedWindows = allWindows.filter(function(w) {
 | 
			
		||||
                return windowTracker.get_window_app (w) == appIcon.app;
 | 
			
		||||
            });
 | 
			
		||||
            if (appIcon.cachedWindows.length > 0)
 | 
			
		||||
                this._addIcon(appIcon);
 | 
			
		||||
            else if (workspace == null)
 | 
			
		||||
                throw new Error('%s appears to be running, but doesn\'t have any windows'.format(appIcon.app.get_name()));
 | 
			
		||||
        }
 | 
			
		||||
        for (let i = 0; i < workspaceIcons.length; i++)
 | 
			
		||||
            this._addIcon(workspaceIcons[i]);
 | 
			
		||||
        if (workspaceIcons.length > 0 && otherIcons.length > 0)
 | 
			
		||||
            this.addSeparator();
 | 
			
		||||
        for (let i = 0; i < otherIcons.length; i++)
 | 
			
		||||
            this._addIcon(otherIcons[i]);
 | 
			
		||||
 | 
			
		||||
        this._curApp = -1;
 | 
			
		||||
        this._iconSize = 0;
 | 
			
		||||
@@ -484,6 +514,8 @@ const AppSwitcher = new Lang.Class({
 | 
			
		||||
        let [iconMinHeight, iconNaturalHeight] = this.icons[j].label.get_preferred_height(-1);
 | 
			
		||||
        let iconSpacing = iconNaturalHeight + iconPadding + iconBorder;
 | 
			
		||||
        let totalSpacing = this._list.spacing * (this._items.length - 1);
 | 
			
		||||
        if (this._separator)
 | 
			
		||||
           totalSpacing += this._separator.width + this._list.spacing;
 | 
			
		||||
 | 
			
		||||
        // We just assume the whole screen here due to weirdness happing with the passed width
 | 
			
		||||
        let primary = Main.layoutManager.primaryMonitor;
 | 
			
		||||
@@ -606,12 +638,24 @@ const ThumbnailList = new Lang.Class({
 | 
			
		||||
    _init : function(windows) {
 | 
			
		||||
        this.parent(false);
 | 
			
		||||
 | 
			
		||||
        let activeWorkspace = global.screen.get_active_workspace();
 | 
			
		||||
 | 
			
		||||
        // We fake the value of 'separatorAdded' when the app has no window
 | 
			
		||||
        // on the current workspace, to avoid displaying a useless separator in
 | 
			
		||||
        // that case.
 | 
			
		||||
        let separatorAdded = windows.length == 0 || windows[0].get_workspace() != activeWorkspace;
 | 
			
		||||
 | 
			
		||||
        this._labels = new Array();
 | 
			
		||||
        this._thumbnailBins = new Array();
 | 
			
		||||
        this._clones = new Array();
 | 
			
		||||
        this._windows = windows;
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < windows.length; i++) {
 | 
			
		||||
            if (!separatorAdded && windows[i].get_workspace() != activeWorkspace) {
 | 
			
		||||
              this.addSeparator();
 | 
			
		||||
              separatorAdded = true;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let box = new St.BoxLayout({ style_class: 'thumbnail-box',
 | 
			
		||||
                                         vertical: true });
 | 
			
		||||
 | 
			
		||||
@@ -668,7 +712,7 @@ const ThumbnailList = new Lang.Class({
 | 
			
		||||
const WindowIcon = new Lang.Class({
 | 
			
		||||
    Name: 'WindowIcon',
 | 
			
		||||
 | 
			
		||||
    _init: function(window, mode) {
 | 
			
		||||
    _init: function(window) {
 | 
			
		||||
        this.window = window;
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.BoxLayout({ style_class: 'alt-tab-app',
 | 
			
		||||
@@ -686,7 +730,8 @@ const WindowIcon = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._icon.destroy_all_children();
 | 
			
		||||
 | 
			
		||||
        switch (mode) {
 | 
			
		||||
        let settings = new Gio.Settings({ schema: 'org.gnome.shell.window-switcher' });
 | 
			
		||||
        switch (settings.get_enum('app-icon-mode')) {
 | 
			
		||||
            case AppIconMode.THUMBNAIL_ONLY:
 | 
			
		||||
                size = WINDOW_PREVIEW_SIZE;
 | 
			
		||||
                this._icon.add_actor(_createWindowClone(mutterWindow, WINDOW_PREVIEW_SIZE));
 | 
			
		||||
@@ -724,7 +769,7 @@ const WindowList = new Lang.Class({
 | 
			
		||||
    Name: 'WindowList',
 | 
			
		||||
    Extends: SwitcherPopup.SwitcherList,
 | 
			
		||||
 | 
			
		||||
    _init : function(windows, mode) {
 | 
			
		||||
    _init : function(windows) {
 | 
			
		||||
        this.parent(true);
 | 
			
		||||
 | 
			
		||||
        this._label = new St.Label({ x_align: Clutter.ActorAlign.CENTER,
 | 
			
		||||
@@ -736,7 +781,7 @@ const WindowList = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < windows.length; i++) {
 | 
			
		||||
            let win = windows[i];
 | 
			
		||||
            let icon = new WindowIcon(win, mode);
 | 
			
		||||
            let icon = new WindowIcon(win);
 | 
			
		||||
 | 
			
		||||
            this.addItem(icon.actor, icon.label);
 | 
			
		||||
            this.icons.push(icon);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,84 +0,0 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Mainloop = imports.mainloop;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const Atk = imports.gi.Atk;
 | 
			
		||||
 | 
			
		||||
const ANIMATED_ICON_UPDATE_TIMEOUT = 100;
 | 
			
		||||
 | 
			
		||||
const Animation = new Lang.Class({
 | 
			
		||||
    Name: 'Animation',
 | 
			
		||||
 | 
			
		||||
    _init: function(filename, width, height, speed) {
 | 
			
		||||
        this.actor = new St.Bin();
 | 
			
		||||
        this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
 | 
			
		||||
        this._speed = speed;
 | 
			
		||||
 | 
			
		||||
        this._isLoaded = false;
 | 
			
		||||
        this._isPlaying = false;
 | 
			
		||||
        this._timeoutId = 0;
 | 
			
		||||
        this._frame = 0;
 | 
			
		||||
        this._animations = St.TextureCache.get_default().load_sliced_image (filename, width, height,
 | 
			
		||||
                                                                            Lang.bind(this, this._animationsLoaded));
 | 
			
		||||
        this.actor.set_child(this._animations);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    play: function() {
 | 
			
		||||
        if (this._isLoaded && this._timeoutId == 0) {
 | 
			
		||||
            if (this._frame == 0)
 | 
			
		||||
                this._showFrame(0);
 | 
			
		||||
 | 
			
		||||
            this._timeoutId = Mainloop.timeout_add(this._speed, Lang.bind(this, this._update));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._isPlaying = true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    stop: function() {
 | 
			
		||||
        if (this._timeoutId > 0) {
 | 
			
		||||
            Mainloop.source_remove(this._timeoutId);
 | 
			
		||||
            this._timeoutId = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._isPlaying = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _showFrame: function(frame) {
 | 
			
		||||
        let oldFrameActor = this._animations.get_child_at_index(this._frame);
 | 
			
		||||
        if (oldFrameActor)
 | 
			
		||||
            oldFrameActor.hide();
 | 
			
		||||
 | 
			
		||||
        this._frame = (frame % this._animations.get_n_children());
 | 
			
		||||
 | 
			
		||||
        let newFrameActor = this._animations.get_child_at_index(this._frame);
 | 
			
		||||
        if (newFrameActor)
 | 
			
		||||
            newFrameActor.show();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _update: function() {
 | 
			
		||||
        this._showFrame(this._frame + 1);
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _animationsLoaded: function() {
 | 
			
		||||
        this._isLoaded = true;
 | 
			
		||||
 | 
			
		||||
        if (this._isPlaying)
 | 
			
		||||
            this.play();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDestroy: function() {
 | 
			
		||||
        this.stop();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const AnimatedIcon = new Lang.Class({
 | 
			
		||||
    Name: 'AnimatedIcon',
 | 
			
		||||
    Extends: Animation,
 | 
			
		||||
 | 
			
		||||
    _init: function(filename, size) {
 | 
			
		||||
        this.parent(filename, size, size, ANIMATED_ICON_UPDATE_TIMEOUT);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										1395
									
								
								js/ui/appDisplay.js
									
									
									
									
									
								
							
							
						
						@@ -14,15 +14,15 @@ const AppFavorites = new Lang.Class({
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._favorites = {};
 | 
			
		||||
        global.settings.connect('changed::' + this.FAVORITE_APPS_KEY, Lang.bind(this, this._onFavsChanged));
 | 
			
		||||
        this.reload();
 | 
			
		||||
        this._reload();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onFavsChanged: function() {
 | 
			
		||||
        this.reload();
 | 
			
		||||
        this._reload();
 | 
			
		||||
        this.emit('changed');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    reload: function() {
 | 
			
		||||
    _reload: function() {
 | 
			
		||||
        let ids = global.settings.get_strv(this.FAVORITE_APPS_KEY);
 | 
			
		||||
        let appSys = Shell.AppSystem.get_default();
 | 
			
		||||
        let apps = ids.map(function (id) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,815 +0,0 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const GDesktopEnums = imports.gi.GDesktopEnums;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const GnomeDesktop = imports.gi.GnomeDesktop;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Meta = imports.gi.Meta;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
const Tweener = imports.ui.tweener;
 | 
			
		||||
 | 
			
		||||
const BACKGROUND_SCHEMA = 'org.gnome.desktop.background';
 | 
			
		||||
const PRIMARY_COLOR_KEY = 'primary-color';
 | 
			
		||||
const SECONDARY_COLOR_KEY = 'secondary-color';
 | 
			
		||||
const COLOR_SHADING_TYPE_KEY = 'color-shading-type';
 | 
			
		||||
const BACKGROUND_STYLE_KEY = 'picture-options';
 | 
			
		||||
const PICTURE_OPACITY_KEY = 'picture-opacity';
 | 
			
		||||
const PICTURE_URI_KEY = 'picture-uri';
 | 
			
		||||
 | 
			
		||||
const FADE_ANIMATION_TIME = 1.0;
 | 
			
		||||
 | 
			
		||||
// These parameters affect how often we redraw.
 | 
			
		||||
// The first is how different (percent crossfaded) the slide show
 | 
			
		||||
// has to look before redrawing and the second is the minimum
 | 
			
		||||
// frequency (in seconds) we're willing to wake up
 | 
			
		||||
const ANIMATION_OPACITY_STEP_INCREMENT = 4.0;
 | 
			
		||||
const ANIMATION_MIN_WAKEUP_INTERVAL = 1.0;
 | 
			
		||||
 | 
			
		||||
let _backgroundCache = null;
 | 
			
		||||
 | 
			
		||||
const BackgroundCache = new Lang.Class({
 | 
			
		||||
    Name: 'BackgroundCache',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
       this._patterns = [];
 | 
			
		||||
       this._images = [];
 | 
			
		||||
       this._pendingFileLoads = [];
 | 
			
		||||
       this._fileMonitors = {};
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getPatternContent: function(params) {
 | 
			
		||||
        params = Params.parse(params, { monitorIndex: 0,
 | 
			
		||||
                                        color: null,
 | 
			
		||||
                                        secondColor: null,
 | 
			
		||||
                                        shadingType: null,
 | 
			
		||||
                                        effects: Meta.BackgroundEffects.NONE });
 | 
			
		||||
 | 
			
		||||
        let content = null;
 | 
			
		||||
        let candidateContent = null;
 | 
			
		||||
        for (let i = 0; i < this._patterns.length; i++) {
 | 
			
		||||
            if (!this._patterns[i])
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            if (this._patterns[i].get_shading() != params.shadingType)
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            if (!params.color.equal(this._patterns[i].get_color()))
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            if (params.shadingType != GDesktopEnums.BackgroundShading.SOLID &&
 | 
			
		||||
                !params.secondColor.equal(this._patterns[i].get_second_color()))
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            candidateContent = this._patterns[i];
 | 
			
		||||
 | 
			
		||||
            if (params.effects != this._patterns[i].effects)
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (candidateContent) {
 | 
			
		||||
            content = candidateContent.copy(params.monitorIndex, params.effects);
 | 
			
		||||
        } else {
 | 
			
		||||
            content = new Meta.Background({ meta_screen: global.screen,
 | 
			
		||||
                                            monitor: params.monitorIndex,
 | 
			
		||||
                                            effects: params.effects });
 | 
			
		||||
 | 
			
		||||
            if (params.shadingType == GDesktopEnums.BackgroundShading.SOLID) {
 | 
			
		||||
                content.load_color(params.color);
 | 
			
		||||
            } else {
 | 
			
		||||
                content.load_gradient(params.shadingType, params.color, params.secondColor);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._patterns.push(content);
 | 
			
		||||
 | 
			
		||||
        return content;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _monitorFile: function(filename) {
 | 
			
		||||
        if (this._fileMonitors[filename])
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let file = Gio.File.new_for_path(filename);
 | 
			
		||||
        let monitor = file.monitor(Gio.FileMonitorFlags.NONE, null);
 | 
			
		||||
 | 
			
		||||
        let signalId = monitor.connect('changed',
 | 
			
		||||
                                       Lang.bind(this, function() {
 | 
			
		||||
                                           for (let i = 0; i < this._images.length; i++) {
 | 
			
		||||
                                               if (this._images[i].get_filename() == filename)
 | 
			
		||||
                                                   this._images.splice(i, 1);
 | 
			
		||||
                                           }
 | 
			
		||||
 | 
			
		||||
                                           monitor.disconnect(signalId);
 | 
			
		||||
 | 
			
		||||
                                           this.emit('file-changed', filename);
 | 
			
		||||
                                       }));
 | 
			
		||||
 | 
			
		||||
        this._fileMonitors[filename] = monitor;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _removeContent: function(contentList, content) {
 | 
			
		||||
        let index = contentList.indexOf(content);
 | 
			
		||||
 | 
			
		||||
        if (index >= 0)
 | 
			
		||||
            contentList.splice(index, 1);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    removePatternContent: function(content) {
 | 
			
		||||
        this._removeContent(this._patterns, content);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    removeImageContent: function(content) {
 | 
			
		||||
        let filename = content.get_filename();
 | 
			
		||||
 | 
			
		||||
        if (filename && this._fileMonitors[filename])
 | 
			
		||||
            delete this._fileMonitors[filename];
 | 
			
		||||
 | 
			
		||||
        this._removeContent(this._images, content);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _loadImageContent: function(params) {
 | 
			
		||||
        params = Params.parse(params, { monitorIndex: 0,
 | 
			
		||||
                                        style: null,
 | 
			
		||||
                                        filename: null,
 | 
			
		||||
                                        effects: Meta.BackgroundEffects.NONE,
 | 
			
		||||
                                        cancellable: null,
 | 
			
		||||
                                        onFinished: null });
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < this._pendingFileLoads.length; i++) {
 | 
			
		||||
            if (this._pendingFileLoads[i].filename == params.filename &&
 | 
			
		||||
                this._pendingFileLoads[i].style == params.style) {
 | 
			
		||||
                this._pendingFileLoads[i].callers.push({ shouldCopy: true,
 | 
			
		||||
                                                         monitorIndex: params.monitorIndex,
 | 
			
		||||
                                                         effects: params.effects,
 | 
			
		||||
                                                         onFinished: params.onFinished });
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._pendingFileLoads.push({ filename: params.filename,
 | 
			
		||||
                                      style: params.style,
 | 
			
		||||
                                      callers: [{ shouldCopy: false,
 | 
			
		||||
                                                  monitorIndex: params.monitorIndex,
 | 
			
		||||
                                                  effects: params.effects,
 | 
			
		||||
                                                  onFinished: params.onFinished }] });
 | 
			
		||||
 | 
			
		||||
        let content = new Meta.Background({ meta_screen: global.screen,
 | 
			
		||||
                                            monitor: params.monitorIndex,
 | 
			
		||||
                                            effects: params.effects });
 | 
			
		||||
 | 
			
		||||
        content.load_file_async(params.filename,
 | 
			
		||||
                                params.style,
 | 
			
		||||
                                params.cancellable,
 | 
			
		||||
                                Lang.bind(this,
 | 
			
		||||
                                          function(object, result) {
 | 
			
		||||
                                              try {
 | 
			
		||||
                                                  content.load_file_finish(result);
 | 
			
		||||
 | 
			
		||||
                                                  this._monitorFile(params.filename);
 | 
			
		||||
                                                  this._images.push(content);
 | 
			
		||||
                                              } catch(e) {
 | 
			
		||||
                                                  content = null;
 | 
			
		||||
                                              }
 | 
			
		||||
 | 
			
		||||
                                              for (let i = 0; i < this._pendingFileLoads.length; i++) {
 | 
			
		||||
                                                  let pendingLoad = this._pendingFileLoads[i];
 | 
			
		||||
                                                  if (pendingLoad.filename != params.filename ||
 | 
			
		||||
                                                      pendingLoad.style != params.style)
 | 
			
		||||
                                                      continue;
 | 
			
		||||
 | 
			
		||||
                                                  for (let j = 0; j < pendingLoad.callers.length; j++) {
 | 
			
		||||
                                                      if (pendingLoad.callers[j].onFinished) {
 | 
			
		||||
                                                          if (content && pendingLoad.callers[j].shouldCopy) {
 | 
			
		||||
                                                              content = object.copy(pendingLoad.callers[j].monitorIndex,
 | 
			
		||||
                                                                                    pendingLoad.callers[j].effects);
 | 
			
		||||
 | 
			
		||||
                                                          }
 | 
			
		||||
 | 
			
		||||
                                                          pendingLoad.callers[j].onFinished(content);
 | 
			
		||||
                                                      }
 | 
			
		||||
                                                  }
 | 
			
		||||
 | 
			
		||||
                                                  this._pendingFileLoads.splice(i, 1);
 | 
			
		||||
                                              }
 | 
			
		||||
                                          }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getImageContent: function(params) {
 | 
			
		||||
        params = Params.parse(params, { monitorIndex: 0,
 | 
			
		||||
                                        style: null,
 | 
			
		||||
                                        filename: null,
 | 
			
		||||
                                        effects: Meta.BackgroundEffects.NONE,
 | 
			
		||||
                                        cancellable: null,
 | 
			
		||||
                                        onFinished: null });
 | 
			
		||||
 | 
			
		||||
        let content = null;
 | 
			
		||||
        let candidateContent = null;
 | 
			
		||||
        for (let i = 0; i < this._images.length; i++) {
 | 
			
		||||
            if (!this._images[i])
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            if (this._images[i].get_style() != params.style)
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            if (this._images[i].get_filename() != params.filename)
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            if (params.style == GDesktopEnums.BackgroundStyle.SPANNED &&
 | 
			
		||||
                this._images[i].monitor_index != this._monitorIndex)
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            candidateContent = this._images[i];
 | 
			
		||||
 | 
			
		||||
            if (params.effects != this._images[i].effects)
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (candidateContent) {
 | 
			
		||||
            content = candidateContent.copy(params.monitorIndex, params.effects);
 | 
			
		||||
 | 
			
		||||
            if (params.cancellable && params.cancellable.is_cancelled())
 | 
			
		||||
                content = null;
 | 
			
		||||
            else
 | 
			
		||||
                this._images.push(content);
 | 
			
		||||
 | 
			
		||||
            if (params.onFinished)
 | 
			
		||||
                params.onFinished(content);
 | 
			
		||||
        } else {
 | 
			
		||||
            this._loadImageContent({ filename: params.filename,
 | 
			
		||||
                                     style: params.style,
 | 
			
		||||
                                     effects: params.effects,
 | 
			
		||||
                                     monitorIndex: params.monitorIndex,
 | 
			
		||||
                                     cancellable: params.cancellable,
 | 
			
		||||
                                     onFinished: params.onFinished });
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getAnimation: function(params) {
 | 
			
		||||
        params = Params.parse(params, { filename: null,
 | 
			
		||||
                                        onLoaded: null });
 | 
			
		||||
 | 
			
		||||
        if (this._animationFilename == params.filename) {
 | 
			
		||||
            if (params.onLoaded) {
 | 
			
		||||
                GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
 | 
			
		||||
                    params.onLoaded(this._animation);
 | 
			
		||||
                }));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let animation = new Animation({ filename: params.filename });
 | 
			
		||||
 | 
			
		||||
        animation.load(Lang.bind(this, function() {
 | 
			
		||||
                           this._monitorFile(params.filename);
 | 
			
		||||
                           this._animationFilename = params.filename;
 | 
			
		||||
                           this._animation = animation;
 | 
			
		||||
 | 
			
		||||
                           if (params.onLoaded) {
 | 
			
		||||
                               GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
 | 
			
		||||
                                   params.onLoaded(this._animation);
 | 
			
		||||
                               }));
 | 
			
		||||
                           }
 | 
			
		||||
                       }));
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(BackgroundCache.prototype);
 | 
			
		||||
 | 
			
		||||
function getBackgroundCache() {
 | 
			
		||||
    if (!_backgroundCache)
 | 
			
		||||
        _backgroundCache = new BackgroundCache();
 | 
			
		||||
    return _backgroundCache;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Background = new Lang.Class({
 | 
			
		||||
    Name: 'Background',
 | 
			
		||||
 | 
			
		||||
    _init: function(params) {
 | 
			
		||||
        params = Params.parse(params, { monitorIndex: 0,
 | 
			
		||||
                                        layoutManager: Main.layoutManager,
 | 
			
		||||
                                        effects: Meta.BackgroundEffects.NONE,
 | 
			
		||||
                                        settings: null });
 | 
			
		||||
        this.actor = new Meta.BackgroundGroup();
 | 
			
		||||
        this.actor._delegate = this;
 | 
			
		||||
 | 
			
		||||
        this._destroySignalId = this.actor.connect('destroy',
 | 
			
		||||
                                                   Lang.bind(this, this._destroy));
 | 
			
		||||
 | 
			
		||||
        this._settings = params.settings;
 | 
			
		||||
        this._monitorIndex = params.monitorIndex;
 | 
			
		||||
        this._layoutManager = params.layoutManager;
 | 
			
		||||
        this._effects = params.effects;
 | 
			
		||||
        this._fileWatches = {};
 | 
			
		||||
        this._pattern = null;
 | 
			
		||||
        // contains a single image for static backgrounds and
 | 
			
		||||
        // two images (from and to) for slide shows
 | 
			
		||||
        this._images = {};
 | 
			
		||||
 | 
			
		||||
        this._brightness = 1.0;
 | 
			
		||||
        this._vignetteSharpness = 0.2;
 | 
			
		||||
        this._saturation = 1.0;
 | 
			
		||||
        this._cancellable = new Gio.Cancellable();
 | 
			
		||||
        this.isLoaded = false;
 | 
			
		||||
 | 
			
		||||
        this._settingsChangedSignalId = this._settings.connect('changed', Lang.bind(this, function() {
 | 
			
		||||
                                            this.emit('changed');
 | 
			
		||||
                                        }));
 | 
			
		||||
 | 
			
		||||
        this._load();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _destroy: function() {
 | 
			
		||||
        this._cancellable.cancel();
 | 
			
		||||
 | 
			
		||||
        if (this._updateAnimationTimeoutId) {
 | 
			
		||||
            GLib.source_remove (this._updateAnimationTimeoutId);
 | 
			
		||||
            this._updateAnimationTimeoutId = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let i;
 | 
			
		||||
        let keys = Object.keys(this._fileWatches);
 | 
			
		||||
        for (i = 0; i < keys.length; i++) {
 | 
			
		||||
            this._cache.disconnect(this._fileWatches[keys[i]]);
 | 
			
		||||
        }
 | 
			
		||||
        this._fileWatches = null;
 | 
			
		||||
 | 
			
		||||
        if (this._pattern) {
 | 
			
		||||
            if (this._pattern.content)
 | 
			
		||||
                this._cache.removePatternContent(this._pattern.content);
 | 
			
		||||
 | 
			
		||||
            this._pattern.destroy();
 | 
			
		||||
            this._pattern = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        keys = Object.keys(this._images);
 | 
			
		||||
        for (i = 0; i < keys.length; i++) {
 | 
			
		||||
            let actor = this._images[keys[i]];
 | 
			
		||||
 | 
			
		||||
            if (actor.content)
 | 
			
		||||
                this._cache.removeImageContent(actor.content);
 | 
			
		||||
 | 
			
		||||
            actor.destroy();
 | 
			
		||||
            this._images[keys[i]] = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.actor.disconnect(this._destroySignalId);
 | 
			
		||||
        this._destroySignalId = 0;
 | 
			
		||||
 | 
			
		||||
        if (this._settingsChangedSignalId != 0)
 | 
			
		||||
            this._settings.disconnect(this._settingsChangedSignalId);
 | 
			
		||||
        this._settingsChangedSignalId = 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _setLoaded: function() {
 | 
			
		||||
        if (this.isLoaded)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this.isLoaded = true;
 | 
			
		||||
 | 
			
		||||
        GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
 | 
			
		||||
            this.emit('loaded');
 | 
			
		||||
            return false;
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _loadPattern: function() {
 | 
			
		||||
        let colorString, res, color, secondColor;
 | 
			
		||||
 | 
			
		||||
        colorString = this._settings.get_string(PRIMARY_COLOR_KEY);
 | 
			
		||||
        [res, color] = Clutter.Color.from_string(colorString);
 | 
			
		||||
        colorString = this._settings.get_string(SECONDARY_COLOR_KEY);
 | 
			
		||||
        [res, secondColor] = Clutter.Color.from_string(colorString);
 | 
			
		||||
 | 
			
		||||
        let shadingType = this._settings.get_enum(COLOR_SHADING_TYPE_KEY);
 | 
			
		||||
 | 
			
		||||
        let content = this._cache.getPatternContent({ monitorIndex: this._monitorIndex,
 | 
			
		||||
                                                      effects: this._effects,
 | 
			
		||||
                                                      color: color,
 | 
			
		||||
                                                      secondColor: secondColor,
 | 
			
		||||
                                                      shadingType: shadingType });
 | 
			
		||||
 | 
			
		||||
        this._pattern = new Meta.BackgroundActor();
 | 
			
		||||
        this.actor.add_child(this._pattern);
 | 
			
		||||
 | 
			
		||||
        this._pattern.content = content;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _watchCacheFile: function(filename) {
 | 
			
		||||
        if (this._fileWatches[filename])
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let signalId = this._cache.connect('file-changed',
 | 
			
		||||
                                           Lang.bind(this, function(cache, changedFile) {
 | 
			
		||||
                                               if (changedFile == filename) {
 | 
			
		||||
                                                   this.emit('changed');
 | 
			
		||||
                                               }
 | 
			
		||||
                                           }));
 | 
			
		||||
        this._fileWatches[filename] = signalId;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _addImage: function(content, index, filename) {
 | 
			
		||||
        content.saturation = this._saturation;
 | 
			
		||||
        content.brightness = this._brightness;
 | 
			
		||||
        content.vignette_sharpness = this._vignetteSharpness;
 | 
			
		||||
 | 
			
		||||
        let actor = new Meta.BackgroundActor();
 | 
			
		||||
        actor.content = content;
 | 
			
		||||
 | 
			
		||||
        // The background pattern is the first actor in
 | 
			
		||||
        // the group, and all images should be above that.
 | 
			
		||||
        this.actor.insert_child_at_index(actor, index + 1);
 | 
			
		||||
 | 
			
		||||
        this._images[index] = actor;
 | 
			
		||||
        this._watchCacheFile(filename);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateImage: function(content, index, filename) {
 | 
			
		||||
        content.saturation = this._saturation;
 | 
			
		||||
        content.brightness = this._brightness;
 | 
			
		||||
        content.vignette_sharpness = this._vignetteSharpness;
 | 
			
		||||
 | 
			
		||||
        this._cache.removeImageContent(this._images[index].content);
 | 
			
		||||
        this._images[index].content = content;
 | 
			
		||||
        this._watchCacheFile(filename);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateAnimationProgress: function() {
 | 
			
		||||
        if (this._images[1])
 | 
			
		||||
            this._images[1].opacity = this._animation.transitionProgress * 255;
 | 
			
		||||
 | 
			
		||||
        this._queueUpdateAnimation();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateAnimation: function() {
 | 
			
		||||
        this._updateAnimationTimeoutId = 0;
 | 
			
		||||
 | 
			
		||||
        this._animation.update(this._layoutManager.monitors[this._monitorIndex]);
 | 
			
		||||
        let files = this._animation.keyFrameFiles;
 | 
			
		||||
 | 
			
		||||
        if (files.length == 0) {
 | 
			
		||||
            this._setLoaded();
 | 
			
		||||
            this._queueUpdateAnimation();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let numPendingImages = files.length;
 | 
			
		||||
        for (let i = 0; i < files.length; i++) {
 | 
			
		||||
            if (this._images[i] && this._images[i].content &&
 | 
			
		||||
                this._images[i].content.get_filename() == files[i]) {
 | 
			
		||||
 | 
			
		||||
                numPendingImages--;
 | 
			
		||||
                if (numPendingImages == 0)
 | 
			
		||||
                    this._updateAnimationProgress();
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            this._cache.getImageContent({ monitorIndex: this._monitorIndex,
 | 
			
		||||
                                          effects: this._effects,
 | 
			
		||||
                                          style: this._style,
 | 
			
		||||
                                          filename: files[i],
 | 
			
		||||
                                          cancellable: this._cancellable,
 | 
			
		||||
                                          onFinished: Lang.bind(this, function(content, i) {
 | 
			
		||||
                                              numPendingImages--;
 | 
			
		||||
 | 
			
		||||
                                              if (!content) {
 | 
			
		||||
                                                  this._setLoaded();
 | 
			
		||||
                                                  if (numPendingImages == 0)
 | 
			
		||||
                                                      this._updateAnimationProgress();
 | 
			
		||||
                                                  return;
 | 
			
		||||
                                              }
 | 
			
		||||
 | 
			
		||||
                                              if (!this._images[i]) {
 | 
			
		||||
                                                  this._addImage(content, i, files[i]);
 | 
			
		||||
                                              } else {
 | 
			
		||||
                                                  this._updateImage(content, i, files[i]);
 | 
			
		||||
                                              }
 | 
			
		||||
 | 
			
		||||
                                              if (numPendingImages == 0) {
 | 
			
		||||
                                                  this._setLoaded();
 | 
			
		||||
                                                  this._updateAnimationProgress();
 | 
			
		||||
                                              }
 | 
			
		||||
                                          }, i)
 | 
			
		||||
                                        });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _queueUpdateAnimation: function() {
 | 
			
		||||
        if (this._updateAnimationTimeoutId != 0)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (!this._cancellable || this._cancellable.is_cancelled())
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (!this._animation.transitionDuration)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let nSteps = 255 / ANIMATION_OPACITY_STEP_INCREMENT;
 | 
			
		||||
        let timePerStep = (this._animation.transitionDuration * 1000) / nSteps;
 | 
			
		||||
 | 
			
		||||
        let interval = Math.max(ANIMATION_MIN_WAKEUP_INTERVAL * 1000,
 | 
			
		||||
                                timePerStep);
 | 
			
		||||
 | 
			
		||||
        if (interval > GLib.MAXUINT32)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._updateAnimationTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
 | 
			
		||||
                                                      interval,
 | 
			
		||||
                                                      Lang.bind(this, function() {
 | 
			
		||||
                                                                    this._updateAnimationTimeoutId = 0;
 | 
			
		||||
                                                                    this._updateAnimation();
 | 
			
		||||
                                                                    return false;
 | 
			
		||||
                                                                }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _loadAnimation: function(filename) {
 | 
			
		||||
        this._cache.getAnimation({ filename: filename,
 | 
			
		||||
                                             onLoaded: Lang.bind(this, function(animation) {
 | 
			
		||||
                                                 this._animation = animation;
 | 
			
		||||
 | 
			
		||||
                                                 if (!this._animation || this._cancellable.is_cancelled()) {
 | 
			
		||||
                                                     this._setLoaded();
 | 
			
		||||
                                                     return;
 | 
			
		||||
                                                 }
 | 
			
		||||
 | 
			
		||||
                                                 this._updateAnimation();
 | 
			
		||||
                                                 this._watchCacheFile(filename);
 | 
			
		||||
                                             })
 | 
			
		||||
                                           });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _loadFile: function(filename) {
 | 
			
		||||
        this._cache.getImageContent({ monitorIndex: this._monitorIndex,
 | 
			
		||||
                                      effects: this._effects,
 | 
			
		||||
                                      style: this._style,
 | 
			
		||||
                                      filename: filename,
 | 
			
		||||
                                      cancellable: this._cancellable,
 | 
			
		||||
                                      onFinished: Lang.bind(this, function(content) {
 | 
			
		||||
                                          if (!content) {
 | 
			
		||||
                                              if (!this._cancellable.is_cancelled())
 | 
			
		||||
                                                  this._loadAnimation(filename);
 | 
			
		||||
                                              return;
 | 
			
		||||
                                          }
 | 
			
		||||
 | 
			
		||||
                                          this._addImage(content, 0, filename);
 | 
			
		||||
                                          this._setLoaded();
 | 
			
		||||
                                      })
 | 
			
		||||
                                    });
 | 
			
		||||
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _load: function () {
 | 
			
		||||
        this._cache = getBackgroundCache();
 | 
			
		||||
 | 
			
		||||
        this._loadPattern(this._cache);
 | 
			
		||||
 | 
			
		||||
        this._style = this._settings.get_enum(BACKGROUND_STYLE_KEY);
 | 
			
		||||
        if (this._style == GDesktopEnums.BackgroundStyle.NONE) {
 | 
			
		||||
            this._setLoaded();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let uri = this._settings.get_string(PICTURE_URI_KEY);
 | 
			
		||||
        let filename;
 | 
			
		||||
        if (GLib.uri_parse_scheme(uri) != null)
 | 
			
		||||
            filename = Gio.File.new_for_uri(uri).get_path();
 | 
			
		||||
        else
 | 
			
		||||
            filename = uri;
 | 
			
		||||
 | 
			
		||||
        if (!filename) {
 | 
			
		||||
            this._setLoaded();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._loadFile(filename);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get saturation() {
 | 
			
		||||
        return this._saturation;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    set saturation(saturation) {
 | 
			
		||||
        this._saturation = saturation;
 | 
			
		||||
 | 
			
		||||
        if (this._pattern && this._pattern.content)
 | 
			
		||||
            this._pattern.content.saturation = saturation;
 | 
			
		||||
 | 
			
		||||
        let keys = Object.keys(this._images);
 | 
			
		||||
        for (let i = 0; i < keys.length; i++) {
 | 
			
		||||
            let image = this._images[keys[i]];
 | 
			
		||||
            if (image && image.content)
 | 
			
		||||
                image.content.saturation = saturation;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get brightness() {
 | 
			
		||||
        return this._brightness;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    set brightness(factor) {
 | 
			
		||||
        this._brightness = factor;
 | 
			
		||||
        if (this._pattern && this._pattern.content)
 | 
			
		||||
            this._pattern.content.brightness = factor;
 | 
			
		||||
 | 
			
		||||
        let keys = Object.keys(this._images);
 | 
			
		||||
        for (let i = 0; i < keys.length; i++) {
 | 
			
		||||
            let image = this._images[keys[i]];
 | 
			
		||||
            if (image && image.content)
 | 
			
		||||
                image.content.brightness = factor;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get vignetteSharpness() {
 | 
			
		||||
        return this._vignetteSharpness;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    set vignetteSharpness(sharpness) {
 | 
			
		||||
        this._vignetteSharpness = sharpness;
 | 
			
		||||
        if (this._pattern && this._pattern.content)
 | 
			
		||||
            this._pattern.content.vignette_sharpness = sharpness;
 | 
			
		||||
 | 
			
		||||
        let keys = Object.keys(this._images);
 | 
			
		||||
        for (let i = 0; i < keys.length; i++) {
 | 
			
		||||
            let image = this._images[keys[i]];
 | 
			
		||||
            if (image && image.content)
 | 
			
		||||
                image.content.vignette_sharpness = sharpness;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(Background.prototype);
 | 
			
		||||
 | 
			
		||||
const SystemBackground = new Lang.Class({
 | 
			
		||||
    Name: 'SystemBackground',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._cache = getBackgroundCache();
 | 
			
		||||
        this.actor = new Meta.BackgroundActor();
 | 
			
		||||
 | 
			
		||||
        this._cache.getImageContent({ style: GDesktopEnums.BackgroundStyle.WALLPAPER,
 | 
			
		||||
                                      filename: global.datadir + '/theme/noise-texture.png',
 | 
			
		||||
                                      effects: Meta.BackgroundEffects.NONE,
 | 
			
		||||
                                      onFinished: Lang.bind(this, function(content) {
 | 
			
		||||
                                          this.actor.content = content;
 | 
			
		||||
                                          this.emit('loaded');
 | 
			
		||||
                                      })
 | 
			
		||||
                                    });
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(SystemBackground.prototype);
 | 
			
		||||
 | 
			
		||||
const Animation = new Lang.Class({
 | 
			
		||||
    Name: 'Animation',
 | 
			
		||||
 | 
			
		||||
    _init: function(params) {
 | 
			
		||||
        params = Params.parse(params, { filename: null });
 | 
			
		||||
 | 
			
		||||
        this.filename = params.filename;
 | 
			
		||||
        this.keyFrameFiles = [];
 | 
			
		||||
        this.transitionProgress = 0.0;
 | 
			
		||||
        this.transitionDuration = 0.0;
 | 
			
		||||
        this.loaded = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    load: function(callback) {
 | 
			
		||||
        let file = Gio.File.new_for_path(this.filename);
 | 
			
		||||
 | 
			
		||||
        this._show = new GnomeDesktop.BGSlideShow({ filename: this.filename });
 | 
			
		||||
 | 
			
		||||
        this._show.load_async(null,
 | 
			
		||||
                              Lang.bind(this,
 | 
			
		||||
                                        function(object, result) {
 | 
			
		||||
                                            this.loaded = true;
 | 
			
		||||
                                            if (callback)
 | 
			
		||||
                                                callback();
 | 
			
		||||
                                        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    update: function(monitor) {
 | 
			
		||||
        this.keyFrameFiles = [];
 | 
			
		||||
 | 
			
		||||
        if (!this._show)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (this._show.get_num_slides() < 1)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let [progress, duration, isFixed, file1, file2] = this._show.get_current_slide(monitor.width, monitor.height);
 | 
			
		||||
 | 
			
		||||
        this.transitionDuration = duration;
 | 
			
		||||
        this.transitionProgress = progress;
 | 
			
		||||
 | 
			
		||||
        if (file1)
 | 
			
		||||
            this.keyFrameFiles.push(file1);
 | 
			
		||||
 | 
			
		||||
        if (file2)
 | 
			
		||||
            this.keyFrameFiles.push(file2);
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(Animation.prototype);
 | 
			
		||||
 | 
			
		||||
const BackgroundManager = new Lang.Class({
 | 
			
		||||
    Name: 'BackgroundManager',
 | 
			
		||||
 | 
			
		||||
    _init: function(params) {
 | 
			
		||||
        params = Params.parse(params, { container: null,
 | 
			
		||||
                                        layoutManager: Main.layoutManager,
 | 
			
		||||
                                        monitorIndex: null,
 | 
			
		||||
                                        effects: Meta.BackgroundEffects.NONE,
 | 
			
		||||
                                        controlPosition: true,
 | 
			
		||||
                                        settingsSchema: BACKGROUND_SCHEMA });
 | 
			
		||||
 | 
			
		||||
        this._settings = new Gio.Settings({ schema: params.settingsSchema });
 | 
			
		||||
        this._container = params.container;
 | 
			
		||||
        this._layoutManager = params.layoutManager;
 | 
			
		||||
        this._effects = params.effects;
 | 
			
		||||
        this._monitorIndex = params.monitorIndex;
 | 
			
		||||
        this._controlPosition = params.controlPosition;
 | 
			
		||||
 | 
			
		||||
        this.background = this._createBackground();
 | 
			
		||||
        this._newBackground = null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroy: function() {
 | 
			
		||||
        if (this._newBackground) {
 | 
			
		||||
            this._newBackground.actor.destroy();
 | 
			
		||||
            this._newBackground = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this.background) {
 | 
			
		||||
            this.background.actor.destroy();
 | 
			
		||||
            this.background = null;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateBackground: function(background, monitorIndex) {
 | 
			
		||||
        let newBackground = this._createBackground(monitorIndex);
 | 
			
		||||
        newBackground.vignetteSharpness = background.vignetteSharpness;
 | 
			
		||||
        newBackground.brightness = background.brightness;
 | 
			
		||||
        newBackground.saturation = background.saturation;
 | 
			
		||||
        newBackground.visible = background.visible;
 | 
			
		||||
 | 
			
		||||
        newBackground.loadedSignalId = newBackground.connect('loaded',
 | 
			
		||||
            Lang.bind(this, function() {
 | 
			
		||||
                newBackground.disconnect(newBackground.loadedSignalId);
 | 
			
		||||
                newBackground.loadedSignalId = 0;
 | 
			
		||||
                Tweener.addTween(background.actor,
 | 
			
		||||
                                 { opacity: 0,
 | 
			
		||||
                                   time: FADE_ANIMATION_TIME,
 | 
			
		||||
                                   transition: 'easeOutQuad',
 | 
			
		||||
                                   onComplete: Lang.bind(this, function() {
 | 
			
		||||
                                       if (this._newBackground == newBackground) {
 | 
			
		||||
                                           this.background = newBackground;
 | 
			
		||||
                                           this._newBackground = null;
 | 
			
		||||
                                       } else {
 | 
			
		||||
                                           newBackground.actor.destroy();
 | 
			
		||||
                                       }
 | 
			
		||||
 | 
			
		||||
                                       background.actor.destroy();
 | 
			
		||||
 | 
			
		||||
                                       this.emit('changed');
 | 
			
		||||
                                   })
 | 
			
		||||
                                 });
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        this._newBackground = newBackground;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createBackground: function() {
 | 
			
		||||
        let background = new Background({ monitorIndex: this._monitorIndex,
 | 
			
		||||
                                          layoutManager: this._layoutManager,
 | 
			
		||||
                                          effects: this._effects,
 | 
			
		||||
                                          settings: this._settings });
 | 
			
		||||
        this._container.add_child(background.actor);
 | 
			
		||||
 | 
			
		||||
        let monitor = this._layoutManager.monitors[this._monitorIndex];
 | 
			
		||||
 | 
			
		||||
        background.actor.set_size(monitor.width, monitor.height);
 | 
			
		||||
        if (this._controlPosition) {
 | 
			
		||||
            background.actor.set_position(monitor.x, monitor.y);
 | 
			
		||||
            background.actor.lower_bottom();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        background.changeSignalId = background.connect('changed', Lang.bind(this, function() {
 | 
			
		||||
            background.disconnect(background.changeSignalId);
 | 
			
		||||
            background.changeSignalId = 0;
 | 
			
		||||
            this._updateBackground(background, this._monitorIndex);
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        background.actor.connect('destroy', Lang.bind(this, function() {
 | 
			
		||||
            if (background.changeSignalId)
 | 
			
		||||
                background.disconnect(background.changeSignalId);
 | 
			
		||||
 | 
			
		||||
            if (background.loadedSignalId)
 | 
			
		||||
                background.disconnect(background.loadedSignalId);
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        return background;
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(BackgroundManager.prototype);
 | 
			
		||||
@@ -1,68 +0,0 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
 | 
			
		||||
const BoxPointer = imports.ui.boxpointer;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const PopupMenu = imports.ui.popupMenu;
 | 
			
		||||
 | 
			
		||||
const BackgroundMenu = new Lang.Class({
 | 
			
		||||
    Name: 'BackgroundMenu',
 | 
			
		||||
    Extends: PopupMenu.PopupMenu,
 | 
			
		||||
 | 
			
		||||
    _init: function(source) {
 | 
			
		||||
        this.parent(source, 0, St.Side.TOP);
 | 
			
		||||
 | 
			
		||||
        this.addSettingsAction(_("Settings"), 'gnome-control-center.desktop');
 | 
			
		||||
        this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
 | 
			
		||||
        this.addSettingsAction(_("Change Background…"), 'gnome-background-panel.desktop');
 | 
			
		||||
 | 
			
		||||
        this.actor.add_style_class_name('background-menu');
 | 
			
		||||
 | 
			
		||||
        Main.uiGroup.add_actor(this.actor);
 | 
			
		||||
        this.actor.hide();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
function addBackgroundMenu(actor) {
 | 
			
		||||
    let cursor = new St.Bin({ opacity: 0 });
 | 
			
		||||
    Main.uiGroup.add_actor(cursor);
 | 
			
		||||
 | 
			
		||||
    actor.reactive = true;
 | 
			
		||||
    actor._backgroundMenu = new BackgroundMenu(cursor);
 | 
			
		||||
    actor._backgroundManager = new PopupMenu.PopupMenuManager({ actor: actor });
 | 
			
		||||
    actor._backgroundManager.addMenu(actor._backgroundMenu);
 | 
			
		||||
 | 
			
		||||
    function openMenu() {
 | 
			
		||||
        let [x, y] = global.get_pointer();
 | 
			
		||||
        cursor.set_position(x, y);
 | 
			
		||||
        actor._backgroundMenu.open(BoxPointer.PopupAnimation.NONE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let clickAction = new Clutter.ClickAction();
 | 
			
		||||
    clickAction.connect('long-press', function(action, actor, state) {
 | 
			
		||||
        if (state == Clutter.LongPressState.QUERY)
 | 
			
		||||
            return action.get_button() == 1 && !actor._backgroundMenu.isOpen;
 | 
			
		||||
        if (state == Clutter.LongPressState.ACTIVATE) {
 | 
			
		||||
            openMenu();
 | 
			
		||||
            actor._backgroundManager.ignoreRelease();
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    });
 | 
			
		||||
    clickAction.connect('clicked', function(action) {
 | 
			
		||||
        if (action.get_button() == 3)
 | 
			
		||||
            openMenu();
 | 
			
		||||
    });
 | 
			
		||||
    actor.add_action(clickAction);
 | 
			
		||||
 | 
			
		||||
    actor.connect('destroy', function() {
 | 
			
		||||
                      actor._backgroundMenu.destroy();
 | 
			
		||||
                      actor._backgroundMenu = null;
 | 
			
		||||
                      actor._backgroundManager = null;
 | 
			
		||||
 | 
			
		||||
                      cursor.destroy();
 | 
			
		||||
                  });
 | 
			
		||||
}
 | 
			
		||||
@@ -3,9 +3,8 @@
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Meta = imports.gi.Meta;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const Tweener = imports.ui.tweener;
 | 
			
		||||
@@ -39,7 +38,6 @@ const BoxPointer = new Lang.Class({
 | 
			
		||||
        this._arrowSide = arrowSide;
 | 
			
		||||
        this._userArrowSide = arrowSide;
 | 
			
		||||
        this._arrowOrigin = 0;
 | 
			
		||||
        this._arrowActor = null;
 | 
			
		||||
        this.actor = new St.Bin({ x_fill: true,
 | 
			
		||||
                                  y_fill: true });
 | 
			
		||||
        this._container = new Shell.GenericContainer();
 | 
			
		||||
@@ -62,10 +60,6 @@ const BoxPointer = new Lang.Class({
 | 
			
		||||
        this._muteInput();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get arrowSide() {
 | 
			
		||||
        return this._arrowSide;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _muteInput: function() {
 | 
			
		||||
        if (this._capturedEventId == 0)
 | 
			
		||||
            this._capturedEventId = this.actor.connect('captured-event',
 | 
			
		||||
@@ -185,9 +179,7 @@ const BoxPointer = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getPreferredHeight: function(actor, forWidth, alloc) {
 | 
			
		||||
        let themeNode = this.actor.get_theme_node();
 | 
			
		||||
        let borderWidth = themeNode.get_length('-arrow-border-width');
 | 
			
		||||
        let [minSize, naturalSize] = this.bin.get_preferred_height(forWidth - 2 * borderWidth);
 | 
			
		||||
        let [minSize, naturalSize] = this.bin.get_preferred_height(forWidth);
 | 
			
		||||
        alloc.min_size = minSize;
 | 
			
		||||
        alloc.natural_size = naturalSize;
 | 
			
		||||
        this._adjustAllocationForArrow(false, alloc);
 | 
			
		||||
@@ -228,27 +220,31 @@ const BoxPointer = new Lang.Class({
 | 
			
		||||
        this.bin.allocate(childBox, flags);
 | 
			
		||||
 | 
			
		||||
        if (this._sourceActor && this._sourceActor.mapped) {
 | 
			
		||||
            this._reposition();
 | 
			
		||||
            this._updateFlip();
 | 
			
		||||
            this._reposition(this._sourceActor, this._arrowAlignment);
 | 
			
		||||
 | 
			
		||||
            if (this._shouldFlip()) {
 | 
			
		||||
                switch (this._arrowSide) {
 | 
			
		||||
                case St.Side.TOP:
 | 
			
		||||
                    this._arrowSide = St.Side.BOTTOM;
 | 
			
		||||
                    break;
 | 
			
		||||
                case St.Side.BOTTOM:
 | 
			
		||||
                    this._arrowSide = St.Side.TOP;
 | 
			
		||||
                    break;
 | 
			
		||||
                case St.Side.LEFT:
 | 
			
		||||
                    this._arrowSide = St.Side.RIGHT;
 | 
			
		||||
                    break;
 | 
			
		||||
                case St.Side.RIGHT:
 | 
			
		||||
                    this._arrowSide = St.Side.LEFT;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                this._reposition(this._sourceActor, this._arrowAlignment);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _drawBorder: function(area) {
 | 
			
		||||
        let themeNode = this.actor.get_theme_node();
 | 
			
		||||
 | 
			
		||||
        if (this._arrowActor) {
 | 
			
		||||
            let [sourceX, sourceY] = this._arrowActor.get_transformed_position();
 | 
			
		||||
            let [sourceWidth, sourceHeight] = this._arrowActor.get_transformed_size();
 | 
			
		||||
            let [absX, absY] = this.actor.get_transformed_position();
 | 
			
		||||
 | 
			
		||||
            if (this._arrowSide == St.Side.TOP ||
 | 
			
		||||
                this._arrowSide == St.Side.BOTTOM) {
 | 
			
		||||
                this._arrowOrigin = sourceX - absX + sourceWidth / 2;
 | 
			
		||||
            } else {
 | 
			
		||||
                this._arrowOrigin = sourceY - absY + sourceHeight / 2;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let borderWidth = themeNode.get_length('-arrow-border-width');
 | 
			
		||||
        let base = themeNode.get_length('-arrow-base');
 | 
			
		||||
        let rise = themeNode.get_length('-arrow-rise');
 | 
			
		||||
@@ -409,11 +405,11 @@ const BoxPointer = new Lang.Class({
 | 
			
		||||
            cr.setLineWidth(borderWidth);
 | 
			
		||||
            cr.stroke();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        cr.$dispose();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setPosition: function(sourceActor, alignment) {
 | 
			
		||||
        this._arrowSide = this._userArrowSide;
 | 
			
		||||
 | 
			
		||||
        // We need to show it now to force an allocation,
 | 
			
		||||
        // so that we can query the correct size.
 | 
			
		||||
        this.actor.show();
 | 
			
		||||
@@ -421,8 +417,7 @@ const BoxPointer = new Lang.Class({
 | 
			
		||||
        this._sourceActor = sourceActor;
 | 
			
		||||
        this._arrowAlignment = alignment;
 | 
			
		||||
 | 
			
		||||
        this._reposition();
 | 
			
		||||
        this._updateFlip();
 | 
			
		||||
        this._reposition(sourceActor, alignment);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setSourceAlignment: function(alignment) {
 | 
			
		||||
@@ -434,10 +429,7 @@ const BoxPointer = new Lang.Class({
 | 
			
		||||
        this.setPosition(this._sourceActor, this._arrowAlignment);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _reposition: function() {
 | 
			
		||||
        let sourceActor = this._sourceActor;
 | 
			
		||||
        let alignment = this._arrowAlignment;
 | 
			
		||||
 | 
			
		||||
    _reposition: function(sourceActor, alignment) {
 | 
			
		||||
        // Position correctly relative to the sourceActor
 | 
			
		||||
        let sourceNode = sourceActor.get_theme_node();
 | 
			
		||||
        let sourceContentBox = sourceNode.get_content_box(sourceActor.get_allocation_box());
 | 
			
		||||
@@ -558,20 +550,10 @@ const BoxPointer = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // @actor: an actor relative to which the arrow is positioned.
 | 
			
		||||
    // Differently from setPosition, this will not move the boxpointer itself,
 | 
			
		||||
    // on the arrow
 | 
			
		||||
    setArrowActor: function(actor) {
 | 
			
		||||
        if (this._arrowActor != actor) {
 | 
			
		||||
            this._arrowActor = actor;
 | 
			
		||||
            this._border.queue_repaint();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _shiftActor : function() {
 | 
			
		||||
        // Since the position of the BoxPointer depends on the allocated size
 | 
			
		||||
        // of the BoxPointer and the position of the source actor, trying
 | 
			
		||||
        // to position the BoxPointer via the x/y properties will result in
 | 
			
		||||
        // to position the BoxPoiner via the x/y properties will result in
 | 
			
		||||
        // allocation loops and warnings. Instead we do the positioning via
 | 
			
		||||
        // the anchor point, which is independent of allocation, and leave
 | 
			
		||||
        // x == y == 0.
 | 
			
		||||
@@ -579,49 +561,37 @@ const BoxPointer = new Lang.Class({
 | 
			
		||||
                                    -(this._yPosition + this._yOffset));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _calculateArrowSide: function(arrowSide) {
 | 
			
		||||
    _shouldFlip: function() {
 | 
			
		||||
        let sourceAllocation = Shell.util_get_transformed_allocation(this._sourceActor);
 | 
			
		||||
        let [minWidth, minHeight, boxWidth, boxHeight] = this._container.get_preferred_size();
 | 
			
		||||
        let boxAllocation = Shell.util_get_transformed_allocation(this.actor);
 | 
			
		||||
        let boxWidth = boxAllocation.x2 - boxAllocation.x1;
 | 
			
		||||
        let boxHeight = boxAllocation.y2 - boxAllocation.y1;
 | 
			
		||||
        let monitor = Main.layoutManager.findMonitorForActor(this.actor);
 | 
			
		||||
 | 
			
		||||
        switch (arrowSide) {
 | 
			
		||||
        switch (this._arrowSide) {
 | 
			
		||||
        case St.Side.TOP:
 | 
			
		||||
            if (sourceAllocation.y2 + boxHeight > monitor.y + monitor.height &&
 | 
			
		||||
            if (boxAllocation.y2 > monitor.y + monitor.height &&
 | 
			
		||||
                boxHeight < sourceAllocation.y1 - monitor.y)
 | 
			
		||||
                return St.Side.BOTTOM;
 | 
			
		||||
                return true;
 | 
			
		||||
            break;
 | 
			
		||||
        case St.Side.BOTTOM:
 | 
			
		||||
            if (sourceAllocation.y1 - boxHeight < monitor.y &&
 | 
			
		||||
            if (boxAllocation.y1 < monitor.y &&
 | 
			
		||||
                boxHeight < monitor.y + monitor.height - sourceAllocation.y2)
 | 
			
		||||
                return St.Side.TOP;
 | 
			
		||||
                return true;
 | 
			
		||||
            break;
 | 
			
		||||
        case St.Side.LEFT:
 | 
			
		||||
            if (sourceAllocation.x2 + boxWidth > monitor.x + monitor.width &&
 | 
			
		||||
            if (boxAllocation.x2 > monitor.x + monitor.width &&
 | 
			
		||||
                boxWidth < sourceAllocation.x1 - monitor.x)
 | 
			
		||||
                return St.Side.RIGHT;
 | 
			
		||||
                return true;
 | 
			
		||||
            break;
 | 
			
		||||
        case St.Side.RIGHT:
 | 
			
		||||
            if (sourceAllocation.x1 - boxWidth < monitor.x &&
 | 
			
		||||
            if (boxAllocation.x1 < monitor.x &&
 | 
			
		||||
                boxWidth < monitor.x + monitor.width - sourceAllocation.x2)
 | 
			
		||||
                return St.Side.LEFT;
 | 
			
		||||
                return true;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return arrowSide;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateFlip: function() {
 | 
			
		||||
        let arrowSide = this._calculateArrowSide(this._userArrowSide);
 | 
			
		||||
        if (this._arrowSide != arrowSide) {
 | 
			
		||||
            this._arrowSide = arrowSide;
 | 
			
		||||
            this._reposition();
 | 
			
		||||
            Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
 | 
			
		||||
                this._container.queue_relayout();
 | 
			
		||||
                return false;
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
            this.emit('arrow-side-changed');
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    set xOffset(offset) {
 | 
			
		||||
@@ -648,21 +618,5 @@ const BoxPointer = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    get opacity() {
 | 
			
		||||
        return this.actor.opacity;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    updateArrowSide: function(side) {
 | 
			
		||||
        this._arrowSide = side;
 | 
			
		||||
        this._border.queue_repaint();
 | 
			
		||||
 | 
			
		||||
        this.emit('arrow-side-changed');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getPadding: function(side) {
 | 
			
		||||
        return this.bin.get_theme_node().get_padding(side);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getArrowHeight: function() {
 | 
			
		||||
        return this.actor.get_theme_node().get_length('-arrow-rise');
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(BoxPointer.prototype);
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,6 @@
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
@@ -63,18 +62,15 @@ function _formatEventTime(event, clockFormat) {
 | 
			
		||||
    } else {
 | 
			
		||||
        switch (clockFormat) {
 | 
			
		||||
        case '24h':
 | 
			
		||||
            /* Translators: Shown in calendar event list, if 24h format,
 | 
			
		||||
               \u2236 is a ratio character, similar to : */
 | 
			
		||||
            ret = event.date.toLocaleFormat(C_("event list time", "%H\u2236%M"));
 | 
			
		||||
            /* Translators: Shown in calendar event list, if 24h format */
 | 
			
		||||
            ret = event.date.toLocaleFormat(C_("event list time", "%H:%M"));
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        default:
 | 
			
		||||
            /* explicit fall-through */
 | 
			
		||||
        case '12h':
 | 
			
		||||
            /* Translators: Shown in calendar event list, if 12h format,
 | 
			
		||||
               \u2236 is a ratio character, similar to : and \u2009 is
 | 
			
		||||
               a thin space */
 | 
			
		||||
            ret = event.date.toLocaleFormat(C_("event list time", "%l\u2236%M\u2009%p"));
 | 
			
		||||
            /* Transators: Shown in calendar event list, if 12h format */
 | 
			
		||||
            ret = event.date.toLocaleFormat(C_("event list time", "%l:%M %p"));
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -168,12 +164,6 @@ const EmptyEventSource = new Lang.Class({
 | 
			
		||||
    Name: 'EmptyEventSource',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.isLoading = false;
 | 
			
		||||
        this.isDummy = true;
 | 
			
		||||
        this.hasCalendars = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroy: function() {
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    requestRange: function(begin, end) {
 | 
			
		||||
@@ -197,18 +187,21 @@ const CalendarServerIface = <interface name="org.gnome.Shell.CalendarServer">
 | 
			
		||||
    <arg type="b" direction="in" />
 | 
			
		||||
    <arg type="a(sssbxxa{sv})" direction="out" />
 | 
			
		||||
</method>
 | 
			
		||||
<property name="HasCalendars" type="b" access="read" />
 | 
			
		||||
<signal name="Changed" />
 | 
			
		||||
</interface>;
 | 
			
		||||
 | 
			
		||||
const CalendarServerInfo  = Gio.DBusInterfaceInfo.new_for_xml(CalendarServerIface);
 | 
			
		||||
 | 
			
		||||
function CalendarServer() {
 | 
			
		||||
    return new Gio.DBusProxy({ g_connection: Gio.DBus.session,
 | 
			
		||||
                               g_interface_name: CalendarServerInfo.name,
 | 
			
		||||
                               g_interface_info: CalendarServerInfo,
 | 
			
		||||
                               g_name: 'org.gnome.Shell.CalendarServer',
 | 
			
		||||
                               g_object_path: '/org/gnome/Shell/CalendarServer' });
 | 
			
		||||
    var self = new Gio.DBusProxy({ g_connection: Gio.DBus.session,
 | 
			
		||||
                                   g_interface_name: CalendarServerInfo.name,
 | 
			
		||||
                                   g_interface_info: CalendarServerInfo,
 | 
			
		||||
                                   g_name: 'org.gnome.Shell.CalendarServer',
 | 
			
		||||
                                   g_object_path: '/org/gnome/Shell/CalendarServer',
 | 
			
		||||
                                   g_flags: Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES });
 | 
			
		||||
 | 
			
		||||
    self.init(null);
 | 
			
		||||
    return self;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _datesEqual(a, b) {
 | 
			
		||||
@@ -235,49 +228,18 @@ const DBusEventSource = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._resetCache();
 | 
			
		||||
        this.isLoading = false;
 | 
			
		||||
        this.isDummy = false;
 | 
			
		||||
 | 
			
		||||
        this._initialized = false;
 | 
			
		||||
        this._dbusProxy = new CalendarServer();
 | 
			
		||||
        this._dbusProxy.init_async(GLib.PRIORITY_DEFAULT, null, Lang.bind(this, function(object, result) {
 | 
			
		||||
            try {
 | 
			
		||||
                this._dbusProxy.init_finish(result);
 | 
			
		||||
            } catch(e) {
 | 
			
		||||
                log('Error loading calendars: ' + e.message);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        this._dbusProxy.connectSignal('Changed', Lang.bind(this, this._onChanged));
 | 
			
		||||
 | 
			
		||||
            this._dbusProxy.connectSignal('Changed', Lang.bind(this, this._onChanged));
 | 
			
		||||
 | 
			
		||||
            this._dbusProxy.connect('notify::g-name-owner', Lang.bind(this, function() {
 | 
			
		||||
                if (this._dbusProxy.g_name_owner)
 | 
			
		||||
                    this._onNameAppeared();
 | 
			
		||||
                else
 | 
			
		||||
                    this._onNameVanished();
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
            this._dbusProxy.connect('g-properties-changed', Lang.bind(this, function() {
 | 
			
		||||
                this.emit('notify::has-calendars');
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
            this._initialized = true;
 | 
			
		||||
            this.emit('notify::has-calendars');
 | 
			
		||||
            this._onNameAppeared();
 | 
			
		||||
        this._dbusProxy.connect('notify::g-name-owner', Lang.bind(this, function() {
 | 
			
		||||
            if (this._dbusProxy.g_name_owner)
 | 
			
		||||
                this._onNameAppeared();
 | 
			
		||||
            else
 | 
			
		||||
                this._onNameVanished();
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroy: function() {
 | 
			
		||||
        this._dbusProxy.run_dispose();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get hasCalendars() {
 | 
			
		||||
        if (this._initialized)
 | 
			
		||||
            return this._dbusProxy.HasCalendars;
 | 
			
		||||
        else
 | 
			
		||||
            return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _resetCache: function() {
 | 
			
		||||
        this._events = [];
 | 
			
		||||
        this._lastRequestBegin = null;
 | 
			
		||||
@@ -317,15 +279,10 @@ const DBusEventSource = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._events = newEvents;
 | 
			
		||||
        this.isLoading = false;
 | 
			
		||||
        this.emit('changed');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _loadEvents: function(forceReload) {
 | 
			
		||||
        // Ignore while loading
 | 
			
		||||
        if (!this._initialized)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (this._curRequestBegin && this._curRequestEnd){
 | 
			
		||||
            let callFlags = Gio.DBusCallFlags.NO_AUTO_START;
 | 
			
		||||
            if (forceReload)
 | 
			
		||||
@@ -340,7 +297,6 @@ const DBusEventSource = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    requestRange: function(begin, end, forceReload) {
 | 
			
		||||
        if (forceReload || !(_datesEqual(begin, this._lastRequestBegin) && _datesEqual(end, this._lastRequestEnd))) {
 | 
			
		||||
            this.isLoading = true;
 | 
			
		||||
            this._lastRequestBegin = begin;
 | 
			
		||||
            this._lastRequestEnd = end;
 | 
			
		||||
            this._curRequestBegin = begin;
 | 
			
		||||
@@ -415,11 +371,19 @@ const Calendar = new Lang.Class({
 | 
			
		||||
    // @eventSource: is an object implementing the EventSource API, e.g. the
 | 
			
		||||
    // requestRange(), getEvents(), hasEvents() methods and the ::changed signal.
 | 
			
		||||
    setEventSource: function(eventSource) {
 | 
			
		||||
        if (this._eventSource) {
 | 
			
		||||
            this._eventSource.disconnect(this._eventSourceChangedId);
 | 
			
		||||
            this._eventSource = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._eventSource = eventSource;
 | 
			
		||||
        this._eventSource.connect('changed', Lang.bind(this, function() {
 | 
			
		||||
            this._update(false);
 | 
			
		||||
        }));
 | 
			
		||||
        this._update(true);
 | 
			
		||||
 | 
			
		||||
        if (this._eventSource) {
 | 
			
		||||
            this._eventSourceChangedId = this._eventSource.connect('changed', Lang.bind(this, function() {
 | 
			
		||||
                this._update(false);
 | 
			
		||||
            }));
 | 
			
		||||
            this._update(true);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Sets the calendar to show a specific date
 | 
			
		||||
@@ -443,21 +407,16 @@ const Calendar = new Lang.Class({
 | 
			
		||||
        this.actor.add(this._topBox,
 | 
			
		||||
                       { row: 0, col: 0, col_span: offsetCols + 7 });
 | 
			
		||||
 | 
			
		||||
        this._backButton = new St.Button({ style_class: 'calendar-change-month-back',
 | 
			
		||||
                                           accessible_name: _("Previous month"),
 | 
			
		||||
                                           can_focus: true });
 | 
			
		||||
        this._topBox.add(this._backButton);
 | 
			
		||||
        this._backButton.connect('clicked', Lang.bind(this, this._onPrevMonthButtonClicked));
 | 
			
		||||
        let back = new St.Button({ style_class: 'calendar-change-month-back' });
 | 
			
		||||
        this._topBox.add(back);
 | 
			
		||||
        back.connect('clicked', Lang.bind(this, this._onPrevMonthButtonClicked));
 | 
			
		||||
 | 
			
		||||
        this._monthLabel = new St.Label({style_class: 'calendar-month-label',
 | 
			
		||||
                                         can_focus: true });
 | 
			
		||||
        this._monthLabel = new St.Label({style_class: 'calendar-month-label'});
 | 
			
		||||
        this._topBox.add(this._monthLabel, { expand: true, x_fill: false, x_align: St.Align.MIDDLE });
 | 
			
		||||
 | 
			
		||||
        this._forwardButton = new St.Button({ style_class: 'calendar-change-month-forward',
 | 
			
		||||
                                              accessible_name: _("Next month"),
 | 
			
		||||
                                              can_focus: true });
 | 
			
		||||
        this._topBox.add(this._forwardButton);
 | 
			
		||||
        this._forwardButton.connect('clicked', Lang.bind(this, this._onNextMonthButtonClicked));
 | 
			
		||||
        let forward = new St.Button({ style_class: 'calendar-change-month-forward' });
 | 
			
		||||
        this._topBox.add(forward);
 | 
			
		||||
        forward.connect('clicked', Lang.bind(this, this._onNextMonthButtonClicked));
 | 
			
		||||
 | 
			
		||||
        // Add weekday labels...
 | 
			
		||||
        //
 | 
			
		||||
@@ -516,12 +475,10 @@ const Calendar = new Lang.Class({
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._backButton.grab_key_focus();
 | 
			
		||||
 | 
			
		||||
        this.setDate(newDate, false);
 | 
			
		||||
    },
 | 
			
		||||
   },
 | 
			
		||||
 | 
			
		||||
    _onNextMonthButtonClicked: function() {
 | 
			
		||||
   _onNextMonthButtonClicked: function() {
 | 
			
		||||
        let newDate = new Date(this._selectedDate);
 | 
			
		||||
        let oldMonth = newDate.getMonth();
 | 
			
		||||
        if (oldMonth == 11) {
 | 
			
		||||
@@ -540,9 +497,7 @@ const Calendar = new Lang.Class({
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._forwardButton.grab_key_focus();
 | 
			
		||||
 | 
			
		||||
        this.setDate(newDate, false);
 | 
			
		||||
       this.setDate(newDate, false);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onSettingsChange: function() {
 | 
			
		||||
@@ -565,60 +520,30 @@ const Calendar = new Lang.Class({
 | 
			
		||||
            children[i].destroy();
 | 
			
		||||
 | 
			
		||||
        // Start at the beginning of the week before the start of the month
 | 
			
		||||
        //
 | 
			
		||||
        // We want to show always 6 weeks (to keep the calendar menu at the same
 | 
			
		||||
        // height if there are no events), so we pad it according to the following
 | 
			
		||||
        // policy:
 | 
			
		||||
        //
 | 
			
		||||
        // 1 - If a month has 6 weeks, we place no padding (example: Dec 2012)
 | 
			
		||||
        // 2 - If a month has 5 weeks and it starts on week start, we pad one week
 | 
			
		||||
        //     before it (example: Apr 2012)
 | 
			
		||||
        // 3 - If a month has 5 weeks and it starts on any other day, we pad one week
 | 
			
		||||
        //     after it (example: Nov 2012)
 | 
			
		||||
        // 4 - If a month has 4 weeks, we pad one week before and one after it
 | 
			
		||||
        //     (example: Feb 2010)
 | 
			
		||||
        //
 | 
			
		||||
        // Actually computing the number of weeks is complex, but we know that the
 | 
			
		||||
        // problematic categories (2 and 4) always start on week start, and that
 | 
			
		||||
        // all months at the end have 6 weeks.
 | 
			
		||||
 | 
			
		||||
        let beginDate = new Date(this._selectedDate);
 | 
			
		||||
        beginDate.setDate(1);
 | 
			
		||||
        beginDate.setSeconds(0);
 | 
			
		||||
        beginDate.setHours(12);
 | 
			
		||||
        let year = beginDate.getYear();
 | 
			
		||||
 | 
			
		||||
        let daysToWeekStart = (7 + beginDate.getDay() - this._weekStart) % 7;
 | 
			
		||||
        let startsOnWeekStart = daysToWeekStart == 0;
 | 
			
		||||
        let weekPadding = startsOnWeekStart ? 7 : 0;
 | 
			
		||||
 | 
			
		||||
        beginDate.setTime(beginDate.getTime() - (weekPadding + daysToWeekStart) * MSECS_IN_DAY);
 | 
			
		||||
        beginDate.setTime(beginDate.getTime() - daysToWeekStart * MSECS_IN_DAY);
 | 
			
		||||
 | 
			
		||||
        let iter = new Date(beginDate);
 | 
			
		||||
        let row = 2;
 | 
			
		||||
        // nRows here means 6 weeks + one header + one navbar
 | 
			
		||||
        let nRows = 8;
 | 
			
		||||
        while (row < 8) {
 | 
			
		||||
            let button = new St.Button({ label: iter.getDate().toString(),
 | 
			
		||||
                                         can_focus: true });
 | 
			
		||||
        while (true) {
 | 
			
		||||
            let button = new St.Button({ label: iter.getDate().toString() });
 | 
			
		||||
            let rtl = button.get_text_direction() == Clutter.TextDirection.RTL;
 | 
			
		||||
 | 
			
		||||
            if (this._eventSource.isDummy)
 | 
			
		||||
            if (!this._eventSource)
 | 
			
		||||
                button.reactive = false;
 | 
			
		||||
 | 
			
		||||
            let iterStr = iter.toUTCString();
 | 
			
		||||
            button.connect('clicked', Lang.bind(this, function() {
 | 
			
		||||
                this._shouldDateGrabFocus = true;
 | 
			
		||||
 | 
			
		||||
                let newlySelectedDate = new Date(iterStr);
 | 
			
		||||
                this.setDate(newlySelectedDate, false);
 | 
			
		||||
 | 
			
		||||
                this._shouldDateGrabFocus = false;
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
            let hasEvents = this._eventSource.hasEvents(iter);
 | 
			
		||||
            let hasEvents = this._eventSource && this._eventSource.hasEvents(iter);
 | 
			
		||||
            let styleClass = 'calendar-day-base calendar-day';
 | 
			
		||||
 | 
			
		||||
            if (_isWorkDay(iter))
 | 
			
		||||
                styleClass += ' calendar-work-day'
 | 
			
		||||
            else
 | 
			
		||||
@@ -638,6 +563,9 @@ const Calendar = new Lang.Class({
 | 
			
		||||
            else if (iter.getMonth() != this._selectedDate.getMonth())
 | 
			
		||||
                styleClass += ' calendar-other-month-day';
 | 
			
		||||
 | 
			
		||||
            if (_sameDay(this._selectedDate, iter))
 | 
			
		||||
                button.add_style_pseudo_class('active');
 | 
			
		||||
 | 
			
		||||
            if (hasEvents)
 | 
			
		||||
                styleClass += ' calendar-day-with-events'
 | 
			
		||||
 | 
			
		||||
@@ -647,13 +575,6 @@ const Calendar = new Lang.Class({
 | 
			
		||||
            this.actor.add(button,
 | 
			
		||||
                           { row: row, col: offsetCols + (7 + iter.getDay() - this._weekStart) % 7 });
 | 
			
		||||
 | 
			
		||||
            if (_sameDay(this._selectedDate, iter)) {
 | 
			
		||||
                button.add_style_pseudo_class('active');
 | 
			
		||||
 | 
			
		||||
                if (this._shouldDateGrabFocus)
 | 
			
		||||
                    button.grab_key_focus();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (this._useWeekdate && iter.getDay() == 4) {
 | 
			
		||||
                let label = new St.Label({ text: _getCalendarWeekForDate(iter).toString(),
 | 
			
		||||
                                           style_class: 'calendar-day-base calendar-week-number'});
 | 
			
		||||
@@ -662,13 +583,17 @@ const Calendar = new Lang.Class({
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            iter.setTime(iter.getTime() + MSECS_IN_DAY);
 | 
			
		||||
 | 
			
		||||
            if (iter.getDay() == this._weekStart)
 | 
			
		||||
            if (iter.getDay() == this._weekStart) {
 | 
			
		||||
                // We stop on the first "first day of the week" after the month we are displaying
 | 
			
		||||
                if (iter.getMonth() > this._selectedDate.getMonth() || iter.getYear() > this._selectedDate.getYear())
 | 
			
		||||
                    break;
 | 
			
		||||
                row++;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        // Signal to the event source that we are interested in events
 | 
			
		||||
        // only from this date range
 | 
			
		||||
        this._eventSource.requestRange(beginDate, iter, forceReload);
 | 
			
		||||
        if (this._eventSource)
 | 
			
		||||
            this._eventSource.requestRange(beginDate, iter, forceReload);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@@ -678,7 +603,7 @@ const EventsList = new Lang.Class({
 | 
			
		||||
    Name: 'EventsList',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.actor = new St.Table({ style_class: 'events-table' });
 | 
			
		||||
        this.actor = new St.BoxLayout({ vertical: true, style_class: 'events-header-vbox'});
 | 
			
		||||
        this._date = new Date();
 | 
			
		||||
        this._desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
 | 
			
		||||
        this._desktopSettings.connect('changed', Lang.bind(this, this._update));
 | 
			
		||||
@@ -686,76 +611,70 @@ const EventsList = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setEventSource: function(eventSource) {
 | 
			
		||||
        if (this._eventSource) {
 | 
			
		||||
            this._eventSource.disconnect(this._eventSourceChangedId);
 | 
			
		||||
            this._eventSource = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._eventSource = eventSource;
 | 
			
		||||
        this._eventSource.connect('changed', Lang.bind(this, this._update));
 | 
			
		||||
 | 
			
		||||
        if (this._eventSource) {
 | 
			
		||||
            this._eventSourceChangedId = this._eventSource.connect('changed', Lang.bind(this, this._update));
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _addEvent: function(event, index, includeDayName) {
 | 
			
		||||
        let dayString;
 | 
			
		||||
        if (includeDayName)
 | 
			
		||||
            dayString = _getEventDayAbbreviation(event.date.getDay());
 | 
			
		||||
        else
 | 
			
		||||
            dayString = '';
 | 
			
		||||
 | 
			
		||||
        let dayLabel = new St.Label({ style_class: 'events-day-dayname',
 | 
			
		||||
                                      text: dayString });
 | 
			
		||||
        dayLabel.clutter_text.line_wrap = false;
 | 
			
		||||
        dayLabel.clutter_text.ellipsize = false;
 | 
			
		||||
 | 
			
		||||
        this.actor.add(dayLabel, { row: index, col: 0,
 | 
			
		||||
                                   x_expand: false, x_align: St.Align.END,
 | 
			
		||||
                                   y_fill: false, y_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        let clockFormat = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);
 | 
			
		||||
        let timeString = _formatEventTime(event, clockFormat);
 | 
			
		||||
        let timeLabel = new St.Label({ style_class: 'events-day-time',
 | 
			
		||||
                                       text: timeString });
 | 
			
		||||
        timeLabel.clutter_text.line_wrap = false;
 | 
			
		||||
        timeLabel.clutter_text.ellipsize = false;
 | 
			
		||||
 | 
			
		||||
        this.actor.add(timeLabel, { row: index, col: 1,
 | 
			
		||||
                                    x_expand: false, x_align: St.Align.MIDDLE,
 | 
			
		||||
                                    y_fill: false, y_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        let titleLabel = new St.Label({ style_class: 'events-day-task',
 | 
			
		||||
                                        text: event.summary });
 | 
			
		||||
        titleLabel.clutter_text.line_wrap = true;
 | 
			
		||||
        titleLabel.clutter_text.ellipsize = false;
 | 
			
		||||
 | 
			
		||||
        this.actor.add(titleLabel, { row: index, col: 2,
 | 
			
		||||
                                     x_expand: true, x_align: St.Align.START,
 | 
			
		||||
                                     y_fill: false, y_align: St.Align.START });
 | 
			
		||||
    _addEvent: function(dayNameBox, timeBox, eventTitleBox, includeDayName, day, time, desc) {
 | 
			
		||||
        if (includeDayName) {
 | 
			
		||||
            dayNameBox.add(new St.Label( { style_class: 'events-day-dayname',
 | 
			
		||||
                                           text: day } ),
 | 
			
		||||
                           { x_fill: true } );
 | 
			
		||||
        }
 | 
			
		||||
        timeBox.add(new St.Label( { style_class: 'events-day-time',
 | 
			
		||||
                                    text: time} ),
 | 
			
		||||
                    { x_fill: true } );
 | 
			
		||||
        eventTitleBox.add(new St.Label( { style_class: 'events-day-task',
 | 
			
		||||
                                          text: desc} ));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _addPeriod: function(header, index, begin, end, includeDayName, showNothingScheduled) {
 | 
			
		||||
    _addPeriod: function(header, begin, end, includeDayName, showNothingScheduled) {
 | 
			
		||||
        if (!this._eventSource)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let events = this._eventSource.getEvents(begin, end);
 | 
			
		||||
 | 
			
		||||
        if (events.length == 0 && !showNothingScheduled)
 | 
			
		||||
            return index;
 | 
			
		||||
        let clockFormat = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);;
 | 
			
		||||
 | 
			
		||||
        this.actor.add(new St.Label({ style_class: 'events-day-header', text: header }),
 | 
			
		||||
                       { row: index, col: 0, col_span: 3,
 | 
			
		||||
                         // In theory, x_expand should be true here, but x_expand
 | 
			
		||||
                         // is a property of the column for StTable, ie all day cells
 | 
			
		||||
                         // get it too
 | 
			
		||||
                         x_expand: false, x_align: St.Align.START,
 | 
			
		||||
                         y_fill: false, y_align: St.Align.START });
 | 
			
		||||
        index++;
 | 
			
		||||
        if (events.length == 0 && !showNothingScheduled)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let vbox = new St.BoxLayout( {vertical: true} );
 | 
			
		||||
        this.actor.add(vbox);
 | 
			
		||||
 | 
			
		||||
        vbox.add(new St.Label({ style_class: 'events-day-header', text: header }));
 | 
			
		||||
        let box = new St.BoxLayout({style_class: 'events-header-hbox'});
 | 
			
		||||
        let dayNameBox = new St.BoxLayout({ vertical: true, style_class: 'events-day-name-box' });
 | 
			
		||||
        let timeBox = new St.BoxLayout({ vertical: true, style_class: 'events-time-box' });
 | 
			
		||||
        let eventTitleBox = new St.BoxLayout({ vertical: true, style_class: 'events-event-box' });
 | 
			
		||||
        box.add(dayNameBox, {x_fill: false});
 | 
			
		||||
        box.add(timeBox, {x_fill: false});
 | 
			
		||||
        box.add(eventTitleBox, {expand: true});
 | 
			
		||||
        vbox.add(box);
 | 
			
		||||
 | 
			
		||||
        for (let n = 0; n < events.length; n++) {
 | 
			
		||||
            this._addEvent(events[n], index, includeDayName);
 | 
			
		||||
            index++;
 | 
			
		||||
            let event = events[n];
 | 
			
		||||
            let dayString = _getEventDayAbbreviation(event.date.getDay());
 | 
			
		||||
            let timeString = _formatEventTime(event, clockFormat);
 | 
			
		||||
            let summaryString = event.summary;
 | 
			
		||||
            this._addEvent(dayNameBox, timeBox, eventTitleBox, includeDayName, dayString, timeString, summaryString);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (events.length == 0 && showNothingScheduled) {
 | 
			
		||||
            let now = new Date();
 | 
			
		||||
            /* Translators: Text to show if there are no events */
 | 
			
		||||
            let nothingEvent = new CalendarEvent(now, now, _("Nothing Scheduled"), true);
 | 
			
		||||
            this._addEvent(nothingEvent, index, false);
 | 
			
		||||
            index++;
 | 
			
		||||
            let timeString = _formatEventTime(nothingEvent, clockFormat);
 | 
			
		||||
            this._addEvent(dayNameBox, timeBox, eventTitleBox, false, "", timeString, nothingEvent.summary);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return index;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _showOtherDay: function(day) {
 | 
			
		||||
@@ -772,21 +691,20 @@ const EventsList = new Lang.Class({
 | 
			
		||||
        else
 | 
			
		||||
            /* Translators: Shown on calendar heading when selected day occurs on different year */
 | 
			
		||||
            dayString = day.toLocaleFormat(C_("calendar heading", "%A, %B %d, %Y"));
 | 
			
		||||
        this._addPeriod(dayString, 0, dayBegin, dayEnd, false, true);
 | 
			
		||||
        this._addPeriod(dayString, dayBegin, dayEnd, false, true);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _showToday: function() {
 | 
			
		||||
        this.actor.destroy_all_children();
 | 
			
		||||
        let index = 0;
 | 
			
		||||
 | 
			
		||||
        let now = new Date();
 | 
			
		||||
        let dayBegin = _getBeginningOfDay(now);
 | 
			
		||||
        let dayEnd = _getEndOfDay(now);
 | 
			
		||||
        index = this._addPeriod(_("Today"), index, dayBegin, dayEnd, false, true);
 | 
			
		||||
        this._addPeriod(_("Today"), dayBegin, dayEnd, false, true);
 | 
			
		||||
 | 
			
		||||
        let tomorrowBegin = new Date(dayBegin.getTime() + 86400 * 1000);
 | 
			
		||||
        let tomorrowEnd = new Date(dayEnd.getTime() + 86400 * 1000);
 | 
			
		||||
        index = this._addPeriod(_("Tomorrow"), index, tomorrowBegin, tomorrowEnd, false, true);
 | 
			
		||||
        this._addPeriod(_("Tomorrow"), tomorrowBegin, tomorrowEnd, false, true);
 | 
			
		||||
 | 
			
		||||
        let dayInWeek = (dayEnd.getDay() - this._weekStart + 7) % 7;
 | 
			
		||||
 | 
			
		||||
@@ -797,7 +715,7 @@ const EventsList = new Lang.Class({
 | 
			
		||||
             */
 | 
			
		||||
            let thisWeekBegin = new Date(dayBegin.getTime() + 2 * 86400 * 1000);
 | 
			
		||||
            let thisWeekEnd = new Date(dayEnd.getTime() + (6 - dayInWeek) * 86400 * 1000);
 | 
			
		||||
            index = this._addPeriod(_("This week"), index, thisWeekBegin, thisWeekEnd, true, false);
 | 
			
		||||
            this._addPeriod(_("This week"), thisWeekBegin, thisWeekEnd, true, false);
 | 
			
		||||
        } else {
 | 
			
		||||
            /* otherwise it's one of the two last days of the week ... show
 | 
			
		||||
             * "Next week" and include events up until and including *next*
 | 
			
		||||
@@ -805,7 +723,7 @@ const EventsList = new Lang.Class({
 | 
			
		||||
             */
 | 
			
		||||
            let nextWeekBegin = new Date(dayBegin.getTime() + 2 * 86400 * 1000);
 | 
			
		||||
            let nextWeekEnd = new Date(dayEnd.getTime() + (13 - dayInWeek) * 86400 * 1000);
 | 
			
		||||
            index = this._addPeriod(_("Next week"), index, nextWeekBegin, nextWeekEnd, true, false);
 | 
			
		||||
            this._addPeriod(_("Next week"), nextWeekBegin, nextWeekEnd, true, false);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -818,9 +736,6 @@ const EventsList = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _update: function() {
 | 
			
		||||
        if (this._eventSource.isLoading)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let today = new Date();
 | 
			
		||||
        if (_sameDay (this._date, today)) {
 | 
			
		||||
            this._showToday();
 | 
			
		||||
 
 | 
			
		||||
@@ -1,40 +1,115 @@
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Pango = imports.gi.Pango;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
 | 
			
		||||
const CheckBoxContainer = new Lang.Class({
 | 
			
		||||
    Name: 'CheckBoxContainer',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.actor = new Shell.GenericContainer();
 | 
			
		||||
        this.actor.connect('get-preferred-width',
 | 
			
		||||
                           Lang.bind(this, this._getPreferredWidth));
 | 
			
		||||
        this.actor.connect('get-preferred-height',
 | 
			
		||||
                           Lang.bind(this, this._getPreferredHeight));
 | 
			
		||||
        this.actor.connect('allocate',
 | 
			
		||||
                           Lang.bind(this, this._allocate));
 | 
			
		||||
        this.actor.connect('style-changed', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                let node = this.actor.get_theme_node();
 | 
			
		||||
                this._spacing = node.get_length('spacing');
 | 
			
		||||
            }));
 | 
			
		||||
        this.actor.request_mode = Clutter.RequestMode.HEIGHT_FOR_WIDTH;
 | 
			
		||||
 | 
			
		||||
        this._box = new St.Bin();
 | 
			
		||||
        this.actor.add_actor(this._box);
 | 
			
		||||
 | 
			
		||||
        this.label = new St.Label();
 | 
			
		||||
        this.label.clutter_text.set_line_wrap(true);
 | 
			
		||||
        this.label.clutter_text.set_ellipsize(Pango.EllipsizeMode.NONE);
 | 
			
		||||
        this.actor.add_actor(this.label);
 | 
			
		||||
 | 
			
		||||
        this._spacing = 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getPreferredWidth: function(actor, forHeight, alloc) {
 | 
			
		||||
        let [minWidth, natWidth] = this._box.get_preferred_width(forHeight);
 | 
			
		||||
 | 
			
		||||
        alloc.min_size = minWidth + this._spacing;
 | 
			
		||||
        alloc.natural_size = natWidth + this._spacing;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getPreferredHeight: function(actor, forWidth, alloc) {
 | 
			
		||||
        /* FIXME: StBoxlayout currently does not handle
 | 
			
		||||
           height-for-width children correctly, so hard-code
 | 
			
		||||
           two lines for the label until that problem is fixed.
 | 
			
		||||
 | 
			
		||||
           https://bugzilla.gnome.org/show_bug.cgi?id=672543 */
 | 
			
		||||
/*
 | 
			
		||||
        let [minBoxHeight, natBoxHeight] =
 | 
			
		||||
            this._box.get_preferred_height(forWidth);
 | 
			
		||||
        let [minLabelHeight, natLabelHeight] =
 | 
			
		||||
            this.label.get_preferred_height(forWidth);
 | 
			
		||||
 | 
			
		||||
        alloc.min_size = Math.max(minBoxHeight, minLabelHeight);
 | 
			
		||||
        alloc.natural_size = Math.max(natBoxHeight, natLabelHeight);
 | 
			
		||||
*/
 | 
			
		||||
        let [minBoxHeight, natBoxHeight] =
 | 
			
		||||
            this._box.get_preferred_height(-1);
 | 
			
		||||
        let [minLabelHeight, natLabelHeight] =
 | 
			
		||||
            this.label.get_preferred_height(-1);
 | 
			
		||||
 | 
			
		||||
        alloc.min_size = Math.max(minBoxHeight, 2 * minLabelHeight);
 | 
			
		||||
        alloc.natural_size = Math.max(natBoxHeight, 2 * natLabelHeight);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _allocate: function(actor, box, flags) {
 | 
			
		||||
        let availWidth = box.x2 - box.x1;
 | 
			
		||||
        let availHeight = box.y2 - box.y1;
 | 
			
		||||
 | 
			
		||||
        let childBox = new Clutter.ActorBox();
 | 
			
		||||
        let [minBoxWidth, natBoxWidth] =
 | 
			
		||||
            this._box.get_preferred_width(-1);
 | 
			
		||||
        let [minBoxHeight, natBoxHeight] =
 | 
			
		||||
            this._box.get_preferred_height(-1);
 | 
			
		||||
        childBox.x1 = box.x1;
 | 
			
		||||
        childBox.x2 = box.x1 + natBoxWidth;
 | 
			
		||||
        childBox.y1 = box.y1;
 | 
			
		||||
        childBox.y2 = box.y1 + natBoxHeight;
 | 
			
		||||
        this._box.allocate(childBox, flags);
 | 
			
		||||
 | 
			
		||||
        childBox.x1 = box.x1 + natBoxWidth + this._spacing;
 | 
			
		||||
        childBox.x2 = availWidth - childBox.x1;
 | 
			
		||||
        childBox.y1 = box.y1;
 | 
			
		||||
        childBox.y2 = box.y2;
 | 
			
		||||
        this.label.allocate(childBox, flags);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const CheckBox = new Lang.Class({
 | 
			
		||||
    Name: 'CheckBox',
 | 
			
		||||
 | 
			
		||||
    _init: function(label) {
 | 
			
		||||
        let container = new St.BoxLayout();
 | 
			
		||||
        this.actor = new St.Button({ style_class: 'check-box',
 | 
			
		||||
                                     child: container,
 | 
			
		||||
                                     button_mask: St.ButtonMask.ONE,
 | 
			
		||||
                                     toggle_mode: true,
 | 
			
		||||
                                     can_focus: true,
 | 
			
		||||
                                     x_fill: true,
 | 
			
		||||
                                     y_fill: true });
 | 
			
		||||
 | 
			
		||||
        this._box = new St.Bin();
 | 
			
		||||
        this._box.set_y_align(Clutter.ActorAlign.START);
 | 
			
		||||
        container.add_actor(this._box);
 | 
			
		||||
 | 
			
		||||
        this._label = new St.Label();
 | 
			
		||||
        this._label.clutter_text.set_line_wrap(true);
 | 
			
		||||
        this._label.clutter_text.set_ellipsize(Pango.EllipsizeMode.NONE);
 | 
			
		||||
        container.add_actor(this._label);
 | 
			
		||||
        this._container = new CheckBoxContainer();
 | 
			
		||||
        this.actor.set_child(this._container.actor);
 | 
			
		||||
 | 
			
		||||
        if (label)
 | 
			
		||||
            this.setLabel(label);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setLabel: function(label) {
 | 
			
		||||
        this._label.set_text(label);
 | 
			
		||||
        this._container.label.set_text(label);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getLabelActor: function() {
 | 
			
		||||
        return this._label;
 | 
			
		||||
        return this._container.label;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ const Params = imports.misc.params;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
 | 
			
		||||
const GnomeSession = imports.misc.gnomeSession;
 | 
			
		||||
const LoginManager = imports.misc.loginManager;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const ShellMountOperation = imports.ui.shellMountOperation;
 | 
			
		||||
 | 
			
		||||
@@ -32,6 +33,7 @@ const AutomountManager = new Lang.Class({
 | 
			
		||||
                                    Lang.bind(this, this._InhibitorsChanged));
 | 
			
		||||
        this._inhibited = false;
 | 
			
		||||
 | 
			
		||||
        this._loginManager = LoginManager.getLoginManager();
 | 
			
		||||
        this._volumeMonitor = Gio.VolumeMonitor.get();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -83,29 +85,25 @@ const AutomountManager = new Lang.Class({
 | 
			
		||||
    _onDriveConnected: function() {
 | 
			
		||||
        // if we're not in the current ConsoleKit session,
 | 
			
		||||
        // or screensaver is active, don't play sounds
 | 
			
		||||
        if (!this._session.SessionIsActive)
 | 
			
		||||
        if (!this._loginManager.sessionActive)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        global.play_theme_sound(0, 'device-added-media',
 | 
			
		||||
                                _("External drive connected"),
 | 
			
		||||
                                null);
 | 
			
		||||
        global.play_theme_sound(0, 'device-added-media');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDriveDisconnected: function() {
 | 
			
		||||
        // if we're not in the current ConsoleKit session,
 | 
			
		||||
        // or screensaver is active, don't play sounds
 | 
			
		||||
        if (!this._session.SessionIsActive)
 | 
			
		||||
        if (!this._loginManager.sessionActive)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        global.play_theme_sound(0, 'device-removed-media',
 | 
			
		||||
                                _("External drive disconnected"),
 | 
			
		||||
                                null);
 | 
			
		||||
        global.play_theme_sound(0, 'device-removed-media');        
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDriveEjectButton: function(monitor, drive) {
 | 
			
		||||
        // TODO: this code path is not tested, as the GVfs volume monitor
 | 
			
		||||
        // doesn't emit this signal just yet.
 | 
			
		||||
        if (!this._session.SessionIsActive)
 | 
			
		||||
        if (!this._loginManager.sessionActive)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        // we force stop/eject in this case, so we don't have to pass a
 | 
			
		||||
@@ -145,7 +143,7 @@ const AutomountManager = new Lang.Class({
 | 
			
		||||
        if (params.checkSession) {
 | 
			
		||||
            // if we're not in the current ConsoleKit session,
 | 
			
		||||
            // don't attempt automount
 | 
			
		||||
            if (!this._session.SessionIsActive)
 | 
			
		||||
            if (!this._loginManager.sessionActive)
 | 
			
		||||
                return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@ const Lang = imports.lang;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
const GnomeSession = imports.misc.gnomeSession;
 | 
			
		||||
const LoginManager = imports.misc.loginManager;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const MessageTray = imports.ui.messageTray;
 | 
			
		||||
const ShellMountOperation = imports.ui.shellMountOperation;
 | 
			
		||||
@@ -31,7 +31,7 @@ function shouldAutorunMount(mount, forTransient) {
 | 
			
		||||
    if (!volume || (!volume.allowAutorun && forTransient))
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
    if (root.is_native() && isMountRootHidden(root))
 | 
			
		||||
    if (!root.is_native() || isMountRootHidden(root))
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
@@ -162,7 +162,8 @@ const AutorunManager = new Lang.Class({
 | 
			
		||||
    Name: 'AutorunManager',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._session = new GnomeSession.SessionManager();
 | 
			
		||||
        this._loginManager = LoginManager.getLoginManager();
 | 
			
		||||
 | 
			
		||||
        this._volumeMonitor = Gio.VolumeMonitor.get();
 | 
			
		||||
 | 
			
		||||
        this._transDispatcher = new AutorunTransientDispatcher(this);
 | 
			
		||||
@@ -214,7 +215,7 @@ const AutorunManager = new Lang.Class({
 | 
			
		||||
    _onMountAdded: function(monitor, mount) {
 | 
			
		||||
        // don't do anything if our session is not the currently
 | 
			
		||||
        // active one
 | 
			
		||||
        if (!this._session.SessionIsActive)
 | 
			
		||||
        if (!this._loginManager.sessionActive)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._processMount(mount, true);
 | 
			
		||||
@@ -292,7 +293,7 @@ const AutorunResidentSource = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _init: function(manager) {
 | 
			
		||||
        this.parent(_("Removable Devices"), 'media-removable');
 | 
			
		||||
        this.resident = true;
 | 
			
		||||
        this.showInLockScreen = false;
 | 
			
		||||
 | 
			
		||||
        this._mounts = [];
 | 
			
		||||
 | 
			
		||||
@@ -300,10 +301,6 @@ const AutorunResidentSource = new Lang.Class({
 | 
			
		||||
        this._notification = new AutorunResidentNotification(this._manager, this);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createPolicy: function() {
 | 
			
		||||
        return new MessageTray.NotificationPolicy({ showInLockScreen: false });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    buildRightClickMenu: function() {
 | 
			
		||||
        return null;
 | 
			
		||||
    },
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,8 @@ const ModalDialog = imports.ui.modalDialog;
 | 
			
		||||
const ShellEntry = imports.ui.shellEntry;
 | 
			
		||||
const CheckBox = imports.ui.checkBox;
 | 
			
		||||
 | 
			
		||||
let prompter = null;
 | 
			
		||||
 | 
			
		||||
const KeyringDialog = new Lang.Class({
 | 
			
		||||
    Name: 'KeyringDialog',
 | 
			
		||||
    Extends: ModalDialog.ModalDialog,
 | 
			
		||||
@@ -23,7 +25,7 @@ const KeyringDialog = new Lang.Class({
 | 
			
		||||
        this.prompt = new Shell.KeyringPrompt();
 | 
			
		||||
        this.prompt.connect('show-password', Lang.bind(this, this._onShowPassword));
 | 
			
		||||
        this.prompt.connect('show-confirm', Lang.bind(this, this._onShowConfirm));
 | 
			
		||||
        this.prompt.connect('prompt-close', Lang.bind(this, this._onHidePrompt));
 | 
			
		||||
        this.prompt.connect('hide-prompt', Lang.bind(this, this._onHidePrompt));
 | 
			
		||||
 | 
			
		||||
        let mainContentBox = new St.BoxLayout({ style_class: 'prompt-dialog-main-layout',
 | 
			
		||||
                                                vertical: false });
 | 
			
		||||
@@ -58,46 +60,41 @@ const KeyringDialog = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._controlTable = null;
 | 
			
		||||
 | 
			
		||||
        let buttons = [{ label: '',
 | 
			
		||||
                         action: Lang.bind(this, this._onCancelButton),
 | 
			
		||||
                         key:    Clutter.Escape
 | 
			
		||||
                       },
 | 
			
		||||
                       { label: '',
 | 
			
		||||
                         action: Lang.bind(this, this._onContinueButton),
 | 
			
		||||
                         default: true
 | 
			
		||||
                       }]
 | 
			
		||||
 | 
			
		||||
        this._cancelButton = this.addButton({ label: '',
 | 
			
		||||
                                              action: Lang.bind(this, this._onCancelButton),
 | 
			
		||||
                                              key: Clutter.Escape },
 | 
			
		||||
                                            { expand: true, x_fill: false, x_align: St.Align.START });
 | 
			
		||||
        this.placeSpinner({ expand: false,
 | 
			
		||||
                            x_fill: false,
 | 
			
		||||
                            y_fill: false,
 | 
			
		||||
                            x_align: St.Align.END,
 | 
			
		||||
                            y_align: St.Align.MIDDLE });
 | 
			
		||||
        this._continueButton = this.addButton({ label: '',
 | 
			
		||||
                                                action: Lang.bind(this, this._onContinueButton),
 | 
			
		||||
                                                default: true },
 | 
			
		||||
                                              { expand: false, x_fill: false, x_align: St.Align.END });
 | 
			
		||||
        this.setButtons(buttons);
 | 
			
		||||
        this._cancelButton = buttons[0].button;
 | 
			
		||||
        this._continueButton = buttons[1].button;
 | 
			
		||||
 | 
			
		||||
        this.prompt.bind_property('cancel-label', this._cancelButton, 'label', GObject.BindingFlags.SYNC_CREATE);
 | 
			
		||||
        this.prompt.bind_property('continue-label', this._continueButton, 'label', GObject.BindingFlags.SYNC_CREATE);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _buildControlTable: function() {
 | 
			
		||||
        let layout = new Clutter.TableLayout();
 | 
			
		||||
        let table = new St.Widget({ style_class: 'keyring-dialog-control-table',
 | 
			
		||||
                                    layout_manager: layout });
 | 
			
		||||
        layout.hookup_style(table);
 | 
			
		||||
        let table = new St.Table({ style_class: 'keyring-dialog-control-table' });
 | 
			
		||||
        let row = 0;
 | 
			
		||||
 | 
			
		||||
        if (this.prompt.password_visible) {
 | 
			
		||||
            let label = new St.Label({ style_class: 'prompt-dialog-password-label' });
 | 
			
		||||
            let label = new St.Label(({ style_class: 'prompt-dialog-password-label' }));
 | 
			
		||||
            label.set_text(_("Password:"));
 | 
			
		||||
            label.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
 | 
			
		||||
            layout.pack(label, 0, row);
 | 
			
		||||
            layout.child_set(label, { x_expand: false, y_fill: false,
 | 
			
		||||
                                      x_align: Clutter.TableAlignment.START });
 | 
			
		||||
            table.add(label, { row: row, col: 0,
 | 
			
		||||
                               x_expand: false, x_fill: true,
 | 
			
		||||
                               x_align: St.Align.START,
 | 
			
		||||
                               y_fill: false, y_align: St.Align.MIDDLE });
 | 
			
		||||
            this._passwordEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
 | 
			
		||||
                                                 text: '',
 | 
			
		||||
                                                 can_focus: true });
 | 
			
		||||
                                                 can_focus: true});
 | 
			
		||||
            this._passwordEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
 | 
			
		||||
            ShellEntry.addContextMenu(this._passwordEntry, { isPassword: true });
 | 
			
		||||
            this._passwordEntry.clutter_text.connect('activate', Lang.bind(this, this._onPasswordActivate));
 | 
			
		||||
            layout.pack(this._passwordEntry, 1, row);
 | 
			
		||||
            table.add(this._passwordEntry, { row: row, col: 1, x_expand: true, x_fill: true, x_align: St.Align.START });
 | 
			
		||||
            row++;
 | 
			
		||||
        } else {
 | 
			
		||||
            this._passwordEntry = null;
 | 
			
		||||
@@ -106,16 +103,17 @@ const KeyringDialog = new Lang.Class({
 | 
			
		||||
        if (this.prompt.confirm_visible) {
 | 
			
		||||
            var label = new St.Label(({ style_class: 'prompt-dialog-password-label' }));
 | 
			
		||||
            label.set_text(_("Type again:"));
 | 
			
		||||
            layout.pack(label, 0, row);
 | 
			
		||||
            layout.child_set(label, { x_expand: false, y_fill: false,
 | 
			
		||||
                                      x_align: Clutter.TableAlignment.START });
 | 
			
		||||
            table.add(label, { row: row, col: 0,
 | 
			
		||||
                               x_expand: false, x_fill: true,
 | 
			
		||||
                               x_align: St.Align.START,
 | 
			
		||||
                               y_fill: false, y_align: St.Align.MIDDLE });
 | 
			
		||||
            this._confirmEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
 | 
			
		||||
                                                text: '',
 | 
			
		||||
                                                can_focus: true });
 | 
			
		||||
                                                can_focus: true});
 | 
			
		||||
            this._confirmEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
 | 
			
		||||
            ShellEntry.addContextMenu(this._confirmEntry, { isPassword: true });
 | 
			
		||||
            this._confirmEntry.clutter_text.connect('activate', Lang.bind(this, this._onConfirmActivate));
 | 
			
		||||
            layout.pack(this._confirmEntry, 1, row);
 | 
			
		||||
            table.add(this._confirmEntry, { row: row, col: 1, x_expand: true, x_fill: true, x_align: St.Align.START });
 | 
			
		||||
            row++;
 | 
			
		||||
        } else {
 | 
			
		||||
            this._confirmEntry = null;
 | 
			
		||||
@@ -128,14 +126,14 @@ const KeyringDialog = new Lang.Class({
 | 
			
		||||
            let choice = new CheckBox.CheckBox();
 | 
			
		||||
            this.prompt.bind_property('choice-label', choice.getLabelActor(), 'text', GObject.BindingFlags.SYNC_CREATE);
 | 
			
		||||
            this.prompt.bind_property('choice-chosen', choice.actor, 'checked', GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.BIDIRECTIONAL);
 | 
			
		||||
            layout.pack(choice.actor, 1, row);
 | 
			
		||||
            table.add(choice.actor, { row: row, col: 1, x_expand: false, x_fill: true, x_align: St.Align.START });
 | 
			
		||||
            row++;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let warning = new St.Label({ style_class: 'prompt-dialog-error-label' });
 | 
			
		||||
        warning.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
 | 
			
		||||
        warning.clutter_text.line_wrap = true;
 | 
			
		||||
        layout.pack(warning, 1, row);
 | 
			
		||||
        table.add(warning, { row: row, col: 1, x_expand: false, x_fill: false, x_align: St.Align.START });
 | 
			
		||||
        this.prompt.bind_property('warning-visible', warning, 'visible', GObject.BindingFlags.SYNC_CREATE);
 | 
			
		||||
        this.prompt.bind_property('warning', warning, 'text', GObject.BindingFlags.SYNC_CREATE);
 | 
			
		||||
 | 
			
		||||
@@ -148,22 +146,6 @@ const KeyringDialog = new Lang.Class({
 | 
			
		||||
        this._messageBox.add(table, { x_fill: true, y_fill: true });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateSensitivity: function(sensitive) {
 | 
			
		||||
        if (this._passwordEntry) {
 | 
			
		||||
            this._passwordEntry.reactive = sensitive;
 | 
			
		||||
            this._passwordEntry.clutter_text.editable = sensitive;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this._confirmEntry) {
 | 
			
		||||
            this._confirmEntry.reactive = sensitive;
 | 
			
		||||
            this._confirmEntry.clutter_text.editable = sensitive;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._continueButton.can_focus = sensitive;
 | 
			
		||||
        this._continueButton.reactive = sensitive;
 | 
			
		||||
        this.setWorking(!sensitive);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _ensureOpen: function() {
 | 
			
		||||
        // NOTE: ModalDialog.open() is safe to call if the dialog is
 | 
			
		||||
        // already open - it just returns true without side-effects
 | 
			
		||||
@@ -185,14 +167,12 @@ const KeyringDialog = new Lang.Class({
 | 
			
		||||
    _onShowPassword: function(prompt) {
 | 
			
		||||
        this._buildControlTable();
 | 
			
		||||
        this._ensureOpen();
 | 
			
		||||
        this._updateSensitivity(true);
 | 
			
		||||
        this._passwordEntry.grab_key_focus();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onShowConfirm: function(prompt) {
 | 
			
		||||
        this._buildControlTable();
 | 
			
		||||
        this._ensureOpen();
 | 
			
		||||
        this._updateSensitivity(true);
 | 
			
		||||
        this._continueButton.grab_key_focus();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -212,7 +192,6 @@ const KeyringDialog = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onContinueButton: function() {
 | 
			
		||||
        this._updateSensitivity(false);
 | 
			
		||||
        this.prompt.complete();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -221,56 +200,27 @@ const KeyringDialog = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const KeyringDummyDialog = new Lang.Class({
 | 
			
		||||
    Name: 'KeyringDummyDialog',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.prompt = new Shell.KeyringPrompt();
 | 
			
		||||
        this.prompt.connect('show-password',
 | 
			
		||||
                            Lang.bind(this, this._cancelPrompt));
 | 
			
		||||
        this.prompt.connect('show-confirm', Lang.bind(this,
 | 
			
		||||
                            this._cancelPrompt));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _cancelPrompt: function() {
 | 
			
		||||
        this.prompt.cancel();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const KeyringPrompter = new Lang.Class({
 | 
			
		||||
    Name: 'KeyringPrompter',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._prompter = new Gcr.SystemPrompter();
 | 
			
		||||
        this._prompter.connect('new-prompt', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                let dialog = this._enabled ? new KeyringDialog()
 | 
			
		||||
                                           : new KeyringDummyDialog();
 | 
			
		||||
                this._currentPrompt = dialog.prompt;
 | 
			
		||||
                return this._currentPrompt;
 | 
			
		||||
            }));
 | 
			
		||||
        this._prompter.connect('new-prompt', function(prompter) {
 | 
			
		||||
            let dialog = new KeyringDialog();
 | 
			
		||||
            return dialog.prompt;
 | 
			
		||||
        });
 | 
			
		||||
        this._dbusId = null;
 | 
			
		||||
        this._registered = false;
 | 
			
		||||
        this._enabled = false;
 | 
			
		||||
        this._currentPrompt = null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    enable: function() {
 | 
			
		||||
        if (!this._registered) {
 | 
			
		||||
            this._prompter.register(Gio.DBus.session);
 | 
			
		||||
            this._dbusId = Gio.DBus.session.own_name('org.gnome.keyring.SystemPrompter',
 | 
			
		||||
                                                     Gio.BusNameOwnerFlags.ALLOW_REPLACEMENT, null, null);
 | 
			
		||||
            this._registered = true;
 | 
			
		||||
        }
 | 
			
		||||
        this._enabled = true;
 | 
			
		||||
        this._prompter.register(Gio.DBus.session);
 | 
			
		||||
        this._dbusId = Gio.DBus.session.own_name('org.gnome.keyring.SystemPrompter',
 | 
			
		||||
                                                 Gio.BusNameOwnerFlags.REPLACE, null, null);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    disable: function() {
 | 
			
		||||
        this._enabled = false;
 | 
			
		||||
 | 
			
		||||
        if (this._prompter.prompting)
 | 
			
		||||
            this._currentPrompt.cancel();
 | 
			
		||||
        this._currentPrompt = null;
 | 
			
		||||
        this._prompter.unregister(false);
 | 
			
		||||
        Gio.DBus.session.unown_name(this._dbusId);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -62,9 +62,14 @@ const NetworkSecretDialog = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        if (this._content.message != null) {
 | 
			
		||||
            let descriptionLabel = new St.Label({ style_class: 'prompt-dialog-description',
 | 
			
		||||
                                                  text: this._content.message });
 | 
			
		||||
                                                  text: this._content.message,
 | 
			
		||||
                                                  // HACK: for reasons unknown to me, the label
 | 
			
		||||
                                                  // is not asked the correct height for width,
 | 
			
		||||
                                                  // and thus is underallocated
 | 
			
		||||
                                                  // place a fixed height to avoid overflowing
 | 
			
		||||
                                                  style: 'height: 3em'
 | 
			
		||||
                                                });
 | 
			
		||||
            descriptionLabel.clutter_text.line_wrap = true;
 | 
			
		||||
            descriptionLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
 | 
			
		||||
 | 
			
		||||
            messageBox.add(descriptionLabel,
 | 
			
		||||
                           { y_fill:  true,
 | 
			
		||||
@@ -72,18 +77,13 @@ const NetworkSecretDialog = new Lang.Class({
 | 
			
		||||
                             expand: true });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let layout = new Clutter.TableLayout();
 | 
			
		||||
        let secretTable = new St.Widget({ style_class: 'network-dialog-secret-table',
 | 
			
		||||
                                          layout_manager: layout });
 | 
			
		||||
        layout.hookup_style(secretTable);
 | 
			
		||||
 | 
			
		||||
        let secretTable = new St.Table({ style_class: 'network-dialog-secret-table' });
 | 
			
		||||
        let initialFocusSet = false;
 | 
			
		||||
        let pos = 0;
 | 
			
		||||
        for (let i = 0; i < this._content.secrets.length; i++) {
 | 
			
		||||
            let secret = this._content.secrets[i];
 | 
			
		||||
            let label = new St.Label({ style_class: 'prompt-dialog-password-label',
 | 
			
		||||
                                       text: secret.label });
 | 
			
		||||
            label.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
 | 
			
		||||
 | 
			
		||||
            let reactive = secret.key != null;
 | 
			
		||||
 | 
			
		||||
@@ -116,10 +116,11 @@ const NetworkSecretDialog = new Lang.Class({
 | 
			
		||||
            } else
 | 
			
		||||
                secret.valid = true;
 | 
			
		||||
 | 
			
		||||
            layout.pack(label, 0, pos);
 | 
			
		||||
            layout.child_set(label, { x_expand: false, y_fill: false,
 | 
			
		||||
                                      x_align: Clutter.TableAlignment.START });
 | 
			
		||||
            layout.pack(secret.entry, 1, pos);
 | 
			
		||||
            secretTable.add(label, { row: pos, col: 0,
 | 
			
		||||
                                     x_expand: false, x_fill: true,
 | 
			
		||||
                                     x_align: St.Align.START,
 | 
			
		||||
                                     y_fill: false, y_align: St.Align.MIDDLE });
 | 
			
		||||
            secretTable.add(secret.entry, { row: pos, col: 1, x_expand: true, x_fill: true, y_align: St.Align.END });
 | 
			
		||||
            pos++;
 | 
			
		||||
 | 
			
		||||
            if (secret.password)
 | 
			
		||||
@@ -384,7 +385,11 @@ const VPNRequestHandler = new Lang.Class({
 | 
			
		||||
            this._childPid = pid;
 | 
			
		||||
            this._stdin = new Gio.UnixOutputStream({ fd: stdin, close_fd: true });
 | 
			
		||||
            this._stdout = new Gio.UnixInputStream({ fd: stdout, close_fd: true });
 | 
			
		||||
            GLib.close(stderr);
 | 
			
		||||
            // We need this one too, even if don't actually care of what the process
 | 
			
		||||
            // has to say on stderr, because otherwise the fd opened by g_spawn_async_with_pipes
 | 
			
		||||
            // is kept open indefinitely
 | 
			
		||||
            let stderrStream = new Gio.UnixInputStream({ fd: stderr, close_fd: true });
 | 
			
		||||
            stderrStream.close(null);
 | 
			
		||||
            this._dataStdout = new Gio.DataInputStream({ base_stream: this._stdout });
 | 
			
		||||
 | 
			
		||||
            if (this._newStylePlugin)
 | 
			
		||||
@@ -582,19 +587,18 @@ const NetworkAgent = new Lang.Class({
 | 
			
		||||
    Name: 'NetworkAgent',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._native = new Shell.NetworkAgent({ identifier: 'org.gnome.Shell.NetworkAgent' });
 | 
			
		||||
        this._native = new Shell.NetworkAgent({ auto_register: false,
 | 
			
		||||
                                                identifier: 'org.gnome.Shell.NetworkAgent' });
 | 
			
		||||
 | 
			
		||||
        this._dialogs = { };
 | 
			
		||||
        this._vpnRequests = { };
 | 
			
		||||
 | 
			
		||||
        this._native.connect('new-request', Lang.bind(this, this._newRequest));
 | 
			
		||||
        this._native.connect('cancel-request', Lang.bind(this, this._cancelRequest));
 | 
			
		||||
 | 
			
		||||
        this._enabled = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    enable: function() {
 | 
			
		||||
        this._enabled = true;
 | 
			
		||||
        this._native.auto_register = true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    disable: function() {
 | 
			
		||||
@@ -608,15 +612,12 @@ const NetworkAgent = new Lang.Class({
 | 
			
		||||
            this._vpnRequests[requestId].cancel(true);
 | 
			
		||||
        this._vpnRequests = { };
 | 
			
		||||
 | 
			
		||||
        this._enabled = false;
 | 
			
		||||
        this._native.auto_register = false;
 | 
			
		||||
        if (this._native.registered)
 | 
			
		||||
            this._native.unregister();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _newRequest:  function(agent, requestId, connection, settingName, hints, flags) {
 | 
			
		||||
        if (!this._enabled) {
 | 
			
		||||
            agent.respond(requestId, Shell.NetworkAgentResponse.USER_CANCELED);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (settingName == 'vpn') {
 | 
			
		||||
            this._vpnRequest(requestId, connection, hints, flags);
 | 
			
		||||
            return;
 | 
			
		||||
 
 | 
			
		||||
@@ -16,7 +16,7 @@ const PolkitAgent = imports.gi.PolkitAgent;
 | 
			
		||||
const Components = imports.ui.components;
 | 
			
		||||
const ModalDialog = imports.ui.modalDialog;
 | 
			
		||||
const ShellEntry = imports.ui.shellEntry;
 | 
			
		||||
const UserWidget = imports.ui.userWidget;
 | 
			
		||||
const UserMenu = imports.ui.userMenu;
 | 
			
		||||
 | 
			
		||||
const DIALOG_ICON_SIZE = 48;
 | 
			
		||||
 | 
			
		||||
@@ -31,6 +31,7 @@ const AuthenticationDialog = new Lang.Class({
 | 
			
		||||
        this.message = message;
 | 
			
		||||
        this.userNames = userNames;
 | 
			
		||||
        this._wasDismissed = false;
 | 
			
		||||
        this._completed = false;
 | 
			
		||||
 | 
			
		||||
        let mainContentBox = new St.BoxLayout({ style_class: 'prompt-dialog-main-layout',
 | 
			
		||||
                                                vertical: false });
 | 
			
		||||
@@ -100,9 +101,9 @@ const AuthenticationDialog = new Lang.Class({
 | 
			
		||||
            let userBox = new St.BoxLayout({ style_class: 'polkit-dialog-user-layout',
 | 
			
		||||
                                             vertical: false });
 | 
			
		||||
            messageBox.add(userBox);
 | 
			
		||||
            this._userAvatar = new UserWidget.Avatar(this._user,
 | 
			
		||||
                                                     { iconSize: DIALOG_ICON_SIZE,
 | 
			
		||||
                                                       styleClass: 'polkit-dialog-user-icon' });
 | 
			
		||||
            this._userAvatar = new UserMenu.UserAvatarWidget(this._user,
 | 
			
		||||
                                                             { iconSize: DIALOG_ICON_SIZE,
 | 
			
		||||
                                                               styleClass: 'polkit-dialog-user-icon' });
 | 
			
		||||
            this._userAvatar.actor.hide();
 | 
			
		||||
            userBox.add(this._userAvatar.actor,
 | 
			
		||||
                        { x_fill:  true,
 | 
			
		||||
@@ -158,34 +159,29 @@ const AuthenticationDialog = new Lang.Class({
 | 
			
		||||
        messageBox.add(this._nullMessageLabel);
 | 
			
		||||
        this._nullMessageLabel.show();
 | 
			
		||||
 | 
			
		||||
        this._cancelButton = this.addButton({ label: _("Cancel"),
 | 
			
		||||
                                              action: Lang.bind(this, this.cancel),
 | 
			
		||||
                                              key: Clutter.Escape },
 | 
			
		||||
                                            { expand: true, x_fill: false, x_align: St.Align.START });
 | 
			
		||||
        this.placeSpinner({ expand: false,
 | 
			
		||||
                            x_fill: false,
 | 
			
		||||
                            y_fill: false,
 | 
			
		||||
                            x_align: St.Align.END,
 | 
			
		||||
                            y_align: St.Align.MIDDLE });
 | 
			
		||||
        this._okButton = this.addButton({ label:  _("Authenticate"),
 | 
			
		||||
                                          action: Lang.bind(this, this._onAuthenticateButtonPressed),
 | 
			
		||||
                                          default: true },
 | 
			
		||||
                                        { expand: false, x_fill: false, x_align: St.Align.END });
 | 
			
		||||
        this.setButtons([{ label: _("Cancel"),
 | 
			
		||||
                           action: Lang.bind(this, this.cancel),
 | 
			
		||||
                           key:    Clutter.Escape
 | 
			
		||||
                         },
 | 
			
		||||
                         { label:  _("Authenticate"),
 | 
			
		||||
                           action: Lang.bind(this, this._onAuthenticateButtonPressed),
 | 
			
		||||
                           default: true
 | 
			
		||||
                         }]);
 | 
			
		||||
 | 
			
		||||
        this._doneEmitted = false;
 | 
			
		||||
 | 
			
		||||
        this._identityToAuth = Polkit.UnixUser.new_for_name(userName);
 | 
			
		||||
        this._cookie = cookie;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    performAuthentication: function() {
 | 
			
		||||
        this.destroySession();
 | 
			
		||||
        this._session = new PolkitAgent.Session({ identity: this._identityToAuth,
 | 
			
		||||
                                                  cookie: this._cookie });
 | 
			
		||||
        this._session.connect('completed', Lang.bind(this, this._onSessionCompleted));
 | 
			
		||||
        this._session.connect('request', Lang.bind(this, this._onSessionRequest));
 | 
			
		||||
        this._session.connect('show-error', Lang.bind(this, this._onSessionShowError));
 | 
			
		||||
        this._session.connect('show-info', Lang.bind(this, this._onSessionShowInfo));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    startAuthentication: function() {
 | 
			
		||||
        this._session.initiate();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -207,29 +203,19 @@ const AuthenticationDialog = new Lang.Class({
 | 
			
		||||
            log('polkitAuthenticationAgent: Failed to show modal dialog.' +
 | 
			
		||||
                ' Dismissing authentication request for action-id ' + this.actionId +
 | 
			
		||||
                ' cookie ' + this._cookie);
 | 
			
		||||
            this._emitDone(true);
 | 
			
		||||
            this._emitDone(false, true);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _emitDone: function(dismissed) {
 | 
			
		||||
    _emitDone: function(keepVisible, dismissed) {
 | 
			
		||||
        if (!this._doneEmitted) {
 | 
			
		||||
            this._doneEmitted = true;
 | 
			
		||||
            this.emit('done', dismissed);
 | 
			
		||||
            this.emit('done', keepVisible, dismissed);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateSensitivity: function(sensitive) {
 | 
			
		||||
        this._passwordEntry.reactive = sensitive;
 | 
			
		||||
        this._passwordEntry.clutter_text.editable = sensitive;
 | 
			
		||||
 | 
			
		||||
        this._okButton.can_focus = sensitive;
 | 
			
		||||
        this._okButton.reactive = sensitive;
 | 
			
		||||
        this.setWorking(!sensitive);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onEntryActivate: function() {
 | 
			
		||||
        let response = this._passwordEntry.get_text();
 | 
			
		||||
        this._updateSensitivity(false);
 | 
			
		||||
        this._session.response(response);
 | 
			
		||||
        // When the user responds, dismiss already shown info and
 | 
			
		||||
        // error texts (if any)
 | 
			
		||||
@@ -243,16 +229,12 @@ const AuthenticationDialog = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onSessionCompleted: function(session, gainedAuthorization) {
 | 
			
		||||
        if (this._completed || this._doneEmitted)
 | 
			
		||||
        if (this._completed)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._completed = true;
 | 
			
		||||
 | 
			
		||||
        /* Yay, all done */
 | 
			
		||||
        if (gainedAuthorization) {
 | 
			
		||||
            this._emitDone(false);
 | 
			
		||||
 | 
			
		||||
        } else {
 | 
			
		||||
        if (!gainedAuthorization) {
 | 
			
		||||
            /* Unless we are showing an existing error message from the PAM
 | 
			
		||||
             * module (the PAM module could be reporting the authentication
 | 
			
		||||
             * error providing authentication-method specific information),
 | 
			
		||||
@@ -268,10 +250,8 @@ const AuthenticationDialog = new Lang.Class({
 | 
			
		||||
                this._infoMessageLabel.hide();
 | 
			
		||||
                this._nullMessageLabel.hide();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            /* Try and authenticate again */
 | 
			
		||||
            this.performAuthentication();
 | 
			
		||||
        }
 | 
			
		||||
        this._emitDone(!gainedAuthorization, false);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onSessionRequest: function(session, request, echo_on) {
 | 
			
		||||
@@ -289,7 +269,6 @@ const AuthenticationDialog = new Lang.Class({
 | 
			
		||||
        this._passwordBox.show();
 | 
			
		||||
        this._passwordEntry.set_text('');
 | 
			
		||||
        this._passwordEntry.grab_key_focus();
 | 
			
		||||
        this._updateSensitivity(true);
 | 
			
		||||
        this._ensureOpen();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -315,7 +294,6 @@ const AuthenticationDialog = new Lang.Class({
 | 
			
		||||
        if (this._session) {
 | 
			
		||||
            if (!this._completed)
 | 
			
		||||
                this._session.cancel();
 | 
			
		||||
            this._completed = false;
 | 
			
		||||
            this._session = null;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
@@ -330,7 +308,7 @@ const AuthenticationDialog = new Lang.Class({
 | 
			
		||||
    cancel: function() {
 | 
			
		||||
        this._wasDismissed = true;
 | 
			
		||||
        this.close(global.get_current_time());
 | 
			
		||||
        this._emitDone(true);
 | 
			
		||||
        this._emitDone(false, true);
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(AuthenticationDialog.prototype);
 | 
			
		||||
@@ -340,6 +318,7 @@ const AuthenticationAgent = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._currentDialog = null;
 | 
			
		||||
        this._isCompleting = false;
 | 
			
		||||
        this._handle = null;
 | 
			
		||||
        this._native = new Shell.PolkitAuthenticationAgent();
 | 
			
		||||
        this._native.connect('initiate', Lang.bind(this, this._onInitiate));
 | 
			
		||||
@@ -347,19 +326,11 @@ const AuthenticationAgent = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    enable: function() {
 | 
			
		||||
        try {
 | 
			
		||||
            this._native.register();
 | 
			
		||||
        } catch(e) {
 | 
			
		||||
            log('Failed to register AuthenticationAgent');
 | 
			
		||||
        }
 | 
			
		||||
        this._native.register();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    disable: function() {
 | 
			
		||||
        try {
 | 
			
		||||
            this._native.unregister();
 | 
			
		||||
        } catch(e) {
 | 
			
		||||
            log('Failed to unregister AuthenticationAgent');
 | 
			
		||||
        }
 | 
			
		||||
        this._native.unregister();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onInitiate: function(nativeAgent, actionId, message, iconName, cookie, userNames) {
 | 
			
		||||
@@ -376,24 +347,45 @@ const AuthenticationAgent = new Lang.Class({
 | 
			
		||||
        // discussion.
 | 
			
		||||
 | 
			
		||||
        this._currentDialog.connect('done', Lang.bind(this, this._onDialogDone));
 | 
			
		||||
        this._currentDialog.performAuthentication();
 | 
			
		||||
        this._currentDialog.startAuthentication();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onCancel: function(nativeAgent) {
 | 
			
		||||
        this._completeRequest(false);
 | 
			
		||||
        this._completeRequest(false, false);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDialogDone: function(dialog, dismissed) {
 | 
			
		||||
        this._completeRequest(dismissed);
 | 
			
		||||
    _onDialogDone: function(dialog, keepVisible, dismissed) {
 | 
			
		||||
        this._completeRequest(keepVisible, dismissed);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _completeRequest: function(dismissed) {
 | 
			
		||||
    _reallyCompleteRequest: function(dismissed) {
 | 
			
		||||
        this._currentDialog.close();
 | 
			
		||||
        this._currentDialog.destroySession();
 | 
			
		||||
        this._currentDialog = null;
 | 
			
		||||
        this._isCompleting = false;
 | 
			
		||||
 | 
			
		||||
        this._native.complete(dismissed);
 | 
			
		||||
        this._native.complete(dismissed)
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _completeRequest: function(keepVisible, wasDismissed) {
 | 
			
		||||
        if (this._isCompleting)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._isCompleting = true;
 | 
			
		||||
 | 
			
		||||
        if (keepVisible) {
 | 
			
		||||
            // Give the user 2 seconds to read 'Authentication Failure' before
 | 
			
		||||
            // dismissing the dialog
 | 
			
		||||
            Mainloop.timeout_add(2000,
 | 
			
		||||
                                 Lang.bind(this,
 | 
			
		||||
                                           function() {
 | 
			
		||||
                                               this._reallyCompleteRequest(wasDismissed);
 | 
			
		||||
                                               return false;
 | 
			
		||||
                                           }));
 | 
			
		||||
        } else {
 | 
			
		||||
            this._reallyCompleteRequest(wasDismissed);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const Component = AuthenticationAgent;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										62
									
								
								js/ui/components/recorder.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,62 @@
 | 
			
		||||
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const Meta = imports.gi.Meta;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
 | 
			
		||||
const Recorder = new Lang.Class({
 | 
			
		||||
    Name: 'Recorder',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._recorderSettings = new Gio.Settings({ schema: 'org.gnome.shell.recorder' });
 | 
			
		||||
        this._desktopLockdownSettings = new Gio.Settings({ schema: 'org.gnome.desktop.lockdown' });
 | 
			
		||||
        this._bindingSettings = new Gio.Settings({ schema: 'org.gnome.shell.keybindings' });
 | 
			
		||||
        this._recorder = null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    enable: function() {
 | 
			
		||||
        Main.wm.addKeybinding('toggle-recording',
 | 
			
		||||
                              this._bindingSettings,
 | 
			
		||||
                              Meta.KeyBindingFlags.NONE,
 | 
			
		||||
                              Main.KeybindingMode.NORMAL |
 | 
			
		||||
                              Main.KeybindingMode.OVERVIEW,
 | 
			
		||||
                              Lang.bind(this, this._toggleRecorder));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    disable: function() {
 | 
			
		||||
        Main.wm.removeKeybinding('toggle-recording');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _ensureRecorder: function() {
 | 
			
		||||
        if (this._recorder == null)
 | 
			
		||||
            this._recorder = new Shell.Recorder({ stage: global.stage });
 | 
			
		||||
        return this._recorder;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _toggleRecorder: function() {
 | 
			
		||||
        let recorder = this._ensureRecorder();
 | 
			
		||||
        if (recorder.is_recording()) {
 | 
			
		||||
            recorder.close();
 | 
			
		||||
            Meta.enable_unredirect_for_screen(global.screen);
 | 
			
		||||
        } else if (!this._desktopLockdownSettings.get_boolean('disable-save-to-disk')) {
 | 
			
		||||
            // read the parameters from GSettings always in case they have changed
 | 
			
		||||
            recorder.set_framerate(this._recorderSettings.get_int('framerate'));
 | 
			
		||||
            /* Translators: this is a filename used for screencast recording */
 | 
			
		||||
            // xgettext:no-c-format
 | 
			
		||||
            recorder.set_file_template(_("Screencast from %d %t") + '.' + this._recorderSettings.get_string('file-extension'));
 | 
			
		||||
            let pipeline = this._recorderSettings.get_string('pipeline');
 | 
			
		||||
 | 
			
		||||
            if (!pipeline.match(/^\s*$/))
 | 
			
		||||
                recorder.set_pipeline(pipeline);
 | 
			
		||||
            else
 | 
			
		||||
                recorder.set_pipeline(null);
 | 
			
		||||
 | 
			
		||||
            Meta.disable_unredirect_for_screen(global.screen);
 | 
			
		||||
            recorder.record();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const Component = Recorder;
 | 
			
		||||
@@ -17,7 +17,7 @@ const Params = imports.misc.params;
 | 
			
		||||
const PopupMenu = imports.ui.popupMenu;
 | 
			
		||||
 | 
			
		||||
// See Notification.appendMessage
 | 
			
		||||
const SCROLLBACK_IMMEDIATE_TIME = 3 * 60; // 3 minutes
 | 
			
		||||
const SCROLLBACK_IMMEDIATE_TIME = 60; // 1 minute
 | 
			
		||||
const SCROLLBACK_RECENT_TIME = 15 * 60; // 15 minutes
 | 
			
		||||
const SCROLLBACK_RECENT_LENGTH = 20;
 | 
			
		||||
const SCROLLBACK_IDLE_LENGTH = 5;
 | 
			
		||||
@@ -415,8 +415,6 @@ const TelepathyClient = new Lang.Class({
 | 
			
		||||
    _ensureAppSource: function() {
 | 
			
		||||
        if (this._appSource == null) {
 | 
			
		||||
            this._appSource = new MessageTray.Source(_("Chat"), 'empathy');
 | 
			
		||||
            this._appSource.policy = new MessageTray.NotificationApplicationPolicy('empathy');
 | 
			
		||||
 | 
			
		||||
            Main.messageTray.add(this._appSource);
 | 
			
		||||
            this._appSource.connect('destroy', Lang.bind(this, function () {
 | 
			
		||||
                this._appSource = null;
 | 
			
		||||
@@ -486,10 +484,6 @@ const ChatSource = new Lang.Class({
 | 
			
		||||
        return rightClickMenu;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createPolicy: function() {
 | 
			
		||||
        return new MessageTray.NotificationApplicationPolicy('empathy');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateAlias: function() {
 | 
			
		||||
        let oldAlias = this.title;
 | 
			
		||||
        let newAlias = this._contact.get_alias();
 | 
			
		||||
@@ -639,10 +633,6 @@ const ChatSource = new Lang.Class({
 | 
			
		||||
        return this._pendingMessages.length;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get indicatorCount() {
 | 
			
		||||
        return this.count;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get unseenCount() {
 | 
			
		||||
        return this.count;
 | 
			
		||||
    },
 | 
			
		||||
@@ -966,8 +956,7 @@ const ChatNotification = new Lang.Class({
 | 
			
		||||
        let timeLabel = this._append({ body: this._formatTimestamp(lastMessageDate),
 | 
			
		||||
                                       group: 'meta',
 | 
			
		||||
                                       styles: ['chat-meta-message'],
 | 
			
		||||
                                       childProps: { expand: true, x_fill: false,
 | 
			
		||||
                                                     x_align: St.Align.END },
 | 
			
		||||
                                       childProps: { expand: true, x_fill: false },
 | 
			
		||||
                                       noTimestamp: true,
 | 
			
		||||
                                       timestamp: lastMessageTime });
 | 
			
		||||
 | 
			
		||||
@@ -1059,10 +1048,6 @@ const ApproverSource = new Lang.Class({
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createPolicy: function() {
 | 
			
		||||
        return new MessageTray.NotificationApplicationPolicy('empathy');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroy: function() {
 | 
			
		||||
        if (this._invalidId != 0) {
 | 
			
		||||
            this._dispatchOp.disconnect(this._invalidId);
 | 
			
		||||
@@ -1134,8 +1119,6 @@ const AudioVideoNotification = new Lang.Class({
 | 
			
		||||
        this.parent(source, title, null, { customContent: true });
 | 
			
		||||
        this.setResident(true);
 | 
			
		||||
 | 
			
		||||
        this.setUrgency(MessageTray.Urgency.CRITICAL);
 | 
			
		||||
 | 
			
		||||
        this.addButton('reject', _("Decline"));
 | 
			
		||||
        /* translators: this is a button label (verb), not a noun */
 | 
			
		||||
        this.addButton('answer', _("Answer"));
 | 
			
		||||
@@ -1363,8 +1346,9 @@ const AccountNotification = new Lang.Class({
 | 
			
		||||
        this.connect('action-invoked', Lang.bind(this, function(self, action) {
 | 
			
		||||
            switch (action) {
 | 
			
		||||
            case 'view':
 | 
			
		||||
                let cmd = 'empathy-accounts --select-account=' +
 | 
			
		||||
                          account.get_path_suffix();
 | 
			
		||||
                let cmd = '/usr/bin/empathy-accounts'
 | 
			
		||||
                        + ' --select-account=%s'
 | 
			
		||||
                        .format(account.get_path_suffix());
 | 
			
		||||
                let app_info = Gio.app_info_create_from_commandline(cmd, null, 0);
 | 
			
		||||
                app_info.launch([], global.create_app_launch_context());
 | 
			
		||||
                break;
 | 
			
		||||
 
 | 
			
		||||
@@ -58,10 +58,15 @@ const CtrlAltTabManager = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    focusGroup: function(item, timestamp) {
 | 
			
		||||
        if (item.focusCallback)
 | 
			
		||||
        if (item.focusCallback) {
 | 
			
		||||
            item.focusCallback(timestamp);
 | 
			
		||||
        else
 | 
			
		||||
        } else {
 | 
			
		||||
            if (global.stage_input_mode == Shell.StageInputMode.NONREACTIVE ||
 | 
			
		||||
                global.stage_input_mode == Shell.StageInputMode.NORMAL)
 | 
			
		||||
                global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
 | 
			
		||||
 | 
			
		||||
            item.root.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Sort the items into a consistent order; panel first, tray last,
 | 
			
		||||
@@ -84,33 +89,21 @@ const CtrlAltTabManager = new Lang.Class({
 | 
			
		||||
        let items = this._items.filter(function (item) { return item.proxy.mapped; });
 | 
			
		||||
 | 
			
		||||
        // And add the windows metacity would show in its Ctrl-Alt-Tab list
 | 
			
		||||
        if (Main.sessionMode.hasWindows && !Main.overview.visible) {
 | 
			
		||||
        if (!Main.overview.visible) {
 | 
			
		||||
            let screen = global.screen;
 | 
			
		||||
            let display = screen.get_display();
 | 
			
		||||
            let windows = display.get_tab_list(Meta.TabList.DOCKS, screen, screen.get_active_workspace ());
 | 
			
		||||
            let windowTracker = Shell.WindowTracker.get_default();
 | 
			
		||||
            let textureCache = St.TextureCache.get_default();
 | 
			
		||||
            for (let i = 0; i < windows.length; i++) {
 | 
			
		||||
                let icon = null;
 | 
			
		||||
                let iconName = null;
 | 
			
		||||
                if (windows[i].get_window_type () == Meta.WindowType.DESKTOP) {
 | 
			
		||||
                    iconName = 'video-display-symbolic';
 | 
			
		||||
                } else {
 | 
			
		||||
                    let app = windowTracker.get_window_app(windows[i]);
 | 
			
		||||
                    if (app)
 | 
			
		||||
                        icon = app.create_icon_texture(POPUP_APPICON_SIZE);
 | 
			
		||||
                    else
 | 
			
		||||
                        icon = textureCache.bind_pixbuf_property(windows[i], 'icon');
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                let icon;
 | 
			
		||||
                let app = windowTracker.get_window_app(windows[i]);
 | 
			
		||||
                if (app)
 | 
			
		||||
                    icon = app.create_icon_texture(POPUP_APPICON_SIZE);
 | 
			
		||||
                else
 | 
			
		||||
                    icon = textureCache.bind_pixbuf_property(windows[i], 'icon');
 | 
			
		||||
                items.push({ name: windows[i].title,
 | 
			
		||||
                             proxy: windows[i].get_compositor_private(),
 | 
			
		||||
                             focusCallback: Lang.bind(windows[i],
 | 
			
		||||
                                 function(timestamp) {
 | 
			
		||||
                                     Main.activateWindow(this, timestamp);
 | 
			
		||||
                                 }),
 | 
			
		||||
                             iconActor: icon,
 | 
			
		||||
                             iconName: iconName,
 | 
			
		||||
                             sortGroup: SortGroup.MIDDLE });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -132,6 +125,8 @@ const CtrlAltTabManager = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _focusWindows: function(timestamp) {
 | 
			
		||||
        global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
 | 
			
		||||
        global.stage.key_focus = null;
 | 
			
		||||
        global.screen.focus_default_window(timestamp);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										289
									
								
								js/ui/dash.js
									
									
									
									
									
								
							
							
						
						@@ -22,8 +22,11 @@ const DASH_ITEM_LABEL_HIDE_TIME = 0.1;
 | 
			
		||||
const DASH_ITEM_HOVER_TIMEOUT = 300;
 | 
			
		||||
 | 
			
		||||
function getAppFromSource(source) {
 | 
			
		||||
    if (source instanceof AppDisplay.AppIcon) {
 | 
			
		||||
    if (source instanceof AppDisplay.AppWellIcon) {
 | 
			
		||||
        return source.app;
 | 
			
		||||
    } else if (source.metaWindow) {
 | 
			
		||||
        let tracker = Shell.WindowTracker.get_default();
 | 
			
		||||
        return tracker.get_window_app(source.metaWindow);
 | 
			
		||||
    } else {
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
@@ -33,26 +36,30 @@ function getAppFromSource(source) {
 | 
			
		||||
// when requesting a size
 | 
			
		||||
const DashItemContainer = new Lang.Class({
 | 
			
		||||
    Name: 'DashItemContainer',
 | 
			
		||||
    Extends: St.Widget,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.parent({ style_class: 'dash-item-container' });
 | 
			
		||||
        this.actor = new Shell.GenericContainer({ style_class: 'dash-item-container' });
 | 
			
		||||
        this.actor.connect('get-preferred-width',
 | 
			
		||||
                           Lang.bind(this, this._getPreferredWidth));
 | 
			
		||||
        this.actor.connect('get-preferred-height',
 | 
			
		||||
                           Lang.bind(this, this._getPreferredHeight));
 | 
			
		||||
        this.actor.connect('allocate',
 | 
			
		||||
                           Lang.bind(this, this._allocate));
 | 
			
		||||
        this.actor._delegate = this;
 | 
			
		||||
 | 
			
		||||
        this._labelText = "";
 | 
			
		||||
        this.label = new St.Label({ style_class: 'dash-label'});
 | 
			
		||||
        this.label.hide();
 | 
			
		||||
        Main.layoutManager.addChrome(this.label);
 | 
			
		||||
        this.label_actor = this.label;
 | 
			
		||||
        this.actor.label_actor = this.label;
 | 
			
		||||
 | 
			
		||||
        this.child = null;
 | 
			
		||||
        this._childScale = 0;
 | 
			
		||||
        this._childOpacity = 0;
 | 
			
		||||
        this._childScale = 1;
 | 
			
		||||
        this._childOpacity = 255;
 | 
			
		||||
        this.animatingOut = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    vfunc_allocate: function(box, flags) {
 | 
			
		||||
        this.set_allocation(box, flags);
 | 
			
		||||
 | 
			
		||||
    _allocate: function(actor, box, flags) {
 | 
			
		||||
        if (this.child == null)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
@@ -74,28 +81,28 @@ const DashItemContainer = new Lang.Class({
 | 
			
		||||
        this.child.allocate(childBox, flags);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    vfunc_get_preferred_height: function(forWidth) {
 | 
			
		||||
        let themeNode = this.get_theme_node();
 | 
			
		||||
    _getPreferredHeight: function(actor, forWidth, alloc) {
 | 
			
		||||
        alloc.min_size = 0;
 | 
			
		||||
        alloc.natural_size = 0;
 | 
			
		||||
 | 
			
		||||
        if (this.child == null)
 | 
			
		||||
            return [0, 0];
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        forWidth = themeNode.adjust_for_width(forWidth);
 | 
			
		||||
        let [minHeight, natHeight] = this.child.get_preferred_height(forWidth);
 | 
			
		||||
        return themeNode.adjust_preferred_height(minHeight * this.child.scale_y,
 | 
			
		||||
                                                 natHeight * this.child.scale_y);
 | 
			
		||||
        alloc.min_size += minHeight * this.child.scale_y;
 | 
			
		||||
        alloc.natural_size += natHeight * this.child.scale_y;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    vfunc_get_preferred_width: function(forHeight) {
 | 
			
		||||
        let themeNode = this.get_theme_node();
 | 
			
		||||
    _getPreferredWidth: function(actor, forHeight, alloc) {
 | 
			
		||||
        alloc.min_size = 0;
 | 
			
		||||
        alloc.natural_size = 0;
 | 
			
		||||
 | 
			
		||||
        if (this.child == null)
 | 
			
		||||
            return [0, 0];
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        forHeight = themeNode.adjust_for_height(forHeight);
 | 
			
		||||
        let [minWidth, natWidth] = this.child.get_preferred_width(forHeight);
 | 
			
		||||
        return themeNode.adjust_preferred_width(minWidth * this.child.scale_y,
 | 
			
		||||
                                                natWidth * this.child.scale_y);
 | 
			
		||||
        alloc.min_size = minWidth * this.child.scale_y;
 | 
			
		||||
        alloc.natural_size = natWidth * this.child.scale_y;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    showLabel: function() {
 | 
			
		||||
@@ -106,9 +113,9 @@ const DashItemContainer = new Lang.Class({
 | 
			
		||||
        this.label.opacity = 0;
 | 
			
		||||
        this.label.show();
 | 
			
		||||
 | 
			
		||||
        let [stageX, stageY] = this.get_transformed_position();
 | 
			
		||||
        let [stageX, stageY] = this.actor.get_transformed_position();
 | 
			
		||||
 | 
			
		||||
        let itemHeight = this.allocation.y2 - this.allocation.y1;
 | 
			
		||||
        let itemHeight = this.actor.allocation.y2 - this.actor.allocation.y1;
 | 
			
		||||
 | 
			
		||||
        let labelHeight = this.label.get_height();
 | 
			
		||||
        let yOffset = Math.floor((itemHeight - labelHeight) / 2)
 | 
			
		||||
@@ -122,7 +129,7 @@ const DashItemContainer = new Lang.Class({
 | 
			
		||||
        if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
 | 
			
		||||
            x = stageX - this.label.get_width() - xOffset;
 | 
			
		||||
        else
 | 
			
		||||
            x = stageX + this.get_width() + xOffset;
 | 
			
		||||
            x = stageX + this.actor.get_width() + xOffset;
 | 
			
		||||
 | 
			
		||||
        this.label.set_position(x, y);
 | 
			
		||||
        Tweener.addTween(this.label,
 | 
			
		||||
@@ -152,25 +159,22 @@ const DashItemContainer = new Lang.Class({
 | 
			
		||||
        if (this.child == actor)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this.destroy_all_children();
 | 
			
		||||
        this.actor.destroy_all_children();
 | 
			
		||||
 | 
			
		||||
        this.child = actor;
 | 
			
		||||
        this.add_actor(this.child);
 | 
			
		||||
 | 
			
		||||
        this.child.set_scale_with_gravity(this._childScale, this._childScale,
 | 
			
		||||
                                          Clutter.Gravity.CENTER);
 | 
			
		||||
        this.child.set_opacity(this._childOpacity);
 | 
			
		||||
        this.actor.add_actor(this.child);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    show: function(animate) {
 | 
			
		||||
    animateIn: function() {
 | 
			
		||||
        if (this.child == null)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let time = animate ? DASH_ANIMATION_TIME : 0;
 | 
			
		||||
        this.childScale = 0;
 | 
			
		||||
        this.childOpacity = 0;
 | 
			
		||||
        Tweener.addTween(this,
 | 
			
		||||
                         { childScale: 1.0,
 | 
			
		||||
                           childOpacity: 255,
 | 
			
		||||
                           time: time,
 | 
			
		||||
                           time: DASH_ANIMATION_TIME,
 | 
			
		||||
                           transition: 'easeOutQuad'
 | 
			
		||||
                         });
 | 
			
		||||
    },
 | 
			
		||||
@@ -179,7 +183,7 @@ const DashItemContainer = new Lang.Class({
 | 
			
		||||
        if (this.label)
 | 
			
		||||
            this.label.destroy();
 | 
			
		||||
 | 
			
		||||
        this.parent();
 | 
			
		||||
        this.actor.destroy();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    animateOutAndDestroy: function() {
 | 
			
		||||
@@ -187,18 +191,19 @@ const DashItemContainer = new Lang.Class({
 | 
			
		||||
            this.label.destroy();
 | 
			
		||||
 | 
			
		||||
        if (this.child == null) {
 | 
			
		||||
            this.destroy();
 | 
			
		||||
            this.actor.destroy();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.animatingOut = true;
 | 
			
		||||
        this.childScale = 1.0;
 | 
			
		||||
        Tweener.addTween(this,
 | 
			
		||||
                         { childScale: 0.0,
 | 
			
		||||
                           childOpacity: 0,
 | 
			
		||||
                           time: DASH_ANIMATION_TIME,
 | 
			
		||||
                           transition: 'easeOutQuad',
 | 
			
		||||
                           onComplete: Lang.bind(this, function() {
 | 
			
		||||
                               this.destroy();
 | 
			
		||||
                               this.actor.destroy();
 | 
			
		||||
                           })
 | 
			
		||||
                         });
 | 
			
		||||
    },
 | 
			
		||||
@@ -211,7 +216,7 @@ const DashItemContainer = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this.child.set_scale_with_gravity(scale, scale,
 | 
			
		||||
                                          Clutter.Gravity.CENTER);
 | 
			
		||||
        this.queue_relayout();
 | 
			
		||||
        this.actor.queue_relayout();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get childScale() {
 | 
			
		||||
@@ -225,7 +230,7 @@ const DashItemContainer = new Lang.Class({
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this.child.set_opacity(opacity);
 | 
			
		||||
        this.queue_redraw();
 | 
			
		||||
        this.actor.queue_redraw();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get childOpacity() {
 | 
			
		||||
@@ -287,7 +292,13 @@ const ShowAppsIcon = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    handleDragOver: function(source, actor, x, y, time) {
 | 
			
		||||
        if (!this._canRemoveApp(getAppFromSource(source)))
 | 
			
		||||
        let app = getAppFromSource(source);
 | 
			
		||||
        if (app == null)
 | 
			
		||||
            return DND.DragMotionResult.NO_DROP;
 | 
			
		||||
 | 
			
		||||
        let id = app.get_id();
 | 
			
		||||
        let isFavorite = AppFavorites.getAppFavorites().isFavorite(id);
 | 
			
		||||
        if (!isFavorite)
 | 
			
		||||
            return DND.DragMotionResult.NO_DROP;
 | 
			
		||||
 | 
			
		||||
        return DND.DragMotionResult.MOVE_DROP;
 | 
			
		||||
@@ -295,7 +306,7 @@ const ShowAppsIcon = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    acceptDrop: function(source, actor, x, y, time) {
 | 
			
		||||
        let app = getAppFromSource(source);
 | 
			
		||||
        if (!this._canRemoveApp(app))
 | 
			
		||||
        if (app == null)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        let id = app.get_id();
 | 
			
		||||
@@ -320,16 +331,6 @@ const DragPlaceholderItem = new Lang.Class({
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const EmptyDropTargetItem = new Lang.Class({
 | 
			
		||||
    Name: 'EmptyDropTargetItem',
 | 
			
		||||
    Extends: DashItemContainer,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.parent();
 | 
			
		||||
        this.setChild(new St.Bin({ style_class: 'empty-dash-drop-target' }));
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const DashActor = new Lang.Class({
 | 
			
		||||
    Name: 'DashActor',
 | 
			
		||||
    Extends: St.Widget,
 | 
			
		||||
@@ -360,23 +361,6 @@ const DashActor = new Lang.Class({
 | 
			
		||||
        childBox.y1 = contentBox.y2 - showAppsNatHeight;
 | 
			
		||||
        childBox.y2 = contentBox.y2;
 | 
			
		||||
        showAppsButton.allocate(childBox, flags);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    vfunc_get_preferred_height: function(forWidth) {
 | 
			
		||||
        // We want to request the natural height of all our children
 | 
			
		||||
        // as our natural height, so we chain up to StWidget (which
 | 
			
		||||
        // then calls BoxLayout), but we only request the showApps
 | 
			
		||||
        // button as the minimum size
 | 
			
		||||
 | 
			
		||||
        let [, natHeight] = this.parent(forWidth);
 | 
			
		||||
 | 
			
		||||
        let themeNode = this.get_theme_node();
 | 
			
		||||
        let adjustedForWidth = themeNode.adjust_for_width(forWidth);
 | 
			
		||||
        let [, showAppsButton] = this.get_children();
 | 
			
		||||
        let [minHeight, ] = showAppsButton.get_preferred_height(adjustedForWidth);
 | 
			
		||||
        [minHeight, ] = themeNode.adjust_preferred_height(minHeight, natHeight);
 | 
			
		||||
 | 
			
		||||
        return [minHeight, natHeight];
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@@ -402,14 +386,12 @@ const Dash = new Lang.Class({
 | 
			
		||||
        this._container.add_actor(this._box);
 | 
			
		||||
 | 
			
		||||
        this._showAppsIcon = new ShowAppsIcon();
 | 
			
		||||
        this._showAppsIcon.childScale = 1;
 | 
			
		||||
        this._showAppsIcon.childOpacity = 255;
 | 
			
		||||
        this._showAppsIcon.icon.setIconSize(this.iconSize);
 | 
			
		||||
        this._hookUpLabel(this._showAppsIcon);
 | 
			
		||||
 | 
			
		||||
        this.showAppsButton = this._showAppsIcon.toggleButton;
 | 
			
		||||
 | 
			
		||||
        this._container.add_actor(this._showAppsIcon);
 | 
			
		||||
        this._container.add_actor(this._showAppsIcon.actor);
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.Bin({ child: this._container });
 | 
			
		||||
        this.actor.connect('notify::height', Lang.bind(this,
 | 
			
		||||
@@ -423,10 +405,7 @@ const Dash = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._appSystem = Shell.AppSystem.get_default();
 | 
			
		||||
 | 
			
		||||
        this._appSystem.connect('installed-changed', Lang.bind(this, function() {
 | 
			
		||||
            AppFavorites.getAppFavorites().reload();
 | 
			
		||||
            this._queueRedisplay();
 | 
			
		||||
        }));
 | 
			
		||||
        this._appSystem.connect('installed-changed', Lang.bind(this, this._queueRedisplay));
 | 
			
		||||
        AppFavorites.getAppFavorites().connect('changed', Lang.bind(this, this._queueRedisplay));
 | 
			
		||||
        this._appSystem.connect('app-state-changed', Lang.bind(this, this._queueRedisplay));
 | 
			
		||||
 | 
			
		||||
@@ -436,10 +415,12 @@ const Dash = new Lang.Class({
 | 
			
		||||
                              Lang.bind(this, this._onDragEnd));
 | 
			
		||||
        Main.overview.connect('item-drag-cancelled',
 | 
			
		||||
                              Lang.bind(this, this._onDragCancelled));
 | 
			
		||||
 | 
			
		||||
        // Translators: this is the name of the dock/favorites area on
 | 
			
		||||
        // the left of the overview
 | 
			
		||||
        Main.ctrlAltTabManager.addGroup(this.actor, _("Dash"), 'user-bookmarks-symbolic');
 | 
			
		||||
        Main.overview.connect('window-drag-begin',
 | 
			
		||||
                              Lang.bind(this, this._onDragBegin));
 | 
			
		||||
        Main.overview.connect('window-drag-cancelled',
 | 
			
		||||
                              Lang.bind(this, this._onDragCancelled));
 | 
			
		||||
        Main.overview.connect('window-drag-end',
 | 
			
		||||
                              Lang.bind(this, this._onDragEnd));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDragBegin: function() {
 | 
			
		||||
@@ -448,12 +429,6 @@ const Dash = new Lang.Class({
 | 
			
		||||
            dragMotion: Lang.bind(this, this._onDragMotion)
 | 
			
		||||
        };
 | 
			
		||||
        DND.addDragMonitor(this._dragMonitor);
 | 
			
		||||
 | 
			
		||||
        if (this._box.get_n_children() == 0) {
 | 
			
		||||
            this._emptyDropTarget = new EmptyDropTargetItem();
 | 
			
		||||
            this._box.insert_child_at_index(this._emptyDropTarget, 0);
 | 
			
		||||
            this._emptyDropTarget.show(true);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDragCancelled: function() {
 | 
			
		||||
@@ -470,7 +445,6 @@ const Dash = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _endDrag: function() {
 | 
			
		||||
        this._clearDragPlaceholder();
 | 
			
		||||
        this._clearEmptyDropTarget();
 | 
			
		||||
        this._showAppsIcon.setDragApp(null);
 | 
			
		||||
        DND.removeDragMonitor(this._dragMonitor);
 | 
			
		||||
    },
 | 
			
		||||
@@ -481,7 +455,7 @@ const Dash = new Lang.Class({
 | 
			
		||||
            return DND.DragMotionResult.CONTINUE;
 | 
			
		||||
 | 
			
		||||
        let showAppsHovered =
 | 
			
		||||
                this._showAppsIcon.contains(dragEvent.targetActor);
 | 
			
		||||
                this._showAppsIcon.actor.contains(dragEvent.targetActor);
 | 
			
		||||
 | 
			
		||||
        if (!this._box.contains(dragEvent.targetActor) || showAppsHovered)
 | 
			
		||||
            this._clearDragPlaceholder();
 | 
			
		||||
@@ -505,27 +479,21 @@ const Dash = new Lang.Class({
 | 
			
		||||
        Main.queueDeferredWork(this._workId);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _hookUpLabel: function(item, appIcon) {
 | 
			
		||||
    _hookUpLabel: function(item) {
 | 
			
		||||
        item.child.connect('notify::hover', Lang.bind(this, function() {
 | 
			
		||||
            this._syncLabel(item, appIcon);
 | 
			
		||||
            this._onHover(item);
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        Main.overview.connect('hiding', Lang.bind(this, function() {
 | 
			
		||||
            this._labelShowing = false;
 | 
			
		||||
            item.hideLabel();
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        if (appIcon) {
 | 
			
		||||
            appIcon.connect('sync-tooltip', Lang.bind(this, function() {
 | 
			
		||||
                this._syncLabel(item, appIcon);
 | 
			
		||||
            }));
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createAppItem: function(app) {
 | 
			
		||||
        let appIcon = new AppDisplay.AppIcon(app,
 | 
			
		||||
                                             { setSizeManually: true,
 | 
			
		||||
                                               showLabel: false });
 | 
			
		||||
        let appIcon = new AppDisplay.AppWellIcon(app,
 | 
			
		||||
                                                 { setSizeManually: true,
 | 
			
		||||
                                                   showLabel: false });
 | 
			
		||||
        appIcon._draggable.connect('drag-begin',
 | 
			
		||||
                                   Lang.bind(this, function() {
 | 
			
		||||
                                       appIcon.actor.opacity = 50;
 | 
			
		||||
@@ -542,13 +510,13 @@ const Dash = new Lang.Class({
 | 
			
		||||
        let item = new DashItemContainer();
 | 
			
		||||
        item.setChild(appIcon.actor);
 | 
			
		||||
 | 
			
		||||
        // Override default AppIcon label_actor, now the
 | 
			
		||||
        // Override default AppWellIcon label_actor, now the
 | 
			
		||||
        // accessible_name is set at DashItemContainer.setLabelText
 | 
			
		||||
        appIcon.actor.label_actor = null;
 | 
			
		||||
        item.setLabelText(app.get_name());
 | 
			
		||||
 | 
			
		||||
        appIcon.icon.setIconSize(this.iconSize);
 | 
			
		||||
        this._hookUpLabel(item, appIcon);
 | 
			
		||||
        this._hookUpLabel(item);
 | 
			
		||||
 | 
			
		||||
        return item;
 | 
			
		||||
    },
 | 
			
		||||
@@ -566,10 +534,8 @@ const Dash = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _syncLabel: function (item, appIcon) {
 | 
			
		||||
        let shouldShow = appIcon ? appIcon.shouldShowTooltip() : item.child.get_hover();
 | 
			
		||||
 | 
			
		||||
        if (shouldShow) {
 | 
			
		||||
    _onHover: function (item) {
 | 
			
		||||
        if (item.child.get_hover()) {
 | 
			
		||||
            if (this._showLabelTimeoutId == 0) {
 | 
			
		||||
                let timeout = this._labelShowing ? 0 : DASH_ITEM_HOVER_TIMEOUT;
 | 
			
		||||
                this._showLabelTimeoutId = Mainloop.timeout_add(timeout,
 | 
			
		||||
@@ -604,13 +570,13 @@ const Dash = new Lang.Class({
 | 
			
		||||
        // animating out (which means they will be destroyed at the end of
 | 
			
		||||
        // the animation)
 | 
			
		||||
        let iconChildren = this._box.get_children().filter(function(actor) {
 | 
			
		||||
            return actor.child &&
 | 
			
		||||
                   actor.child._delegate &&
 | 
			
		||||
                   actor.child._delegate.icon &&
 | 
			
		||||
                   !actor.animatingOut;
 | 
			
		||||
            return actor._delegate.child &&
 | 
			
		||||
                   actor._delegate.child._delegate &&
 | 
			
		||||
                   actor._delegate.child._delegate.icon &&
 | 
			
		||||
                   !actor._delegate.animatingOut;
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        iconChildren.push(this._showAppsIcon);
 | 
			
		||||
        iconChildren.push(this._showAppsIcon.actor);
 | 
			
		||||
 | 
			
		||||
        if (this._maxHeight == -1)
 | 
			
		||||
            return;
 | 
			
		||||
@@ -623,18 +589,23 @@ const Dash = new Lang.Class({
 | 
			
		||||
        let availHeight = maxContent.y2 - maxContent.y1;
 | 
			
		||||
        let spacing = themeNode.get_length('spacing');
 | 
			
		||||
 | 
			
		||||
        let firstButton = iconChildren[0].child;
 | 
			
		||||
        let firstIcon = firstButton._delegate.icon;
 | 
			
		||||
 | 
			
		||||
        let firstIcon = iconChildren[0]._delegate.child._delegate.icon;
 | 
			
		||||
 | 
			
		||||
        let minHeight, natHeight;
 | 
			
		||||
 | 
			
		||||
        // Enforce the current icon size during the size request
 | 
			
		||||
        let [currentWidth, currentHeight] = firstIcon.icon.get_size();
 | 
			
		||||
        // Enforce the current icon size during the size request if
 | 
			
		||||
        // the icon is animating
 | 
			
		||||
        if (firstIcon._animating) {
 | 
			
		||||
            let [currentWidth, currentHeight] = firstIcon.icon.get_size();
 | 
			
		||||
 | 
			
		||||
        firstIcon.icon.set_size(this.iconSize, this.iconSize);
 | 
			
		||||
        [minHeight, natHeight] = firstButton.get_preferred_height(-1);
 | 
			
		||||
            firstIcon.icon.set_size(this.iconSize, this.iconSize);
 | 
			
		||||
            [minHeight, natHeight] = iconChildren[0].get_preferred_height(-1);
 | 
			
		||||
 | 
			
		||||
        firstIcon.icon.set_size(currentWidth, currentHeight);
 | 
			
		||||
            firstIcon.icon.set_size(currentWidth, currentHeight);
 | 
			
		||||
        } else {
 | 
			
		||||
            [minHeight, natHeight] = iconChildren[0].get_preferred_height(-1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Subtract icon padding and box spacing from the available height
 | 
			
		||||
        availHeight -= iconChildren.length * (natHeight - this.iconSize) +
 | 
			
		||||
@@ -659,7 +630,7 @@ const Dash = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        let scale = oldIconSize / newIconSize;
 | 
			
		||||
        for (let i = 0; i < iconChildren.length; i++) {
 | 
			
		||||
            let icon = iconChildren[i].child._delegate.icon;
 | 
			
		||||
            let icon = iconChildren[i]._delegate.child._delegate.icon;
 | 
			
		||||
 | 
			
		||||
            // Set the new size immediately, to keep the icons' sizes
 | 
			
		||||
            // in sync with this.iconSize
 | 
			
		||||
@@ -679,11 +650,15 @@ const Dash = new Lang.Class({
 | 
			
		||||
            icon.icon.set_size(icon.icon.width * scale,
 | 
			
		||||
                               icon.icon.height * scale);
 | 
			
		||||
 | 
			
		||||
            icon._animating = true;
 | 
			
		||||
            Tweener.addTween(icon.icon,
 | 
			
		||||
                             { width: targetWidth,
 | 
			
		||||
                               height: targetHeight,
 | 
			
		||||
                               time: DASH_ANIMATION_TIME,
 | 
			
		||||
                               transition: 'easeOutQuad',
 | 
			
		||||
                               onComplete: function() {
 | 
			
		||||
                                   icon._animating = false;
 | 
			
		||||
                               }
 | 
			
		||||
                             });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
@@ -694,13 +669,13 @@ const Dash = new Lang.Class({
 | 
			
		||||
        let running = this._appSystem.get_running();
 | 
			
		||||
 | 
			
		||||
        let children = this._box.get_children().filter(function(actor) {
 | 
			
		||||
                return actor.child &&
 | 
			
		||||
                       actor.child._delegate &&
 | 
			
		||||
                       actor.child._delegate.app;
 | 
			
		||||
                return actor._delegate.child &&
 | 
			
		||||
                       actor._delegate.child._delegate &&
 | 
			
		||||
                       actor._delegate.child._delegate.app;
 | 
			
		||||
            });
 | 
			
		||||
        // Apps currently in the dash
 | 
			
		||||
        let oldApps = children.map(function(actor) {
 | 
			
		||||
                return actor.child._delegate.app;
 | 
			
		||||
                return actor._delegate.child._delegate.app;
 | 
			
		||||
            });
 | 
			
		||||
        // Apps supposed to be in the dash
 | 
			
		||||
        let newApps = [];
 | 
			
		||||
@@ -766,7 +741,7 @@ const Dash = new Lang.Class({
 | 
			
		||||
            let insertHere = newApps[newIndex + 1] &&
 | 
			
		||||
                             newApps[newIndex + 1] == oldApps[oldIndex];
 | 
			
		||||
            let alreadyRemoved = removedActors.reduce(function(result, actor) {
 | 
			
		||||
                let removedApp = actor.child._delegate.app;
 | 
			
		||||
                let removedApp = actor._delegate.child._delegate.app;
 | 
			
		||||
                return result || removedApp == newApps[newIndex];
 | 
			
		||||
            }, false);
 | 
			
		||||
 | 
			
		||||
@@ -783,11 +758,11 @@ const Dash = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < addedItems.length; i++)
 | 
			
		||||
            this._box.insert_child_at_index(addedItems[i].item,
 | 
			
		||||
            this._box.insert_child_at_index(addedItems[i].item.actor,
 | 
			
		||||
                                            addedItems[i].pos);
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < removedActors.length; i++) {
 | 
			
		||||
            let item = removedActors[i];
 | 
			
		||||
            let item = removedActors[i]._delegate;
 | 
			
		||||
 | 
			
		||||
            // Don't animate item removal when the overview is transitioning
 | 
			
		||||
            // or hidden
 | 
			
		||||
@@ -801,39 +776,25 @@ const Dash = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        // Skip animations on first run when adding the initial set
 | 
			
		||||
        // of items, to avoid all items zooming in at once
 | 
			
		||||
 | 
			
		||||
        let animate = this._shownInitially && Main.overview.visible &&
 | 
			
		||||
            !Main.overview.animationInProgress;
 | 
			
		||||
 | 
			
		||||
        if (!this._shownInitially)
 | 
			
		||||
        if (!this._shownInitially) {
 | 
			
		||||
            this._shownInitially = true;
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < addedItems.length; i++) {
 | 
			
		||||
            addedItems[i].item.show(animate);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=692744
 | 
			
		||||
        // Without it, StBoxLayout may use a stale size cache
 | 
			
		||||
        this._box.queue_relayout();
 | 
			
		||||
        // Don't animate item addition when the overview is transitioning
 | 
			
		||||
        // or hidden
 | 
			
		||||
        if (!Main.overview.visible || Main.overview.animationInProgress)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < addedItems.length; i++)
 | 
			
		||||
            addedItems[i].item.animateIn();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _clearDragPlaceholder: function() {
 | 
			
		||||
        if (this._dragPlaceholder) {
 | 
			
		||||
            this._animatingPlaceholdersCount++;
 | 
			
		||||
            this._dragPlaceholder.animateOutAndDestroy();
 | 
			
		||||
            this._dragPlaceholder.connect('destroy',
 | 
			
		||||
                Lang.bind(this, function() {
 | 
			
		||||
                    this._animatingPlaceholdersCount--;
 | 
			
		||||
                }));
 | 
			
		||||
            this._dragPlaceholder = null;
 | 
			
		||||
        }
 | 
			
		||||
        this._dragPlaceholderPos = -1;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _clearEmptyDropTarget: function() {
 | 
			
		||||
        if (this._emptyDropTarget) {
 | 
			
		||||
            this._emptyDropTarget.animateOutAndDestroy();
 | 
			
		||||
            this._emptyDropTarget = null;
 | 
			
		||||
            this._dragPlaceholderPos = -1;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -857,22 +818,27 @@ const Dash = new Lang.Class({
 | 
			
		||||
        // the remove target has the same size as "normal" items, we don't
 | 
			
		||||
        // need to do the same adjustment there.
 | 
			
		||||
        if (this._dragPlaceholder) {
 | 
			
		||||
            boxHeight -= this._dragPlaceholder.height;
 | 
			
		||||
            boxHeight -= this._dragPlaceholder.actor.height;
 | 
			
		||||
            numChildren--;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let pos;
 | 
			
		||||
        if (!this._emptyDropTarget)
 | 
			
		||||
            pos = Math.floor(y * numChildren / boxHeight);
 | 
			
		||||
        else
 | 
			
		||||
            pos = 0; // always insert at the top when dash is empty
 | 
			
		||||
        let pos = Math.floor(y * numChildren / boxHeight);
 | 
			
		||||
 | 
			
		||||
        if (pos != this._dragPlaceholderPos && pos <= numFavorites && this._animatingPlaceholdersCount == 0) {
 | 
			
		||||
            this._dragPlaceholderPos = pos;
 | 
			
		||||
 | 
			
		||||
            // Don't allow positioning before or after self
 | 
			
		||||
            if (favPos != -1 && (pos == favPos || pos == favPos + 1)) {
 | 
			
		||||
                this._clearDragPlaceholder();
 | 
			
		||||
                if (this._dragPlaceholder) {
 | 
			
		||||
                    this._dragPlaceholder.animateOutAndDestroy();
 | 
			
		||||
                    this._animatingPlaceholdersCount++;
 | 
			
		||||
                    this._dragPlaceholder.actor.connect('destroy',
 | 
			
		||||
                        Lang.bind(this, function() {
 | 
			
		||||
                            this._animatingPlaceholdersCount--;
 | 
			
		||||
                        }));
 | 
			
		||||
                }
 | 
			
		||||
                this._dragPlaceholder = null;
 | 
			
		||||
 | 
			
		||||
                return DND.DragMotionResult.CONTINUE;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@@ -881,7 +847,7 @@ const Dash = new Lang.Class({
 | 
			
		||||
            // an animation
 | 
			
		||||
            let fadeIn;
 | 
			
		||||
            if (this._dragPlaceholder) {
 | 
			
		||||
                this._dragPlaceholder.destroy();
 | 
			
		||||
                this._dragPlaceholder.actor.destroy();
 | 
			
		||||
                fadeIn = false;
 | 
			
		||||
            } else {
 | 
			
		||||
                fadeIn = true;
 | 
			
		||||
@@ -890,16 +856,17 @@ const Dash = new Lang.Class({
 | 
			
		||||
            this._dragPlaceholder = new DragPlaceholderItem();
 | 
			
		||||
            this._dragPlaceholder.child.set_width (this.iconSize);
 | 
			
		||||
            this._dragPlaceholder.child.set_height (this.iconSize / 2);
 | 
			
		||||
            this._box.insert_child_at_index(this._dragPlaceholder,
 | 
			
		||||
            this._box.insert_child_at_index(this._dragPlaceholder.actor,
 | 
			
		||||
                                            this._dragPlaceholderPos);
 | 
			
		||||
            this._dragPlaceholder.show(fadeIn);
 | 
			
		||||
            if (fadeIn)
 | 
			
		||||
                this._dragPlaceholder.animateIn();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Remove the drag placeholder if we are not in the
 | 
			
		||||
        // "favorites zone"
 | 
			
		||||
        if (pos > numFavorites)
 | 
			
		||||
        if (pos > numFavorites && this._dragPlaceholder) {
 | 
			
		||||
            this._clearDragPlaceholder();
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        if (!this._dragPlaceholder)
 | 
			
		||||
            return DND.DragMotionResult.NO_DROP;
 | 
			
		||||
 | 
			
		||||
@@ -930,10 +897,10 @@ const Dash = new Lang.Class({
 | 
			
		||||
        let children = this._box.get_children();
 | 
			
		||||
        for (let i = 0; i < this._dragPlaceholderPos; i++) {
 | 
			
		||||
            if (this._dragPlaceholder &&
 | 
			
		||||
                children[i] == this._dragPlaceholder)
 | 
			
		||||
                children[i] == this._dragPlaceholder.actor)
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            let childId = children[i].child._delegate.app.get_id();
 | 
			
		||||
            let childId = children[i]._delegate.child._delegate.app.get_id();
 | 
			
		||||
            if (childId == id)
 | 
			
		||||
                continue;
 | 
			
		||||
            if (childId in favorites)
 | 
			
		||||
 
 | 
			
		||||
@@ -32,7 +32,6 @@ function _onVertSepRepaint (area)
 | 
			
		||||
    cr.setDash([1, 3], 1); // Hard-code for now
 | 
			
		||||
    cr.setLineWidth(stippleWidth);
 | 
			
		||||
    cr.stroke();
 | 
			
		||||
    cr.$dispose();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const DateMenuButton = new Lang.Class({
 | 
			
		||||
@@ -49,13 +48,16 @@ const DateMenuButton = new Lang.Class({
 | 
			
		||||
            menuAlignment = 1.0 - menuAlignment;
 | 
			
		||||
        this.parent(menuAlignment);
 | 
			
		||||
 | 
			
		||||
        this._clockDisplay = new St.Label({ y_align: Clutter.ActorAlign.CENTER });
 | 
			
		||||
        this.actor.label_actor = this._clockDisplay;
 | 
			
		||||
        this.actor.add_actor(this._clockDisplay);
 | 
			
		||||
        this.actor.add_style_class_name ('clock-display');
 | 
			
		||||
        // At this moment calendar menu is not keyboard navigable at
 | 
			
		||||
        // all (so not accessible), so it doesn't make sense to set as
 | 
			
		||||
        // role ATK_ROLE_MENU like other elements of the panel.
 | 
			
		||||
        this.actor.accessible_role = Atk.Role.LABEL;
 | 
			
		||||
 | 
			
		||||
        hbox = new St.BoxLayout({ name: 'calendarArea' });
 | 
			
		||||
        this.menu.box.add_child(hbox);
 | 
			
		||||
        this._clockDisplay = new St.Label();
 | 
			
		||||
        this.actor.add_actor(this._clockDisplay);
 | 
			
		||||
 | 
			
		||||
        hbox = new St.BoxLayout({name: 'calendarArea' });
 | 
			
		||||
        this.menu.addActor(hbox);
 | 
			
		||||
 | 
			
		||||
        // Fill up the first column
 | 
			
		||||
 | 
			
		||||
@@ -63,8 +65,9 @@ const DateMenuButton = new Lang.Class({
 | 
			
		||||
        hbox.add(vbox);
 | 
			
		||||
 | 
			
		||||
        // Date
 | 
			
		||||
        this._date = new St.Label({ style_class: 'datemenu-date-label',
 | 
			
		||||
                                    can_focus: true });
 | 
			
		||||
        this._date = new St.Label();
 | 
			
		||||
        this.actor.label_actor = this._clockDisplay;
 | 
			
		||||
        this._date.style_class = 'datemenu-date-label';
 | 
			
		||||
        vbox.add(this._date);
 | 
			
		||||
 | 
			
		||||
        this._eventList = new Calendar.EventsList();
 | 
			
		||||
@@ -80,23 +83,14 @@ const DateMenuButton = new Lang.Class({
 | 
			
		||||
                               }));
 | 
			
		||||
        vbox.add(this._calendar.actor);
 | 
			
		||||
 | 
			
		||||
        let separator = new PopupMenu.PopupSeparatorMenuItem();
 | 
			
		||||
        vbox.add(separator.actor, { y_align: St.Align.END, expand: true, y_fill: false });
 | 
			
		||||
 | 
			
		||||
        this._openCalendarItem = new PopupMenu.PopupMenuItem(_("Open Calendar"));
 | 
			
		||||
        this._openCalendarItem.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
 | 
			
		||||
        vbox.add(this._openCalendarItem.actor, {y_align: St.Align.END, expand: true, y_fill: false});
 | 
			
		||||
 | 
			
		||||
        this._openClocksItem = new PopupMenu.PopupMenuItem(_("Open Clocks"));
 | 
			
		||||
        this._openClocksItem.connect('activate', Lang.bind(this, this._onOpenClocksActivate));
 | 
			
		||||
        vbox.add(this._openClocksItem.actor, {y_align: St.Align.END, expand: true, y_fill: false});
 | 
			
		||||
 | 
			
		||||
        Shell.AppSystem.get_default().connect('installed-changed',
 | 
			
		||||
                                              Lang.bind(this, this._appInstalledChanged));
 | 
			
		||||
 | 
			
		||||
        item = this.menu.addSettingsAction(_("Date & Time Settings"), 'gnome-datetime-panel.desktop');
 | 
			
		||||
        item = this.menu.addSettingsAction(_("Date and Time Settings"), 'gnome-datetime-panel.desktop');
 | 
			
		||||
        if (item) {
 | 
			
		||||
            let separator = new PopupMenu.PopupSeparatorMenuItem();
 | 
			
		||||
            separator.setColumnWidths(1);
 | 
			
		||||
            vbox.add(separator.actor, {y_align: St.Align.END, expand: true, y_fill: false});
 | 
			
		||||
 | 
			
		||||
            item.actor.show_on_set_parent = false;
 | 
			
		||||
            item.actor.can_focus = false;
 | 
			
		||||
            item.actor.reparent(vbox);
 | 
			
		||||
            this._dateAndTimeSeparator = separator;
 | 
			
		||||
        }
 | 
			
		||||
@@ -107,7 +101,22 @@ const DateMenuButton = new Lang.Class({
 | 
			
		||||
        hbox.add(this._separator);
 | 
			
		||||
 | 
			
		||||
        // Fill up the second column
 | 
			
		||||
        hbox.add(this._eventList.actor, { expand: true, y_fill: false, y_align: St.Align.START });
 | 
			
		||||
        vbox = new St.BoxLayout({ name: 'calendarEventsArea',
 | 
			
		||||
                                  vertical: true });
 | 
			
		||||
        hbox.add(vbox, { expand: true });
 | 
			
		||||
 | 
			
		||||
        // Event list
 | 
			
		||||
        vbox.add(this._eventList.actor, { expand: true });
 | 
			
		||||
 | 
			
		||||
        this._openCalendarItem = new PopupMenu.PopupMenuItem(_("Open Calendar"));
 | 
			
		||||
        this._openCalendarItem.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
 | 
			
		||||
        this._openCalendarItem.actor.can_focus = false;
 | 
			
		||||
        vbox.add(this._openCalendarItem.actor, {y_align: St.Align.END, expand: true, y_fill: false});
 | 
			
		||||
 | 
			
		||||
        this._calendarSettings = new Gio.Settings({ schema: 'org.gnome.desktop.default-applications.office.calendar' });
 | 
			
		||||
        this._calendarSettings.connect('changed::exec',
 | 
			
		||||
                                       Lang.bind(this, this._calendarSettingsChanged));
 | 
			
		||||
        this._calendarSettingsChanged();
 | 
			
		||||
 | 
			
		||||
        // Whenever the menu is opened, select today
 | 
			
		||||
        this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) {
 | 
			
		||||
@@ -142,40 +151,30 @@ const DateMenuButton = new Lang.Class({
 | 
			
		||||
        this._sessionUpdated();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _appInstalledChanged: function() {
 | 
			
		||||
        this._calendarApp = undefined;
 | 
			
		||||
        this._updateEventsVisibility();
 | 
			
		||||
    _calendarSettingsChanged: function() {
 | 
			
		||||
        let exec = this._calendarSettings.get_string('exec');
 | 
			
		||||
        let fullExec = GLib.find_program_in_path(exec);
 | 
			
		||||
        this._openCalendarItem.actor.visible = fullExec != null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateEventsVisibility: function() {
 | 
			
		||||
        let visible = this._eventSource.hasCalendars;
 | 
			
		||||
        this._openCalendarItem.actor.visible = visible &&
 | 
			
		||||
            (this._getCalendarApp() != null);
 | 
			
		||||
        this._openClocksItem.actor.visible = visible &&
 | 
			
		||||
            (this._getClockApp() != null);
 | 
			
		||||
    _setEventsVisibility: function(visible) {
 | 
			
		||||
        this._openCalendarItem.actor.visible = visible;
 | 
			
		||||
        this._separator.visible = visible;
 | 
			
		||||
        this._eventList.actor.visible = visible;
 | 
			
		||||
        if (visible) {
 | 
			
		||||
            let alignment = 0.25;
 | 
			
		||||
            if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
 | 
			
		||||
                alignment = 1.0 - alignment;
 | 
			
		||||
            this.menu._arrowAlignment = alignment;
 | 
			
		||||
          let alignment = 0.25;
 | 
			
		||||
          if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
 | 
			
		||||
            alignment = 1.0 - alignment;
 | 
			
		||||
          this.menu._arrowAlignment = alignment;
 | 
			
		||||
          this._eventList.actor.get_parent().show();
 | 
			
		||||
        } else {
 | 
			
		||||
            this.menu._arrowAlignment = 0.5;
 | 
			
		||||
          this.menu._arrowAlignment = 0.5;
 | 
			
		||||
          this._eventList.actor.get_parent().hide();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _setEventSource: function(eventSource) {
 | 
			
		||||
        if (this._eventSource)
 | 
			
		||||
            this._eventSource.destroy();
 | 
			
		||||
 | 
			
		||||
        this._calendar.setEventSource(eventSource);
 | 
			
		||||
        this._eventList.setEventSource(eventSource);
 | 
			
		||||
 | 
			
		||||
        this._eventSource = eventSource;
 | 
			
		||||
        this._eventSource.connect('notify::has-calendars', Lang.bind(this, function() {
 | 
			
		||||
            this._updateEventsVisibility();
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _sessionUpdated: function() {
 | 
			
		||||
@@ -184,10 +183,10 @@ const DateMenuButton = new Lang.Class({
 | 
			
		||||
        if (showEvents) {
 | 
			
		||||
            eventSource = new Calendar.DBusEventSource();
 | 
			
		||||
        } else {
 | 
			
		||||
            eventSource = new Calendar.EmptyEventSource();
 | 
			
		||||
            eventSource = null;
 | 
			
		||||
        }
 | 
			
		||||
        this._setEventSource(eventSource);
 | 
			
		||||
        this._updateEventsVisibility();
 | 
			
		||||
        this._setEventsVisibility(showEvents);
 | 
			
		||||
 | 
			
		||||
        // This needs to be handled manually, as the code to
 | 
			
		||||
        // autohide separators doesn't work across the vbox
 | 
			
		||||
@@ -204,34 +203,26 @@ const DateMenuButton = new Lang.Class({
 | 
			
		||||
        this._date.set_text(displayDate.toLocaleFormat(dateFormat));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getCalendarApp: function() {
 | 
			
		||||
        if (this._calendarApp !== undefined)
 | 
			
		||||
            return this._calendarApp;
 | 
			
		||||
 | 
			
		||||
        let apps = Gio.AppInfo.get_recommended_for_type('text/calendar');
 | 
			
		||||
        if (apps && (apps.length > 0))
 | 
			
		||||
            this._calendarApp = apps[0];
 | 
			
		||||
        else
 | 
			
		||||
            this._calendarApp = null;
 | 
			
		||||
        return this._calendarApp;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getClockApp: function() {
 | 
			
		||||
        return Shell.AppSystem.get_default().lookup_app('gnome-clocks.desktop');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onOpenCalendarActivate: function() {
 | 
			
		||||
        this.menu.close();
 | 
			
		||||
 | 
			
		||||
        let app = this._getCalendarApp();
 | 
			
		||||
        if (app.get_id() == 'evolution.desktop')
 | 
			
		||||
            app = Gio.DesktopAppInfo.new('evolution-calendar.desktop');
 | 
			
		||||
        app.launch([], global.create_app_launch_context());
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onOpenClocksActivate: function() {
 | 
			
		||||
        this.menu.close();
 | 
			
		||||
        let app = this._getClockApp();
 | 
			
		||||
        app.activate();
 | 
			
		||||
        let tool = this._calendarSettings.get_string('exec');
 | 
			
		||||
        if (tool.length == 0 || tool.substr(0, 9) == 'evolution') {
 | 
			
		||||
            // TODO: pass the selected day
 | 
			
		||||
            let app = Shell.AppSystem.get_default().lookup_app('evolution-calendar.desktop');
 | 
			
		||||
            app.activate();
 | 
			
		||||
        } else {
 | 
			
		||||
            let needTerm = this._calendarSettings.get_boolean('needs-term');
 | 
			
		||||
            if (needTerm) {
 | 
			
		||||
                let terminalSettings = new Gio.Settings({ schema: 'org.gnome.desktop.default-applications.terminal' });
 | 
			
		||||
                let term = terminalSettings.get_string('exec');
 | 
			
		||||
                let arg = terminalSettings.get_string('exec-arg');
 | 
			
		||||
                if (arg != '')
 | 
			
		||||
                    Util.spawn([term, arg, tool]);
 | 
			
		||||
                else
 | 
			
		||||
                    Util.spawn([term, tool]);
 | 
			
		||||
            } else {
 | 
			
		||||
                Util.spawnCommandLine(tool)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										213
									
								
								js/ui/dnd.js
									
									
									
									
									
								
							
							
						
						@@ -1,11 +1,9 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Gtk = imports.gi.Gtk;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Meta = imports.gi.Meta;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const Tweener = imports.ui.tweener;
 | 
			
		||||
@@ -28,9 +26,9 @@ const DragMotionResult = {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const DRAG_CURSOR_MAP = {
 | 
			
		||||
    0: Meta.Cursor.DND_UNSUPPORTED_TARGET,
 | 
			
		||||
    1: Meta.Cursor.DND_COPY,
 | 
			
		||||
    2: Meta.Cursor.DND_MOVE
 | 
			
		||||
    0: Shell.Cursor.DND_UNSUPPORTED_TARGET,
 | 
			
		||||
    1: Shell.Cursor.DND_COPY,
 | 
			
		||||
    2: Shell.Cursor.DND_MOVE
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const DragDropResult = {
 | 
			
		||||
@@ -45,7 +43,9 @@ let dragMonitors = [];
 | 
			
		||||
 | 
			
		||||
function _getEventHandlerActor() {
 | 
			
		||||
    if (!eventHandlerActor) {
 | 
			
		||||
        eventHandlerActor = new Clutter.Actor({ width: 0, height: 0 });
 | 
			
		||||
        eventHandlerActor = new Clutter.Rectangle();
 | 
			
		||||
        eventHandlerActor.width = 0;
 | 
			
		||||
        eventHandlerActor.height = 0;
 | 
			
		||||
        Main.uiGroup.add_actor(eventHandlerActor);
 | 
			
		||||
        // We connect to 'event' rather than 'captured-event' because the capturing phase doesn't happen
 | 
			
		||||
        // when you've grabbed the pointer.
 | 
			
		||||
@@ -85,8 +85,11 @@ const _Draggable = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this.actor.connect('destroy', Lang.bind(this, function() {
 | 
			
		||||
            this._actorDestroyed = true;
 | 
			
		||||
 | 
			
		||||
            if (this._dragInProgress && this._dragCancellable)
 | 
			
		||||
            // If the drag actor is destroyed and we were going to fix
 | 
			
		||||
            // up its hover state, fix up the parent hover state instead
 | 
			
		||||
            if (this.actor == this._firstLeaveActor)
 | 
			
		||||
                this._firstLeaveActor = this._dragOrigParent;
 | 
			
		||||
            if (this._dragInProgress)
 | 
			
		||||
                this._cancelDrag(global.get_current_time());
 | 
			
		||||
            this.disconnectAll();
 | 
			
		||||
        }));
 | 
			
		||||
@@ -99,7 +102,12 @@ const _Draggable = new Lang.Class({
 | 
			
		||||
        this._buttonDown = false; // The mouse button has been pressed and has not yet been released.
 | 
			
		||||
        this._dragInProgress = false; // The drag has been started, and has not been dropped or cancelled yet.
 | 
			
		||||
        this._animationInProgress = false; // The drag is over and the item is in the process of animating to its original position (snapping back or reverting).
 | 
			
		||||
        this._dragCancellable = true;
 | 
			
		||||
 | 
			
		||||
        // During the drag, we eat enter/leave events so that actors don't prelight.
 | 
			
		||||
        // But we remember the actors that we first left/last entered so we can
 | 
			
		||||
        // fix up the hover state after the drag ends.
 | 
			
		||||
        this._firstLeaveActor = null;
 | 
			
		||||
        this._lastEnterActor = null;
 | 
			
		||||
 | 
			
		||||
        this._eventsGrabbed = false;
 | 
			
		||||
    },
 | 
			
		||||
@@ -128,26 +136,25 @@ const _Draggable = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _ungrabActor: function() {
 | 
			
		||||
        Clutter.ungrab_pointer();
 | 
			
		||||
        if (!this._onEventId)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        Clutter.ungrab_pointer();
 | 
			
		||||
        this.actor.disconnect(this._onEventId);
 | 
			
		||||
        this._onEventId = null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _grabEvents: function() {
 | 
			
		||||
        if (!this._eventsGrabbed) {
 | 
			
		||||
            this._eventsGrabbed = Main.pushModal(_getEventHandlerActor());
 | 
			
		||||
            if (this._eventsGrabbed)
 | 
			
		||||
                Clutter.grab_pointer(_getEventHandlerActor());
 | 
			
		||||
            Clutter.grab_pointer(_getEventHandlerActor());
 | 
			
		||||
            Clutter.grab_keyboard(_getEventHandlerActor());
 | 
			
		||||
            this._eventsGrabbed = true;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _ungrabEvents: function() {
 | 
			
		||||
        if (this._eventsGrabbed) {
 | 
			
		||||
            Clutter.ungrab_pointer();
 | 
			
		||||
            Main.popModal(_getEventHandlerActor());
 | 
			
		||||
            Clutter.ungrab_keyboard();
 | 
			
		||||
            this._eventsGrabbed = false;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
@@ -186,24 +193,16 @@ const _Draggable = new Lang.Class({
 | 
			
		||||
                this._cancelDrag(event.get_time());
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
        } else if (event.type() == Clutter.EventType.LEAVE) {
 | 
			
		||||
            if (this._firstLeaveActor == null)
 | 
			
		||||
                this._firstLeaveActor = event.get_source();
 | 
			
		||||
        } else if (event.type() == Clutter.EventType.ENTER) {
 | 
			
		||||
            this._lastEnterActor = event.get_source();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * fakeRelease:
 | 
			
		||||
     *
 | 
			
		||||
     * Fake a release event.
 | 
			
		||||
     * Must be called if you want to intercept release events on draggable
 | 
			
		||||
     * actors for other purposes (for example if you're using
 | 
			
		||||
     * PopupMenu.ignoreRelease())
 | 
			
		||||
     */
 | 
			
		||||
    fakeRelease: function() {
 | 
			
		||||
        this._buttonDown = false;
 | 
			
		||||
        this._ungrabActor();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * startDrag:
 | 
			
		||||
     * @stageX: X coordinate of event
 | 
			
		||||
@@ -229,7 +228,7 @@ const _Draggable = new Lang.Class({
 | 
			
		||||
        if (this._onEventId)
 | 
			
		||||
            this._ungrabActor();
 | 
			
		||||
        this._grabEvents();
 | 
			
		||||
        global.screen.set_cursor(Meta.Cursor.DND_IN_DRAG);
 | 
			
		||||
        global.set_cursor(Shell.Cursor.DND_IN_DRAG);
 | 
			
		||||
 | 
			
		||||
        this._dragX = this._dragStartX = stageX;
 | 
			
		||||
        this._dragY = this._dragStartY = stageY;
 | 
			
		||||
@@ -275,19 +274,19 @@ const _Draggable = new Lang.Class({
 | 
			
		||||
            this._dragOrigY = this._dragActor.y;
 | 
			
		||||
            this._dragOrigScale = this._dragActor.scale_x;
 | 
			
		||||
 | 
			
		||||
            // Set the actor's scale such that it will keep the same
 | 
			
		||||
            // transformed size when it's reparented to the uiGroup
 | 
			
		||||
            let [scaledWidth, scaledHeight] = this.actor.get_transformed_size();
 | 
			
		||||
            this._dragActor.set_scale(scaledWidth / this.actor.width,
 | 
			
		||||
                                      scaledHeight / this.actor.height);
 | 
			
		||||
            this._dragActor.reparent(Main.uiGroup);
 | 
			
		||||
            this._dragActor.raise_top();
 | 
			
		||||
            Shell.util_set_hidden_from_pick(this._dragActor, true);
 | 
			
		||||
 | 
			
		||||
            let [actorStageX, actorStageY] = this.actor.get_transformed_position();
 | 
			
		||||
            this._dragOffsetX = actorStageX - this._dragStartX;
 | 
			
		||||
            this._dragOffsetY = actorStageY - this._dragStartY;
 | 
			
		||||
 | 
			
		||||
            this._dragActor.reparent(Main.uiGroup);
 | 
			
		||||
            this._dragActor.raise_top();
 | 
			
		||||
            Shell.util_set_hidden_from_pick(this._dragActor, true);
 | 
			
		||||
            // Set the actor's scale such that it will keep the same
 | 
			
		||||
            // transformed size when it's reparented to the uiGroup
 | 
			
		||||
            let [scaledWidth, scaledHeight] = this.actor.get_transformed_size();
 | 
			
		||||
            this.actor.set_scale(scaledWidth / this.actor.width,
 | 
			
		||||
                                 scaledHeight / this.actor.height);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._dragOrigOpacity = this._dragActor.opacity;
 | 
			
		||||
@@ -344,65 +343,60 @@ const _Draggable = new Lang.Class({
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateDragHover : function () {
 | 
			
		||||
        let target = this._dragActor.get_stage().get_actor_at_pos(Clutter.PickMode.ALL,
 | 
			
		||||
                                                                  this._dragX, this._dragY);
 | 
			
		||||
        let dragEvent = {
 | 
			
		||||
            x: this._dragX,
 | 
			
		||||
            y: this._dragY,
 | 
			
		||||
            dragActor: this._dragActor,
 | 
			
		||||
            source: this.actor._delegate,
 | 
			
		||||
            targetActor: target
 | 
			
		||||
        };
 | 
			
		||||
        for (let i = 0; i < dragMonitors.length; i++) {
 | 
			
		||||
            let motionFunc = dragMonitors[i].dragMotion;
 | 
			
		||||
            if (motionFunc) {
 | 
			
		||||
                let result = motionFunc(dragEvent);
 | 
			
		||||
                if (result != DragMotionResult.CONTINUE) {
 | 
			
		||||
                    global.screen.set_cursor(DRAG_CURSOR_MAP[result]);
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        while (target) {
 | 
			
		||||
            if (target._delegate && target._delegate.handleDragOver) {
 | 
			
		||||
                let [r, targX, targY] = target.transform_stage_point(this._dragX, this._dragY);
 | 
			
		||||
                // We currently loop through all parents on drag-over even if one of the children has handled it.
 | 
			
		||||
                // We can check the return value of the function and break the loop if it's true if we don't want
 | 
			
		||||
                // to continue checking the parents.
 | 
			
		||||
                let result = target._delegate.handleDragOver(this.actor._delegate,
 | 
			
		||||
                                                             this._dragActor,
 | 
			
		||||
                                                             targX,
 | 
			
		||||
                                                             targY,
 | 
			
		||||
                                                             0);
 | 
			
		||||
                if (result != DragMotionResult.CONTINUE) {
 | 
			
		||||
                    global.screen.set_cursor(DRAG_CURSOR_MAP[result]);
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            target = target.get_parent();
 | 
			
		||||
        }
 | 
			
		||||
        global.screen.set_cursor(Meta.Cursor.DND_IN_DRAG);
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _queueUpdateDragHover: function() {
 | 
			
		||||
        if (this._updateHoverId)
 | 
			
		||||
            GLib.source_remove(this._updateHoverId);
 | 
			
		||||
 | 
			
		||||
        this._updateHoverId = GLib.idle_add(GLib.PRIORITY_DEFAULT,
 | 
			
		||||
                                            Lang.bind(this, this._updateDragHover));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateDragPosition : function (event) {
 | 
			
		||||
        let [stageX, stageY] = event.get_coords();
 | 
			
		||||
        this._dragX = stageX;
 | 
			
		||||
        this._dragY = stageY;
 | 
			
		||||
        this._dragActor.set_position(stageX + this._dragOffsetX,
 | 
			
		||||
                                     stageY + this._dragOffsetY);
 | 
			
		||||
 | 
			
		||||
        this._queueUpdateDragHover();
 | 
			
		||||
        // If we are dragging, update the position
 | 
			
		||||
        if (this._dragActor) {
 | 
			
		||||
            this._dragActor.set_position(stageX + this._dragOffsetX,
 | 
			
		||||
                                         stageY + this._dragOffsetY);
 | 
			
		||||
 | 
			
		||||
            let target = this._dragActor.get_stage().get_actor_at_pos(Clutter.PickMode.ALL,
 | 
			
		||||
                                                                      stageX, stageY);
 | 
			
		||||
 | 
			
		||||
            // We call observers only once per motion with the innermost
 | 
			
		||||
            // target actor. If necessary, the observer can walk the
 | 
			
		||||
            // parent itself.
 | 
			
		||||
            let dragEvent = {
 | 
			
		||||
                x: stageX,
 | 
			
		||||
                y: stageY,
 | 
			
		||||
                dragActor: this._dragActor,
 | 
			
		||||
                source: this.actor._delegate,
 | 
			
		||||
                targetActor: target
 | 
			
		||||
            };
 | 
			
		||||
            for (let i = 0; i < dragMonitors.length; i++) {
 | 
			
		||||
                let motionFunc = dragMonitors[i].dragMotion;
 | 
			
		||||
                if (motionFunc) {
 | 
			
		||||
                    let result = motionFunc(dragEvent);
 | 
			
		||||
                    if (result != DragMotionResult.CONTINUE) {
 | 
			
		||||
                        global.set_cursor(DRAG_CURSOR_MAP[result]);
 | 
			
		||||
                        return true;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            while (target) {
 | 
			
		||||
                if (target._delegate && target._delegate.handleDragOver) {
 | 
			
		||||
                    let [r, targX, targY] = target.transform_stage_point(stageX, stageY);
 | 
			
		||||
                    // We currently loop through all parents on drag-over even if one of the children has handled it.
 | 
			
		||||
                    // We can check the return value of the function and break the loop if it's true if we don't want
 | 
			
		||||
                    // to continue checking the parents.
 | 
			
		||||
                    let result = target._delegate.handleDragOver(this.actor._delegate,
 | 
			
		||||
                                                                 this._dragActor,
 | 
			
		||||
                                                                 targX,
 | 
			
		||||
                                                                 targY,
 | 
			
		||||
                                                                 event.get_time());
 | 
			
		||||
                    if (result != DragMotionResult.CONTINUE) {
 | 
			
		||||
                        global.set_cursor(DRAG_CURSOR_MAP[result]);
 | 
			
		||||
                        return true;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                target = target.get_parent();
 | 
			
		||||
            }
 | 
			
		||||
            global.set_cursor(Shell.Cursor.DND_IN_DRAG);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -431,11 +425,6 @@ const _Draggable = new Lang.Class({
 | 
			
		||||
                }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // At this point it is too late to cancel a drag by destroying
 | 
			
		||||
        // the actor, the fate of which is decided by acceptDrop and its
 | 
			
		||||
        // side-effects
 | 
			
		||||
        this._dragCancellable = false;
 | 
			
		||||
 | 
			
		||||
        while (target) {
 | 
			
		||||
            if (target._delegate && target._delegate.acceptDrop) {
 | 
			
		||||
                let [r, targX, targY] = target.transform_stage_point(dropX, dropY);
 | 
			
		||||
@@ -444,6 +433,8 @@ const _Draggable = new Lang.Class({
 | 
			
		||||
                                                targX,
 | 
			
		||||
                                                targY,
 | 
			
		||||
                                                event.get_time())) {
 | 
			
		||||
                    if (this._actorDestroyed)
 | 
			
		||||
                        return true;
 | 
			
		||||
                    // If it accepted the drop without taking the actor,
 | 
			
		||||
                    // handle it ourselves.
 | 
			
		||||
                    if (this._dragActor.get_parent() == Main.uiGroup) {
 | 
			
		||||
@@ -455,7 +446,7 @@ const _Draggable = new Lang.Class({
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    this._dragInProgress = false;
 | 
			
		||||
                    global.screen.set_cursor(Meta.Cursor.DEFAULT);
 | 
			
		||||
                    global.unset_cursor();
 | 
			
		||||
                    this.emit('drag-end', event.get_time(), true);
 | 
			
		||||
                    this._dragComplete();
 | 
			
		||||
                    return true;
 | 
			
		||||
@@ -507,7 +498,7 @@ const _Draggable = new Lang.Class({
 | 
			
		||||
        let [snapBackX, snapBackY, snapBackScale] = this._getRestoreLocation();
 | 
			
		||||
 | 
			
		||||
        if (this._actorDestroyed) {
 | 
			
		||||
            global.screen.set_cursor(Meta.Cursor.DEFAULT);
 | 
			
		||||
            global.unset_cursor();
 | 
			
		||||
            if (!this._buttonDown)
 | 
			
		||||
                this._dragComplete();
 | 
			
		||||
            this.emit('drag-end', eventTime, false);
 | 
			
		||||
@@ -561,7 +552,7 @@ const _Draggable = new Lang.Class({
 | 
			
		||||
        } else {
 | 
			
		||||
            dragActor.destroy();
 | 
			
		||||
        }
 | 
			
		||||
        global.screen.set_cursor(Meta.Cursor.DEFAULT);
 | 
			
		||||
        global.unset_cursor();
 | 
			
		||||
        this.emit('drag-end', eventTime, false);
 | 
			
		||||
 | 
			
		||||
        this._animationInProgress = false;
 | 
			
		||||
@@ -569,16 +560,32 @@ const _Draggable = new Lang.Class({
 | 
			
		||||
            this._dragComplete();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Actor is an actor we have entered or left during the drag; call
 | 
			
		||||
    // st_widget_sync_hover on all StWidget ancestors
 | 
			
		||||
    _syncHover: function(actor) {
 | 
			
		||||
        while (actor) {
 | 
			
		||||
            let parent = actor.get_parent();
 | 
			
		||||
            if (actor instanceof St.Widget)
 | 
			
		||||
                actor.sync_hover();
 | 
			
		||||
 | 
			
		||||
            actor = parent;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _dragComplete: function() {
 | 
			
		||||
        if (!this._actorDestroyed)
 | 
			
		||||
            Shell.util_set_hidden_from_pick(this._dragActor, false);
 | 
			
		||||
 | 
			
		||||
        this._ungrabEvents();
 | 
			
		||||
        global.sync_pointer();
 | 
			
		||||
 | 
			
		||||
        if (this._updateHoverId) {
 | 
			
		||||
            GLib.source_remove(this._updateHoverId);
 | 
			
		||||
            this._updateHoverId = 0;
 | 
			
		||||
        if (this._firstLeaveActor) {
 | 
			
		||||
            this._syncHover(this._firstLeaveActor);
 | 
			
		||||
            this._firstLeaveActor = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this._lastEnterActor) {
 | 
			
		||||
            this._syncHover(this._lastEnterActor);
 | 
			
		||||
            this._lastEnterActor = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._dragActor = undefined;
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,7 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Mainloop = imports.mainloop;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
 | 
			
		||||
const AccountsService = imports.gi.AccountsService;
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
@@ -31,10 +31,10 @@ const St = imports.gi.St;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
 | 
			
		||||
const GnomeSession = imports.misc.gnomeSession;
 | 
			
		||||
const LoginManager = imports.misc.loginManager;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const ModalDialog = imports.ui.modalDialog;
 | 
			
		||||
const Tweener = imports.ui.tweener;
 | 
			
		||||
const UserWidget = imports.ui.userWidget;
 | 
			
		||||
const UserMenu = imports.ui.userMenu;
 | 
			
		||||
 | 
			
		||||
let _endSessionDialog = null;
 | 
			
		||||
 | 
			
		||||
@@ -50,7 +50,6 @@ const EndSessionDialogIface = <interface name="org.gnome.SessionManager.EndSessi
 | 
			
		||||
    <arg type="u" direction="in" />
 | 
			
		||||
    <arg type="ao" direction="in" />
 | 
			
		||||
</method>
 | 
			
		||||
<method name="Close" />
 | 
			
		||||
<signal name="ConfirmedLogout" />
 | 
			
		||||
<signal name="ConfirmedReboot" />
 | 
			
		||||
<signal name="ConfirmedShutdown" />
 | 
			
		||||
@@ -61,96 +60,63 @@ const EndSessionDialogIface = <interface name="org.gnome.SessionManager.EndSessi
 | 
			
		||||
const logoutDialogContent = {
 | 
			
		||||
    subjectWithUser: C_("title", "Log Out %s"),
 | 
			
		||||
    subject: C_("title", "Log Out"),
 | 
			
		||||
    descriptionWithUser: function(user, seconds) {
 | 
			
		||||
    inhibitedDescription: _("Click Log Out to quit these applications and log out of the system."),
 | 
			
		||||
    uninhibitedDescriptionWithUser: function(user, seconds) {
 | 
			
		||||
        return ngettext("%s will be logged out automatically in %d second.",
 | 
			
		||||
                        "%s will be logged out automatically in %d seconds.",
 | 
			
		||||
                        seconds).format(user, seconds);
 | 
			
		||||
    },
 | 
			
		||||
    description: function(seconds) {
 | 
			
		||||
    uninhibitedDescription: function(seconds) {
 | 
			
		||||
        return ngettext("You will be logged out automatically in %d second.",
 | 
			
		||||
                        "You will be logged out automatically in %d seconds.",
 | 
			
		||||
                        seconds).format(seconds);
 | 
			
		||||
    },
 | 
			
		||||
    endDescription: _("Logging out of the system."),
 | 
			
		||||
    confirmButtons: [{ signal: 'ConfirmedLogout',
 | 
			
		||||
                       label:  C_("button", "Log Out") }],
 | 
			
		||||
    iconStyleClass: 'end-session-dialog-logout-icon',
 | 
			
		||||
    showOtherSessions: false,
 | 
			
		||||
    iconStyleClass: 'end-session-dialog-logout-icon'
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const shutdownDialogContent = {
 | 
			
		||||
    subject: C_("title", "Power Off"),
 | 
			
		||||
    description: function(seconds) {
 | 
			
		||||
    inhibitedDescription: _("Click Power Off to quit these applications and power off the system."),
 | 
			
		||||
    uninhibitedDescription: function(seconds) {
 | 
			
		||||
        return ngettext("The system will power off automatically in %d second.",
 | 
			
		||||
                        "The system will power off automatically in %d seconds.",
 | 
			
		||||
                        seconds).format(seconds);
 | 
			
		||||
    },
 | 
			
		||||
    endDescription: _("Powering off the system."),
 | 
			
		||||
    confirmButtons: [{ signal: 'ConfirmedReboot',
 | 
			
		||||
                       label:  C_("button", "Restart") },
 | 
			
		||||
                     { signal: 'ConfirmedShutdown',
 | 
			
		||||
                       label:  C_("button", "Power Off") }],
 | 
			
		||||
    iconName: 'system-shutdown-symbolic',
 | 
			
		||||
    iconStyleClass: 'end-session-dialog-shutdown-icon',
 | 
			
		||||
    showOtherSessions: true,
 | 
			
		||||
    iconStyleClass: 'end-session-dialog-shutdown-icon'
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const restartDialogContent = {
 | 
			
		||||
    subject: C_("title", "Restart"),
 | 
			
		||||
    description: function(seconds) {
 | 
			
		||||
    inhibitedDescription: _("Click Restart to quit these applications and restart the system."),
 | 
			
		||||
    uninhibitedDescription: function(seconds) {
 | 
			
		||||
        return ngettext("The system will restart automatically in %d second.",
 | 
			
		||||
                        "The system will restart automatically in %d seconds.",
 | 
			
		||||
                        seconds).format(seconds);
 | 
			
		||||
    },
 | 
			
		||||
    endDescription: _("Restarting the system."),
 | 
			
		||||
    confirmButtons: [{ signal: 'ConfirmedReboot',
 | 
			
		||||
                       label:  C_("button", "Restart") }],
 | 
			
		||||
    iconName: 'view-refresh-symbolic',
 | 
			
		||||
    iconStyleClass: 'end-session-dialog-shutdown-icon',
 | 
			
		||||
    showOtherSessions: true,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const restartInstallDialogContent = {
 | 
			
		||||
 | 
			
		||||
    subject: C_("title", "Restart & Install Updates"),
 | 
			
		||||
    description: function(seconds) {
 | 
			
		||||
        return ngettext("The system will automatically restart and install updates in %d second.",
 | 
			
		||||
                        "The system will automatically restart and install updates in %d seconds.",
 | 
			
		||||
                        seconds).format(seconds);
 | 
			
		||||
    },
 | 
			
		||||
    confirmButtons: [{ signal: 'ConfirmedReboot',
 | 
			
		||||
                       label:  C_("button", "Restart & Install") }],
 | 
			
		||||
    iconName: 'view-refresh-symbolic',
 | 
			
		||||
    iconStyleClass: 'end-session-dialog-shutdown-icon',
 | 
			
		||||
    showOtherSessions: true,
 | 
			
		||||
    iconStyleClass: 'end-session-dialog-shutdown-icon'
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const DialogContent = {
 | 
			
		||||
    0 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_LOGOUT */: logoutDialogContent,
 | 
			
		||||
    1 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_SHUTDOWN */: shutdownDialogContent,
 | 
			
		||||
    2 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_RESTART */: restartDialogContent,
 | 
			
		||||
    3: restartInstallDialogContent
 | 
			
		||||
    2 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_RESTART */: restartDialogContent
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const MAX_USERS_IN_SESSION_DIALOG = 5;
 | 
			
		||||
 | 
			
		||||
const LogindSessionIface = <interface name='org.freedesktop.login1.Session'>
 | 
			
		||||
    <property name="Id" type="s" access="read"/>
 | 
			
		||||
    <property name="Remote" type="b" access="read"/>
 | 
			
		||||
    <property name="Class" type="s" access="read"/>
 | 
			
		||||
    <property name="Type" type="s" access="read"/>
 | 
			
		||||
    <property name="State" type="s" access="read"/>
 | 
			
		||||
</interface>;
 | 
			
		||||
 | 
			
		||||
const LogindSession = Gio.DBusProxy.makeProxyWrapper(LogindSessionIface);
 | 
			
		||||
 | 
			
		||||
function findAppFromInhibitor(inhibitor) {
 | 
			
		||||
    let desktopFile;
 | 
			
		||||
    try {
 | 
			
		||||
        [desktopFile] = inhibitor.GetAppIdSync();
 | 
			
		||||
    } catch(e) {
 | 
			
		||||
        // XXX -- sometimes JIT inhibitors generated by gnome-session
 | 
			
		||||
        // get removed too soon. Don't fail in this case.
 | 
			
		||||
        log('gnome-session gave us a dead inhibitor: %s'.format(inhibitor.get_object_path()));
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
    let [desktopFile] = inhibitor.GetAppIdSync();
 | 
			
		||||
 | 
			
		||||
    if (!GLib.str_has_suffix(desktopFile, '.desktop'))
 | 
			
		||||
        desktopFile += '.desktop';
 | 
			
		||||
@@ -158,6 +124,58 @@ function findAppFromInhibitor(inhibitor) {
 | 
			
		||||
    return Shell.AppSystem.get_default().lookup_heuristic_basename(desktopFile);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const ListItem = new Lang.Class({
 | 
			
		||||
    Name: 'ListItem',
 | 
			
		||||
 | 
			
		||||
    _init: function(app, reason) {
 | 
			
		||||
        this._app = app;
 | 
			
		||||
        this._reason = reason;
 | 
			
		||||
 | 
			
		||||
        if (this._reason == null)
 | 
			
		||||
          this._reason = '';
 | 
			
		||||
 | 
			
		||||
        let layout = new St.BoxLayout({ vertical: false});
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.Button({ style_class: 'end-session-dialog-app-list-item',
 | 
			
		||||
                                     can_focus:   true,
 | 
			
		||||
                                     child:       layout,
 | 
			
		||||
                                     reactive:    true,
 | 
			
		||||
                                     x_align:     St.Align.START,
 | 
			
		||||
                                     x_fill:      true });
 | 
			
		||||
 | 
			
		||||
        this._icon = this._app.create_icon_texture(_ITEM_ICON_SIZE);
 | 
			
		||||
 | 
			
		||||
        let iconBin = new St.Bin({ style_class: 'end-session-dialog-app-list-item-icon',
 | 
			
		||||
                                   child:       this._icon });
 | 
			
		||||
        layout.add(iconBin);
 | 
			
		||||
 | 
			
		||||
        let textLayout = new St.BoxLayout({ style_class: 'end-session-dialog-app-list-item-text-box',
 | 
			
		||||
                                            vertical:    true });
 | 
			
		||||
        layout.add(textLayout);
 | 
			
		||||
 | 
			
		||||
        this._nameLabel = new St.Label({ text:        this._app.get_name(),
 | 
			
		||||
                                         style_class: 'end-session-dialog-app-list-item-name' });
 | 
			
		||||
        textLayout.add(this._nameLabel,
 | 
			
		||||
                       { expand: false,
 | 
			
		||||
                         x_fill: true });
 | 
			
		||||
 | 
			
		||||
        this._descriptionLabel = new St.Label({ text:        this._reason,
 | 
			
		||||
                                                style_class: 'end-session-dialog-app-list-item-description' });
 | 
			
		||||
        this.actor.label_actor = this._nameLabel;
 | 
			
		||||
        textLayout.add(this._descriptionLabel,
 | 
			
		||||
                       { expand: true,
 | 
			
		||||
                         x_fill: true });
 | 
			
		||||
 | 
			
		||||
        this.actor.connect('clicked', Lang.bind(this, this._onClicked));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onClicked: function() {
 | 
			
		||||
        this.emit('activate');
 | 
			
		||||
        this._app.activate();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(ListItem.prototype);
 | 
			
		||||
 | 
			
		||||
// The logout timer only shows updates every 10 seconds
 | 
			
		||||
// until the last 10 seconds, then it shows updates every
 | 
			
		||||
// second.  This function takes a given time and returns
 | 
			
		||||
@@ -205,26 +223,24 @@ const EndSessionDialog = new Lang.Class({
 | 
			
		||||
    Extends: ModalDialog.ModalDialog,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.parent({ styleClass: 'end-session-dialog',
 | 
			
		||||
                      destroyOnClose: false });
 | 
			
		||||
        this.parent({ styleClass: 'end-session-dialog' });
 | 
			
		||||
 | 
			
		||||
        this._loginManager = LoginManager.getLoginManager();
 | 
			
		||||
        this._userManager = AccountsService.UserManager.get_default();
 | 
			
		||||
        this._user = this._userManager.get_user(GLib.get_user_name());
 | 
			
		||||
        this._updatesFile = Gio.File.new_for_path('/system-update');
 | 
			
		||||
        this._user = AccountsService.UserManager.get_default().get_user(GLib.get_user_name());
 | 
			
		||||
 | 
			
		||||
        this._secondsLeft = 0;
 | 
			
		||||
        this._totalSecondsToStayOpen = 0;
 | 
			
		||||
        this._applications = [];
 | 
			
		||||
        this._sessions = [];
 | 
			
		||||
        this._inhibitors = [];
 | 
			
		||||
 | 
			
		||||
        this.connect('destroy',
 | 
			
		||||
                     Lang.bind(this, this._onDestroy));
 | 
			
		||||
        this.connect('opened',
 | 
			
		||||
                     Lang.bind(this, this._onOpened));
 | 
			
		||||
 | 
			
		||||
        this._userLoadedId = this._user.connect('notify::is_loaded', Lang.bind(this, this._sync));
 | 
			
		||||
        this._userChangedId = this._user.connect('changed', Lang.bind(this, this._sync));
 | 
			
		||||
        this._userLoadedId = this._user.connect('notify::is_loaded',
 | 
			
		||||
                                                Lang.bind(this, this._updateContent));
 | 
			
		||||
 | 
			
		||||
        this._userChangedId = this._user.connect('changed',
 | 
			
		||||
                                                 Lang.bind(this, this._updateContent));
 | 
			
		||||
 | 
			
		||||
        let mainContentLayout = new St.BoxLayout({ vertical: false });
 | 
			
		||||
        this.contentLayout.add(mainContentLayout,
 | 
			
		||||
@@ -256,28 +272,28 @@ const EndSessionDialog = new Lang.Class({
 | 
			
		||||
                          { y_fill:  true,
 | 
			
		||||
                            y_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        this._scrollView = new St.ScrollView({ style_class: 'end-session-dialog-list' });
 | 
			
		||||
        this._scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
 | 
			
		||||
        this.contentLayout.add(this._scrollView,
 | 
			
		||||
        let scrollView = new St.ScrollView({ style_class: 'end-session-dialog-app-list'});
 | 
			
		||||
        scrollView.set_policy(Gtk.PolicyType.NEVER,
 | 
			
		||||
                              Gtk.PolicyType.AUTOMATIC);
 | 
			
		||||
        this.contentLayout.add(scrollView,
 | 
			
		||||
                               { x_fill: true,
 | 
			
		||||
                                 y_fill: true });
 | 
			
		||||
        this._scrollView.hide();
 | 
			
		||||
        scrollView.hide();
 | 
			
		||||
 | 
			
		||||
        this._inhibitorSection = new St.BoxLayout({ vertical: true,
 | 
			
		||||
                                                    style_class: 'end-session-dialog-inhibitor-layout' });
 | 
			
		||||
        this._scrollView.add_actor(this._inhibitorSection);
 | 
			
		||||
 | 
			
		||||
        this._applicationHeader = new St.Label({ style_class: 'end-session-dialog-list-header',
 | 
			
		||||
                                                 text: _("Some applications are busy or have unsaved work.") });
 | 
			
		||||
        this._applicationList = new St.BoxLayout({ vertical: true });
 | 
			
		||||
        this._inhibitorSection.add_actor(this._applicationHeader);
 | 
			
		||||
        this._inhibitorSection.add_actor(this._applicationList);
 | 
			
		||||
        scrollView.add_actor(this._applicationList);
 | 
			
		||||
 | 
			
		||||
        this._sessionHeader = new St.Label({ style_class: 'end-session-dialog-list-header',
 | 
			
		||||
                                             text: _("Other users are logged in.") });
 | 
			
		||||
        this._sessionList = new St.BoxLayout({ vertical: true });
 | 
			
		||||
        this._inhibitorSection.add_actor(this._sessionHeader);
 | 
			
		||||
        this._inhibitorSection.add_actor(this._sessionList);
 | 
			
		||||
        this._applicationList.connect('actor-added',
 | 
			
		||||
                                      Lang.bind(this, function() {
 | 
			
		||||
                                          if (this._applicationList.get_n_children() == 1)
 | 
			
		||||
                                              scrollView.show();
 | 
			
		||||
                                      }));
 | 
			
		||||
 | 
			
		||||
        this._applicationList.connect('actor-removed',
 | 
			
		||||
                                      Lang.bind(this, function() {
 | 
			
		||||
                                          if (this._applicationList.get_n_children() == 0)
 | 
			
		||||
                                              scrollView.hide();
 | 
			
		||||
                                      }));
 | 
			
		||||
 | 
			
		||||
        this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(EndSessionDialogIface, this);
 | 
			
		||||
        this._dbusImpl.export(Gio.DBus.session, '/org/gnome/SessionManager/EndSessionDialog');
 | 
			
		||||
@@ -288,42 +304,52 @@ const EndSessionDialog = new Lang.Class({
 | 
			
		||||
        this._user.disconnect(this._userChangedId);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _sync: function() {
 | 
			
		||||
        let open = (this.state == ModalDialog.State.OPENING || this.state == ModalDialog.State.OPENED);
 | 
			
		||||
        if (!open)
 | 
			
		||||
    _updateDescription: function() {
 | 
			
		||||
        if (this.state != ModalDialog.State.OPENING &&
 | 
			
		||||
            this.state != ModalDialog.State.OPENED)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (this._type == 2 && this._updatesFile.query_exists(null))
 | 
			
		||||
            this._type = 3;
 | 
			
		||||
 | 
			
		||||
        let dialogContent = DialogContent[this._type];
 | 
			
		||||
 | 
			
		||||
        let subject = dialogContent.subject;
 | 
			
		||||
 | 
			
		||||
        let description;
 | 
			
		||||
        let displayTime = _roundSecondsToInterval(this._totalSecondsToStayOpen,
 | 
			
		||||
                                                  this._secondsLeft,
 | 
			
		||||
                                                  10);
 | 
			
		||||
 | 
			
		||||
        if (this._user.is_loaded) {
 | 
			
		||||
            let realName = this._user.get_real_name();
 | 
			
		||||
        if (this._inhibitors.length > 0) {
 | 
			
		||||
            this._stopTimer();
 | 
			
		||||
            description = dialogContent.inhibitedDescription;
 | 
			
		||||
        } else if (this._secondsLeft > 0 && this._inhibitors.length == 0) {
 | 
			
		||||
            let displayTime = _roundSecondsToInterval(this._totalSecondsToStayOpen,
 | 
			
		||||
                                                      this._secondsLeft,
 | 
			
		||||
                                                      10);
 | 
			
		||||
 | 
			
		||||
            if (realName != null) {
 | 
			
		||||
                if (dialogContent.subjectWithUser)
 | 
			
		||||
                    subject = dialogContent.subjectWithUser.format(realName);
 | 
			
		||||
            if (this._user.is_loaded) {
 | 
			
		||||
                let realName = this._user.get_real_name();
 | 
			
		||||
 | 
			
		||||
                if (dialogContent.descriptionWithUser)
 | 
			
		||||
                    description = dialogContent.descriptionWithUser(realName, displayTime);
 | 
			
		||||
                else
 | 
			
		||||
                    description = dialogContent.description(displayTime);
 | 
			
		||||
                if (realName != null) {
 | 
			
		||||
                    if (dialogContent.subjectWithUser)
 | 
			
		||||
                        subject = dialogContent.subjectWithUser.format(realName);
 | 
			
		||||
 | 
			
		||||
                    if (dialogContent.uninhibitedDescriptionWithUser)
 | 
			
		||||
                        description = dialogContent.uninhibitedDescriptionWithUser(realName, displayTime);
 | 
			
		||||
                    else
 | 
			
		||||
                        description = dialogContent.uninhibitedDescription(displayTime);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!description)
 | 
			
		||||
                description = dialogContent.uninhibitedDescription(displayTime);
 | 
			
		||||
        } else {
 | 
			
		||||
            description = dialogContent.endDescription;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!description)
 | 
			
		||||
            description = dialogContent.description(displayTime);
 | 
			
		||||
 | 
			
		||||
        _setLabelText(this._descriptionLabel, description);
 | 
			
		||||
        _setLabelText(this._subjectLabel, subject);
 | 
			
		||||
        _setLabelText(this._descriptionLabel, description);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateContent: function() {
 | 
			
		||||
        if (this.state != ModalDialog.State.OPENING &&
 | 
			
		||||
            this.state != ModalDialog.State.OPENED)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let dialogContent = DialogContent[this._type];
 | 
			
		||||
        if (dialogContent.iconName) {
 | 
			
		||||
@@ -331,18 +357,14 @@ const EndSessionDialog = new Lang.Class({
 | 
			
		||||
                                                icon_size: _DIALOG_ICON_SIZE,
 | 
			
		||||
                                                style_class: dialogContent.iconStyleClass });
 | 
			
		||||
        } else {
 | 
			
		||||
            let avatarWidget = new UserWidget.Avatar(this._user,
 | 
			
		||||
                                                     { iconSize: _DIALOG_ICON_SIZE,
 | 
			
		||||
                                                       styleClass: dialogContent.iconStyleClass });
 | 
			
		||||
            let avatarWidget = new UserMenu.UserAvatarWidget(this._user,
 | 
			
		||||
                                                             { iconSize: _DIALOG_ICON_SIZE,
 | 
			
		||||
                                                               styleClass: dialogContent.iconStyleClass });
 | 
			
		||||
            this._iconBin.child = avatarWidget.actor;
 | 
			
		||||
            avatarWidget.update();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let hasApplications = this._applications.length > 0;
 | 
			
		||||
        let hasSessions = this._sessions.length > 0;
 | 
			
		||||
        this._scrollView.visible = hasApplications || hasSessions;
 | 
			
		||||
        this._applicationHeader.visible = hasApplications;
 | 
			
		||||
        this._sessionHeader.visible = hasSessions;
 | 
			
		||||
        this._updateDescription();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateButtons: function() {
 | 
			
		||||
@@ -355,12 +377,7 @@ const EndSessionDialog = new Lang.Class({
 | 
			
		||||
            let signal = dialogContent.confirmButtons[i].signal;
 | 
			
		||||
            let label = dialogContent.confirmButtons[i].label;
 | 
			
		||||
            buttons.push({ action: Lang.bind(this, function() {
 | 
			
		||||
                                       this.close(true);
 | 
			
		||||
                                       let signalId = this.connect('closed',
 | 
			
		||||
                                                                   Lang.bind(this, function() {
 | 
			
		||||
                                                                       this.disconnect(signalId);
 | 
			
		||||
                                                                       this._confirm(signal);
 | 
			
		||||
                                                                   }));
 | 
			
		||||
                                       this._confirm(signal);
 | 
			
		||||
                                   }),
 | 
			
		||||
                           label: label });
 | 
			
		||||
        }
 | 
			
		||||
@@ -368,17 +385,15 @@ const EndSessionDialog = new Lang.Class({
 | 
			
		||||
        this.setButtons(buttons);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    close: function(skipSignal) {
 | 
			
		||||
    close: function() {
 | 
			
		||||
        this.parent();
 | 
			
		||||
 | 
			
		||||
        if (!skipSignal)
 | 
			
		||||
            this._dbusImpl.emit_signal('Closed', null);
 | 
			
		||||
        this._dbusImpl.emit_signal('Closed', null);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    cancel: function() {
 | 
			
		||||
        this._stopTimer();
 | 
			
		||||
        this._dbusImpl.emit_signal('Canceled', null);
 | 
			
		||||
        this.close();
 | 
			
		||||
        this.close(global.get_current_time());
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _confirm: function(signal) {
 | 
			
		||||
@@ -388,68 +403,32 @@ const EndSessionDialog = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onOpened: function() {
 | 
			
		||||
        this._sync();
 | 
			
		||||
        if (this._inhibitors.length == 0)
 | 
			
		||||
            this._startTimer();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _startTimer: function() {
 | 
			
		||||
        let startTime = GLib.get_monotonic_time();
 | 
			
		||||
        this._secondsLeft = this._totalSecondsToStayOpen;
 | 
			
		||||
 | 
			
		||||
        this._timerId = Mainloop.timeout_add_seconds(1, Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                let currentTime = GLib.get_monotonic_time();
 | 
			
		||||
                let secondsElapsed = ((currentTime - startTime) / 1000000);
 | 
			
		||||
 | 
			
		||||
                this._secondsLeft = this._totalSecondsToStayOpen - secondsElapsed;
 | 
			
		||||
                if (this._secondsLeft > 0) {
 | 
			
		||||
                    this._sync();
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                let dialogContent = DialogContent[this._type];
 | 
			
		||||
                let button = dialogContent.confirmButtons[dialogContent.confirmButtons.length - 1];
 | 
			
		||||
                this._confirm(button.signal);
 | 
			
		||||
 | 
			
		||||
                return false;
 | 
			
		||||
            }));
 | 
			
		||||
        Tweener.addTween(this,
 | 
			
		||||
                         { _secondsLeft: 0,
 | 
			
		||||
                           time: this._secondsLeft,
 | 
			
		||||
                           transition: 'linear',
 | 
			
		||||
                           onUpdate: Lang.bind(this, this._updateDescription),
 | 
			
		||||
                           onComplete: Lang.bind(this, function() {
 | 
			
		||||
                                           let dialogContent = DialogContent[this._type];
 | 
			
		||||
                                           let button = dialogContent.confirmButtons[dialogContent.confirmButtons.length - 1];
 | 
			
		||||
                                           this._confirm(button.signal);
 | 
			
		||||
                                       }),
 | 
			
		||||
                         });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _stopTimer: function() {
 | 
			
		||||
        if (this._timerId > 0) {
 | 
			
		||||
            Mainloop.source_remove(this._timerId);
 | 
			
		||||
            this._timerId = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Tweener.removeTweens(this);
 | 
			
		||||
        this._secondsLeft = 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _constructListItemForApp: function(inhibitor, app) {
 | 
			
		||||
        let actor = new St.BoxLayout({ style_class: 'end-session-dialog-app-list-item',
 | 
			
		||||
                                       can_focus: true });
 | 
			
		||||
        actor.add(app.create_icon_texture(_ITEM_ICON_SIZE));
 | 
			
		||||
 | 
			
		||||
        let textLayout = new St.BoxLayout({ vertical: true,
 | 
			
		||||
                                            y_expand: true,
 | 
			
		||||
                                            y_align: Clutter.ActorAlign.CENTER });
 | 
			
		||||
        actor.add(textLayout);
 | 
			
		||||
 | 
			
		||||
        let nameLabel = new St.Label({ text: app.get_name(),
 | 
			
		||||
                                       style_class: 'end-session-dialog-app-list-item-name' });
 | 
			
		||||
        textLayout.add(nameLabel);
 | 
			
		||||
        actor.label_actor = nameLabel;
 | 
			
		||||
 | 
			
		||||
        let [reason] = inhibitor.GetReasonSync();
 | 
			
		||||
        if (reason) {
 | 
			
		||||
            let reasonLabel = new St.Label({ text: reason,
 | 
			
		||||
                                             style_class: 'end-session-dialog-app-list-item-description' });
 | 
			
		||||
            textLayout.add(reasonLabel);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return actor;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onInhibitorLoaded: function(inhibitor) {
 | 
			
		||||
        if (this._applications.indexOf(inhibitor) < 0) {
 | 
			
		||||
        if (this._inhibitors.indexOf(inhibitor) < 0) {
 | 
			
		||||
            // Stale inhibitor
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
@@ -457,91 +436,28 @@ const EndSessionDialog = new Lang.Class({
 | 
			
		||||
        let app = findAppFromInhibitor(inhibitor);
 | 
			
		||||
 | 
			
		||||
        if (app) {
 | 
			
		||||
            let actor = this._constructListItemForApp(inhibitor, app);
 | 
			
		||||
            this._applicationList.add(actor);
 | 
			
		||||
            let [reason] = inhibitor.GetReasonSync();
 | 
			
		||||
            let item = new ListItem(app, reason);
 | 
			
		||||
            item.connect('activate',
 | 
			
		||||
                         Lang.bind(this, function() {
 | 
			
		||||
                             this.close(global.get_current_time());
 | 
			
		||||
                         }));
 | 
			
		||||
            this._applicationList.add(item.actor, { x_fill: true });
 | 
			
		||||
            this._stopTimer();
 | 
			
		||||
        } else {
 | 
			
		||||
            // inhibiting app is a service, not an application
 | 
			
		||||
            this._applications.splice(this._applications.indexOf(inhibitor), 1);
 | 
			
		||||
            this._inhibitors.splice(this._inhibitors.indexOf(inhibitor), 1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._sync();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _constructListItemForSession: function(session) {
 | 
			
		||||
        let avatar = new UserWidget.Avatar(session.user, { iconSize: _ITEM_ICON_SIZE });
 | 
			
		||||
        avatar.update();
 | 
			
		||||
 | 
			
		||||
        let userName = session.user.get_real_name() ? session.user.get_real_name() : session.username;
 | 
			
		||||
        let userLabelText;
 | 
			
		||||
 | 
			
		||||
        if (session.remote)
 | 
			
		||||
            /* Translators: Remote here refers to a remote session, like a ssh login */
 | 
			
		||||
            userLabelText = _("%s (remote)").format(userName);
 | 
			
		||||
        else if (session.type == "tty")
 | 
			
		||||
            /* Translators: Console here refers to a tty like a VT console */
 | 
			
		||||
            userLabelText = _("%s (console)").format(userName);
 | 
			
		||||
        else
 | 
			
		||||
            userLabelText = userName;
 | 
			
		||||
 | 
			
		||||
        let actor = new St.BoxLayout({ style_class: 'end-session-dialog-session-list-item',
 | 
			
		||||
                                       can_focus: true });
 | 
			
		||||
        actor.add(avatar.actor);
 | 
			
		||||
 | 
			
		||||
        let nameLabel = new St.Label({ text: userLabelText,
 | 
			
		||||
                                       style_class: 'end-session-dialog-session-list-item-name',
 | 
			
		||||
                                       y_expand: true,
 | 
			
		||||
                                       y_align: Clutter.ActorAlign.CENTER });
 | 
			
		||||
        actor.add(nameLabel);
 | 
			
		||||
        actor.label_actor = nameLabel;
 | 
			
		||||
 | 
			
		||||
        return actor;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _loadSessions: function() {
 | 
			
		||||
        this._loginManager.listSessions(Lang.bind(this, function(result) {
 | 
			
		||||
            let n = 0;
 | 
			
		||||
            for (let i = 0; i < result.length; i++) {
 | 
			
		||||
                let[id, uid, userName, seat, sessionPath] = result[i];
 | 
			
		||||
                let proxy = new LogindSession(Gio.DBus.system, 'org.freedesktop.login1', sessionPath);
 | 
			
		||||
 | 
			
		||||
                if (proxy.Class != 'user')
 | 
			
		||||
                    continue;
 | 
			
		||||
 | 
			
		||||
                if (proxy.State == 'closing')
 | 
			
		||||
                    continue;
 | 
			
		||||
 | 
			
		||||
                if (proxy.Id == GLib.getenv('XDG_SESSION_ID'))
 | 
			
		||||
                    continue;
 | 
			
		||||
 | 
			
		||||
                let session = { user: this._userManager.get_user(userName),
 | 
			
		||||
                                username: userName,
 | 
			
		||||
                                type: proxy.Type,
 | 
			
		||||
                                remote: proxy.Remote };
 | 
			
		||||
                this._sessions.push(session);
 | 
			
		||||
 | 
			
		||||
                let actor = this._constructListItemForSession(session);
 | 
			
		||||
                this._sessionList.add(actor);
 | 
			
		||||
 | 
			
		||||
                // limit the number of entries
 | 
			
		||||
                n++;
 | 
			
		||||
                if (n == MAX_USERS_IN_SESSION_DIALOG)
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this._sync();
 | 
			
		||||
        }));
 | 
			
		||||
        this._updateContent();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    OpenAsync: function(parameters, invocation) {
 | 
			
		||||
        let [type, timestamp, totalSecondsToStayOpen, inhibitorObjectPaths] = parameters;
 | 
			
		||||
        this._totalSecondsToStayOpen = totalSecondsToStayOpen;
 | 
			
		||||
        this._type = type;
 | 
			
		||||
 | 
			
		||||
        this._applications = [];
 | 
			
		||||
        this._inhibitors = [];
 | 
			
		||||
        this._applicationList.destroy_all_children();
 | 
			
		||||
 | 
			
		||||
        this._sessions = [];
 | 
			
		||||
        this._sessionList.destroy_all_children();
 | 
			
		||||
        this._type = type;
 | 
			
		||||
 | 
			
		||||
        if (!(this._type in DialogContent)) {
 | 
			
		||||
            invocation.return_dbus_error('org.gnome.Shell.ModalDialog.TypeError',
 | 
			
		||||
@@ -554,12 +470,9 @@ const EndSessionDialog = new Lang.Class({
 | 
			
		||||
                this._onInhibitorLoaded(proxy);
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
            this._applications.push(inhibitor);
 | 
			
		||||
            this._inhibitors.push(inhibitor);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (DialogContent[type].showOtherSessions)
 | 
			
		||||
            this._loadSessions();
 | 
			
		||||
 | 
			
		||||
        this._updateButtons();
 | 
			
		||||
 | 
			
		||||
        if (!this.open(timestamp)) {
 | 
			
		||||
@@ -568,17 +481,12 @@ const EndSessionDialog = new Lang.Class({
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._startTimer();
 | 
			
		||||
        this._sync();
 | 
			
		||||
        this._updateContent();
 | 
			
		||||
 | 
			
		||||
        let signalId = this.connect('opened',
 | 
			
		||||
                                    Lang.bind(this, function() {
 | 
			
		||||
                                        invocation.return_value(null);
 | 
			
		||||
                                        this.disconnect(signalId);
 | 
			
		||||
                                    }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    Close: function(parameters, invocation) {
 | 
			
		||||
        this.close();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -5,14 +5,11 @@ imports.gi.versions.Gio = '2.0';
 | 
			
		||||
imports.gi.versions.Gdk = '3.0';
 | 
			
		||||
imports.gi.versions.GdkPixbuf = '2.0';
 | 
			
		||||
imports.gi.versions.Gtk = '3.0';
 | 
			
		||||
imports.gi.versions.TelepathyGLib = '0.12';
 | 
			
		||||
imports.gi.versions.TelepathyLogger = '0.2';
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;;
 | 
			
		||||
const Gettext = imports.gettext;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Gtk = imports.gi.Gtk;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
@@ -42,22 +39,6 @@ function _patchContainerClass(containerClass) {
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _patchLayoutClass(layoutClass, styleProps) {
 | 
			
		||||
    if (styleProps)
 | 
			
		||||
        layoutClass.prototype.hookup_style = function(container) {
 | 
			
		||||
            container.connect('style-changed', Lang.bind(this, function() {
 | 
			
		||||
                let node = container.get_theme_node();
 | 
			
		||||
                for (let prop in styleProps)
 | 
			
		||||
                    this[prop] = node.get_length(styleProps[prop]);
 | 
			
		||||
            }));
 | 
			
		||||
        };
 | 
			
		||||
    layoutClass.prototype.child_set = function(actor, props) {
 | 
			
		||||
        let meta = this.get_child_meta(actor.get_parent(), actor);
 | 
			
		||||
        for (let prop in props)
 | 
			
		||||
            meta[prop] = props[prop];
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _makeLoggingFunc(func) {
 | 
			
		||||
    return function() {
 | 
			
		||||
        return func([].join.call(arguments, ', '));
 | 
			
		||||
@@ -79,12 +60,6 @@ function init() {
 | 
			
		||||
    _patchContainerClass(St.BoxLayout);
 | 
			
		||||
    _patchContainerClass(St.Table);
 | 
			
		||||
 | 
			
		||||
    _patchLayoutClass(Clutter.TableLayout, { row_spacing: 'spacing-rows',
 | 
			
		||||
                                             column_spacing: 'spacing-columns' });
 | 
			
		||||
    _patchLayoutClass(Clutter.GridLayout, { row_spacing: 'spacing-rows',
 | 
			
		||||
                                            column_spacing: 'spacing-columns' });
 | 
			
		||||
    _patchLayoutClass(Clutter.BoxLayout, { spacing: 'spacing' });
 | 
			
		||||
 | 
			
		||||
    Clutter.Actor.prototype.toString = function() {
 | 
			
		||||
        return St.describe_actor(this);
 | 
			
		||||
    };
 | 
			
		||||
 
 | 
			
		||||
@@ -215,7 +215,7 @@ const InstallExtensionDialog = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onCancelButtonPressed: function(button, event) {
 | 
			
		||||
        this.close();
 | 
			
		||||
        this.close(global.get_current_time());
 | 
			
		||||
        this._invocation.return_value(GLib.Variant.new('(s)', ['cancelled']));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -257,7 +257,7 @@ const InstallExtensionDialog = new Lang.Class({
 | 
			
		||||
            gotExtensionZipFile(session, message, uuid, dir, callback, errback);
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        this.close();
 | 
			
		||||
        this.close(global.get_current_time());
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -76,11 +76,7 @@ function disableExtension(uuid) {
 | 
			
		||||
        theme.unload_stylesheet(extension.stylesheet.get_path());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        extension.stateObj.disable();
 | 
			
		||||
    } catch(e) {
 | 
			
		||||
        logExtensionError(uuid, e);
 | 
			
		||||
    }
 | 
			
		||||
    extension.stateObj.disable();
 | 
			
		||||
 | 
			
		||||
    for (let i = 0; i < order.length; i++) {
 | 
			
		||||
        let uuid = order[i];
 | 
			
		||||
@@ -93,10 +89,8 @@ function disableExtension(uuid) {
 | 
			
		||||
 | 
			
		||||
    extensionOrder.splice(orderIdx, 1);
 | 
			
		||||
 | 
			
		||||
    if ( extension.state != ExtensionState.ERROR ) {
 | 
			
		||||
        extension.state = ExtensionState.DISABLED;
 | 
			
		||||
        _signals.emit('extension-state-changed', extension);
 | 
			
		||||
    }
 | 
			
		||||
    extension.state = ExtensionState.DISABLED;
 | 
			
		||||
    _signals.emit('extension-state-changed', extension);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function enableExtension(uuid) {
 | 
			
		||||
@@ -112,26 +106,17 @@ function enableExtension(uuid) {
 | 
			
		||||
 | 
			
		||||
    extensionOrder.push(uuid);
 | 
			
		||||
 | 
			
		||||
    let stylesheetNames = [global.session_mode + '.css', 'stylesheet.css'];
 | 
			
		||||
    for (let i = 0; i < stylesheetNames.length; i++) {
 | 
			
		||||
        let stylesheetFile = extension.dir.get_child(stylesheetNames[i]);
 | 
			
		||||
        if (stylesheetFile.query_exists(null)) {
 | 
			
		||||
            let theme = St.ThemeContext.get_for_stage(global.stage).get_theme();
 | 
			
		||||
            theme.load_stylesheet(stylesheetFile.get_path());
 | 
			
		||||
            extension.stylesheet = stylesheetFile;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    let stylesheetFile = extension.dir.get_child('stylesheet.css');
 | 
			
		||||
    if (stylesheetFile.query_exists(null)) {
 | 
			
		||||
        let theme = St.ThemeContext.get_for_stage(global.stage).get_theme();
 | 
			
		||||
        theme.load_stylesheet(stylesheetFile.get_path());
 | 
			
		||||
        extension.stylesheet = stylesheetFile;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        extension.stateObj.enable();
 | 
			
		||||
        extension.state = ExtensionState.ENABLED;
 | 
			
		||||
        _signals.emit('extension-state-changed', extension);
 | 
			
		||||
        return;
 | 
			
		||||
    } catch(e) {
 | 
			
		||||
        logExtensionError(uuid, e);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    extension.stateObj.enable();
 | 
			
		||||
 | 
			
		||||
    extension.state = ExtensionState.ENABLED;
 | 
			
		||||
    _signals.emit('extension-state-changed', extension);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function logExtensionError(uuid, error) {
 | 
			
		||||
@@ -161,8 +146,7 @@ function loadExtension(extension) {
 | 
			
		||||
    } else {
 | 
			
		||||
        let enabled = enabledExtensions.indexOf(extension.uuid) != -1;
 | 
			
		||||
        if (enabled) {
 | 
			
		||||
            if (!initExtension(extension.uuid))
 | 
			
		||||
                return;
 | 
			
		||||
            initExtension(extension.uuid);
 | 
			
		||||
            if (extension.state == ExtensionState.DISABLED)
 | 
			
		||||
                enableExtension(extension.uuid);
 | 
			
		||||
        } else {
 | 
			
		||||
@@ -217,12 +201,7 @@ function initExtension(uuid) {
 | 
			
		||||
    extensionModule = extension.imports.extension;
 | 
			
		||||
 | 
			
		||||
    if (extensionModule.init) {
 | 
			
		||||
        try {
 | 
			
		||||
            extensionState = extensionModule.init(extension);
 | 
			
		||||
        } catch(e) {
 | 
			
		||||
            logExtensionError(uuid, e);
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        extensionState = extensionModule.init(extension);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!extensionState)
 | 
			
		||||
@@ -231,7 +210,6 @@ function initExtension(uuid) {
 | 
			
		||||
 | 
			
		||||
    extension.state = ExtensionState.DISABLED;
 | 
			
		||||
    _signals.emit('extension-loaded', uuid);
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getEnabledExtensions() {
 | 
			
		||||
@@ -253,7 +231,11 @@ function onEnabledExtensionsChanged() {
 | 
			
		||||
    newEnabledExtensions.filter(function(uuid) {
 | 
			
		||||
        return enabledExtensions.indexOf(uuid) == -1;
 | 
			
		||||
    }).forEach(function(uuid) {
 | 
			
		||||
        try {
 | 
			
		||||
            enableExtension(uuid);
 | 
			
		||||
        } catch(e) {
 | 
			
		||||
            logExtensionError(uuid, e);
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // Find and disable all the newly disabled extensions: UUIDs found in the
 | 
			
		||||
@@ -261,7 +243,11 @@ function onEnabledExtensionsChanged() {
 | 
			
		||||
    enabledExtensions.filter(function(item) {
 | 
			
		||||
        return newEnabledExtensions.indexOf(item) == -1;
 | 
			
		||||
    }).forEach(function(uuid) {
 | 
			
		||||
        try {
 | 
			
		||||
            disableExtension(uuid);
 | 
			
		||||
        } catch(e) {
 | 
			
		||||
            logExtensionError(uuid, e);
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    enabledExtensions = newEnabledExtensions;
 | 
			
		||||
@@ -273,7 +259,11 @@ function _loadExtensions() {
 | 
			
		||||
 | 
			
		||||
    let finder = new ExtensionUtils.ExtensionFinder();
 | 
			
		||||
    finder.connect('extension-found', function(signals, extension) {
 | 
			
		||||
        loadExtension(extension);
 | 
			
		||||
        try {
 | 
			
		||||
            loadExtension(extension);
 | 
			
		||||
        } catch(e) {
 | 
			
		||||
            logExtensionError(extension.uuid, e);
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    finder.scanExtensions();
 | 
			
		||||
}
 | 
			
		||||
@@ -298,7 +288,7 @@ function disableAllExtensions() {
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    if (initted) {
 | 
			
		||||
        extensionOrder.slice().reverse().forEach(function(uuid) {
 | 
			
		||||
        enabledExtensions.forEach(function(uuid) {
 | 
			
		||||
            disableExtension(uuid);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										45
									
								
								js/ui/flashspot.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,45 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
 | 
			
		||||
const Lightbox = imports.ui.lightbox;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const Tweener = imports.ui.tweener;
 | 
			
		||||
 | 
			
		||||
const FLASHSPOT_ANIMATION_TIME = 0.25; // seconds
 | 
			
		||||
 | 
			
		||||
const Flashspot = new Lang.Class({
 | 
			
		||||
    Name: 'Flashspot',
 | 
			
		||||
    Extends: Lightbox.Lightbox,
 | 
			
		||||
 | 
			
		||||
    _init: function(area) {
 | 
			
		||||
        this.parent(Main.uiGroup, { inhibitEvents: true,
 | 
			
		||||
                                    width: area.width,
 | 
			
		||||
                                    height: area.height });
 | 
			
		||||
 | 
			
		||||
        this.actor.style_class = 'flashspot';
 | 
			
		||||
        this.actor.set_position(area.x, area.y);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    fire: function() {
 | 
			
		||||
        this.actor.opacity = 0;
 | 
			
		||||
        Tweener.addTween(this.actor,
 | 
			
		||||
                         { opacity: 255,
 | 
			
		||||
                           time: FLASHSPOT_ANIMATION_TIME,
 | 
			
		||||
                           transition: 'linear',
 | 
			
		||||
                           onComplete: Lang.bind(this, this._onFireShowComplete)
 | 
			
		||||
                         });
 | 
			
		||||
        this.actor.show();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onFireShowComplete: function() {
 | 
			
		||||
        Tweener.addTween(this.actor,
 | 
			
		||||
                         { opacity: 0,
 | 
			
		||||
                           time: FLASHSPOT_ANIMATION_TIME,
 | 
			
		||||
                           transition: 'linear',
 | 
			
		||||
                           onComplete: Lang.bind(this, function() {
 | 
			
		||||
                               this.destroy();
 | 
			
		||||
                           })
 | 
			
		||||
                         });
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
@@ -1,65 +0,0 @@
 | 
			
		||||
/** -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 2012 Inclusive Design Research Centre, OCAD University.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU Lesser General Public
 | 
			
		||||
 * License as published by the Free Software Foundation; either
 | 
			
		||||
 * version 2 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This library is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
			
		||||
 * Lesser General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Lesser General Public
 | 
			
		||||
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 * Author:
 | 
			
		||||
 *   Joseph Scheuhammer <clown@alum.mit.edu>
 | 
			
		||||
 * Contributor:
 | 
			
		||||
 *   Magdalen Berns <m.berns@sms.ed.ac.uk>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
const Atspi = imports.gi.Atspi;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
 | 
			
		||||
const CARETMOVED        = 'object:text-caret-moved';
 | 
			
		||||
const STATECHANGED      = 'object:state-changed';
 | 
			
		||||
 | 
			
		||||
const FocusCaretTracker = new Lang.Class({
 | 
			
		||||
    Name: 'FocusCaretTracker',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        Atspi.init();
 | 
			
		||||
        Atspi.set_timeout(250, 250);
 | 
			
		||||
        this._atspiListener = Atspi.EventListener.new(Lang.bind(this, this._onChanged));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onChanged: function(event) {
 | 
			
		||||
        if (event.type.indexOf(STATECHANGED) == 0)
 | 
			
		||||
            this.emit('focus-changed', event);
 | 
			
		||||
        else if (event.type == CARETMOVED)
 | 
			
		||||
            this.emit('caret-moved', event);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    registerFocusListener: function() {
 | 
			
		||||
        return this._atspiListener.register(STATECHANGED + ':focused') &&
 | 
			
		||||
               this._atspiListener.register(STATECHANGED + ':selected');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    registerCaretListener: function() {
 | 
			
		||||
        return this._atspiListener.register(CARETMOVED);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    deregisterFocusListener: function() {
 | 
			
		||||
        return this._atspiListener.deregister(STATECHANGED + ':focused') &&
 | 
			
		||||
               this._atspiListener.deregister(STATECHANGED + ':selected');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    deregisterCaretListener: function() {
 | 
			
		||||
        return this._atspiListener.deregister(CARETMOVED);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(FocusCaretTracker.prototype);
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Gtk = imports.gi.Gtk;
 | 
			
		||||
@@ -10,29 +10,15 @@ const St = imports.gi.St;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
 | 
			
		||||
let _capturedEventId = 0;
 | 
			
		||||
let _grabHelperStack = [];
 | 
			
		||||
function _onCapturedEvent(actor, event) {
 | 
			
		||||
    let grabHelper = _grabHelperStack[_grabHelperStack.length - 1];
 | 
			
		||||
    return grabHelper.onCapturedEvent(event);
 | 
			
		||||
}
 | 
			
		||||
function _navigateActor(actor) {
 | 
			
		||||
    if (!actor)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
function _pushGrabHelper(grabHelper) {
 | 
			
		||||
    _grabHelperStack.push(grabHelper);
 | 
			
		||||
 | 
			
		||||
    if (_capturedEventId == 0)
 | 
			
		||||
        _capturedEventId = global.stage.connect('captured-event', _onCapturedEvent);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _popGrabHelper(grabHelper) {
 | 
			
		||||
    let poppedHelper = _grabHelperStack.pop();
 | 
			
		||||
    if (poppedHelper != grabHelper)
 | 
			
		||||
        throw new Error("incorrect grab helper pop");
 | 
			
		||||
 | 
			
		||||
    if (_grabHelperStack.length == 0) {
 | 
			
		||||
        global.stage.disconnect(_capturedEventId);
 | 
			
		||||
        _capturedEventId = 0;
 | 
			
		||||
    }
 | 
			
		||||
    let needsGrab = true;
 | 
			
		||||
    if (actor instanceof St.Widget)
 | 
			
		||||
        needsGrab = !actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
 | 
			
		||||
    if (needsGrab)
 | 
			
		||||
        actor.grab_key_focus();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GrabHelper:
 | 
			
		||||
@@ -56,9 +42,13 @@ const GrabHelper = new Lang.Class({
 | 
			
		||||
        this._grabStack = [];
 | 
			
		||||
 | 
			
		||||
        this._actors = [];
 | 
			
		||||
        this._capturedEventId = 0;
 | 
			
		||||
        this._keyFocusNotifyId = 0;
 | 
			
		||||
        this._focusWindowChangedId = 0;
 | 
			
		||||
        this._ignoreRelease = false;
 | 
			
		||||
 | 
			
		||||
        this._modalCount = 0;
 | 
			
		||||
        this._grabFocusCount = 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // addActor:
 | 
			
		||||
@@ -87,7 +77,7 @@ const GrabHelper = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _isWithinGrabbedActor: function(actor) {
 | 
			
		||||
        let currentActor = this.currentGrab.actor;
 | 
			
		||||
       let currentActor = this.currentGrab.actor;
 | 
			
		||||
        while (actor) {
 | 
			
		||||
            if (this._actors.indexOf(actor) != -1)
 | 
			
		||||
                return true;
 | 
			
		||||
@@ -138,36 +128,38 @@ const GrabHelper = new Lang.Class({
 | 
			
		||||
    // grab:
 | 
			
		||||
    // @params: A bunch of parameters, see below
 | 
			
		||||
    //
 | 
			
		||||
    // The general effect of a "grab" is to ensure that the passed in actor
 | 
			
		||||
    // and all actors inside the grab get exclusive control of the mouse and
 | 
			
		||||
    // keyboard, with the grab automatically being dropped if the user tries
 | 
			
		||||
    // to dismiss it. The actor is passed in through @params.actor.
 | 
			
		||||
    // Grabs the mouse and keyboard, according to the GrabHelper's
 | 
			
		||||
    // parameters. If @newFocus is not %null, then the keyboard focus
 | 
			
		||||
    // is moved to the first #StWidget:can-focus widget inside it.
 | 
			
		||||
    //
 | 
			
		||||
    // grab() can be called multiple times, with the scope of the grab being
 | 
			
		||||
    // changed to a different actor every time. A nested grab does not have
 | 
			
		||||
    // to have its grabbed actor inside the parent grab actors.
 | 
			
		||||
    // The grab will automatically be dropped if:
 | 
			
		||||
    //   - The user clicks outside the grabbed actors
 | 
			
		||||
    //   - The user types Escape
 | 
			
		||||
    //   - The keyboard focus is moved outside the grabbed actors
 | 
			
		||||
    //   - A window is focused
 | 
			
		||||
    //
 | 
			
		||||
    // Grabs can be automatically dropped if the user tries to dismiss it
 | 
			
		||||
    // in one of two ways: the user clicking outside the currently grabbed
 | 
			
		||||
    // actor, or the user typing the Escape key.
 | 
			
		||||
    // If @params.actor is not null, then it will be focused as the
 | 
			
		||||
    // new actor. If you attempt to grab an already focused actor, the
 | 
			
		||||
    // request to be focused will be ignored. The actor will not be
 | 
			
		||||
    // added to the grab stack, so do not call a paired ungrab().
 | 
			
		||||
    //
 | 
			
		||||
    // If the user clicks outside the grabbed actors, and the clicked on
 | 
			
		||||
    // actor is part of a previous grab in the stack, grabs will be popped
 | 
			
		||||
    // until that grab is active. However, the click event will not be
 | 
			
		||||
    // replayed to the actor.
 | 
			
		||||
    // If @params contains { modal: true }, then grab() will push a modal
 | 
			
		||||
    // on the owner of the GrabHelper. As long as there is at least one
 | 
			
		||||
    // { modal: true } actor on the grab stack, the grab will be kept.
 | 
			
		||||
    // When the last { modal: true } actor is ungrabbed, then the modal
 | 
			
		||||
    // will be dropped. A modal grab can fail if there is already a grab
 | 
			
		||||
    // in effect from aother application; in this case the function returns
 | 
			
		||||
    // false and nothing happens. Non-modal grabs can never fail.
 | 
			
		||||
    //
 | 
			
		||||
    // If the user types the Escape key, one grab from the grab stack will
 | 
			
		||||
    // be popped.
 | 
			
		||||
    //
 | 
			
		||||
    // When a grab is popped by user interacting as described above, if you
 | 
			
		||||
    // pass a callback as @params.onUngrab, it will be called with %true.
 | 
			
		||||
    //
 | 
			
		||||
    // If @params.focus is not null, we'll set the key focus directly
 | 
			
		||||
    // to that actor instead of navigating in @params.actor. This is for
 | 
			
		||||
    // use cases like menus, where we want to grab the menu actor, but keep
 | 
			
		||||
    // focus on the clicked on menu item.
 | 
			
		||||
    // If @params contains { grabFocus: true }, then if you call grab()
 | 
			
		||||
    // while the shell is outside the overview, it will set the stage
 | 
			
		||||
    // input mode to %Shell.StageInputMode.FOCUSED, and ungrab() will
 | 
			
		||||
    // revert it back, and re-focus the previously-focused window (if
 | 
			
		||||
    // another window hasn't been explicitly focused before then).
 | 
			
		||||
    grab: function(params) {
 | 
			
		||||
        params = Params.parse(params, { actor: null,
 | 
			
		||||
                                        modal: false,
 | 
			
		||||
                                        grabFocus: false,
 | 
			
		||||
                                        focus: null,
 | 
			
		||||
                                        onUngrab: null });
 | 
			
		||||
 | 
			
		||||
@@ -180,18 +172,18 @@ const GrabHelper = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        params.savedFocus = focus;
 | 
			
		||||
 | 
			
		||||
        if (!this._takeModalGrab())
 | 
			
		||||
        if (params.modal && !this._takeModalGrab())
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        this._grabStack.push(params);
 | 
			
		||||
        if (params.grabFocus && !this._takeFocusGrab(hadFocus))
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        if (params.focus) {
 | 
			
		||||
        if (params.focus)
 | 
			
		||||
            params.focus.grab_key_focus();
 | 
			
		||||
        } else if (newFocus && hadFocus) {
 | 
			
		||||
            if (!newFocus.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false))
 | 
			
		||||
                newFocus.grab_key_focus();
 | 
			
		||||
        }
 | 
			
		||||
        else if (hadFocus || params.grabFocus)
 | 
			
		||||
            _navigateActor(newFocus);
 | 
			
		||||
 | 
			
		||||
        this._grabStack.push(params);
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -201,7 +193,7 @@ const GrabHelper = new Lang.Class({
 | 
			
		||||
            if (!Main.pushModal(this._owner, this._modalParams))
 | 
			
		||||
                return false;
 | 
			
		||||
 | 
			
		||||
            _pushGrabHelper(this);
 | 
			
		||||
            this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._modalCount++;
 | 
			
		||||
@@ -213,14 +205,63 @@ const GrabHelper = new Lang.Class({
 | 
			
		||||
        if (this._modalCount > 0)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        _popGrabHelper(this);
 | 
			
		||||
 | 
			
		||||
        this._ignoreRelease = false;
 | 
			
		||||
        if (this._capturedEventId > 0) {
 | 
			
		||||
            global.stage.disconnect(this._capturedEventId);
 | 
			
		||||
            this._capturedEventId = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Main.popModal(this._owner);
 | 
			
		||||
        global.sync_pointer();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _takeFocusGrab: function(hadFocus) {
 | 
			
		||||
        let firstGrab = (this._grabFocusCount == 0);
 | 
			
		||||
        if (firstGrab) {
 | 
			
		||||
            let metaDisplay = global.screen.get_display();
 | 
			
		||||
 | 
			
		||||
            this._grabbedFromKeynav = hadFocus;
 | 
			
		||||
            this._preGrabInputMode = global.stage_input_mode;
 | 
			
		||||
 | 
			
		||||
            if (this._preGrabInputMode == Shell.StageInputMode.NONREACTIVE ||
 | 
			
		||||
                this._preGrabInputMode == Shell.StageInputMode.NORMAL) {
 | 
			
		||||
                global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this._keyFocusNotifyId = global.stage.connect('notify::key-focus', Lang.bind(this, this._onKeyFocusChanged));
 | 
			
		||||
            this._focusWindowChangedId = metaDisplay.connect('notify::focus-window', Lang.bind(this, this._focusWindowChanged));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._grabFocusCount++;
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _releaseFocusGrab: function() {
 | 
			
		||||
        this._grabFocusCount--;
 | 
			
		||||
        if (this._grabFocusCount > 0)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (this._keyFocusNotifyId > 0) {
 | 
			
		||||
            global.stage.disconnect(this._keyFocusNotifyId);
 | 
			
		||||
            this._keyFocusNotifyId = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!this._focusWindowChanged > 0) {
 | 
			
		||||
            let metaDisplay = global.screen.get_display();
 | 
			
		||||
            metaDisplay.disconnect(this._focusWindowChangedId);
 | 
			
		||||
            this._focusWindowChangedId = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let prePopInputMode = global.stage_input_mode;
 | 
			
		||||
 | 
			
		||||
        if (this._grabbedFromKeynav) {
 | 
			
		||||
            if (this._preGrabInputMode == Shell.StageInputMode.FOCUSED &&
 | 
			
		||||
                prePopInputMode != Shell.StageInputMode.FULLSCREEN)
 | 
			
		||||
                global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        global.screen.focus_default_window(global.get_current_time());
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // ignoreRelease:
 | 
			
		||||
    //
 | 
			
		||||
    // Make sure that the next button release event evaluated by the
 | 
			
		||||
@@ -234,14 +275,10 @@ const GrabHelper = new Lang.Class({
 | 
			
		||||
    // ungrab:
 | 
			
		||||
    // @params: The parameters for the grab; see below.
 | 
			
		||||
    //
 | 
			
		||||
    // Pops @params.actor from the grab stack, potentially dropping
 | 
			
		||||
    // the grab. If the actor is not on the grab stack, this call is
 | 
			
		||||
    // ignored with no ill effects.
 | 
			
		||||
    // Pops an actor from the grab stack, potentially dropping the grab.
 | 
			
		||||
    //
 | 
			
		||||
    // If the actor is not at the top of the grab stack, grabs are
 | 
			
		||||
    // popped until the grabbed actor is at the top of the grab stack.
 | 
			
		||||
    // The onUngrab callback for every grab is called for every popped
 | 
			
		||||
    // grab with the parameter %false.
 | 
			
		||||
    // If the actor that was popped from the grab stack was not the actor
 | 
			
		||||
    // That was passed in, this call is ignored.
 | 
			
		||||
    ungrab: function(params) {
 | 
			
		||||
        params = Params.parse(params, { actor: this.currentGrab.actor,
 | 
			
		||||
                                        isUser: false });
 | 
			
		||||
@@ -264,25 +301,21 @@ const GrabHelper = new Lang.Class({
 | 
			
		||||
            if (poppedGrab.onUngrab)
 | 
			
		||||
                poppedGrab.onUngrab(params.isUser);
 | 
			
		||||
 | 
			
		||||
            this._releaseModalGrab();
 | 
			
		||||
            if (poppedGrab.modal)
 | 
			
		||||
                this._releaseModalGrab();
 | 
			
		||||
 | 
			
		||||
            if (poppedGrab.grabFocus)
 | 
			
		||||
                this._releaseFocusGrab();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (hadFocus) {
 | 
			
		||||
            let poppedGrab = poppedGrabs[0];
 | 
			
		||||
            if (poppedGrab.savedFocus)
 | 
			
		||||
                poppedGrab.savedFocus.grab_key_focus();
 | 
			
		||||
            _navigateActor(poppedGrab.savedFocus);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    onCapturedEvent: function(event) {
 | 
			
		||||
    _onCapturedEvent: function(actor, event) {
 | 
			
		||||
        let type = event.type();
 | 
			
		||||
 | 
			
		||||
        if (type == Clutter.EventType.KEY_PRESS &&
 | 
			
		||||
            event.get_key_symbol() == Clutter.KEY_Escape) {
 | 
			
		||||
            this.ungrab({ isUser: true });
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let press = type == Clutter.EventType.BUTTON_PRESS;
 | 
			
		||||
        let release = type == Clutter.EventType.BUTTON_RELEASE;
 | 
			
		||||
        let button = press || release;
 | 
			
		||||
@@ -292,6 +325,15 @@ const GrabHelper = new Lang.Class({
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!button && this._modalCount == 0)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        if (type == Clutter.EventType.KEY_PRESS &&
 | 
			
		||||
            event.get_key_symbol() == Clutter.KEY_Escape) {
 | 
			
		||||
            this.ungrab({ isUser: true });
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this._isWithinGrabbedActor(event.get_source()))
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
@@ -305,9 +347,20 @@ const GrabHelper = new Lang.Class({
 | 
			
		||||
                this._ignoreRelease = true;
 | 
			
		||||
            let i = this._actorInGrabStack(event.get_source()) + 1;
 | 
			
		||||
            this.ungrab({ actor: this._grabStack[i].actor, isUser: true });
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
        return this._modalCount > 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onKeyFocusChanged: function() {
 | 
			
		||||
        let focus = global.stage.key_focus;
 | 
			
		||||
        if (!focus || !this._isWithinGrabbedActor(focus))
 | 
			
		||||
            this.ungrab({ isUser: true });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _focusWindowChanged: function() {
 | 
			
		||||
        let metaDisplay = global.screen.get_display();
 | 
			
		||||
        if (metaDisplay.focus_window != null)
 | 
			
		||||
            this.ungrab({ isUser: true });
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -3,149 +3,107 @@
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const IBus = imports.gi.IBus;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
const BoxPointer = imports.ui.boxpointer;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const PopupMenu = imports.ui.popupMenu;
 | 
			
		||||
 | 
			
		||||
const MAX_CANDIDATES_PER_PAGE = 16;
 | 
			
		||||
 | 
			
		||||
const CandidateArea = new Lang.Class({
 | 
			
		||||
    Name: 'CandidateArea',
 | 
			
		||||
    Extends: PopupMenu.PopupBaseMenuItem,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.actor = new St.BoxLayout({ vertical: true,
 | 
			
		||||
                                        visible: false });
 | 
			
		||||
        this._candidateBoxes = [];
 | 
			
		||||
        this.parent({ reactive: false });
 | 
			
		||||
 | 
			
		||||
        // St.Table exhibits some sizing problems so let's go with a
 | 
			
		||||
        // clutter layout manager for now.
 | 
			
		||||
        this._table = new Clutter.Actor();
 | 
			
		||||
        this.addActor(this._table);
 | 
			
		||||
 | 
			
		||||
        this._tableLayout = new Clutter.TableLayout();
 | 
			
		||||
        this._table.set_layout_manager(this._tableLayout);
 | 
			
		||||
 | 
			
		||||
        this._indexLabels = [];
 | 
			
		||||
        this._candidateLabels = [];
 | 
			
		||||
        for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
 | 
			
		||||
            let box = new St.BoxLayout({ style_class: 'candidate-box',
 | 
			
		||||
                                         reactive: true,
 | 
			
		||||
                                         track_hover: true });
 | 
			
		||||
            box._indexLabel = new St.Label({ style_class: 'candidate-index' });
 | 
			
		||||
            box._candidateLabel = new St.Label({ style_class: 'candidate-label' });
 | 
			
		||||
            box.add(box._indexLabel, { y_fill: false });
 | 
			
		||||
            box.add(box._candidateLabel, { y_fill: false });
 | 
			
		||||
            this._candidateBoxes.push(box);
 | 
			
		||||
            this.actor.add(box);
 | 
			
		||||
 | 
			
		||||
            let j = i;
 | 
			
		||||
            box.connect('button-release-event', Lang.bind(this, function(actor, event) {
 | 
			
		||||
                this.emit('candidate-clicked', j, event.get_button(), event.get_state());
 | 
			
		||||
            }));
 | 
			
		||||
            this._indexLabels.push(new St.Label({ style_class: 'candidate-index' }));
 | 
			
		||||
            this._candidateLabels.push(new St.Label({ style_class: 'candidate-label' }));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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' });
 | 
			
		||||
        this._previousButton.child = new St.Icon({ style_class: 'candidate-page-button-icon' });
 | 
			
		||||
        this._buttonBox.add(this._previousButton, { expand: true });
 | 
			
		||||
 | 
			
		||||
        this._nextButton = new St.Button({ style_class: 'candidate-page-button candidate-page-button-next' });
 | 
			
		||||
        this._nextButton.child = new St.Icon({ style_class: 'candidate-page-button-icon' });
 | 
			
		||||
        this._buttonBox.add(this._nextButton, { expand: true });
 | 
			
		||||
 | 
			
		||||
        this.actor.add(this._buttonBox);
 | 
			
		||||
 | 
			
		||||
        this._previousButton.connect('clicked', Lang.bind(this, function() {
 | 
			
		||||
            this.emit('previous-page');
 | 
			
		||||
        }));
 | 
			
		||||
        this._nextButton.connect('clicked', Lang.bind(this, function() {
 | 
			
		||||
            this.emit('next-page');
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        this._orientation = -1;
 | 
			
		||||
        this._cursorPosition = 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setOrientation: function(orientation) {
 | 
			
		||||
    _setOrientation: function(orientation) {
 | 
			
		||||
        if (this._orientation == orientation)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._orientation = orientation;
 | 
			
		||||
 | 
			
		||||
        if (this._orientation == IBus.Orientation.HORIZONTAL) {
 | 
			
		||||
            this.actor.vertical = false;
 | 
			
		||||
            this.actor.remove_style_class_name('vertical');
 | 
			
		||||
            this.actor.add_style_class_name('horizontal');
 | 
			
		||||
            this._previousButton.child.icon_name = 'go-previous-symbolic';
 | 
			
		||||
            this._nextButton.child.icon_name = 'go-next-symbolic';
 | 
			
		||||
        } else {                // VERTICAL || SYSTEM
 | 
			
		||||
            this.actor.vertical = true;
 | 
			
		||||
            this.actor.add_style_class_name('vertical');
 | 
			
		||||
            this.actor.remove_style_class_name('horizontal');
 | 
			
		||||
            this._previousButton.child.icon_name = 'go-up-symbolic';
 | 
			
		||||
            this._nextButton.child.icon_name = 'go-down-symbolic';
 | 
			
		||||
        }
 | 
			
		||||
        this._table.remove_all_children();
 | 
			
		||||
 | 
			
		||||
        if (this._orientation == IBus.Orientation.HORIZONTAL)
 | 
			
		||||
            for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
 | 
			
		||||
                this._tableLayout.pack(this._indexLabels[i], i*2, 0);
 | 
			
		||||
                this._tableLayout.pack(this._candidateLabels[i], i*2 + 1, 0);
 | 
			
		||||
            }
 | 
			
		||||
        else                    // VERTICAL || SYSTEM
 | 
			
		||||
            for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
 | 
			
		||||
                this._tableLayout.pack(this._indexLabels[i], 0, i);
 | 
			
		||||
                this._tableLayout.pack(this._candidateLabels[i], 1, i);
 | 
			
		||||
            }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setCandidates: function(indexes, candidates, cursorPosition, cursorVisible) {
 | 
			
		||||
    setCandidates: function(indexes, candidates, orientation, cursorPosition, cursorVisible) {
 | 
			
		||||
        this._setOrientation(orientation);
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
 | 
			
		||||
            let visible = i < candidates.length;
 | 
			
		||||
            let box = this._candidateBoxes[i];
 | 
			
		||||
            box.visible = visible;
 | 
			
		||||
            this._indexLabels[i].visible = visible;
 | 
			
		||||
            this._candidateLabels[i].visible = visible;
 | 
			
		||||
 | 
			
		||||
            if (!visible)
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            box._indexLabel.text = ((indexes && indexes[i]) ? indexes[i] : '%x'.format(i + 1));
 | 
			
		||||
            box._candidateLabel.text = candidates[i];
 | 
			
		||||
            this._indexLabels[i].text = ((indexes && indexes[i]) ? indexes[i] : '%x.'.format(i + 1));
 | 
			
		||||
            this._candidateLabels[i].text = candidates[i];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._candidateBoxes[this._cursorPosition].remove_style_pseudo_class('selected');
 | 
			
		||||
        this._candidateLabels[this._cursorPosition].remove_style_pseudo_class('selected');
 | 
			
		||||
        this._cursorPosition = cursorPosition;
 | 
			
		||||
        if (cursorVisible)
 | 
			
		||||
            this._candidateBoxes[cursorPosition].add_style_pseudo_class('selected');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    updateButtons: function(wrapsAround, page, nPages) {
 | 
			
		||||
        if (nPages < 2) {
 | 
			
		||||
            this._buttonBox.hide();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        this._buttonBox.show();
 | 
			
		||||
        this._previousButton.reactive = wrapsAround || page > 0;
 | 
			
		||||
        this._nextButton.reactive = wrapsAround || page < nPages - 1;
 | 
			
		||||
            this._candidateLabels[cursorPosition].add_style_pseudo_class('selected');
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(CandidateArea.prototype);
 | 
			
		||||
 | 
			
		||||
const CandidatePopup = new Lang.Class({
 | 
			
		||||
    Name: 'CandidatePopup',
 | 
			
		||||
    Extends: PopupMenu.PopupMenu,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._cursor = new St.Bin({ opacity: 0 });
 | 
			
		||||
        Main.uiGroup.add_actor(this._cursor);
 | 
			
		||||
 | 
			
		||||
        this._boxPointer = new BoxPointer.BoxPointer(St.Side.TOP);
 | 
			
		||||
        this._boxPointer.actor.visible = false;
 | 
			
		||||
        this._boxPointer.actor.style_class = 'candidate-popup-boxpointer';
 | 
			
		||||
        Main.layoutManager.addChrome(this._boxPointer.actor);
 | 
			
		||||
        this.parent(this._cursor, 0, St.Side.TOP);
 | 
			
		||||
        this.actor.hide();
 | 
			
		||||
        Main.uiGroup.add_actor(this.actor);
 | 
			
		||||
 | 
			
		||||
        let box = new St.BoxLayout({ style_class: 'candidate-popup-content',
 | 
			
		||||
                                     vertical: true });
 | 
			
		||||
        this._boxPointer.bin.set_child(box);
 | 
			
		||||
        this._preeditTextItem = new PopupMenu.PopupMenuItem('', { reactive: false });
 | 
			
		||||
        this._preeditTextItem.actor.hide();
 | 
			
		||||
        this.addMenuItem(this._preeditTextItem);
 | 
			
		||||
 | 
			
		||||
        this._preeditText = new St.Label({ style_class: 'candidate-popup-text',
 | 
			
		||||
                                           visible: false });
 | 
			
		||||
        box.add(this._preeditText);
 | 
			
		||||
        this._auxTextItem = new PopupMenu.PopupMenuItem('', { reactive: false });
 | 
			
		||||
        this._auxTextItem.actor.hide();
 | 
			
		||||
        this.addMenuItem(this._auxTextItem);
 | 
			
		||||
 | 
			
		||||
        this._auxText = new St.Label({ style_class: 'candidate-popup-text',
 | 
			
		||||
                                       visible: false });
 | 
			
		||||
        box.add(this._auxText);
 | 
			
		||||
        this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
 | 
			
		||||
 | 
			
		||||
        this._candidateArea = new CandidateArea();
 | 
			
		||||
        box.add(this._candidateArea.actor);
 | 
			
		||||
 | 
			
		||||
        this._candidateArea.connect('previous-page', Lang.bind(this, function() {
 | 
			
		||||
            this._panelService.page_up();
 | 
			
		||||
        }));
 | 
			
		||||
        this._candidateArea.connect('next-page', Lang.bind(this, function() {
 | 
			
		||||
            this._panelService.page_down();
 | 
			
		||||
        }));
 | 
			
		||||
        this._candidateArea.connect('candidate-clicked', Lang.bind(this, function(ca, index, button, state) {
 | 
			
		||||
            this._panelService.candidate_clicked(index, button, state);
 | 
			
		||||
        }));
 | 
			
		||||
        this._lookupTableItem = new CandidateArea();
 | 
			
		||||
        this._lookupTableItem.actor.hide();
 | 
			
		||||
        this.addMenuItem(this._lookupTableItem);
 | 
			
		||||
 | 
			
		||||
        this._panelService = null;
 | 
			
		||||
    },
 | 
			
		||||
@@ -159,61 +117,66 @@ const CandidatePopup = new Lang.Class({
 | 
			
		||||
                             Lang.bind(this, function(ps, x, y, w, h) {
 | 
			
		||||
                                 this._cursor.set_position(x, y);
 | 
			
		||||
                                 this._cursor.set_size(w, h);
 | 
			
		||||
                                 if (this._boxPointer.actor.visible)
 | 
			
		||||
                                     this._boxPointer.setPosition(this._cursor, 0);
 | 
			
		||||
                             }));
 | 
			
		||||
        panelService.connect('update-preedit-text',
 | 
			
		||||
                             Lang.bind(this, function(ps, text, cursorPosition, visible) {
 | 
			
		||||
                                 this._preeditText.visible = visible;
 | 
			
		||||
                                 if (visible)
 | 
			
		||||
                                     this._preeditTextItem.actor.show();
 | 
			
		||||
                                 else
 | 
			
		||||
                                     this._preeditTextItem.actor.hide();
 | 
			
		||||
                                 this._updateVisibility();
 | 
			
		||||
 | 
			
		||||
                                 this._preeditText.text = text.get_text();
 | 
			
		||||
                                 this._preeditTextItem.actor.label_actor.text = text.get_text();
 | 
			
		||||
 | 
			
		||||
                                 let attrs = text.get_attributes();
 | 
			
		||||
                                 if (attrs)
 | 
			
		||||
                                     this._setTextAttributes(this._preeditText.clutter_text,
 | 
			
		||||
                                     this._setTextAttributes(this._preeditTextItem.actor.label_actor.clutter_text,
 | 
			
		||||
                                                             attrs);
 | 
			
		||||
                             }));
 | 
			
		||||
        panelService.connect('show-preedit-text',
 | 
			
		||||
                             Lang.bind(this, function(ps) {
 | 
			
		||||
                                 this._preeditText.show();
 | 
			
		||||
                                 this._preeditTextItem.actor.show();
 | 
			
		||||
                                 this._updateVisibility();
 | 
			
		||||
                             }));
 | 
			
		||||
        panelService.connect('hide-preedit-text',
 | 
			
		||||
                             Lang.bind(this, function(ps) {
 | 
			
		||||
                                 this._preeditText.hide();
 | 
			
		||||
                                 this._preeditTextItem.actor.hide();
 | 
			
		||||
                                 this._updateVisibility();
 | 
			
		||||
                             }));
 | 
			
		||||
        panelService.connect('update-auxiliary-text',
 | 
			
		||||
                             Lang.bind(this, function(ps, text, visible) {
 | 
			
		||||
                                 this._auxText.visible = visible;
 | 
			
		||||
                                 if (visible)
 | 
			
		||||
                                     this._auxTextItem.actor.show();
 | 
			
		||||
                                 else
 | 
			
		||||
                                     this._auxTextItem.actor.hide();
 | 
			
		||||
                                 this._updateVisibility();
 | 
			
		||||
 | 
			
		||||
                                 this._auxText.text = text.get_text();
 | 
			
		||||
                                 this._auxTextItem.actor.label_actor.text = text.get_text();
 | 
			
		||||
                             }));
 | 
			
		||||
        panelService.connect('show-auxiliary-text',
 | 
			
		||||
                             Lang.bind(this, function(ps) {
 | 
			
		||||
                                 this._auxText.show();
 | 
			
		||||
                                 this._auxTextItem.actor.show();
 | 
			
		||||
                                 this._updateVisibility();
 | 
			
		||||
                             }));
 | 
			
		||||
        panelService.connect('hide-auxiliary-text',
 | 
			
		||||
                             Lang.bind(this, function(ps) {
 | 
			
		||||
                                 this._auxText.hide();
 | 
			
		||||
                                 this._auxTextItem.actor.hide();
 | 
			
		||||
                                 this._updateVisibility();
 | 
			
		||||
                             }));
 | 
			
		||||
        panelService.connect('update-lookup-table',
 | 
			
		||||
                             Lang.bind(this, function(ps, lookupTable, visible) {
 | 
			
		||||
                                 this._candidateArea.actor.visible = visible;
 | 
			
		||||
                                 if (visible)
 | 
			
		||||
                                     this._lookupTableItem.actor.show();
 | 
			
		||||
                                 else
 | 
			
		||||
                                     this._lookupTableItem.actor.hide();
 | 
			
		||||
                                 this._updateVisibility();
 | 
			
		||||
 | 
			
		||||
                                 let nCandidates = lookupTable.get_number_of_candidates();
 | 
			
		||||
                                 let cursorPos = lookupTable.get_cursor_pos();
 | 
			
		||||
                                 let pageSize = lookupTable.get_page_size();
 | 
			
		||||
                                 let nPages = Math.ceil(nCandidates / pageSize);
 | 
			
		||||
                                 let page = ((cursorPos == 0) ? 0 : Math.floor(cursorPos / pageSize));
 | 
			
		||||
                                 let startIndex = page * pageSize;
 | 
			
		||||
                                 let endIndex = Math.min((page + 1) * pageSize, nCandidates);
 | 
			
		||||
 | 
			
		||||
                                 let endIndex = Math.min((page + 1) * pageSize,
 | 
			
		||||
                                                         lookupTable.get_number_of_candidates());
 | 
			
		||||
                                 let indexes = [];
 | 
			
		||||
                                 let indexLabel;
 | 
			
		||||
                                 for (let i = 0; indexLabel = lookupTable.get_label(i); ++i)
 | 
			
		||||
@@ -223,41 +186,37 @@ const CandidatePopup = new Lang.Class({
 | 
			
		||||
                                 for (let i = startIndex; i < endIndex; ++i)
 | 
			
		||||
                                     candidates.push(lookupTable.get_candidate(i).get_text());
 | 
			
		||||
 | 
			
		||||
                                 this._candidateArea.setCandidates(indexes,
 | 
			
		||||
                                                                   candidates,
 | 
			
		||||
                                                                   cursorPos % pageSize,
 | 
			
		||||
                                                                   lookupTable.is_cursor_visible());
 | 
			
		||||
                                 this._candidateArea.setOrientation(lookupTable.get_orientation());
 | 
			
		||||
                                 this._candidateArea.updateButtons(lookupTable.is_round(), page, nPages);
 | 
			
		||||
                                 this._lookupTableItem.setCandidates(indexes,
 | 
			
		||||
                                                                     candidates,
 | 
			
		||||
                                                                     lookupTable.get_orientation(),
 | 
			
		||||
                                                                     cursorPos % pageSize,
 | 
			
		||||
                                                                     lookupTable.is_cursor_visible());
 | 
			
		||||
                             }));
 | 
			
		||||
        panelService.connect('show-lookup-table',
 | 
			
		||||
                             Lang.bind(this, function(ps) {
 | 
			
		||||
                                 this._candidateArea.actor.show();
 | 
			
		||||
                                 this._lookupTableItem.actor.show();
 | 
			
		||||
                                 this._updateVisibility();
 | 
			
		||||
                             }));
 | 
			
		||||
        panelService.connect('hide-lookup-table',
 | 
			
		||||
                             Lang.bind(this, function(ps) {
 | 
			
		||||
                                 this._candidateArea.actor.hide();
 | 
			
		||||
                                 this._lookupTableItem.actor.hide();
 | 
			
		||||
                                 this._updateVisibility();
 | 
			
		||||
                             }));
 | 
			
		||||
        panelService.connect('focus-out',
 | 
			
		||||
                             Lang.bind(this, function(ps) {
 | 
			
		||||
                                 this._boxPointer.hide(BoxPointer.PopupAnimation.NONE);
 | 
			
		||||
                                 this.close(BoxPointer.PopupAnimation.NONE);
 | 
			
		||||
                             }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateVisibility: function() {
 | 
			
		||||
        let isVisible = (this._preeditText.visible ||
 | 
			
		||||
                         this._auxText.visible ||
 | 
			
		||||
                         this._candidateArea.actor.visible);
 | 
			
		||||
        let isVisible = (this._preeditTextItem.actor.visible ||
 | 
			
		||||
                         this._auxTextItem.actor.visible ||
 | 
			
		||||
                         this._lookupTableItem.actor.visible);
 | 
			
		||||
 | 
			
		||||
        if (isVisible) {
 | 
			
		||||
            this._boxPointer.setPosition(this._cursor, 0);
 | 
			
		||||
            this._boxPointer.show(BoxPointer.PopupAnimation.NONE);
 | 
			
		||||
            this._boxPointer.actor.raise_top();
 | 
			
		||||
        } else {
 | 
			
		||||
            this._boxPointer.hide(BoxPointer.PopupAnimation.NONE);
 | 
			
		||||
        }
 | 
			
		||||
        if (isVisible)
 | 
			
		||||
            this.open(BoxPointer.PopupAnimation.NONE);
 | 
			
		||||
        else
 | 
			
		||||
            this.close(BoxPointer.PopupAnimation.NONE);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _setTextAttributes: function(clutterText, ibusAttrList) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,20 +1,14 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Gtk = imports.gi.Gtk;
 | 
			
		||||
const Meta = imports.gi.Meta;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
const Tweener = imports.ui.tweener;
 | 
			
		||||
 | 
			
		||||
const ICON_SIZE = 96;
 | 
			
		||||
const MIN_ICON_SIZE = 16;
 | 
			
		||||
const ICON_SIZE = 48;
 | 
			
		||||
 | 
			
		||||
const EXTRA_SPACE_ANIMATION_TIME = 0.25;
 | 
			
		||||
 | 
			
		||||
const BaseIcon = new Lang.Class({
 | 
			
		||||
    Name: 'BaseIcon',
 | 
			
		||||
@@ -23,12 +17,7 @@ const BaseIcon = new Lang.Class({
 | 
			
		||||
        params = Params.parse(params, { createIcon: null,
 | 
			
		||||
                                        setSizeManually: false,
 | 
			
		||||
                                        showLabel: true });
 | 
			
		||||
 | 
			
		||||
        let styleClass = 'overview-icon';
 | 
			
		||||
        if (params.showLabel)
 | 
			
		||||
            styleClass += ' overview-icon-with-label';
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.Bin({ style_class: styleClass,
 | 
			
		||||
        this.actor = new St.Bin({ style_class: 'overview-icon',
 | 
			
		||||
                                  x_fill: true,
 | 
			
		||||
                                  y_fill: true });
 | 
			
		||||
        this.actor._delegate = this;
 | 
			
		||||
@@ -187,31 +176,16 @@ const IconGrid = new Lang.Class({
 | 
			
		||||
    _init: function(params) {
 | 
			
		||||
        params = Params.parse(params, { rowLimit: null,
 | 
			
		||||
                                        columnLimit: null,
 | 
			
		||||
                                        minRows: 1,
 | 
			
		||||
                                        minColumns: 1,
 | 
			
		||||
                                        fillParent: false,
 | 
			
		||||
                                        xAlign: St.Align.MIDDLE,
 | 
			
		||||
                                        padWithSpacing: false });
 | 
			
		||||
                                        xAlign: St.Align.MIDDLE });
 | 
			
		||||
        this._rowLimit = params.rowLimit;
 | 
			
		||||
        this._colLimit = params.columnLimit;
 | 
			
		||||
        this._minRows = params.minRows;
 | 
			
		||||
        this._minColumns = params.minColumns;
 | 
			
		||||
        this._xAlign = params.xAlign;
 | 
			
		||||
        this._fillParent = params.fillParent;
 | 
			
		||||
        this._padWithSpacing = params.padWithSpacing;
 | 
			
		||||
 | 
			
		||||
        this.topPadding = 0;
 | 
			
		||||
        this.bottomPadding = 0;
 | 
			
		||||
        this.rightPadding = 0;
 | 
			
		||||
        this.leftPadding = 0;
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.BoxLayout({ style_class: 'icon-grid',
 | 
			
		||||
                                        vertical: true });
 | 
			
		||||
        this._items = [];
 | 
			
		||||
        // Pulled from CSS, but hardcode some defaults here
 | 
			
		||||
        this._spacing = 0;
 | 
			
		||||
        this._hItemSize = this._vItemSize = ICON_SIZE;
 | 
			
		||||
        this._fixedHItemSize = this._fixedVItemSize = undefined;
 | 
			
		||||
        this._grid = new Shell.GenericContainer();
 | 
			
		||||
        this.actor.add(this._grid, { expand: true, y_align: St.Align.START });
 | 
			
		||||
        this.actor.connect('style-changed', Lang.bind(this, this._onStyleChanged));
 | 
			
		||||
@@ -222,21 +196,16 @@ const IconGrid = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getPreferredWidth: function (grid, forHeight, alloc) {
 | 
			
		||||
        if (this._fillParent)
 | 
			
		||||
            // Ignore all size requests of children and request a size of 0;
 | 
			
		||||
            // later we'll allocate as many children as fit the parent
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let nChildren = this._grid.get_n_children();
 | 
			
		||||
        let children = this._grid.get_children();
 | 
			
		||||
        let nColumns = this._colLimit ? Math.min(this._colLimit,
 | 
			
		||||
                                                 nChildren)
 | 
			
		||||
                                      : nChildren;
 | 
			
		||||
        let totalSpacing = Math.max(0, nColumns - 1) * this._getSpacing();
 | 
			
		||||
                                                 children.length)
 | 
			
		||||
                                      : children.length;
 | 
			
		||||
        let totalSpacing = Math.max(0, nColumns - 1) * this._spacing;
 | 
			
		||||
        // Kind of a lie, but not really an issue right now.  If
 | 
			
		||||
        // we wanted to support some sort of hidden/overflow that would
 | 
			
		||||
        // need higher level design
 | 
			
		||||
        alloc.min_size = this._getHItemSize() + this.leftPadding + this.rightPadding;
 | 
			
		||||
        alloc.natural_size = nColumns * this._getHItemSize() + totalSpacing + this.leftPadding + this.rightPadding;
 | 
			
		||||
        alloc.min_size = this._hItemSize;
 | 
			
		||||
        alloc.natural_size = nColumns * this._hItemSize + totalSpacing;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getVisibleChildren: function() {
 | 
			
		||||
@@ -248,18 +217,12 @@ const IconGrid = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getPreferredHeight: function (grid, forWidth, alloc) {
 | 
			
		||||
        if (this._fillParent)
 | 
			
		||||
            // Ignore all size requests of children and request a size of 0;
 | 
			
		||||
            // later we'll allocate as many children as fit the parent
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let children = this._getVisibleChildren();
 | 
			
		||||
        let nColumns;
 | 
			
		||||
        if (forWidth < 0)
 | 
			
		||||
            nColumns = children.length;
 | 
			
		||||
        else
 | 
			
		||||
            [nColumns, ] = this._computeLayout(forWidth);
 | 
			
		||||
 | 
			
		||||
            nColumns = this._computeLayout(forWidth)[0];
 | 
			
		||||
        let nRows;
 | 
			
		||||
        if (nColumns > 0)
 | 
			
		||||
            nRows = Math.ceil(children.length / nColumns);
 | 
			
		||||
@@ -267,47 +230,57 @@ const IconGrid = new Lang.Class({
 | 
			
		||||
            nRows = 0;
 | 
			
		||||
        if (this._rowLimit)
 | 
			
		||||
            nRows = Math.min(nRows, this._rowLimit);
 | 
			
		||||
        let totalSpacing = Math.max(0, nRows - 1) * this._getSpacing();
 | 
			
		||||
        let height = nRows * this._getVItemSize() + totalSpacing + this.topPadding + this.bottomPadding;
 | 
			
		||||
        let totalSpacing = Math.max(0, nRows - 1) * this._spacing;
 | 
			
		||||
        let height = nRows * this._vItemSize + totalSpacing;
 | 
			
		||||
        alloc.min_size = height;
 | 
			
		||||
        alloc.natural_size = height;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _allocate: function (grid, box, flags) {
 | 
			
		||||
        if (this._fillParent) {
 | 
			
		||||
            // Reset the passed in box to fill the parent
 | 
			
		||||
            let parentBox = this.actor.get_parent().allocation;
 | 
			
		||||
            let gridBox = this.actor.get_theme_node().get_content_box(parentBox);
 | 
			
		||||
            box = this._grid.get_theme_node().get_content_box(gridBox);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let children = this._getVisibleChildren();
 | 
			
		||||
        let availWidth = box.x2 - box.x1;
 | 
			
		||||
        let availHeight = box.y2 - box.y1;
 | 
			
		||||
        let spacing = this._getSpacing();
 | 
			
		||||
 | 
			
		||||
        let [nColumns, usedWidth] = this._computeLayout(availWidth);
 | 
			
		||||
 | 
			
		||||
        let leftEmptySpace;
 | 
			
		||||
        let leftPadding;
 | 
			
		||||
        switch(this._xAlign) {
 | 
			
		||||
            case St.Align.START:
 | 
			
		||||
                leftEmptySpace = 0;
 | 
			
		||||
                leftPadding = 0;
 | 
			
		||||
                break;
 | 
			
		||||
            case St.Align.MIDDLE:
 | 
			
		||||
                leftEmptySpace = Math.floor((availWidth - usedWidth) / 2);
 | 
			
		||||
                leftPadding = Math.floor((availWidth - usedWidth) / 2);
 | 
			
		||||
                break;
 | 
			
		||||
            case St.Align.END:
 | 
			
		||||
                leftEmptySpace = availWidth - usedWidth;
 | 
			
		||||
                leftPadding = availWidth - usedWidth;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let x = box.x1 + leftEmptySpace + this.leftPadding;
 | 
			
		||||
        let y = box.y1 + this.topPadding;
 | 
			
		||||
        let x = box.x1 + leftPadding;
 | 
			
		||||
        let y = box.y1;
 | 
			
		||||
        let columnIndex = 0;
 | 
			
		||||
        let rowIndex = 0;
 | 
			
		||||
        for (let i = 0; i < children.length; i++) {
 | 
			
		||||
            let childBox = this._calculateChildBox(children[i], x, y, box);
 | 
			
		||||
            let [childMinWidth, childMinHeight, childNaturalWidth, childNaturalHeight]
 | 
			
		||||
                = children[i].get_preferred_size();
 | 
			
		||||
 | 
			
		||||
            if (this._rowLimit && rowIndex >= this._rowLimit ||
 | 
			
		||||
                this._fillParent && childBox.y2 > availHeight - this.bottomPadding) {
 | 
			
		||||
            /* Center the item in its allocation horizontally */
 | 
			
		||||
            let width = Math.min(this._hItemSize, childNaturalWidth);
 | 
			
		||||
            let childXSpacing = Math.max(0, width - childNaturalWidth) / 2;
 | 
			
		||||
            let height = Math.min(this._vItemSize, childNaturalHeight);
 | 
			
		||||
            let childYSpacing = Math.max(0, height - childNaturalHeight) / 2;
 | 
			
		||||
 | 
			
		||||
            let childBox = new Clutter.ActorBox();
 | 
			
		||||
            if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) {
 | 
			
		||||
                let _x = box.x2 - (x + width);
 | 
			
		||||
                childBox.x1 = Math.floor(_x - childXSpacing);
 | 
			
		||||
            } else {
 | 
			
		||||
                childBox.x1 = Math.floor(x + childXSpacing);
 | 
			
		||||
            }
 | 
			
		||||
            childBox.y1 = Math.floor(y + childYSpacing);
 | 
			
		||||
            childBox.x2 = childBox.x1 + width;
 | 
			
		||||
            childBox.y2 = childBox.y1 + height;
 | 
			
		||||
 | 
			
		||||
            if (this._rowLimit && rowIndex >= this._rowLimit) {
 | 
			
		||||
                this._grid.set_skip_paint(children[i], true);
 | 
			
		||||
            } else {
 | 
			
		||||
                children[i].allocate(childBox, flags);
 | 
			
		||||
@@ -321,38 +294,15 @@ const IconGrid = new Lang.Class({
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (columnIndex == 0) {
 | 
			
		||||
                y += this._getVItemSize() + spacing;
 | 
			
		||||
                x = box.x1 + leftEmptySpace + this.leftPadding;
 | 
			
		||||
                y += this._vItemSize + this._spacing;
 | 
			
		||||
                x = box.x1 + leftPadding;
 | 
			
		||||
            } else {
 | 
			
		||||
                x += this._getHItemSize() + spacing;
 | 
			
		||||
                x += this._hItemSize + this._spacing;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _calculateChildBox: function(child, x, y, box) {
 | 
			
		||||
        let [childMinWidth, childMinHeight, childNaturalWidth, childNaturalHeight] =
 | 
			
		||||
             child.get_preferred_size();
 | 
			
		||||
 | 
			
		||||
        /* Center the item in its allocation horizontally */
 | 
			
		||||
        let width = Math.min(this._getHItemSize(), childNaturalWidth);
 | 
			
		||||
        let childXSpacing = Math.max(0, width - childNaturalWidth) / 2;
 | 
			
		||||
        let height = Math.min(this._getVItemSize(), childNaturalHeight);
 | 
			
		||||
        let childYSpacing = Math.max(0, height - childNaturalHeight) / 2;
 | 
			
		||||
 | 
			
		||||
        let childBox = new Clutter.ActorBox();
 | 
			
		||||
        if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) {
 | 
			
		||||
            let _x = box.x2 - (x + width);
 | 
			
		||||
            childBox.x1 = Math.floor(_x - childXSpacing);
 | 
			
		||||
        } else {
 | 
			
		||||
            childBox.x1 = Math.floor(x + childXSpacing);
 | 
			
		||||
        }
 | 
			
		||||
        childBox.y1 = Math.floor(y + childYSpacing);
 | 
			
		||||
        childBox.x2 = childBox.x1 + width;
 | 
			
		||||
        childBox.y2 = childBox.y1 + height;
 | 
			
		||||
        return childBox;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    columnsForWidth: function(rowWidth) {
 | 
			
		||||
    childrenInRow: function(rowWidth) {
 | 
			
		||||
        return this._computeLayout(rowWidth)[0];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -362,17 +312,15 @@ const IconGrid = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _computeLayout: function (forWidth) {
 | 
			
		||||
        let nColumns = 0;
 | 
			
		||||
        let usedWidth = this.leftPadding + this.rightPadding;
 | 
			
		||||
        let spacing = this._getSpacing();
 | 
			
		||||
 | 
			
		||||
        let usedWidth = 0;
 | 
			
		||||
        while ((this._colLimit == null || nColumns < this._colLimit) &&
 | 
			
		||||
               (usedWidth + this._getHItemSize() <= forWidth)) {
 | 
			
		||||
            usedWidth += this._getHItemSize() + spacing;
 | 
			
		||||
               (usedWidth + this._hItemSize <= forWidth)) {
 | 
			
		||||
            usedWidth += this._hItemSize + this._spacing;
 | 
			
		||||
            nColumns += 1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (nColumns > 0)
 | 
			
		||||
            usedWidth -= spacing;
 | 
			
		||||
            usedWidth -= this._spacing;
 | 
			
		||||
 | 
			
		||||
        return [nColumns, usedWidth];
 | 
			
		||||
    },
 | 
			
		||||
@@ -385,49 +333,15 @@ const IconGrid = new Lang.Class({
 | 
			
		||||
        this._grid.queue_relayout();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    nRows: function(forWidth) {
 | 
			
		||||
        let children = this._getVisibleChildren();
 | 
			
		||||
        let nColumns = (forWidth < 0) ? children.length : this._computeLayout(forWidth)[0];
 | 
			
		||||
        let nRows = (nColumns > 0) ? Math.ceil(children.length / nColumns) : 0;
 | 
			
		||||
        if (this._rowLimit)
 | 
			
		||||
            nRows = Math.min(nRows, this._rowLimit);
 | 
			
		||||
        return nRows;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    rowsForHeight: function(forHeight) {
 | 
			
		||||
        return Math.floor((forHeight - (this.topPadding + this.bottomPadding) + this._getSpacing()) / (this._getVItemSize() + this._getSpacing()));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    usedHeightForNRows: function(nRows) {
 | 
			
		||||
        return (this._getVItemSize() + this._getSpacing()) * nRows - this._getSpacing() + this.topPadding + this.bottomPadding;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    usedWidth: function(forWidth) {
 | 
			
		||||
        return this.usedWidthForNColumns(this.columnsForWidth(forWidth));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    usedWidthForNColumns: function(columns) {
 | 
			
		||||
        let usedWidth = columns  * (this._getHItemSize() + this._getSpacing());
 | 
			
		||||
        usedWidth -= this._getSpacing();
 | 
			
		||||
        return usedWidth + this.leftPadding + this.rightPadding;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    removeAll: function() {
 | 
			
		||||
        this._items = [];
 | 
			
		||||
        this._grid.destroy_all_children();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addItem: function(item, index) {
 | 
			
		||||
        if (!item.icon || !item.icon instanceof BaseIcon) {
 | 
			
		||||
            log('Only items with a BaseIcon icon property can be added to IconGrid');
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._items.push(item);
 | 
			
		||||
    addItem: function(actor, index) {
 | 
			
		||||
        if (index !== undefined)
 | 
			
		||||
            this._grid.insert_child_at_index(item.actor, index);
 | 
			
		||||
            this._grid.insert_child_at_index(actor, index);
 | 
			
		||||
        else
 | 
			
		||||
            this._grid.add_actor(item.actor);
 | 
			
		||||
            this._grid.add_actor(actor);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getItemAtIndex: function(index) {
 | 
			
		||||
@@ -436,311 +350,5 @@ const IconGrid = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    visibleItemsCount: function() {
 | 
			
		||||
        return this._grid.get_n_children() - this._grid.get_n_skip_paint();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setSpacing: function(spacing) {
 | 
			
		||||
        this._fixedSpacing = spacing;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getSpacing: function() {
 | 
			
		||||
        return this._fixedSpacing ? this._fixedSpacing : this._spacing;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getHItemSize: function() {
 | 
			
		||||
        return this._fixedHItemSize ? this._fixedHItemSize : this._hItemSize;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getVItemSize: function() {
 | 
			
		||||
        return this._fixedVItemSize ? this._fixedVItemSize : this._vItemSize;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateSpacingForSize: function(availWidth, availHeight) {
 | 
			
		||||
        let maxEmptyVArea = availHeight - this._minRows * this._getVItemSize();
 | 
			
		||||
        let maxEmptyHArea = availWidth - this._minColumns * this._getHItemSize();
 | 
			
		||||
        let maxHSpacing, maxVSpacing;
 | 
			
		||||
 | 
			
		||||
        if (this._padWithSpacing) {
 | 
			
		||||
            // minRows + 1 because we want to put spacing before the first row, so it is like we have one more row
 | 
			
		||||
            // to divide the empty space
 | 
			
		||||
            maxVSpacing = Math.floor(maxEmptyVArea / (this._minRows +1));
 | 
			
		||||
            maxHSpacing = Math.floor(maxEmptyHArea / (this._minColumns +1));
 | 
			
		||||
        } else {
 | 
			
		||||
            if (this._minRows <=  1)
 | 
			
		||||
                maxVSpacing = maxEmptyVArea;
 | 
			
		||||
            else
 | 
			
		||||
                maxVSpacing = Math.floor(maxEmptyVArea / (this._minRows - 1));
 | 
			
		||||
 | 
			
		||||
            if (this._minColumns <=  1)
 | 
			
		||||
                maxHSpacing = maxEmptyHArea;
 | 
			
		||||
            else
 | 
			
		||||
                maxHSpacing = Math.floor(maxEmptyHArea / (this._minColumns - 1));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let maxSpacing = Math.min(maxHSpacing, maxVSpacing);
 | 
			
		||||
        // Limit spacing to the item size
 | 
			
		||||
        maxSpacing = Math.min(maxSpacing, Math.min(this._getVItemSize(), this._getHItemSize()));
 | 
			
		||||
        // The minimum spacing, regardless of whether it satisfies the row/columng minima,
 | 
			
		||||
        // is the spacing we get from CSS.
 | 
			
		||||
        let spacing = Math.max(this._spacing, maxSpacing);
 | 
			
		||||
        this.setSpacing(spacing);
 | 
			
		||||
        if (this._padWithSpacing)
 | 
			
		||||
            this.topPadding = this.rightPadding = this.bottomPadding = this.leftPadding = spacing;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * This function must to be called before iconGrid allocation,
 | 
			
		||||
     * to know how much spacing can the grid has
 | 
			
		||||
     */
 | 
			
		||||
    adaptToSize: function(availWidth, availHeight) {
 | 
			
		||||
        this._fixedHItemSize = this._hItemSize;
 | 
			
		||||
        this._fixedVItemSize = this._vItemSize;
 | 
			
		||||
        this._updateSpacingForSize(availWidth, availHeight);
 | 
			
		||||
        let spacing = this._getSpacing();
 | 
			
		||||
 | 
			
		||||
        if (this.columnsForWidth(availWidth) < this._minColumns || this.rowsForHeight(availHeight) < this._minRows) {
 | 
			
		||||
            let neededWidth = this.usedWidthForNColumns(this._minColumns) - availWidth ;
 | 
			
		||||
            let neededHeight = this.usedHeightForNRows(this._minRows) - availHeight ;
 | 
			
		||||
 | 
			
		||||
            let neededSpacePerItem = (neededWidth > neededHeight) ? Math.ceil(neededWidth / this._minColumns)
 | 
			
		||||
                                                                  : Math.ceil(neededHeight / this._minRows);
 | 
			
		||||
            this._fixedHItemSize = Math.max(this._hItemSize - neededSpacePerItem, MIN_ICON_SIZE);
 | 
			
		||||
            this._fixedVItemSize = Math.max(this._vItemSize - neededSpacePerItem, MIN_ICON_SIZE);
 | 
			
		||||
 | 
			
		||||
            if (this._fixedHItemSize < MIN_ICON_SIZE)
 | 
			
		||||
                this._fixedHItemSize = MIN_ICON_SIZE;
 | 
			
		||||
            if (this._fixedVItemSize < MIN_ICON_SIZE)
 | 
			
		||||
                this._fixedVItemSize = MIN_ICON_SIZE;
 | 
			
		||||
 | 
			
		||||
            this._updateSpacingForSize(availWidth, availHeight);
 | 
			
		||||
        }
 | 
			
		||||
        let scale = Math.min(this._fixedHItemSize, this._fixedVItemSize) / Math.max(this._hItemSize, this._vItemSize);
 | 
			
		||||
        Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() { this._updateChildrenScale(scale); }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Note that this is ICON_SIZE as used by BaseIcon, not elsewhere in IconGrid; it's a bit messed up
 | 
			
		||||
    _updateChildrenScale: function(scale) {
 | 
			
		||||
        for (let i in this._items) {
 | 
			
		||||
            let newIconSize = Math.floor(ICON_SIZE * scale);
 | 
			
		||||
            this._items[i].icon.setIconSize(newIconSize);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const PaginatedIconGrid = new Lang.Class({
 | 
			
		||||
    Name: 'PaginatedIconGrid',
 | 
			
		||||
    Extends: IconGrid,
 | 
			
		||||
 | 
			
		||||
    _init: function(params) {
 | 
			
		||||
        this.parent(params);
 | 
			
		||||
        this._nPages = 0;
 | 
			
		||||
        this._rowsPerPage = 0;
 | 
			
		||||
        this._spaceBetweenPages = 0;
 | 
			
		||||
        this._childrenPerPage = 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getPreferredHeight: function (grid, forWidth, alloc) {
 | 
			
		||||
        alloc.min_size = (this._availableHeightPerPageForItems() + this.bottomPadding + this.topPadding) * this._nPages + this._spaceBetweenPages * this._nPages;
 | 
			
		||||
        alloc.natural_size = (this._availableHeightPerPageForItems() + this.bottomPadding + this.topPadding) * this._nPages + this._spaceBetweenPages * this._nPages;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _allocate: function (grid, box, flags) {
 | 
			
		||||
         if (this._childrenPerPage == 0)
 | 
			
		||||
            log('computePages() must be called before allocate(); pagination will not work.');
 | 
			
		||||
 | 
			
		||||
        if (this._fillParent) {
 | 
			
		||||
            // Reset the passed in box to fill the parent
 | 
			
		||||
            let parentBox = this.actor.get_parent().allocation;
 | 
			
		||||
            let gridBox = this.actor.get_theme_node().get_content_box(parentBox);
 | 
			
		||||
            box = this._grid.get_theme_node().get_content_box(gridBox);
 | 
			
		||||
        }
 | 
			
		||||
        let children = this._getVisibleChildren();
 | 
			
		||||
        let availWidth = box.x2 - box.x1;
 | 
			
		||||
        let availHeight = box.y2 - box.y1;
 | 
			
		||||
        let spacing = this._getSpacing();
 | 
			
		||||
        let [nColumns, usedWidth] = this._computeLayout(availWidth);
 | 
			
		||||
 | 
			
		||||
        let leftEmptySpace;
 | 
			
		||||
        switch(this._xAlign) {
 | 
			
		||||
            case St.Align.START:
 | 
			
		||||
                leftEmptySpace = 0;
 | 
			
		||||
                break;
 | 
			
		||||
            case St.Align.MIDDLE:
 | 
			
		||||
                leftEmptySpace = Math.floor((availWidth - usedWidth) / 2);
 | 
			
		||||
                break;
 | 
			
		||||
            case St.Align.END:
 | 
			
		||||
                leftEmptySpace = availWidth - usedWidth;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let x = box.x1 + leftEmptySpace + this.leftPadding;
 | 
			
		||||
        let y = box.y1 + this.topPadding;
 | 
			
		||||
        let columnIndex = 0;
 | 
			
		||||
        let rowIndex = 0;
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < children.length; i++) {
 | 
			
		||||
            let childBox = this._calculateChildBox(children[i], x, y, box);
 | 
			
		||||
            children[i].allocate(childBox, flags);
 | 
			
		||||
            this._grid.set_skip_paint(children[i], false);
 | 
			
		||||
 | 
			
		||||
            columnIndex++;
 | 
			
		||||
            if (columnIndex == nColumns) {
 | 
			
		||||
                columnIndex = 0;
 | 
			
		||||
                rowIndex++;
 | 
			
		||||
            }
 | 
			
		||||
            if (columnIndex == 0) {
 | 
			
		||||
                y += this._getVItemSize() + spacing;
 | 
			
		||||
                if ((i + 1) % this._childrenPerPage == 0)
 | 
			
		||||
                    y +=  this._spaceBetweenPages - spacing + this.bottomPadding + this.topPadding;
 | 
			
		||||
                x = box.x1 + leftEmptySpace + this.leftPadding;
 | 
			
		||||
            } else
 | 
			
		||||
                x += this._getHItemSize() + spacing;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _computePages: function (availWidthPerPage, availHeightPerPage) {
 | 
			
		||||
        let [nColumns, usedWidth] = this._computeLayout(availWidthPerPage);
 | 
			
		||||
        let nRows;
 | 
			
		||||
        let children = this._getVisibleChildren();
 | 
			
		||||
        if (nColumns > 0)
 | 
			
		||||
            nRows = Math.ceil(children.length / nColumns);
 | 
			
		||||
        else
 | 
			
		||||
            nRows = 0;
 | 
			
		||||
        if (this._rowLimit)
 | 
			
		||||
            nRows = Math.min(nRows, this._rowLimit);
 | 
			
		||||
 | 
			
		||||
        let spacing = this._getSpacing();
 | 
			
		||||
        // We want to contain the grid inside the parent box with padding
 | 
			
		||||
        this._rowsPerPage = this.rowsForHeight(availHeightPerPage);
 | 
			
		||||
        this._nPages = Math.ceil(nRows / this._rowsPerPage);
 | 
			
		||||
        this._spaceBetweenPages = availHeightPerPage - (this.topPadding + this.bottomPadding) - this._availableHeightPerPageForItems();
 | 
			
		||||
        this._childrenPerPage = nColumns * this._rowsPerPage;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    adaptToSize: function(availWidth, availHeight) {
 | 
			
		||||
        this.parent(availWidth, availHeight);
 | 
			
		||||
        this._computePages(availWidth, availHeight);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _availableHeightPerPageForItems: function() {
 | 
			
		||||
        return this.usedHeightForNRows(this._rowsPerPage) - (this.topPadding + this.bottomPadding);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    nPages: function() {
 | 
			
		||||
        return this._nPages;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getPageY: function(pageNumber) {
 | 
			
		||||
        if (!this._nPages)
 | 
			
		||||
            return 0;
 | 
			
		||||
 | 
			
		||||
        let firstPageItem = pageNumber * this._childrenPerPage
 | 
			
		||||
        let childBox = this._getVisibleChildren()[firstPageItem].get_allocation_box();
 | 
			
		||||
        return childBox.y1 - this.topPadding;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getItemPage: function(item) {
 | 
			
		||||
        let children = this._getVisibleChildren();
 | 
			
		||||
        let index = children.indexOf(item);
 | 
			
		||||
        if (index == -1) {
 | 
			
		||||
            throw new Error('Item not found.');
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        return Math.floor(index / this._childrenPerPage);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
    * openExtraSpace:
 | 
			
		||||
    * @sourceItem: the item for which to create extra space
 | 
			
		||||
    * @side: where @sourceItem should be located relative to the created space
 | 
			
		||||
    * @nRows: the amount of space to create
 | 
			
		||||
    *
 | 
			
		||||
    * Pan view to create extra space for @nRows above or below @sourceItem.
 | 
			
		||||
    */
 | 
			
		||||
    openExtraSpace: function(sourceItem, side, nRows) {
 | 
			
		||||
        let children = this._getVisibleChildren();
 | 
			
		||||
        let index = children.indexOf(sourceItem.actor);
 | 
			
		||||
        if (index == -1) {
 | 
			
		||||
            throw new Error('Item not found.');
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        let pageIndex = Math.floor(index / this._childrenPerPage);
 | 
			
		||||
        let pageOffset = pageIndex * this._childrenPerPage;
 | 
			
		||||
 | 
			
		||||
        let childrenPerRow = this._childrenPerPage / this._rowsPerPage;
 | 
			
		||||
        let sourceRow = Math.floor((index - pageOffset) / childrenPerRow);
 | 
			
		||||
 | 
			
		||||
        let nRowsAbove = (side == St.Side.TOP) ? sourceRow + 1
 | 
			
		||||
                                               : sourceRow;
 | 
			
		||||
        let nRowsBelow = this._rowsPerPage - nRowsAbove;
 | 
			
		||||
 | 
			
		||||
        let nRowsUp, nRowsDown;
 | 
			
		||||
        if (side == St.Side.TOP) {
 | 
			
		||||
            nRowsDown = Math.min(nRowsBelow, nRows);
 | 
			
		||||
            nRowsUp = nRows - nRowsDown;
 | 
			
		||||
        } else {
 | 
			
		||||
            nRowsUp = Math.min(nRowsAbove, nRows);
 | 
			
		||||
            nRowsDown = nRows - nRowsUp;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let childrenDown = children.splice(pageOffset +
 | 
			
		||||
                                           nRowsAbove * childrenPerRow,
 | 
			
		||||
                                           nRowsBelow * childrenPerRow);
 | 
			
		||||
        let childrenUp = children.splice(pageOffset,
 | 
			
		||||
                                         nRowsAbove * childrenPerRow);
 | 
			
		||||
 | 
			
		||||
        // Special case: On the last row with no rows below the icon,
 | 
			
		||||
        // there's no need to move any rows either up or down
 | 
			
		||||
        if (childrenDown.length == 0 && nRowsUp == 0) {
 | 
			
		||||
            this._translatedChildren = [];
 | 
			
		||||
            this.emit('space-opened');
 | 
			
		||||
        } else {
 | 
			
		||||
            this._translateChildren(childrenUp, Gtk.DirectionType.UP, nRowsUp);
 | 
			
		||||
            this._translateChildren(childrenDown, Gtk.DirectionType.DOWN, nRowsDown);
 | 
			
		||||
            this._translatedChildren = childrenUp.concat(childrenDown);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _translateChildren: function(children, direction, nRows) {
 | 
			
		||||
        let translationY = nRows * (this._getVItemSize() + this._getSpacing());
 | 
			
		||||
        if (translationY == 0)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (direction == Gtk.DirectionType.UP)
 | 
			
		||||
            translationY *= -1;
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < children.length; i++) {
 | 
			
		||||
            children[i].translation_y = 0;
 | 
			
		||||
            let params = { translation_y: translationY,
 | 
			
		||||
                           time: EXTRA_SPACE_ANIMATION_TIME,
 | 
			
		||||
                           transition: 'easeInOutQuad'
 | 
			
		||||
                         };
 | 
			
		||||
            if (i == (children.length - 1))
 | 
			
		||||
                params.onComplete = Lang.bind(this,
 | 
			
		||||
                    function() {
 | 
			
		||||
                        this.emit('space-opened');
 | 
			
		||||
                    });
 | 
			
		||||
            Tweener.addTween(children[i], params);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    closeExtraSpace: function() {
 | 
			
		||||
        if (!this._translatedChildren || !this._translatedChildren.length) {
 | 
			
		||||
            this.emit('space-closed');
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < this._translatedChildren.length; i++) {
 | 
			
		||||
            if (!this._translatedChildren[i].translation_y)
 | 
			
		||||
                continue;
 | 
			
		||||
            Tweener.addTween(this._translatedChildren[i],
 | 
			
		||||
                             { translation_y: 0,
 | 
			
		||||
                               time: EXTRA_SPACE_ANIMATION_TIME,
 | 
			
		||||
                               transition: 'easeInOutQuad',
 | 
			
		||||
                               onComplete: Lang.bind(this,
 | 
			
		||||
                                   function() {
 | 
			
		||||
                                       this.emit('space-closed');
 | 
			
		||||
                                   })
 | 
			
		||||
                             });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(PaginatedIconGrid.prototype);
 | 
			
		||||
 
 | 
			
		||||
@@ -2,21 +2,18 @@
 | 
			
		||||
 | 
			
		||||
const Caribou = imports.gi.Caribou;
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const DBus = imports.dbus;
 | 
			
		||||
const Gdk = imports.gi.Gdk;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
const BoxPointer = imports.ui.boxpointer;
 | 
			
		||||
const Layout = imports.ui.layout;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const MessageTray = imports.ui.messageTray;
 | 
			
		||||
 | 
			
		||||
const KEYBOARD_REST_TIME = Layout.KEYBOARD_ANIMATION_TIME * 2 * 1000;
 | 
			
		||||
 | 
			
		||||
const KEYBOARD_SCHEMA = 'org.gnome.shell.keyboard';
 | 
			
		||||
const KEYBOARD_TYPE = 'keyboard-type';
 | 
			
		||||
 | 
			
		||||
@@ -59,7 +56,14 @@ const Key = new Lang.Class({
 | 
			
		||||
        if (this._key.name == 'Control_L' || this._key.name == 'Alt_L')
 | 
			
		||||
            this._key.latch = true;
 | 
			
		||||
 | 
			
		||||
        this._key.connect('key-pressed', Lang.bind(this, function ()
 | 
			
		||||
                                                   { this.actor.checked = true }));
 | 
			
		||||
        this._key.connect('key-released', Lang.bind(this, function ()
 | 
			
		||||
                                                    { this.actor.checked = false; }));
 | 
			
		||||
 | 
			
		||||
        if (this._extended_keys.length > 0) {
 | 
			
		||||
            this._grabbed = false;
 | 
			
		||||
            this._eventCaptureId = 0;
 | 
			
		||||
            this._key.connect('notify::show-subkeys', Lang.bind(this, this._onShowSubkeysChanged));
 | 
			
		||||
            this._boxPointer = new BoxPointer.BoxPointer(St.Side.BOTTOM,
 | 
			
		||||
                                                         { x_fill: true,
 | 
			
		||||
@@ -111,23 +115,52 @@ const Key = new Lang.Class({
 | 
			
		||||
        this._boxPointer.bin.add_actor(this._extended_keyboard);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get subkeys() {
 | 
			
		||||
        return this._boxPointer;
 | 
			
		||||
    _onEventCapture: function (actor, event) {
 | 
			
		||||
        let source = event.get_source();
 | 
			
		||||
        let type = event.type();
 | 
			
		||||
 | 
			
		||||
        if ((type == Clutter.EventType.BUTTON_PRESS ||
 | 
			
		||||
             type == Clutter.EventType.BUTTON_RELEASE) &&
 | 
			
		||||
            this._extended_keyboard.contains(source)) {
 | 
			
		||||
            source.extended_key.press();
 | 
			
		||||
            source.extended_key.release();
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        if (type == Clutter.EventType.BUTTON_PRESS) {
 | 
			
		||||
            this._boxPointer.actor.hide();
 | 
			
		||||
            this._ungrab();
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _ungrab: function () {
 | 
			
		||||
        global.stage.disconnect(this._eventCaptureId);
 | 
			
		||||
        this._eventCaptureId = 0;
 | 
			
		||||
        this._grabbed = false;
 | 
			
		||||
        Main.popModal(this.actor);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onShowSubkeysChanged: function () {
 | 
			
		||||
        if (this._key.show_subkeys) {
 | 
			
		||||
            this.actor.fake_release();
 | 
			
		||||
            this._boxPointer.actor.raise_top();
 | 
			
		||||
            this._boxPointer.setPosition(this.actor, 0.5);
 | 
			
		||||
            this.emit('show-subkeys');
 | 
			
		||||
            this.actor.fake_release();
 | 
			
		||||
            this._boxPointer.show(BoxPointer.PopupAnimation.FULL);
 | 
			
		||||
            this.actor.set_hover(false);
 | 
			
		||||
            if (!this._grabbed) {
 | 
			
		||||
                 Main.pushModal(this.actor);
 | 
			
		||||
                 this._eventCaptureId = global.stage.connect('captured-event', Lang.bind(this, this._onEventCapture));
 | 
			
		||||
                 this._grabbed = true;
 | 
			
		||||
            }
 | 
			
		||||
            this._key.release();
 | 
			
		||||
        } else {
 | 
			
		||||
            this.emit('hide-subkeys');
 | 
			
		||||
            if (this._grabbed)
 | 
			
		||||
                this._ungrab();
 | 
			
		||||
            this._boxPointer.hide(BoxPointer.PopupAnimation.FULL);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(Key.prototype);
 | 
			
		||||
 | 
			
		||||
const Keyboard = new Lang.Class({
 | 
			
		||||
    // HACK: we can't set Name, because it collides with Name dbus property
 | 
			
		||||
@@ -142,26 +175,16 @@ const Keyboard = new Lang.Class({
 | 
			
		||||
        this._focusInExtendedKeys = false;
 | 
			
		||||
 | 
			
		||||
        this._timestamp = global.display.get_current_time_roundtrip();
 | 
			
		||||
        Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._redraw));
 | 
			
		||||
 | 
			
		||||
        this._keyboardSettings = new Gio.Settings({ schema: KEYBOARD_SCHEMA });
 | 
			
		||||
        this._keyboardSettings.connect('changed', Lang.bind(this, this._settingsChanged));
 | 
			
		||||
        this._a11yApplicationsSettings = new Gio.Settings({ schema: A11Y_APPLICATIONS_SCHEMA });
 | 
			
		||||
        this._a11yApplicationsSettings.connect('changed', Lang.bind(this, this._settingsChanged));
 | 
			
		||||
        this._settingsChanged();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
        this._showIdleId = 0;
 | 
			
		||||
        this._subkeysBoxPointer = null;
 | 
			
		||||
        this._capturedEventId = 0;
 | 
			
		||||
        this._capturedPress = false;
 | 
			
		||||
 | 
			
		||||
        this._keyboardVisible = false;
 | 
			
		||||
        Main.layoutManager.connect('keyboard-visible-changed', Lang.bind(this, function(o, visible) {
 | 
			
		||||
            this._keyboardVisible = visible;
 | 
			
		||||
        }));
 | 
			
		||||
        this._keyboardRequested = false;
 | 
			
		||||
        this._keyboardRestingId = 0;
 | 
			
		||||
 | 
			
		||||
        Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._redraw));
 | 
			
		||||
    init: function () {
 | 
			
		||||
        this._redraw();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -176,19 +199,25 @@ const Keyboard = new Lang.Class({
 | 
			
		||||
        if (this._keyboard)
 | 
			
		||||
            this._destroyKeyboard();
 | 
			
		||||
 | 
			
		||||
        if (this._enableKeyboard)
 | 
			
		||||
            this._setupKeyboard();
 | 
			
		||||
        else
 | 
			
		||||
        if (this._enableKeyboard) {
 | 
			
		||||
            // If we've been called because the setting actually just
 | 
			
		||||
            // changed to true (as opposed to being called from
 | 
			
		||||
            // this._init()), then we want to pop up the keyboard.
 | 
			
		||||
            let showKeyboard = (settings != null);
 | 
			
		||||
 | 
			
		||||
            // However, caribou-gtk-module or this._onKeyFocusChanged
 | 
			
		||||
            // will probably immediately tell us to hide it, so we
 | 
			
		||||
            // have to fake things out so we'll ignore that request.
 | 
			
		||||
            if (showKeyboard)
 | 
			
		||||
                this._timestamp = global.display.get_current_time_roundtrip() + 1;
 | 
			
		||||
            this._setupKeyboard(showKeyboard);
 | 
			
		||||
        } else
 | 
			
		||||
            Main.layoutManager.hideKeyboard(true);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _destroyKeyboard: function() {
 | 
			
		||||
        if (this._keyboardNotifyId)
 | 
			
		||||
            this._keyboard.disconnect(this._keyboardNotifyId);
 | 
			
		||||
        if (this._keyboardGroupAddedId)
 | 
			
		||||
            this._keyboard.disconnect(this._keyboardGroupAddedId);
 | 
			
		||||
        if (this._keyboardGroupRemovedId)
 | 
			
		||||
            this._keyboard.disconnect(this._keyboardGroupRemovedId);
 | 
			
		||||
        if (this._focusNotifyId)
 | 
			
		||||
            global.stage.disconnect(this._focusNotifyId);
 | 
			
		||||
        this._keyboard = null;
 | 
			
		||||
@@ -198,7 +227,7 @@ const Keyboard = new Lang.Class({
 | 
			
		||||
        this._destroySource();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _setupKeyboard: function() {
 | 
			
		||||
    _setupKeyboard: function(show) {
 | 
			
		||||
        this.actor = new St.BoxLayout({ name: 'keyboard', vertical: true, reactive: true });
 | 
			
		||||
        Main.layoutManager.keyboardBox.add_actor(this.actor);
 | 
			
		||||
        Main.layoutManager.trackChrome(this.actor);
 | 
			
		||||
@@ -219,11 +248,12 @@ const Keyboard = new Lang.Class({
 | 
			
		||||
        this.actor.text_direction = Clutter.TextDirection.LTR;
 | 
			
		||||
 | 
			
		||||
        this._keyboardNotifyId = this._keyboard.connect('notify::active-group', Lang.bind(this, this._onGroupChanged));
 | 
			
		||||
        this._keyboardGroupAddedId = this._keyboard.connect('group-added', Lang.bind(this, this._onGroupAdded));
 | 
			
		||||
        this._keyboardGroupRemovedId = this._keyboard.connect('group-removed', Lang.bind(this, this._onGroupRemoved));
 | 
			
		||||
        this._focusNotifyId = global.stage.connect('notify::key-focus', Lang.bind(this, this._onKeyFocusChanged));
 | 
			
		||||
 | 
			
		||||
        this._createSource();
 | 
			
		||||
        if (show)
 | 
			
		||||
            this.show(Main.layoutManager.focusIndex);
 | 
			
		||||
        else
 | 
			
		||||
            this._createSource();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onKeyFocusChanged: function () {
 | 
			
		||||
@@ -243,102 +273,88 @@ const Keyboard = new Lang.Class({
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let time = global.get_current_time();
 | 
			
		||||
        if (!(focus instanceof Clutter.Text)) {
 | 
			
		||||
        if (focus instanceof Clutter.Text)
 | 
			
		||||
            this.Show(time);
 | 
			
		||||
        else
 | 
			
		||||
            this.Hide(time);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!this._showIdleId)
 | 
			
		||||
            this._showIdleId = GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE,
 | 
			
		||||
                                             Lang.bind(this, function() { this.Show(time); }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createLayersForGroup: function (gname) {
 | 
			
		||||
        let group = this._keyboard.get_group(gname);
 | 
			
		||||
        group.connect('notify::active-level', Lang.bind(this, this._onLevelChanged));
 | 
			
		||||
        let layers = {};
 | 
			
		||||
        let levels = group.get_levels();
 | 
			
		||||
        for (let j = 0; j < levels.length; ++j) {
 | 
			
		||||
            let lname = levels[j];
 | 
			
		||||
            let level = group.get_level(lname);
 | 
			
		||||
            let layout = new St.BoxLayout({ style_class: 'keyboard-layout',
 | 
			
		||||
                                                 vertical: true });
 | 
			
		||||
            this._loadRows(level, layout);
 | 
			
		||||
            layers[lname] = layout;
 | 
			
		||||
            this.actor.add(layout, { x_fill: false });
 | 
			
		||||
 | 
			
		||||
            layout.hide();
 | 
			
		||||
        }
 | 
			
		||||
        return layers;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _addKeys: function () {
 | 
			
		||||
        let groups = this._keyboard.get_groups();
 | 
			
		||||
        for (let i = 0; i < groups.length; ++i) {
 | 
			
		||||
             let gname = groups[i];
 | 
			
		||||
             this._groups[gname] = this._createLayersForGroup(gname);
 | 
			
		||||
             let group = this._keyboard.get_group(gname);
 | 
			
		||||
             group.connect('notify::active-level', Lang.bind(this, this._onLevelChanged));
 | 
			
		||||
             let layers = {};
 | 
			
		||||
             let levels = group.get_levels();
 | 
			
		||||
             for (let j = 0; j < levels.length; ++j) {
 | 
			
		||||
                 let lname = levels[j];
 | 
			
		||||
                 let level = group.get_level(lname);
 | 
			
		||||
                 let layout = new St.BoxLayout({ style_class: 'keyboard-layout',
 | 
			
		||||
                                                 vertical: true });
 | 
			
		||||
                 this._loadRows(level, layout);
 | 
			
		||||
                 layers[lname] = layout;
 | 
			
		||||
                 this.actor.add(layout, { x_fill: false });
 | 
			
		||||
 | 
			
		||||
                 layout.hide();
 | 
			
		||||
             }
 | 
			
		||||
             this._groups[gname] = layers;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._setActiveLayer();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onCapturedEvent: function(actor, event) {
 | 
			
		||||
        let type = event.type();
 | 
			
		||||
        let press = type == Clutter.EventType.BUTTON_PRESS;
 | 
			
		||||
        let release = type == Clutter.EventType.BUTTON_RELEASE;
 | 
			
		||||
    _getTrayIcon: function () {
 | 
			
		||||
        let trayButton = new St.Button ({ label: _("tray"),
 | 
			
		||||
                                          style_class: 'keyboard-key' });
 | 
			
		||||
        trayButton.key_width = 1;
 | 
			
		||||
        trayButton.connect('button-press-event', Lang.bind(this, function () {
 | 
			
		||||
            Main.messageTray.toggle();
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        if (press)
 | 
			
		||||
            this._capturedPress = true;
 | 
			
		||||
        else if (release && this._capturedPress)
 | 
			
		||||
            this._hideSubkeys();
 | 
			
		||||
        Main.overview.connect('showing', Lang.bind(this, function () {
 | 
			
		||||
            trayButton.reactive = false;
 | 
			
		||||
            trayButton.add_style_pseudo_class('grayed');
 | 
			
		||||
        }));
 | 
			
		||||
        Main.overview.connect('hiding', Lang.bind(this, function () {
 | 
			
		||||
            trayButton.reactive = true;
 | 
			
		||||
            trayButton.remove_style_pseudo_class('grayed');
 | 
			
		||||
        }));
 | 
			
		||||
        Main.sessionMode.connect('updated', Lang.bind(this, function() {
 | 
			
		||||
            trayButton.reactive = !Main.sessionMode.isLocked;
 | 
			
		||||
            if (Main.sessionMode.isLocked)
 | 
			
		||||
                trayButton.add_style_pseudo_class('grayed');
 | 
			
		||||
            else
 | 
			
		||||
                trayButton.remove_style_pseudo_class('grayed');
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
        return trayButton;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _addRows : function (keys, layout) {
 | 
			
		||||
        let keyboard_row = new St.BoxLayout();
 | 
			
		||||
        for (let i = 0; i < keys.length; ++i) {
 | 
			
		||||
            let children = keys[i].get_children();
 | 
			
		||||
            let left_box = new St.BoxLayout({ style_class: 'keyboard-row' });
 | 
			
		||||
            let center_box = new St.BoxLayout({ style_class: 'keyboard-row' });
 | 
			
		||||
            let right_box = new St.BoxLayout({ style_class: 'keyboard-row' });
 | 
			
		||||
            let left_box = new St.BoxLayout({ style_class: 'keyboard-row' });
 | 
			
		||||
            for (let j = 0; j < children.length; ++j) {
 | 
			
		||||
                if (this._numOfHorizKeys == 0)
 | 
			
		||||
                    this._numOfHorizKeys = children.length;
 | 
			
		||||
                let key = children[j];
 | 
			
		||||
                let button = new Key(key);
 | 
			
		||||
 | 
			
		||||
                switch (key.align) {
 | 
			
		||||
                case 'right':
 | 
			
		||||
                if (key.align == 'right')
 | 
			
		||||
                    right_box.add(button.actor);
 | 
			
		||||
                    break;
 | 
			
		||||
                case 'center':
 | 
			
		||||
                    center_box.add(button.actor);
 | 
			
		||||
                    break;
 | 
			
		||||
                case 'left':
 | 
			
		||||
                default:
 | 
			
		||||
                else
 | 
			
		||||
                    left_box.add(button.actor);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                if (key.name == 'Caribou_Prefs') {
 | 
			
		||||
                    key.connect('key-released', Lang.bind(this, this.hide));
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                button.connect('show-subkeys', Lang.bind(this, function() {
 | 
			
		||||
                    if (this._subkeysBoxPointer)
 | 
			
		||||
                        this._subkeysBoxPointer.hide(BoxPointer.PopupAnimation.FULL);
 | 
			
		||||
                    this._subkeysBoxPointer = button.subkeys;
 | 
			
		||||
                    this._subkeysBoxPointer.show(BoxPointer.PopupAnimation.FULL);
 | 
			
		||||
                    if (!this._capturedEventId)
 | 
			
		||||
                        this._capturedEventId = this.actor.connect('captured-event',
 | 
			
		||||
                                                                   Lang.bind(this, this._onCapturedEvent));
 | 
			
		||||
                }));
 | 
			
		||||
                button.connect('hide-subkeys', Lang.bind(this, function() {
 | 
			
		||||
                    this._hideSubkeys();
 | 
			
		||||
                }));
 | 
			
		||||
                    // Add new key for hiding message tray
 | 
			
		||||
                    right_box.add(this._getTrayIcon());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            keyboard_row.add(left_box, { expand: true, x_fill: false, x_align: St.Align.START });
 | 
			
		||||
            keyboard_row.add(center_box, { expand: true, x_fill: false, x_align: St.Align.MIDDLE });
 | 
			
		||||
            keyboard_row.add(right_box, { expand: true, x_fill: false, x_align: St.Align.END });
 | 
			
		||||
        }
 | 
			
		||||
        layout.add(keyboard_row);
 | 
			
		||||
@@ -411,14 +427,6 @@ const Keyboard = new Lang.Class({
 | 
			
		||||
        this._redraw();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onGroupAdded: function (keyboard, gname) {
 | 
			
		||||
        this._groups[gname] = this._createLayersForGroup(gname);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onGroupRemoved: function (keyboard, gname) {
 | 
			
		||||
        delete this._groups[gname];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _setActiveLayer: function () {
 | 
			
		||||
        let active_group_name = this._keyboard.active_group;
 | 
			
		||||
        let active_group = this._keyboard.get_group(active_group_name);
 | 
			
		||||
@@ -454,37 +462,7 @@ const Keyboard = new Lang.Class({
 | 
			
		||||
               actor._extended_keys || actor.extended_key;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _clearKeyboardRestTimer: function() {
 | 
			
		||||
        if (!this._keyboardRestingId)
 | 
			
		||||
            return;
 | 
			
		||||
        GLib.source_remove(this._keyboardRestingId);
 | 
			
		||||
        this._keyboardRestingId = 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    show: function (monitor) {
 | 
			
		||||
        this._keyboardRequested = true;
 | 
			
		||||
 | 
			
		||||
        if (this._keyboardVisible) {
 | 
			
		||||
            if (monitor != Main.layoutManager.keyboardIndex) {
 | 
			
		||||
                Main.layoutManager.keyboardIndex = monitor;
 | 
			
		||||
                this._redraw();
 | 
			
		||||
            }
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._clearKeyboardRestTimer();
 | 
			
		||||
        this._keyboardRestingId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
 | 
			
		||||
                                                   KEYBOARD_REST_TIME,
 | 
			
		||||
                                                   Lang.bind(this, function() {
 | 
			
		||||
                                                       this._clearKeyboardRestTimer();
 | 
			
		||||
                                                       this._show(monitor);
 | 
			
		||||
                                                   }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _show: function(monitor) {
 | 
			
		||||
        if (!this._keyboardRequested)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        Main.layoutManager.keyboardIndex = monitor;
 | 
			
		||||
        this._redraw();
 | 
			
		||||
        Main.layoutManager.showKeyboard();
 | 
			
		||||
@@ -492,41 +470,10 @@ const Keyboard = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    hide: function () {
 | 
			
		||||
        this._keyboardRequested = false;
 | 
			
		||||
 | 
			
		||||
        if (!this._keyboardVisible)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._clearKeyboardRestTimer();
 | 
			
		||||
        this._keyboardRestingId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
 | 
			
		||||
                                                   KEYBOARD_REST_TIME,
 | 
			
		||||
                                                   Lang.bind(this, function() {
 | 
			
		||||
                                                       this._clearKeyboardRestTimer();
 | 
			
		||||
                                                       this._hide();
 | 
			
		||||
                                                   }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _hide: function() {
 | 
			
		||||
        if (this._keyboardRequested)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._hideSubkeys();
 | 
			
		||||
        Main.layoutManager.hideKeyboard();
 | 
			
		||||
        this._createSource();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _hideSubkeys: function() {
 | 
			
		||||
        if (this._subkeysBoxPointer) {
 | 
			
		||||
            this._subkeysBoxPointer.hide(BoxPointer.PopupAnimation.FULL);
 | 
			
		||||
            this._subkeysBoxPointer = null;
 | 
			
		||||
        }
 | 
			
		||||
        if (this._capturedEventId) {
 | 
			
		||||
            this.actor.disconnect(this._capturedEventId);
 | 
			
		||||
            this._capturedEventId = 0;
 | 
			
		||||
        }
 | 
			
		||||
        this._capturedPress = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _moveTemporarily: function () {
 | 
			
		||||
        let currentWindow = global.screen.get_display().focus_window;
 | 
			
		||||
        let rect = currentWindow.get_outer_rect();
 | 
			
		||||
@@ -555,13 +502,6 @@ const Keyboard = new Lang.Class({
 | 
			
		||||
        return one - two;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _clearShowIdle: function() {
 | 
			
		||||
        if (!this._showIdleId)
 | 
			
		||||
            return;
 | 
			
		||||
        GLib.source_remove(this._showIdleId);
 | 
			
		||||
        this._showIdleId = 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // D-Bus methods
 | 
			
		||||
    Show: function(timestamp) {
 | 
			
		||||
        if (!this._enableKeyboard)
 | 
			
		||||
@@ -570,8 +510,6 @@ const Keyboard = new Lang.Class({
 | 
			
		||||
        if (this._compareTimestamp(timestamp, this._timestamp) < 0)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._clearShowIdle();
 | 
			
		||||
 | 
			
		||||
        if (timestamp != Clutter.CURRENT_TIME)
 | 
			
		||||
            this._timestamp = timestamp;
 | 
			
		||||
        this.show(Main.layoutManager.focusIndex);
 | 
			
		||||
@@ -584,8 +522,6 @@ const Keyboard = new Lang.Class({
 | 
			
		||||
        if (this._compareTimestamp(timestamp, this._timestamp) < 0)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._clearShowIdle();
 | 
			
		||||
 | 
			
		||||
        if (timestamp != Clutter.CURRENT_TIME)
 | 
			
		||||
            this._timestamp = timestamp;
 | 
			
		||||
        this.hide();
 | 
			
		||||
@@ -620,7 +556,11 @@ const KeyboardSource = new Lang.Class({
 | 
			
		||||
        this.keepTrayOnSummaryClick = true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    handleSummaryClick: function(button) {
 | 
			
		||||
    handleSummaryClick: function() {
 | 
			
		||||
        let event = Clutter.get_current_event();
 | 
			
		||||
        if (event.type() != Clutter.EventType.BUTTON_RELEASE)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        this.open();
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1513
									
								
								js/ui/layout.js
									
									
									
									
									
								
							
							
						
						@@ -3,7 +3,6 @@
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Meta = imports.gi.Meta;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
@@ -42,11 +41,15 @@ const Lightbox = new Lang.Class({
 | 
			
		||||
        params = Params.parse(params, { inhibitEvents: false,
 | 
			
		||||
                                        width: null,
 | 
			
		||||
                                        height: null,
 | 
			
		||||
                                        fadeFactor: DEFAULT_FADE_FACTOR,
 | 
			
		||||
                                        fadeInTime: null,
 | 
			
		||||
                                        fadeOutTime: null,
 | 
			
		||||
                                        fadeFactor: DEFAULT_FADE_FACTOR
 | 
			
		||||
                                      });
 | 
			
		||||
 | 
			
		||||
        this._container = container;
 | 
			
		||||
        this._children = container.get_children();
 | 
			
		||||
        this._fadeInTime = params.fadeInTime;
 | 
			
		||||
        this._fadeOutTime = params.fadeOutTime;
 | 
			
		||||
        this._fadeFactor = params.fadeFactor;
 | 
			
		||||
        this.actor = new St.Bin({ x: 0,
 | 
			
		||||
                                  y: 0,
 | 
			
		||||
@@ -97,39 +100,31 @@ const Lightbox = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    show: function(fadeInTime) {
 | 
			
		||||
        fadeInTime = fadeInTime || 0;
 | 
			
		||||
 | 
			
		||||
        Tweener.removeTweens(this.actor);
 | 
			
		||||
        if (fadeInTime != 0) {
 | 
			
		||||
    show: function() {
 | 
			
		||||
        if (this._fadeInTime) {
 | 
			
		||||
            this.shown = false;
 | 
			
		||||
            this.actor.opacity = 0;
 | 
			
		||||
            Tweener.addTween(this.actor,
 | 
			
		||||
                             { opacity: 255 * this._fadeFactor,
 | 
			
		||||
                               time: fadeInTime,
 | 
			
		||||
                               time: this._fadeInTime,
 | 
			
		||||
                               transition: 'easeOutQuad',
 | 
			
		||||
                               onComplete: Lang.bind(this, function() {
 | 
			
		||||
                                   this.shown = true;
 | 
			
		||||
                                   this.emit('shown');
 | 
			
		||||
                               })
 | 
			
		||||
                             });
 | 
			
		||||
        } else {
 | 
			
		||||
            this.actor.opacity = 255 * this._fadeFactor;
 | 
			
		||||
            this.shown = true;
 | 
			
		||||
            this.emit('shown');
 | 
			
		||||
        }
 | 
			
		||||
        this.actor.show();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    hide: function(fadeOutTime) {
 | 
			
		||||
        fadeOutTime = fadeOutTime || 0;
 | 
			
		||||
 | 
			
		||||
    hide: function() {
 | 
			
		||||
        this.shown = false;
 | 
			
		||||
        Tweener.removeTweens(this.actor);
 | 
			
		||||
        if (fadeOutTime != 0) {
 | 
			
		||||
        if (this._fadeOutTime) {
 | 
			
		||||
            Tweener.addTween(this.actor,
 | 
			
		||||
                             { opacity: 0,
 | 
			
		||||
                               time: fadeOutTime,
 | 
			
		||||
                               time: this._fadeOutTime,
 | 
			
		||||
                               transition: 'easeOutQuad',
 | 
			
		||||
                               onComplete: Lang.bind(this, function() {
 | 
			
		||||
                                   this.actor.hide();
 | 
			
		||||
@@ -202,4 +197,3 @@ const Lightbox = new Lang.Class({
 | 
			
		||||
        this.highlight(null);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(Lightbox.prototype);
 | 
			
		||||
 
 | 
			
		||||
@@ -39,7 +39,6 @@ var commandHeader = 'const Clutter = imports.gi.Clutter; ' +
 | 
			
		||||
                     * in the shell core code too. */
 | 
			
		||||
                    'const stage = global.stage; ' +
 | 
			
		||||
                    /* Special lookingGlass functions */
 | 
			
		||||
                    'const inspect = Lang.bind(Main.lookingGlass, Main.lookingGlass.inspect); ' +
 | 
			
		||||
                    'const it = Main.lookingGlass.getIt(); ' +
 | 
			
		||||
                    'const r = Lang.bind(Main.lookingGlass, Main.lookingGlass.getResult); ';
 | 
			
		||||
 | 
			
		||||
@@ -308,6 +307,10 @@ const Result = new Lang.Class({
 | 
			
		||||
        box.add(resultTxt);
 | 
			
		||||
        let objLink = new ObjLink(this._lookingGlass, o);
 | 
			
		||||
        box.add(objLink.actor);
 | 
			
		||||
        let line = new Clutter.Rectangle({ name: 'Separator' });
 | 
			
		||||
        let padBin = new St.Bin({ name: 'Separator', x_fill: true, y_fill: true });
 | 
			
		||||
        padBin.add_actor(line);
 | 
			
		||||
        this.actor.add(padBin);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@@ -681,8 +684,7 @@ const Memory = new Lang.Class({
 | 
			
		||||
const Extensions = new Lang.Class({
 | 
			
		||||
    Name: 'Extensions',
 | 
			
		||||
 | 
			
		||||
    _init: function(lookingGlass) {
 | 
			
		||||
        this._lookingGlass = lookingGlass;
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.actor = new St.BoxLayout({ vertical: true,
 | 
			
		||||
                                        name: 'lookingGlassExtensions' });
 | 
			
		||||
        this._noExtensions = new St.Label({ style_class: 'lg-extensions-none',
 | 
			
		||||
@@ -849,9 +851,8 @@ const LookingGlass = new Lang.Class({
 | 
			
		||||
        this._updateFont();
 | 
			
		||||
 | 
			
		||||
        // We want it to appear to slide out from underneath the panel
 | 
			
		||||
        Main.uiGroup.add_actor(this.actor);
 | 
			
		||||
        Main.uiGroup.set_child_below_sibling(this.actor,
 | 
			
		||||
                                             Main.layoutManager.panelBox);
 | 
			
		||||
        Main.layoutManager.panelBox.add_actor(this.actor);
 | 
			
		||||
        this.actor.lower_bottom();
 | 
			
		||||
        Main.layoutManager.panelBox.connect('allocation-changed',
 | 
			
		||||
                                            Lang.bind(this, this._queueResize));
 | 
			
		||||
        Main.layoutManager.keyboardBox.connect('allocation-changed',
 | 
			
		||||
@@ -870,7 +871,8 @@ const LookingGlass = new Lang.Class({
 | 
			
		||||
        inspectIcon.connect('button-press-event', Lang.bind(this, function () {
 | 
			
		||||
            let inspector = new Inspector(this);
 | 
			
		||||
            inspector.connect('target', Lang.bind(this, function(i, target, stageX, stageY) {
 | 
			
		||||
                this._pushResult('inspect(' + Math.round(stageX) + ', ' + Math.round(stageY) + ')', target);
 | 
			
		||||
                this._pushResult('<inspect x:' + stageX + ' y:' + stageY + '>',
 | 
			
		||||
                                 target);
 | 
			
		||||
            }));
 | 
			
		||||
            inspector.connect('closed', Lang.bind(this, function() {
 | 
			
		||||
                this.actor.show();
 | 
			
		||||
@@ -910,7 +912,7 @@ const LookingGlass = new Lang.Class({
 | 
			
		||||
        this._memory = new Memory();
 | 
			
		||||
        notebook.appendPage('Memory', this._memory.actor);
 | 
			
		||||
 | 
			
		||||
        this._extensions = new Extensions(this);
 | 
			
		||||
        this._extensions = new Extensions();
 | 
			
		||||
        notebook.appendPage('Extensions', this._extensions.actor);
 | 
			
		||||
 | 
			
		||||
        this._entry.clutter_text.connect('activate', Lang.bind(this, function (o, e) {
 | 
			
		||||
@@ -920,7 +922,7 @@ const LookingGlass = new Lang.Class({
 | 
			
		||||
            let text = o.get_text();
 | 
			
		||||
            // Ensure we don't get newlines in the command; the history file is
 | 
			
		||||
            // newline-separated.
 | 
			
		||||
            text = text.replace('\n', ' ');
 | 
			
		||||
            text.replace('\n', ' ');
 | 
			
		||||
            // Strip leading and trailing whitespace
 | 
			
		||||
            text = text.replace(/^\s+/g, '').replace(/\s+$/g, '');
 | 
			
		||||
            if (text == '')
 | 
			
		||||
@@ -948,7 +950,9 @@ const LookingGlass = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _updateFont: function() {
 | 
			
		||||
        let fontName = this._interfaceSettings.get_string('monospace-font-name');
 | 
			
		||||
        let fontDesc = Pango.FontDescription.from_string(fontName);
 | 
			
		||||
        // This is mishandled by the scanner - should by Pango.FontDescription_from_string(fontName);
 | 
			
		||||
        // https://bugzilla.gnome.org/show_bug.cgi?id=595889
 | 
			
		||||
        let fontDesc = Pango.font_description_from_string(fontName);
 | 
			
		||||
        // We ignore everything but size and style; you'd be crazy to set your system-wide
 | 
			
		||||
        // monospace font to be bold/oblique/etc. Could easily be added here.
 | 
			
		||||
        this.actor.style =
 | 
			
		||||
@@ -986,18 +990,28 @@ const LookingGlass = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _showCompletions: function(completions) {
 | 
			
		||||
        if (!this._completionActor) {
 | 
			
		||||
            this._completionActor = new St.Label({ name: 'LookingGlassAutoCompletionText', style_class: 'lg-completions-text' });
 | 
			
		||||
            this._completionActor.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
 | 
			
		||||
            this._completionActor.clutter_text.line_wrap = true;
 | 
			
		||||
            let actor = new St.BoxLayout({ vertical: true });
 | 
			
		||||
 | 
			
		||||
            this._completionText = new St.Label({ name: 'LookingGlassAutoCompletionText', style_class: 'lg-completions-text' });
 | 
			
		||||
            this._completionText.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
 | 
			
		||||
            this._completionText.clutter_text.line_wrap = true;
 | 
			
		||||
            actor.add(this._completionText);
 | 
			
		||||
 | 
			
		||||
            let line = new Clutter.Rectangle();
 | 
			
		||||
            let padBin = new St.Bin({ x_fill: true, y_fill: true });
 | 
			
		||||
            padBin.add_actor(line);
 | 
			
		||||
            actor.add(padBin);
 | 
			
		||||
 | 
			
		||||
            this._completionActor = actor;
 | 
			
		||||
            this._evalBox.insert_child_below(this._completionActor, this._entryArea);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._completionActor.set_text(completions.join(', '));
 | 
			
		||||
        this._completionText.set_text(completions.join(', '));
 | 
			
		||||
 | 
			
		||||
        // Setting the height to -1 allows us to get its actual preferred height rather than
 | 
			
		||||
        // whatever was last given in set_height by Tweener.
 | 
			
		||||
        this._completionActor.set_height(-1);
 | 
			
		||||
        let [minHeight, naturalHeight] = this._completionActor.get_preferred_height(this._resultsArea.get_width());
 | 
			
		||||
        let [minHeight, naturalHeight] = this._completionText.get_preferred_height(this._resultsArea.get_width());
 | 
			
		||||
 | 
			
		||||
        // Don't reanimate if we are already visible
 | 
			
		||||
        if (this._completionActor.visible) {
 | 
			
		||||
@@ -1043,10 +1057,6 @@ const LookingGlass = new Lang.Class({
 | 
			
		||||
        this._entry.text = '';
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    inspect: function(x, y) {
 | 
			
		||||
        return global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getIt: function () {
 | 
			
		||||
        return this._it;
 | 
			
		||||
    },
 | 
			
		||||
@@ -1072,15 +1082,15 @@ const LookingGlass = new Lang.Class({
 | 
			
		||||
        let myWidth = primary.width * 0.7;
 | 
			
		||||
        let availableHeight = primary.height - Main.layoutManager.keyboardBox.height;
 | 
			
		||||
        let myHeight = Math.min(primary.height * 0.7, availableHeight * 0.9);
 | 
			
		||||
        this.actor.x = primary.x + (primary.width - myWidth) / 2;
 | 
			
		||||
        this._hiddenY = primary.y + Main.layoutManager.panelBox.height - myHeight - 4; // -4 to hide the top corners
 | 
			
		||||
        this.actor.x = (primary.width - myWidth) / 2;
 | 
			
		||||
        this._hiddenY = this.actor.get_parent().height - myHeight - 4; // -4 to hide the top corners
 | 
			
		||||
        this._targetY = this._hiddenY + myHeight;
 | 
			
		||||
        this.actor.y = this._hiddenY;
 | 
			
		||||
        this.actor.width = myWidth;
 | 
			
		||||
        this.actor.height = myHeight;
 | 
			
		||||
        this._objInspector.actor.set_size(Math.floor(myWidth * 0.8), Math.floor(myHeight * 0.8));
 | 
			
		||||
        this._objInspector.actor.set_position(this.actor.x + Math.floor(myWidth * 0.1),
 | 
			
		||||
                                              this._targetY + Math.floor(myHeight * 0.1));
 | 
			
		||||
        this._objInspector.actor.set_position(primary.x + this.actor.x + Math.floor(myWidth * 0.1),
 | 
			
		||||
                                              primary.y + this._targetY + Math.floor(myHeight * 0.1));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    insertObject: function(obj) {
 | 
			
		||||
@@ -1119,7 +1129,7 @@ const LookingGlass = new Lang.Class({
 | 
			
		||||
        if (this._open)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (!Main.pushModal(this._entry, { keybindingMode: Shell.KeyBindingMode.LOOKING_GLASS }))
 | 
			
		||||
        if (!Main.pushModal(this._entry, { keybindingMode: Main.KeybindingMode.LOOKING_GLASS }))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._notebook.selectIndex(0);
 | 
			
		||||
@@ -1150,7 +1160,7 @@ const LookingGlass = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        Main.popModal(this._entry);
 | 
			
		||||
 | 
			
		||||
        Tweener.addTween(this.actor, { time: Math.min(0.5 / St.get_slow_down_factor(), 0.5),
 | 
			
		||||
        Tweener.addTween(this.actor, { time: 0.5 / St.get_slow_down_factor(),
 | 
			
		||||
                                       transition: 'easeOutQuad',
 | 
			
		||||
                                       y: this._hiddenY,
 | 
			
		||||
                                       onComplete: Lang.bind(this, function () {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,5 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Atspi = imports.gi.Atspi;
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const GDesktopEnums = imports.gi.GDesktopEnums;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
@@ -8,10 +7,8 @@ const Shell = imports.gi.Shell;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Mainloop = imports.mainloop;
 | 
			
		||||
const Meta = imports.gi.Meta;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
 | 
			
		||||
const FocusCaretTracker = imports.ui.focusCaretTracker;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const MagnifierDBus = imports.ui.magnifierDBus;
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
@@ -39,8 +36,6 @@ const CONTRAST_BLUE_KEY         = 'contrast-blue';
 | 
			
		||||
const LENS_MODE_KEY             = 'lens-mode';
 | 
			
		||||
const CLAMP_MODE_KEY            = 'scroll-at-edges';
 | 
			
		||||
const MOUSE_TRACKING_KEY        = 'mouse-tracking';
 | 
			
		||||
const FOCUS_TRACKING_KEY        = 'focus-tracking';
 | 
			
		||||
const CARET_TRACKING_KEY        = 'caret-tracking';
 | 
			
		||||
const SHOW_CROSS_HAIRS_KEY      = 'show-cross-hairs';
 | 
			
		||||
const CROSS_HAIRS_THICKNESS_KEY = 'cross-hairs-thickness';
 | 
			
		||||
const CROSS_HAIRS_COLOR_KEY     = 'cross-hairs-color';
 | 
			
		||||
@@ -57,24 +52,10 @@ const Magnifier = new Lang.Class({
 | 
			
		||||
        // Magnifier is a manager of ZoomRegions.
 | 
			
		||||
        this._zoomRegions = [];
 | 
			
		||||
 | 
			
		||||
        // Export to dbus.
 | 
			
		||||
        magDBusService = new MagnifierDBus.ShellMagnifier();
 | 
			
		||||
 | 
			
		||||
        let showAtLaunch = this._settingsInit();
 | 
			
		||||
        this.setActive(showAtLaunch);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _initialize: function() {
 | 
			
		||||
        if (this._initialized)
 | 
			
		||||
            return;
 | 
			
		||||
        this._initialized = true;
 | 
			
		||||
 | 
			
		||||
        this._settingsInitLate();
 | 
			
		||||
 | 
			
		||||
        // Create small clutter tree for the magnified mouse.
 | 
			
		||||
        let cursorTracker = Meta.CursorTracker.get_for_screen(global.screen);
 | 
			
		||||
        let xfixesCursor = Shell.XFixesCursor.get_for_stage(global.stage);
 | 
			
		||||
        this._mouseSprite = new Clutter.Texture();
 | 
			
		||||
        Shell.util_cursor_tracker_to_clutter(cursorTracker, this._mouseSprite);
 | 
			
		||||
        xfixesCursor.update_texture_image(this._mouseSprite);
 | 
			
		||||
        this._cursorRoot = new Clutter.Actor();
 | 
			
		||||
        this._cursorRoot.add_actor(this._mouseSprite);
 | 
			
		||||
 | 
			
		||||
@@ -86,11 +67,15 @@ const Magnifier = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        let aZoomRegion = new ZoomRegion(this, this._cursorRoot);
 | 
			
		||||
        this._zoomRegions.push(aZoomRegion);
 | 
			
		||||
        this._settingsInitRegion(aZoomRegion);
 | 
			
		||||
        let showAtLaunch = this._settingsInit(aZoomRegion);
 | 
			
		||||
        aZoomRegion.scrollContentsTo(this.xMouse, this.yMouse);
 | 
			
		||||
 | 
			
		||||
        cursorTracker.connect('cursor-changed', Lang.bind(this, this._updateMouseSprite));
 | 
			
		||||
        this._cursorTracker = cursorTracker;
 | 
			
		||||
        xfixesCursor.connect('cursor-change', Lang.bind(this, this._updateMouseSprite));
 | 
			
		||||
        this._xfixesCursor = xfixesCursor;
 | 
			
		||||
 | 
			
		||||
        // Export to dbus.
 | 
			
		||||
        magDBusService = new MagnifierDBus.ShellMagnifier();
 | 
			
		||||
        this.setActive(showAtLaunch);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -98,7 +83,7 @@ const Magnifier = new Lang.Class({
 | 
			
		||||
     * Show the system mouse pointer.
 | 
			
		||||
     */
 | 
			
		||||
    showSystemCursor: function() {
 | 
			
		||||
        this._cursorTracker.set_pointer_visible(true);
 | 
			
		||||
        this._xfixesCursor.show();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -106,7 +91,7 @@ const Magnifier = new Lang.Class({
 | 
			
		||||
     * Hide the system mouse pointer.
 | 
			
		||||
     */
 | 
			
		||||
    hideSystemCursor: function() {
 | 
			
		||||
        this._cursorTracker.set_pointer_visible(false);
 | 
			
		||||
        this._xfixesCursor.hide();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -115,12 +100,6 @@ const Magnifier = new Lang.Class({
 | 
			
		||||
     * @activate:   Boolean to activate or de-activate the magnifier.
 | 
			
		||||
     */
 | 
			
		||||
    setActive: function(activate) {
 | 
			
		||||
        if (activate == this.isActive())
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (activate)
 | 
			
		||||
            this._initialize();
 | 
			
		||||
 | 
			
		||||
        this._zoomRegions.forEach (function(zoomRegion, index, array) {
 | 
			
		||||
            zoomRegion.setActive(activate);
 | 
			
		||||
        });
 | 
			
		||||
@@ -133,7 +112,7 @@ const Magnifier = new Lang.Class({
 | 
			
		||||
        // Make sure system mouse pointer is shown when all zoom regions are
 | 
			
		||||
        // invisible.
 | 
			
		||||
        if (!activate)
 | 
			
		||||
            this._cursorTracker.set_pointer_visible(true);
 | 
			
		||||
            this._xfixesCursor.show();
 | 
			
		||||
 | 
			
		||||
        // Notify interested parties of this change
 | 
			
		||||
        this.emit('active-changed', activate);
 | 
			
		||||
@@ -443,73 +422,62 @@ const Magnifier = new Lang.Class({
 | 
			
		||||
    //// Private methods ////
 | 
			
		||||
 | 
			
		||||
    _updateMouseSprite: function() {
 | 
			
		||||
        Shell.util_cursor_tracker_to_clutter(this._cursorTracker, this._mouseSprite);
 | 
			
		||||
        let [xHot, yHot] = this._cursorTracker.get_hot();
 | 
			
		||||
        this._xfixesCursor.update_texture_image(this._mouseSprite);
 | 
			
		||||
        let xHot = this._xfixesCursor.get_hot_x();
 | 
			
		||||
        let yHot = this._xfixesCursor.get_hot_y();
 | 
			
		||||
        this._mouseSprite.set_anchor_point(xHot, yHot);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _settingsInitRegion: function(zoomRegion) {
 | 
			
		||||
        // Mag factor is accurate to two decimal places.
 | 
			
		||||
        let aPref = parseFloat(this._settings.get_double(MAG_FACTOR_KEY).toFixed(2));
 | 
			
		||||
        if (aPref != 0.0)
 | 
			
		||||
            zoomRegion.setMagFactor(aPref, aPref);
 | 
			
		||||
 | 
			
		||||
        aPref = this._settings.get_enum(SCREEN_POSITION_KEY);
 | 
			
		||||
        if (aPref)
 | 
			
		||||
            zoomRegion.setScreenPosition(aPref);
 | 
			
		||||
 | 
			
		||||
        zoomRegion.setLensMode(this._settings.get_boolean(LENS_MODE_KEY));
 | 
			
		||||
        zoomRegion.setClampScrollingAtEdges(!this._settings.get_boolean(CLAMP_MODE_KEY));
 | 
			
		||||
 | 
			
		||||
        aPref = this._settings.get_enum(MOUSE_TRACKING_KEY);
 | 
			
		||||
        if (aPref)
 | 
			
		||||
            zoomRegion.setMouseTrackingMode(aPref);
 | 
			
		||||
 | 
			
		||||
        aPref = this._settings.get_enum(FOCUS_TRACKING_KEY);
 | 
			
		||||
        if (aPref)
 | 
			
		||||
            zoomRegion.setFocusTrackingMode(aPref);
 | 
			
		||||
 | 
			
		||||
        aPref = this._settings.get_enum(CARET_TRACKING_KEY);
 | 
			
		||||
        if (aPref)
 | 
			
		||||
            zoomRegion.setCaretTrackingMode(aPref);
 | 
			
		||||
 | 
			
		||||
        aPref = this._settings.get_boolean(INVERT_LIGHTNESS_KEY);
 | 
			
		||||
        if (aPref)
 | 
			
		||||
            zoomRegion.setInvertLightness(aPref);
 | 
			
		||||
 | 
			
		||||
        aPref = this._settings.get_double(COLOR_SATURATION_KEY);
 | 
			
		||||
        if (aPref)
 | 
			
		||||
            zoomRegion.setColorSaturation(aPref);
 | 
			
		||||
 | 
			
		||||
        let bc = {};
 | 
			
		||||
        bc.r = this._settings.get_double(BRIGHT_RED_KEY);
 | 
			
		||||
        bc.g = this._settings.get_double(BRIGHT_GREEN_KEY);
 | 
			
		||||
        bc.b = this._settings.get_double(BRIGHT_BLUE_KEY);
 | 
			
		||||
        zoomRegion.setBrightness(bc);
 | 
			
		||||
 | 
			
		||||
        bc.r = this._settings.get_double(CONTRAST_RED_KEY);
 | 
			
		||||
        bc.g = this._settings.get_double(CONTRAST_GREEN_KEY);
 | 
			
		||||
        bc.b = this._settings.get_double(CONTRAST_BLUE_KEY);
 | 
			
		||||
        zoomRegion.setContrast(bc);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _settingsInit: function() {
 | 
			
		||||
    _settingsInit: function(zoomRegion) {
 | 
			
		||||
        this._appSettings = new Gio.Settings({ schema: APPLICATIONS_SCHEMA });
 | 
			
		||||
        this._settings = new Gio.Settings({ schema: MAGNIFIER_SCHEMA });
 | 
			
		||||
 | 
			
		||||
        this._appSettings.connect('changed::' + SHOW_KEY, Lang.bind(this, function() {
 | 
			
		||||
            let active = this._appSettings.get_boolean(SHOW_KEY);
 | 
			
		||||
            this.setActive(active);
 | 
			
		||||
        }));
 | 
			
		||||
        if (zoomRegion) {
 | 
			
		||||
            // Mag factor is accurate to two decimal places.
 | 
			
		||||
            let aPref = parseFloat(this._settings.get_double(MAG_FACTOR_KEY).toFixed(2));
 | 
			
		||||
            if (aPref != 0.0)
 | 
			
		||||
                zoomRegion.setMagFactor(aPref, aPref);
 | 
			
		||||
 | 
			
		||||
        return this._appSettings.get_boolean(SHOW_KEY);
 | 
			
		||||
    },
 | 
			
		||||
            aPref = this._settings.get_enum(SCREEN_POSITION_KEY);
 | 
			
		||||
            if (aPref)
 | 
			
		||||
                zoomRegion.setScreenPosition(aPref);
 | 
			
		||||
 | 
			
		||||
            zoomRegion.setLensMode(this._settings.get_boolean(LENS_MODE_KEY));
 | 
			
		||||
            zoomRegion.setClampScrollingAtEdges(!this._settings.get_boolean(CLAMP_MODE_KEY));
 | 
			
		||||
 | 
			
		||||
            aPref = this._settings.get_enum(MOUSE_TRACKING_KEY);
 | 
			
		||||
            if (aPref)
 | 
			
		||||
                zoomRegion.setMouseTrackingMode(aPref);
 | 
			
		||||
 | 
			
		||||
            aPref = this._settings.get_boolean(INVERT_LIGHTNESS_KEY);
 | 
			
		||||
            if (aPref)
 | 
			
		||||
                zoomRegion.setInvertLightness(aPref);
 | 
			
		||||
 | 
			
		||||
            aPref = this._settings.get_double(COLOR_SATURATION_KEY);
 | 
			
		||||
            if (aPref)
 | 
			
		||||
                zoomRegion.setColorSaturation(aPref);
 | 
			
		||||
 | 
			
		||||
            let bc = {};
 | 
			
		||||
            bc.r = this._settings.get_double(BRIGHT_RED_KEY);
 | 
			
		||||
            bc.g = this._settings.get_double(BRIGHT_GREEN_KEY);
 | 
			
		||||
            bc.b = this._settings.get_double(BRIGHT_BLUE_KEY);
 | 
			
		||||
            zoomRegion.setBrightness(bc);
 | 
			
		||||
 | 
			
		||||
            bc.r = this._settings.get_double(CONTRAST_RED_KEY);
 | 
			
		||||
            bc.g = this._settings.get_double(CONTRAST_GREEN_KEY);
 | 
			
		||||
            bc.b = this._settings.get_double(CONTRAST_BLUE_KEY);
 | 
			
		||||
            zoomRegion.setContrast(bc);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    _settingsInitLate: function() {
 | 
			
		||||
        let showCrosshairs = this._settings.get_boolean(SHOW_CROSS_HAIRS_KEY);
 | 
			
		||||
        this.addCrosshairs();
 | 
			
		||||
        this.setCrosshairsVisible(showCrosshairs);
 | 
			
		||||
 | 
			
		||||
        this._appSettings.connect('changed::' + SHOW_KEY,
 | 
			
		||||
                                  Lang.bind(this, function() {
 | 
			
		||||
            this.setActive(this._appSettings.get_boolean(SHOW_KEY));
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        this._settings.connect('changed::' + SCREEN_POSITION_KEY,
 | 
			
		||||
                               Lang.bind(this, this._updateScreenPosition));
 | 
			
		||||
        this._settings.connect('changed::' + MAG_FACTOR_KEY,
 | 
			
		||||
@@ -520,10 +488,6 @@ const Magnifier = new Lang.Class({
 | 
			
		||||
                               Lang.bind(this, this._updateClampMode));
 | 
			
		||||
        this._settings.connect('changed::' + MOUSE_TRACKING_KEY,
 | 
			
		||||
                               Lang.bind(this, this._updateMouseTrackingMode));
 | 
			
		||||
        this._settings.connect('changed::' + FOCUS_TRACKING_KEY,
 | 
			
		||||
                               Lang.bind(this, this._updateFocusTrackingMode));
 | 
			
		||||
        this._settings.connect('changed::' + CARET_TRACKING_KEY,
 | 
			
		||||
                               Lang.bind(this, this._updateCaretTrackingMode));
 | 
			
		||||
 | 
			
		||||
        this._settings.connect('changed::' + INVERT_LIGHTNESS_KEY,
 | 
			
		||||
                               Lang.bind(this, this._updateInvertLightness));
 | 
			
		||||
@@ -573,6 +537,8 @@ const Magnifier = new Lang.Class({
 | 
			
		||||
                               Lang.bind(this, function() {
 | 
			
		||||
            this.setCrosshairsClip(this._settings.get_boolean(CROSS_HAIRS_CLIP_KEY));
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        return this._appSettings.get_boolean(SHOW_KEY);
 | 
			
		||||
   },
 | 
			
		||||
 | 
			
		||||
    _updateScreenPosition: function() {
 | 
			
		||||
@@ -619,24 +585,6 @@ const Magnifier = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateFocusTrackingMode: function() {
 | 
			
		||||
        // Applies only to the first zoom region.
 | 
			
		||||
        if (this._zoomRegions.length) {
 | 
			
		||||
            this._zoomRegions[0].setFocusTrackingMode(
 | 
			
		||||
                this._settings.get_enum(FOCUS_TRACKING_KEY)
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateCaretTrackingMode: function() {
 | 
			
		||||
        // Applies only to the first zoom region.
 | 
			
		||||
        if (this._zoomRegions.length) {
 | 
			
		||||
            this._zoomRegions[0].setCaretTrackingMode(
 | 
			
		||||
                this._settings.get_enum(CARET_TRACKING_KEY)
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateInvertLightness: function() {
 | 
			
		||||
        // Applies only to the first zoom region.
 | 
			
		||||
        if (this._zoomRegions.length) {
 | 
			
		||||
@@ -675,7 +623,7 @@ const Magnifier = new Lang.Class({
 | 
			
		||||
            contrast.b = this._settings.get_double(CONTRAST_BLUE_KEY);
 | 
			
		||||
            this._zoomRegions[0].setContrast(contrast);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(Magnifier.prototype);
 | 
			
		||||
 | 
			
		||||
@@ -684,11 +632,8 @@ const ZoomRegion = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _init: function(magnifier, mouseSourceActor) {
 | 
			
		||||
        this._magnifier = magnifier;
 | 
			
		||||
        this._focusCaretTracker = new FocusCaretTracker.FocusCaretTracker();
 | 
			
		||||
 | 
			
		||||
        this._mouseTrackingMode = GDesktopEnums.MagnifierMouseTrackingMode.NONE;
 | 
			
		||||
        this._focusTrackingMode = GDesktopEnums.MagnifierFocusTrackingMode.NONE;
 | 
			
		||||
        this._caretTrackingMode = GDesktopEnums.MagnifierCaretTrackingMode.NONE;
 | 
			
		||||
        this._clampScrollingAtEdges = false;
 | 
			
		||||
        this._lensMode = false;
 | 
			
		||||
        this._screenPosition = GDesktopEnums.MagnifierScreenPosition.FULL_SCREEN;
 | 
			
		||||
@@ -714,35 +659,9 @@ const ZoomRegion = new Lang.Class({
 | 
			
		||||
        this._xMagFactor = 1;
 | 
			
		||||
        this._yMagFactor = 1;
 | 
			
		||||
        this._followingCursor = false;
 | 
			
		||||
        this._xFocus = 0;
 | 
			
		||||
        this._yFocus = 0;
 | 
			
		||||
        this._xCaret = 0;
 | 
			
		||||
        this._yCaret = 0;
 | 
			
		||||
 | 
			
		||||
        Main.layoutManager.connect('monitors-changed',
 | 
			
		||||
                                   Lang.bind(this, this._monitorsChanged));
 | 
			
		||||
        this._focusCaretTracker.connect('caret-moved',
 | 
			
		||||
                                    Lang.bind(this, this._updateCaret));
 | 
			
		||||
        this._focusCaretTracker.connect('focus-changed',
 | 
			
		||||
                                    Lang.bind(this, this._updateFocus));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateFocus: function(caller, event) {
 | 
			
		||||
        let component = event.source.get_component_iface();
 | 
			
		||||
        if (!component || event.detail1 != 1)
 | 
			
		||||
            return;
 | 
			
		||||
        let extents = component.get_extents(Atspi.CoordType.SCREEN);
 | 
			
		||||
        [this._xFocus, this._yFocus] = [extents.x, extents.y]
 | 
			
		||||
        this._centerFromFocusPosition();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateCaret: function(caller, event) {
 | 
			
		||||
        let text = event.source.get_text_iface();
 | 
			
		||||
        if (!text)
 | 
			
		||||
            return;
 | 
			
		||||
        let extents = text.get_character_extents(text.get_caret_offset(), 0);
 | 
			
		||||
        [this._xCaret, this._yCaret] = [extents.x, extents.y];
 | 
			
		||||
        this._centerFromCaretPosition();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -750,17 +669,14 @@ const ZoomRegion = new Lang.Class({
 | 
			
		||||
     * @activate:   Boolean to show/hide the ZoomRegion.
 | 
			
		||||
     */
 | 
			
		||||
    setActive: function(activate) {
 | 
			
		||||
        if (activate == this.isActive())
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (activate) {
 | 
			
		||||
        if (activate && !this.isActive()) {
 | 
			
		||||
            this._createActors();
 | 
			
		||||
            if (this._isMouseOverRegion())
 | 
			
		||||
                this._magnifier.hideSystemCursor();
 | 
			
		||||
            this._updateMagViewGeometry();
 | 
			
		||||
            this._updateCloneGeometry();
 | 
			
		||||
            this._updateMousePosition();
 | 
			
		||||
        } else {
 | 
			
		||||
        } else if (!activate && this.isActive()) {
 | 
			
		||||
            this._destroyActors();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
@@ -816,30 +732,6 @@ const ZoomRegion = new Lang.Class({
 | 
			
		||||
        return this._mouseTrackingMode;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * setFocusTrackingMode
 | 
			
		||||
     * @mode:     One of the enum FocusTrackingMode values.
 | 
			
		||||
     */
 | 
			
		||||
    setFocusTrackingMode: function(mode) {
 | 
			
		||||
        this._focusTrackingMode = mode;
 | 
			
		||||
        if (this._focusTrackingMode == GDesktopEnums.MagnifierFocusTrackingMode.NONE)
 | 
			
		||||
            this._focusCaretTracker.deregisterFocusListener();
 | 
			
		||||
        else
 | 
			
		||||
            this._focusCaretTracker.registerFocusListener();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * setCaretTrackingMode
 | 
			
		||||
     * @mode:     One of the enum CaretTrackingMode values.
 | 
			
		||||
     */
 | 
			
		||||
    setCaretTrackingMode: function(mode) {
 | 
			
		||||
        this._caretTrackingMode = mode;
 | 
			
		||||
        if (this._caretTrackingMode == GDesktopEnums.MagnifierCaretTrackingMode.NONE)
 | 
			
		||||
            this._focusCaretTracker.deregisterCaretListener();
 | 
			
		||||
        else
 | 
			
		||||
            this._focusCaretTracker.registerCaretListener();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * setViewPort
 | 
			
		||||
     * Sets the position and size of the ZoomRegion on screen.
 | 
			
		||||
@@ -1131,6 +1023,20 @@ const ZoomRegion = new Lang.Class({
 | 
			
		||||
            this._magShaderEffects.setBrightness(this._brightness);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * getBrightness:
 | 
			
		||||
     * Retrive the current brightness of the Zoom Region.
 | 
			
		||||
     * @return  Object containing the brightness change for the red, green,
 | 
			
		||||
     *          and blue channels.
 | 
			
		||||
     */
 | 
			
		||||
    getBrightness: function() {
 | 
			
		||||
        let brightness = {};
 | 
			
		||||
        brightness.r = this._brightness.r;
 | 
			
		||||
        brightness.g = this._brightness.g;
 | 
			
		||||
        brightness.b = this._brightness.b;
 | 
			
		||||
        return brightness;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * setContrast:
 | 
			
		||||
     * Alter the contrast of the magnified view.
 | 
			
		||||
@@ -1337,47 +1243,19 @@ const ZoomRegion = new Lang.Class({
 | 
			
		||||
        let yMouse = this._magnifier.yMouse;
 | 
			
		||||
 | 
			
		||||
        if (this._mouseTrackingMode == GDesktopEnums.MagnifierMouseTrackingMode.PROPORTIONAL) {
 | 
			
		||||
            return this._centerFromPointProportional(xMouse, yMouse);
 | 
			
		||||
            return this._centerFromMouseProportional(xMouse, yMouse);
 | 
			
		||||
        }
 | 
			
		||||
        else if (this._mouseTrackingMode == GDesktopEnums.MagnifierMouseTrackingMode.PUSH) {
 | 
			
		||||
            return this._centerFromPointPush(xMouse, yMouse);
 | 
			
		||||
            return this._centerFromMousePush(xMouse, yMouse);
 | 
			
		||||
        }
 | 
			
		||||
        else if (this._mouseTrackingMode == GDesktopEnums.MagnifierMouseTrackingMode.CENTERED) {
 | 
			
		||||
            return this._centerFromPointCentered(xMouse, yMouse);
 | 
			
		||||
            return this._centerFromMouseCentered(xMouse, yMouse);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return null; // Should never be hit
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _centerFromCaretPosition: function() {
 | 
			
		||||
        let xCaret = this._xCaret;
 | 
			
		||||
        let yCaret = this._yCaret;
 | 
			
		||||
 | 
			
		||||
        if (this._caretTrackingMode == GDesktopEnums.MagnifierCaretTrackingMode.PROPORTIONAL)
 | 
			
		||||
            [xCaret, yCaret] = this._centerFromPointProportional(xCaret, yCaret);
 | 
			
		||||
        else if (this._caretTrackingMode == GDesktopEnums.MagnifierCaretTrackingMode.PUSH)
 | 
			
		||||
            [xCaret, yCaret] = this._centerFromPointPush(xCaret, yCaret);
 | 
			
		||||
        else if (this._caretTrackingMode == GDesktopEnums.MagnifierCaretTrackingMode.CENTERED)
 | 
			
		||||
            [xCaret, yCaret] = this._centerFromPointCentered(xCaret, yCaret);
 | 
			
		||||
 | 
			
		||||
        this.scrollContentsTo(xCaret, yCaret);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _centerFromFocusPosition: function() {
 | 
			
		||||
        let xFocus = this._xFocus;
 | 
			
		||||
        let yFocus = this._yFocus;
 | 
			
		||||
 | 
			
		||||
        if (this._focusTrackingMode == GDesktopEnums.MagnifierFocusTrackingMode.PROPORTIONAL)
 | 
			
		||||
            [xFocus, yFocus] = this._centerFromPointProportional(xFocus, yFocus);
 | 
			
		||||
        else if (this._focusTrackingMode == GDesktopEnums.MagnifierFocusTrackingMode.PUSH)
 | 
			
		||||
            [xFocus, yFocus] = this._centerFromPointPush(xFocus, yFocus);
 | 
			
		||||
        else if (this._focusTrackingMode == GDesktopEnums.MagnifierFocusTrackingMode.CENTERED)
 | 
			
		||||
            [xFocus, yFocus] = this._centerFromPointCentered(xFocus, yFocus);
 | 
			
		||||
 | 
			
		||||
        this.scrollContentsTo(xFocus, yFocus);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _centerFromPointPush: function(xPoint, yPoint) {
 | 
			
		||||
    _centerFromMousePush: function(xMouse, yMouse) {
 | 
			
		||||
        let [xRoi, yRoi, widthRoi, heightRoi] = this.getROI();
 | 
			
		||||
        let [cursorWidth, cursorHeight] = this._mouseSourceActor.get_size();
 | 
			
		||||
        let xPos = xRoi + widthRoi / 2;
 | 
			
		||||
@@ -1385,20 +1263,20 @@ const ZoomRegion = new Lang.Class({
 | 
			
		||||
        let xRoiRight = xRoi + widthRoi - cursorWidth;
 | 
			
		||||
        let yRoiBottom = yRoi + heightRoi - cursorHeight;
 | 
			
		||||
 | 
			
		||||
        if (xPoint < xRoi)
 | 
			
		||||
            xPos -= (xRoi - xPoint);
 | 
			
		||||
        else if (xPoint > xRoiRight)
 | 
			
		||||
            xPos += (xPoint - xRoiRight);
 | 
			
		||||
        if (xMouse < xRoi)
 | 
			
		||||
            xPos -= (xRoi - xMouse);
 | 
			
		||||
        else if (xMouse > xRoiRight)
 | 
			
		||||
            xPos += (xMouse - xRoiRight);
 | 
			
		||||
 | 
			
		||||
        if (yPoint < yRoi)
 | 
			
		||||
            yPos -= (yRoi - yPoint);
 | 
			
		||||
        else if (yPoint > yRoiBottom)
 | 
			
		||||
            yPos += (yPoint - yRoiBottom);
 | 
			
		||||
        if (yMouse < yRoi)
 | 
			
		||||
            yPos -= (yRoi - yMouse);
 | 
			
		||||
        else if (yMouse > yRoiBottom)
 | 
			
		||||
            yPos += (yMouse - yRoiBottom);
 | 
			
		||||
 | 
			
		||||
        return [xPos, yPos];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _centerFromPointProportional: function(xPoint, yPoint) {
 | 
			
		||||
    _centerFromMouseProportional: function(xMouse, yMouse) {
 | 
			
		||||
        let [xRoi, yRoi, widthRoi, heightRoi] = this.getROI();
 | 
			
		||||
        let halfScreenWidth = global.screen_width / 2;
 | 
			
		||||
        let halfScreenHeight = global.screen_height / 2;
 | 
			
		||||
@@ -1407,16 +1285,16 @@ const ZoomRegion = new Lang.Class({
 | 
			
		||||
        let unscaledPadding = Math.min(this._viewPortWidth, this._viewPortHeight) / 5;
 | 
			
		||||
        let xPadding = unscaledPadding / this._xMagFactor;
 | 
			
		||||
        let yPadding = unscaledPadding / this._yMagFactor;
 | 
			
		||||
        let xProportion = (xPoint - halfScreenWidth) / halfScreenWidth;   // -1 ... 1
 | 
			
		||||
        let yProportion = (yPoint - halfScreenHeight) / halfScreenHeight; // -1 ... 1
 | 
			
		||||
        let xPos = xPoint - xProportion * (widthRoi / 2 - xPadding);
 | 
			
		||||
        let yPos = yPoint - yProportion * (heightRoi /2 - yPadding);
 | 
			
		||||
        let xProportion = (xMouse - halfScreenWidth) / halfScreenWidth;   // -1 ... 1
 | 
			
		||||
        let yProportion = (yMouse - halfScreenHeight) / halfScreenHeight; // -1 ... 1
 | 
			
		||||
        let xPos = xMouse - xProportion * (widthRoi / 2 - xPadding);
 | 
			
		||||
        let yPos = yMouse - yProportion * (heightRoi /2 - yPadding);
 | 
			
		||||
 | 
			
		||||
        return [xPos, yPos];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _centerFromPointCentered: function(xPoint, yPoint) {
 | 
			
		||||
        return [xPoint, yPoint];
 | 
			
		||||
    _centerFromMouseCentered: function(xMouse, yMouse) {
 | 
			
		||||
        return [xMouse, yMouse];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _screenToViewPort: function(screenX, screenY) {
 | 
			
		||||
@@ -1633,6 +1511,15 @@ const Crosshairs = new Lang.Class({
 | 
			
		||||
        this._vertBottomHair.set_opacity(opacity);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * getOpacity:
 | 
			
		||||
     * Retriev how opaque the crosshairs are.
 | 
			
		||||
     * @return: A value between 0 (transparent) and 255 (opaque).
 | 
			
		||||
     */
 | 
			
		||||
    getOpacity: function() {
 | 
			
		||||
        return this._horizLeftHair.get_opacity();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * setLength:
 | 
			
		||||
     * Set the length of the vertical and horizontal lines in the crosshairs.
 | 
			
		||||
@@ -1676,6 +1563,15 @@ const Crosshairs = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
     },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * getClip:
 | 
			
		||||
     * Get the dimensions of the clip rectangle.
 | 
			
		||||
     * @return:   An array of the form [width, height].
 | 
			
		||||
     */
 | 
			
		||||
    getClip: function() {
 | 
			
		||||
        return this._clipSize;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * show:
 | 
			
		||||
     * Show the crosshairs.
 | 
			
		||||
@@ -1771,10 +1667,23 @@ const MagShaderEffects = new Lang.Class({
 | 
			
		||||
        this._inverse.set_enabled(invertFlag);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * getInvertLightness:
 | 
			
		||||
     * Report whether the inversion effect is enabled.
 | 
			
		||||
     * @return:     Boolean.
 | 
			
		||||
     */
 | 
			
		||||
    getInvertLightness: function() {
 | 
			
		||||
        return this._inverse.get_enabled();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setColorSaturation: function(factor) {
 | 
			
		||||
        this._colorDesaturation.set_factor(1.0 - factor);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getColorSaturation: function() {
 | 
			
		||||
        return 1.0 - this._colorDesaturation.get_factor();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * setBrightness:
 | 
			
		||||
     * Set the brightness of the magnified view.
 | 
			
		||||
@@ -1799,6 +1708,24 @@ const MagShaderEffects = new Lang.Class({
 | 
			
		||||
        );
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * getBrightness:
 | 
			
		||||
     * Retrieve current brightness of the magnified view.
 | 
			
		||||
     * @return: Object containing the brightness for the red, green,
 | 
			
		||||
     *          and blue channels.  Values of 0.0 represent "standard" 
 | 
			
		||||
     *          brightness (no change), whereas values less or greater than
 | 
			
		||||
     *          0.0 indicate decreased or incresaed brightness, respectively.
 | 
			
		||||
     */
 | 
			
		||||
    getBrightness: function() {
 | 
			
		||||
        let result = {};
 | 
			
		||||
        let [bRed, bGreen, bBlue] = this._brightnessContrast.get_brightness();
 | 
			
		||||
        result.r = bRed;
 | 
			
		||||
        result.g = bGreen;
 | 
			
		||||
        result.b = bBlue;
 | 
			
		||||
 | 
			
		||||
        return result;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Set the contrast of the magnified view.
 | 
			
		||||
     * @contrast:   Object containing the contrast for the red, green,
 | 
			
		||||
@@ -1823,4 +1750,21 @@ const MagShaderEffects = new Lang.Class({
 | 
			
		||||
             bRed != NO_CHANGE || bGreen != NO_CHANGE || bBlue != NO_CHANGE
 | 
			
		||||
        );
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Retrieve current contrast of the magnified view.
 | 
			
		||||
     * @return: Object containing the contrast for the red, green,
 | 
			
		||||
     *          and blue channels.  Values of 0.0 represent "standard"
 | 
			
		||||
     *          contrast (no change), whereas values less or greater than
 | 
			
		||||
     *          0.0 indicate decreased or incresaed contrast, respectively.
 | 
			
		||||
     */
 | 
			
		||||
    getContrast: function() {
 | 
			
		||||
        let resutl = {};
 | 
			
		||||
        let [cRed, cGreen, cBlue] = this._brightnessContrast.get_contrast();
 | 
			
		||||
        result.r = cRed;
 | 
			
		||||
        result.g = cGreen;
 | 
			
		||||
        result.b = cBlue;
 | 
			
		||||
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -4,12 +4,14 @@ const Gio = imports.gi.Gio;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
 | 
			
		||||
const MAG_SERVICE_NAME = 'org.gnome.Magnifier';
 | 
			
		||||
const MAG_SERVICE_PATH = '/org/gnome/Magnifier';
 | 
			
		||||
const ZOOM_SERVICE_NAME = 'org.gnome.Magnifier.ZoomRegion';
 | 
			
		||||
const ZOOM_SERVICE_PATH = '/org/gnome/Magnifier/ZoomRegion';
 | 
			
		||||
 | 
			
		||||
// Subset of gnome-mag's Magnifier dbus interface -- to be expanded.  See:
 | 
			
		||||
// http://git.gnome.org/browse/gnome-mag/tree/xml/...Magnifier.xml
 | 
			
		||||
const MagnifierIface = <interface name="org.gnome.Magnifier">
 | 
			
		||||
const MagnifierIface = <interface name={MAG_SERVICE_NAME}>
 | 
			
		||||
<method name="setActive">
 | 
			
		||||
    <arg type="b" direction="in" />
 | 
			
		||||
</method>
 | 
			
		||||
@@ -64,7 +66,7 @@ const MagnifierIface = <interface name="org.gnome.Magnifier">
 | 
			
		||||
 | 
			
		||||
// Subset of gnome-mag's ZoomRegion dbus interface -- to be expanded.  See:
 | 
			
		||||
// http://git.gnome.org/browse/gnome-mag/tree/xml/...ZoomRegion.xml
 | 
			
		||||
const ZoomRegionIface = <interface name="org.gnome.Magnifier.ZoomRegion">
 | 
			
		||||
const ZoomRegionIface = <interface name={ZOOM_SERVICE_NAME}>
 | 
			
		||||
<method name="setMagFactor">
 | 
			
		||||
    <arg type="d" direction="in" />
 | 
			
		||||
    <arg type="d" direction="in" />
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										431
									
								
								js/ui/main.js
									
									
									
									
									
								
							
							
						
						@@ -18,31 +18,40 @@ const ExtensionSystem = imports.ui.extensionSystem;
 | 
			
		||||
const ExtensionDownloader = imports.ui.extensionDownloader;
 | 
			
		||||
const Keyboard = imports.ui.keyboard;
 | 
			
		||||
const MessageTray = imports.ui.messageTray;
 | 
			
		||||
const OsdWindow = imports.ui.osdWindow;
 | 
			
		||||
const Overview = imports.ui.overview;
 | 
			
		||||
const Panel = imports.ui.panel;
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
const RunDialog = imports.ui.runDialog;
 | 
			
		||||
const Layout = imports.ui.layout;
 | 
			
		||||
const LoginManager = imports.misc.loginManager;
 | 
			
		||||
const LookingGlass = imports.ui.lookingGlass;
 | 
			
		||||
const NotificationDaemon = imports.ui.notificationDaemon;
 | 
			
		||||
const WindowAttentionHandler = imports.ui.windowAttentionHandler;
 | 
			
		||||
const Screencast = imports.ui.screencast;
 | 
			
		||||
const ScreenShield = imports.ui.screenShield;
 | 
			
		||||
const Scripting = imports.ui.scripting;
 | 
			
		||||
const SessionMode = imports.ui.sessionMode;
 | 
			
		||||
const ShellDBus = imports.ui.shellDBus;
 | 
			
		||||
const ShellMountOperation = imports.ui.shellMountOperation;
 | 
			
		||||
const UnlockDialog = imports.ui.unlockDialog;
 | 
			
		||||
const WindowManager = imports.ui.windowManager;
 | 
			
		||||
const Magnifier = imports.ui.magnifier;
 | 
			
		||||
const XdndHandler = imports.ui.xdndHandler;
 | 
			
		||||
const Util = imports.misc.util;
 | 
			
		||||
 | 
			
		||||
const OVERRIDES_SCHEMA = 'org.gnome.shell.overrides';
 | 
			
		||||
const DEFAULT_BACKGROUND_COLOR = Clutter.Color.from_pixel(0x2e3436ff);
 | 
			
		||||
 | 
			
		||||
const A11Y_SCHEMA = 'org.gnome.desktop.a11y.keyboard';
 | 
			
		||||
const STICKY_KEYS_ENABLE = 'stickykeys-enable';
 | 
			
		||||
const KeybindingMode = {
 | 
			
		||||
    NONE:          0,       // block all keybindings
 | 
			
		||||
    NORMAL:        1 << 0,  // window mode
 | 
			
		||||
    OVERVIEW:      1 << 1,
 | 
			
		||||
    LOCK_SCREEN:   1 << 2,
 | 
			
		||||
    UNLOCK_SCREEN: 1 << 3,
 | 
			
		||||
    LOGIN_SCREEN:  1 << 4,
 | 
			
		||||
    MESSAGE_TRAY:  1 << 5,
 | 
			
		||||
    SYSTEM_MODAL:  1 << 6,
 | 
			
		||||
    LOOKING_GLASS: 1 << 7,
 | 
			
		||||
    ALL:           ~0,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
let componentManager = null;
 | 
			
		||||
let panel = null;
 | 
			
		||||
@@ -55,14 +64,12 @@ let screenShield = null;
 | 
			
		||||
let notificationDaemon = null;
 | 
			
		||||
let windowAttentionHandler = null;
 | 
			
		||||
let ctrlAltTabManager = null;
 | 
			
		||||
let osdWindow = null;
 | 
			
		||||
let sessionMode = null;
 | 
			
		||||
let shellDBusService = null;
 | 
			
		||||
let shellMountOpDBusService = null;
 | 
			
		||||
let screenSaverDBus = null;
 | 
			
		||||
let screencastService = null;
 | 
			
		||||
let modalCount = 0;
 | 
			
		||||
let keybindingMode = Shell.KeyBindingMode.NONE;
 | 
			
		||||
let keybindingMode = KeybindingMode.NORMAL;
 | 
			
		||||
let modalActorFocusStack = [];
 | 
			
		||||
let uiGroup = null;
 | 
			
		||||
let magnifier = null;
 | 
			
		||||
@@ -72,30 +79,24 @@ let layoutManager = null;
 | 
			
		||||
let _startDate;
 | 
			
		||||
let _defaultCssStylesheet = null;
 | 
			
		||||
let _cssStylesheet = null;
 | 
			
		||||
let _a11ySettings = null;
 | 
			
		||||
let dynamicWorkspacesSchema = null;
 | 
			
		||||
let _overridesSettings = null;
 | 
			
		||||
 | 
			
		||||
let background = null;
 | 
			
		||||
 | 
			
		||||
function _sessionUpdated() {
 | 
			
		||||
    _loadDefaultStylesheet();
 | 
			
		||||
 | 
			
		||||
    wm.setCustomKeybindingHandler('panel-main-menu',
 | 
			
		||||
                                  Shell.KeyBindingMode.NORMAL |
 | 
			
		||||
                                  Shell.KeyBindingMode.OVERVIEW,
 | 
			
		||||
                                  KeybindingMode.NORMAL |
 | 
			
		||||
                                  KeybindingMode.OVERVIEW,
 | 
			
		||||
                                  sessionMode.hasOverview ? Lang.bind(overview, overview.toggle) : null);
 | 
			
		||||
    wm.allowKeybinding('overlay-key', Shell.KeyBindingMode.NORMAL |
 | 
			
		||||
                                      Shell.KeyBindingMode.OVERVIEW);
 | 
			
		||||
    wm.allowKeybinding('overlay-key', KeybindingMode.NORMAL |
 | 
			
		||||
                                      KeybindingMode.OVERVIEW);
 | 
			
		||||
 | 
			
		||||
    wm.setCustomKeybindingHandler('panel-run-dialog',
 | 
			
		||||
                                  Shell.KeyBindingMode.NORMAL |
 | 
			
		||||
                                  Shell.KeyBindingMode.OVERVIEW,
 | 
			
		||||
                                  KeybindingMode.NORMAL |
 | 
			
		||||
                                  KeybindingMode.OVERVIEW,
 | 
			
		||||
                                  sessionMode.hasRunDialog ? openRunDialog : null);
 | 
			
		||||
 | 
			
		||||
    if (!sessionMode.hasRunDialog) {
 | 
			
		||||
        if (runDialog)
 | 
			
		||||
            runDialog.close();
 | 
			
		||||
        if (lookingGlass)
 | 
			
		||||
            lookingGlass.close();
 | 
			
		||||
    }
 | 
			
		||||
    if (sessionMode.isGreeter)
 | 
			
		||||
        screenShield.showDialog();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function start() {
 | 
			
		||||
@@ -103,42 +104,15 @@ function start() {
 | 
			
		||||
    global.logError = window.log;
 | 
			
		||||
    global.log = window.log;
 | 
			
		||||
 | 
			
		||||
    if (!Meta.is_wayland_compositor)
 | 
			
		||||
        Meta.is_wayland_compositor = function () { return false; };
 | 
			
		||||
 | 
			
		||||
    // Chain up async errors reported from C
 | 
			
		||||
    global.connect('notify-error', function (global, msg, detail) { notifyError(msg, detail); });
 | 
			
		||||
 | 
			
		||||
    Gio.DesktopAppInfo.set_desktop_env('GNOME');
 | 
			
		||||
 | 
			
		||||
    sessionMode = new SessionMode.SessionMode();
 | 
			
		||||
    sessionMode.connect('sessions-loaded', _sessionsLoaded);
 | 
			
		||||
    sessionMode.init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _sessionsLoaded() {
 | 
			
		||||
    sessionMode.connect('updated', _sessionUpdated);
 | 
			
		||||
    _initializePrefs();
 | 
			
		||||
    _initializeUI();
 | 
			
		||||
 | 
			
		||||
    shellDBusService = new ShellDBus.GnomeShell();
 | 
			
		||||
    shellMountOpDBusService = new ShellMountOperation.GnomeShellMountOpHandler();
 | 
			
		||||
 | 
			
		||||
    _sessionUpdated();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _initializePrefs() {
 | 
			
		||||
    let keys = new Gio.Settings({ schema: sessionMode.overridesSchema }).list_keys();
 | 
			
		||||
    for (let i = 0; i < keys.length; i++)
 | 
			
		||||
        Meta.prefs_override_preference_schema(keys[i], sessionMode.overridesSchema);
 | 
			
		||||
 | 
			
		||||
    if (keys.indexOf('dynamic-workspaces') > -1)
 | 
			
		||||
        dynamicWorkspacesSchema = sessionMode.overridesSchema;
 | 
			
		||||
    else
 | 
			
		||||
        dynamicWorkspacesSchema = 'org.gnome.mutter';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _initializeUI() {
 | 
			
		||||
    // Ensure ShellWindowTracker and ShellAppUsage are initialized; this will
 | 
			
		||||
    // also initialize ShellAppSystem first.  ShellAppSystem
 | 
			
		||||
    // needs to load all the .desktop files, and ShellWindowTracker
 | 
			
		||||
@@ -147,29 +121,52 @@ function _initializeUI() {
 | 
			
		||||
    // and recalculate application associations, so to avoid
 | 
			
		||||
    // races for now we initialize it here.  It's better to
 | 
			
		||||
    // be predictable anyways.
 | 
			
		||||
    Shell.WindowTracker.get_default();
 | 
			
		||||
    let tracker = Shell.WindowTracker.get_default();
 | 
			
		||||
    Shell.AppUsage.get_default();
 | 
			
		||||
 | 
			
		||||
    _loadDefaultStylesheet();
 | 
			
		||||
    tracker.connect('startup-sequence-changed', _queueCheckWorkspaces);
 | 
			
		||||
 | 
			
		||||
    // The stage is always covered so Clutter doesn't need to clear it; however
 | 
			
		||||
    // the color is used as the default contents for the Mutter root background
 | 
			
		||||
    // actor so set it anyways.
 | 
			
		||||
    global.stage.color = DEFAULT_BACKGROUND_COLOR;
 | 
			
		||||
    global.stage.no_clear_hint = true;
 | 
			
		||||
 | 
			
		||||
    _defaultCssStylesheet = global.datadir + '/theme/gnome-shell.css';
 | 
			
		||||
    loadTheme();
 | 
			
		||||
 | 
			
		||||
    // Set up stage hierarchy to group all UI actors under one container.
 | 
			
		||||
    uiGroup = new Shell.GenericContainer({ name: 'uiGroup' });
 | 
			
		||||
    uiGroup.connect('allocate',
 | 
			
		||||
                    function (actor, box, flags) {
 | 
			
		||||
                        let children = uiGroup.get_children();
 | 
			
		||||
                        for (let i = 0; i < children.length; i++)
 | 
			
		||||
                            children[i].allocate_preferred_size(flags);
 | 
			
		||||
                    });
 | 
			
		||||
    uiGroup.connect('get-preferred-width',
 | 
			
		||||
                    function(actor, forHeight, alloc) {
 | 
			
		||||
                        let width = global.stage.width;
 | 
			
		||||
                        [alloc.min_size, alloc.natural_size] = [width, width];
 | 
			
		||||
                    });
 | 
			
		||||
    uiGroup.connect('get-preferred-height',
 | 
			
		||||
                    function(actor, forWidth, alloc) {
 | 
			
		||||
                        let height = global.stage.height;
 | 
			
		||||
                        [alloc.min_size, alloc.natural_size] = [height, height];
 | 
			
		||||
                    });
 | 
			
		||||
    global.window_group.reparent(uiGroup);
 | 
			
		||||
    global.overlay_group.reparent(uiGroup);
 | 
			
		||||
    global.stage.add_actor(uiGroup);
 | 
			
		||||
 | 
			
		||||
    // Setup the stage hierarchy early
 | 
			
		||||
    layoutManager = new Layout.LayoutManager();
 | 
			
		||||
 | 
			
		||||
    // Various parts of the codebase still refers to Main.uiGroup
 | 
			
		||||
    // instead using the layoutManager.  This keeps that code
 | 
			
		||||
    // working until it's updated.
 | 
			
		||||
    uiGroup = layoutManager.uiGroup;
 | 
			
		||||
 | 
			
		||||
    screencastService = new Screencast.ScreencastService();
 | 
			
		||||
    xdndHandler = new XdndHandler.XdndHandler();
 | 
			
		||||
    ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
 | 
			
		||||
    osdWindow = new OsdWindow.OsdWindow();
 | 
			
		||||
    overview = new Overview.Overview();
 | 
			
		||||
    wm = new WindowManager.WindowManager();
 | 
			
		||||
    magnifier = new Magnifier.Magnifier();
 | 
			
		||||
    if (LoginManager.canLock())
 | 
			
		||||
    if (UnlockDialog.isSupported())
 | 
			
		||||
        screenShield = new ScreenShield.ScreenShield();
 | 
			
		||||
 | 
			
		||||
    else
 | 
			
		||||
        screenShield = new ScreenShield.ScreenShieldFallback();
 | 
			
		||||
    panel = new Panel.Panel();
 | 
			
		||||
    messageTray = new MessageTray.MessageTray();
 | 
			
		||||
    keyboard = new Keyboard.Keyboard();
 | 
			
		||||
@@ -178,22 +175,19 @@ function _initializeUI() {
 | 
			
		||||
    componentManager = new Components.ComponentManager();
 | 
			
		||||
 | 
			
		||||
    layoutManager.init();
 | 
			
		||||
    keyboard.init();
 | 
			
		||||
    overview.init();
 | 
			
		||||
 | 
			
		||||
    _a11ySettings = new Gio.Settings({ schema: A11Y_SCHEMA });
 | 
			
		||||
 | 
			
		||||
    global.display.connect('overlay-key', Lang.bind(overview, function () {
 | 
			
		||||
        if (!_a11ySettings.get_boolean (STICKY_KEYS_ENABLE))
 | 
			
		||||
            overview.toggle();
 | 
			
		||||
    }));
 | 
			
		||||
    global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT,
 | 
			
		||||
                                            false, -1, 1);
 | 
			
		||||
    global.display.connect('overlay-key', Lang.bind(overview, overview.toggle));
 | 
			
		||||
    sessionMode.connect('updated', _sessionUpdated);
 | 
			
		||||
    _sessionUpdated();
 | 
			
		||||
 | 
			
		||||
    // Provide the bus object for gnome-session to
 | 
			
		||||
    // initiate logouts.
 | 
			
		||||
    EndSessionDialog.init();
 | 
			
		||||
 | 
			
		||||
    // We're ready for the session manager to move to the next phase
 | 
			
		||||
    Meta.register_with_session();
 | 
			
		||||
 | 
			
		||||
    _startDate = new Date();
 | 
			
		||||
 | 
			
		||||
    log('GNOME Shell started at ' + _startDate);
 | 
			
		||||
@@ -205,35 +199,200 @@ function _initializeUI() {
 | 
			
		||||
        Scripting.runPerfScript(module, perfOutput);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _overridesSettings = new Gio.Settings({ schema: OVERRIDES_SCHEMA });
 | 
			
		||||
    _overridesSettings.connect('changed::dynamic-workspaces', _queueCheckWorkspaces);
 | 
			
		||||
 | 
			
		||||
    global.screen.connect('notify::n-workspaces', _nWorkspacesChanged);
 | 
			
		||||
 | 
			
		||||
    global.screen.connect('window-entered-monitor', _windowEnteredMonitor);
 | 
			
		||||
    global.screen.connect('window-left-monitor', _windowLeftMonitor);
 | 
			
		||||
    global.screen.connect('restacked', _windowsRestacked);
 | 
			
		||||
 | 
			
		||||
    _nWorkspacesChanged();
 | 
			
		||||
 | 
			
		||||
    ExtensionDownloader.init();
 | 
			
		||||
    ExtensionSystem.init();
 | 
			
		||||
 | 
			
		||||
    if (sessionMode.isGreeter && screenShield) {
 | 
			
		||||
        layoutManager.connect('startup-prepared', function() {
 | 
			
		||||
            screenShield.showDialog();
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    layoutManager.connect('startup-complete', function() {
 | 
			
		||||
                              if (keybindingMode == Shell.KeyBindingMode.NONE) {
 | 
			
		||||
                                  keybindingMode = Shell.KeyBindingMode.NORMAL;
 | 
			
		||||
                              }
 | 
			
		||||
                              if (screenShield) {
 | 
			
		||||
                                  screenShield.lockIfWasLocked();
 | 
			
		||||
                              }
 | 
			
		||||
                          });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _loadDefaultStylesheet() {
 | 
			
		||||
    if (!sessionMode.isPrimary)
 | 
			
		||||
        return;
 | 
			
		||||
let _workspaces = [];
 | 
			
		||||
let _checkWorkspacesId = 0;
 | 
			
		||||
 | 
			
		||||
    let stylesheet = global.datadir + '/theme/' + sessionMode.stylesheetName;
 | 
			
		||||
    if (_defaultCssStylesheet == stylesheet)
 | 
			
		||||
        return;
 | 
			
		||||
/*
 | 
			
		||||
 * When the last window closed on a workspace is a dialog or splash
 | 
			
		||||
 * screen, we assume that it might be an initial window shown before
 | 
			
		||||
 * the main window of an application, and give the app a grace period
 | 
			
		||||
 * where it can map another window before we remove the workspace.
 | 
			
		||||
 */
 | 
			
		||||
const LAST_WINDOW_GRACE_TIME = 1000;
 | 
			
		||||
 | 
			
		||||
    _defaultCssStylesheet = stylesheet;
 | 
			
		||||
    loadTheme();
 | 
			
		||||
function _checkWorkspaces() {
 | 
			
		||||
    let i;
 | 
			
		||||
    let emptyWorkspaces = [];
 | 
			
		||||
 | 
			
		||||
    if (!Meta.prefs_get_dynamic_workspaces()) {
 | 
			
		||||
        _checkWorkspacesId = 0;
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < _workspaces.length; i++) {
 | 
			
		||||
        let lastRemoved = _workspaces[i]._lastRemovedWindow;
 | 
			
		||||
        if ((lastRemoved &&
 | 
			
		||||
             (lastRemoved.get_window_type() == Meta.WindowType.SPLASHSCREEN ||
 | 
			
		||||
              lastRemoved.get_window_type() == Meta.WindowType.DIALOG ||
 | 
			
		||||
              lastRemoved.get_window_type() == Meta.WindowType.MODAL_DIALOG)) ||
 | 
			
		||||
            _workspaces[i]._keepAliveId)
 | 
			
		||||
                emptyWorkspaces[i] = false;
 | 
			
		||||
        else
 | 
			
		||||
            emptyWorkspaces[i] = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let sequences = Shell.WindowTracker.get_default().get_startup_sequences();
 | 
			
		||||
    for (i = 0; i < sequences.length; i++) {
 | 
			
		||||
        let index = sequences[i].get_workspace();
 | 
			
		||||
        if (index >= 0 && index <= global.screen.n_workspaces)
 | 
			
		||||
            emptyWorkspaces[index] = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let windows = global.get_window_actors();
 | 
			
		||||
    for (i = 0; i < windows.length; i++) {
 | 
			
		||||
        let win = windows[i];
 | 
			
		||||
 | 
			
		||||
        if (win.get_meta_window().is_on_all_workspaces())
 | 
			
		||||
            continue;
 | 
			
		||||
 | 
			
		||||
        let workspaceIndex = win.get_workspace();
 | 
			
		||||
        emptyWorkspaces[workspaceIndex] = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // If we don't have an empty workspace at the end, add one
 | 
			
		||||
    if (!emptyWorkspaces[emptyWorkspaces.length -1]) {
 | 
			
		||||
        global.screen.append_new_workspace(false, global.get_current_time());
 | 
			
		||||
        emptyWorkspaces.push(false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let activeWorkspaceIndex = global.screen.get_active_workspace_index();
 | 
			
		||||
    let removingCurrentWorkspace = (emptyWorkspaces[activeWorkspaceIndex] &&
 | 
			
		||||
                                    activeWorkspaceIndex < emptyWorkspaces.length - 1);
 | 
			
		||||
    // Don't enter the overview when removing multiple empty workspaces at startup
 | 
			
		||||
    let showOverview  = (removingCurrentWorkspace &&
 | 
			
		||||
                         !emptyWorkspaces.every(function(x) { return x; }));
 | 
			
		||||
 | 
			
		||||
    if (removingCurrentWorkspace) {
 | 
			
		||||
        // "Merge" the empty workspace we are removing with the one at the end
 | 
			
		||||
        wm.blockAnimations();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Delete other empty workspaces; do it from the end to avoid index changes
 | 
			
		||||
    for (i = emptyWorkspaces.length - 2; i >= 0; i--) {
 | 
			
		||||
        if (emptyWorkspaces[i])
 | 
			
		||||
            global.screen.remove_workspace(_workspaces[i], global.get_current_time());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (removingCurrentWorkspace) {
 | 
			
		||||
        global.screen.get_workspace_by_index(global.screen.n_workspaces - 1).activate(global.get_current_time());
 | 
			
		||||
        wm.unblockAnimations();
 | 
			
		||||
 | 
			
		||||
        if (!overview.visible && showOverview)
 | 
			
		||||
            overview.show();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _checkWorkspacesId = 0;
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function keepWorkspaceAlive(workspace, duration) {
 | 
			
		||||
    if (workspace._keepAliveId)
 | 
			
		||||
        Mainloop.source_remove(workspace._keepAliveId);
 | 
			
		||||
 | 
			
		||||
    workspace._keepAliveId = Mainloop.timeout_add(duration, function() {
 | 
			
		||||
        workspace._keepAliveId = 0;
 | 
			
		||||
        _queueCheckWorkspaces();
 | 
			
		||||
        return false;
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _windowRemoved(workspace, window) {
 | 
			
		||||
    workspace._lastRemovedWindow = window;
 | 
			
		||||
    _queueCheckWorkspaces();
 | 
			
		||||
    Mainloop.timeout_add(LAST_WINDOW_GRACE_TIME, function() {
 | 
			
		||||
        if (workspace._lastRemovedWindow == window) {
 | 
			
		||||
            workspace._lastRemovedWindow = null;
 | 
			
		||||
            _queueCheckWorkspaces();
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _windowLeftMonitor(metaScreen, monitorIndex, metaWin) {
 | 
			
		||||
    // If the window left the primary monitor, that
 | 
			
		||||
    // might make that workspace empty
 | 
			
		||||
    if (monitorIndex == layoutManager.primaryIndex)
 | 
			
		||||
        _queueCheckWorkspaces();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _windowEnteredMonitor(metaScreen, monitorIndex, metaWin) {
 | 
			
		||||
    // If the window entered the primary monitor, that
 | 
			
		||||
    // might make that workspace non-empty
 | 
			
		||||
    if (monitorIndex == layoutManager.primaryIndex)
 | 
			
		||||
        _queueCheckWorkspaces();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _windowsRestacked() {
 | 
			
		||||
    // Figure out where the pointer is in case we lost track of
 | 
			
		||||
    // it during a grab. (In particular, if a trayicon popup menu
 | 
			
		||||
    // is dismissed, see if we need to close the message tray.)
 | 
			
		||||
    global.sync_pointer();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _queueCheckWorkspaces() {
 | 
			
		||||
    if (_checkWorkspacesId == 0)
 | 
			
		||||
        _checkWorkspacesId = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, _checkWorkspaces);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _nWorkspacesChanged() {
 | 
			
		||||
    let oldNumWorkspaces = _workspaces.length;
 | 
			
		||||
    let newNumWorkspaces = global.screen.n_workspaces;
 | 
			
		||||
 | 
			
		||||
    if (oldNumWorkspaces == newNumWorkspaces)
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
    let lostWorkspaces = [];
 | 
			
		||||
    if (newNumWorkspaces > oldNumWorkspaces) {
 | 
			
		||||
        let w;
 | 
			
		||||
 | 
			
		||||
        // Assume workspaces are only added at the end
 | 
			
		||||
        for (w = oldNumWorkspaces; w < newNumWorkspaces; w++)
 | 
			
		||||
            _workspaces[w] = global.screen.get_workspace_by_index(w);
 | 
			
		||||
 | 
			
		||||
        for (w = oldNumWorkspaces; w < newNumWorkspaces; w++) {
 | 
			
		||||
            let workspace = _workspaces[w];
 | 
			
		||||
            workspace._windowAddedId = workspace.connect('window-added', _queueCheckWorkspaces);
 | 
			
		||||
            workspace._windowRemovedId = workspace.connect('window-removed', _windowRemoved);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    } else {
 | 
			
		||||
        // Assume workspaces are only removed sequentially
 | 
			
		||||
        // (e.g. 2,3,4 - not 2,4,7)
 | 
			
		||||
        let removedIndex;
 | 
			
		||||
        let removedNum = oldNumWorkspaces - newNumWorkspaces;
 | 
			
		||||
        for (let w = 0; w < oldNumWorkspaces; w++) {
 | 
			
		||||
            let workspace = global.screen.get_workspace_by_index(w);
 | 
			
		||||
            if (_workspaces[w] != workspace) {
 | 
			
		||||
                removedIndex = w;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let lostWorkspaces = _workspaces.splice(removedIndex, removedNum);
 | 
			
		||||
        lostWorkspaces.forEach(function(workspace) {
 | 
			
		||||
                                   workspace.disconnect(workspace._windowAddedId);
 | 
			
		||||
                                   workspace.disconnect(workspace._windowRemovedId);
 | 
			
		||||
                               });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _queueCheckWorkspaces();
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -270,8 +429,11 @@ function loadTheme() {
 | 
			
		||||
    let themeContext = St.ThemeContext.get_for_stage (global.stage);
 | 
			
		||||
    let previousTheme = themeContext.get_theme();
 | 
			
		||||
 | 
			
		||||
    let theme = new St.Theme ({ application_stylesheet: _cssStylesheet,
 | 
			
		||||
                                default_stylesheet: _defaultCssStylesheet });
 | 
			
		||||
    let cssStylesheet = _defaultCssStylesheet;
 | 
			
		||||
    if (_cssStylesheet != null)
 | 
			
		||||
        cssStylesheet = _cssStylesheet;
 | 
			
		||||
 | 
			
		||||
    let theme = new St.Theme ({ application_stylesheet: cssStylesheet });
 | 
			
		||||
 | 
			
		||||
    if (previousTheme) {
 | 
			
		||||
        let customStylesheets = previousTheme.get_custom_stylesheets();
 | 
			
		||||
@@ -313,6 +475,17 @@ function notifyError(msg, details) {
 | 
			
		||||
    notify(msg, details);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function isWindowActorDisplayedOnWorkspace(win, workspaceIndex) {
 | 
			
		||||
    return win.get_workspace() == workspaceIndex ||
 | 
			
		||||
        (win.get_meta_window() && win.get_meta_window().is_on_all_workspaces());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getWindowActorsForWorkspace(workspaceIndex) {
 | 
			
		||||
    return global.get_window_actors().filter(function (win) {
 | 
			
		||||
        return isWindowActorDisplayedOnWorkspace(win, workspaceIndex);
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _findModal(actor) {
 | 
			
		||||
    for (let i = 0; i < modalActorFocusStack.length; i++) {
 | 
			
		||||
        if (modalActorFocusStack[i].actor == actor)
 | 
			
		||||
@@ -321,6 +494,10 @@ function _findModal(actor) {
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function isInModalStack(actor) {
 | 
			
		||||
    return _findModal(actor) != -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * pushModal:
 | 
			
		||||
 * @actor: #ClutterActor which will be given keyboard focus
 | 
			
		||||
@@ -343,7 +520,7 @@ function _findModal(actor) {
 | 
			
		||||
 *  - options: Meta.ModalOptions flags to indicate that the pointer is
 | 
			
		||||
 *             already grabbed
 | 
			
		||||
 *
 | 
			
		||||
 *  - keybindingMode: used to set the current Shell.KeyBindingMode to filter
 | 
			
		||||
 *  - keybindingMode: used to set the current Main.KeybindingMode to filter
 | 
			
		||||
 *                    global keybindings; the default of NONE will filter
 | 
			
		||||
 *                    out all keybindings
 | 
			
		||||
 *
 | 
			
		||||
@@ -352,7 +529,7 @@ function _findModal(actor) {
 | 
			
		||||
function pushModal(actor, params) {
 | 
			
		||||
    params = Params.parse(params, { timestamp: global.get_current_time(),
 | 
			
		||||
                                    options: 0,
 | 
			
		||||
                                    keybindingMode: Shell.KeyBindingMode.NONE });
 | 
			
		||||
                                    keybindingMode: KeybindingMode.NONE });
 | 
			
		||||
 | 
			
		||||
    if (modalCount == 0) {
 | 
			
		||||
        if (!global.begin_modal(params.timestamp, params.options)) {
 | 
			
		||||
@@ -362,26 +539,27 @@ function pushModal(actor, params) {
 | 
			
		||||
        Meta.disable_unredirect_for_screen(global.screen);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    global.set_stage_input_mode(Shell.StageInputMode.FULLSCREEN);
 | 
			
		||||
 | 
			
		||||
    modalCount += 1;
 | 
			
		||||
    let actorDestroyId = actor.connect('destroy', function() {
 | 
			
		||||
        let index = _findModal(actor);
 | 
			
		||||
        if (index >= 0)
 | 
			
		||||
            popModal(actor);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    let prevFocus = global.stage.get_key_focus();
 | 
			
		||||
    let prevFocusDestroyId;
 | 
			
		||||
    if (prevFocus != null) {
 | 
			
		||||
        prevFocusDestroyId = prevFocus.connect('destroy', function() {
 | 
			
		||||
    let curFocus = global.stage.get_key_focus();
 | 
			
		||||
    let curFocusDestroyId;
 | 
			
		||||
    if (curFocus != null) {
 | 
			
		||||
        curFocusDestroyId = curFocus.connect('destroy', function() {
 | 
			
		||||
            let index = _findModal(actor);
 | 
			
		||||
            if (index >= 0)
 | 
			
		||||
                modalActorFocusStack[index].prevFocus = null;
 | 
			
		||||
                modalActorFocusStack[index].actor = null;
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    modalActorFocusStack.push({ actor: actor,
 | 
			
		||||
                                focus: curFocus,
 | 
			
		||||
                                destroyId: actorDestroyId,
 | 
			
		||||
                                prevFocus: prevFocus,
 | 
			
		||||
                                prevFocusDestroyId: prevFocusDestroyId,
 | 
			
		||||
                                focusDestroyId: curFocusDestroyId,
 | 
			
		||||
                                keybindingMode: keybindingMode });
 | 
			
		||||
 | 
			
		||||
    keybindingMode = params.keybindingMode;
 | 
			
		||||
@@ -410,7 +588,8 @@ function popModal(actor, timestamp) {
 | 
			
		||||
    if (focusIndex < 0) {
 | 
			
		||||
        global.stage.set_key_focus(null);
 | 
			
		||||
        global.end_modal(timestamp);
 | 
			
		||||
        keybindingMode = Shell.KeyBindingMode.NORMAL;
 | 
			
		||||
        global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
 | 
			
		||||
        keybindingMode = KeybindingMode.NORMAL;
 | 
			
		||||
 | 
			
		||||
        throw new Error('incorrect pop');
 | 
			
		||||
    }
 | 
			
		||||
@@ -421,33 +600,18 @@ function popModal(actor, timestamp) {
 | 
			
		||||
    record.actor.disconnect(record.destroyId);
 | 
			
		||||
 | 
			
		||||
    if (focusIndex == modalActorFocusStack.length - 1) {
 | 
			
		||||
        if (record.prevFocus)
 | 
			
		||||
            record.prevFocus.disconnect(record.prevFocusDestroyId);
 | 
			
		||||
        if (record.focus)
 | 
			
		||||
            record.focus.disconnect(record.focusDestroyId);
 | 
			
		||||
        keybindingMode = record.keybindingMode;
 | 
			
		||||
        global.stage.set_key_focus(record.prevFocus);
 | 
			
		||||
        global.stage.set_key_focus(record.focus);
 | 
			
		||||
    } else {
 | 
			
		||||
        // If we have:
 | 
			
		||||
        //     global.stage.set_focus(a);
 | 
			
		||||
        //     Main.pushModal(b);
 | 
			
		||||
        //     Main.pushModal(c);
 | 
			
		||||
        //     Main.pushModal(d);
 | 
			
		||||
        //
 | 
			
		||||
        // then we have the stack:
 | 
			
		||||
        //     [{ prevFocus: a, actor: b },
 | 
			
		||||
        //      { prevFocus: b, actor: c },
 | 
			
		||||
        //      { prevFocus: c, actor: d }]
 | 
			
		||||
        //
 | 
			
		||||
        // When actor c is destroyed/popped, if we only simply remove the
 | 
			
		||||
        // record, then the focus stack will be [a, c], rather than the correct
 | 
			
		||||
        // [a, b]. Shift the focus stack up before removing the record to ensure
 | 
			
		||||
        // that we get the correct result.
 | 
			
		||||
        let t = modalActorFocusStack[modalActorFocusStack.length - 1];
 | 
			
		||||
        if (t.prevFocus)
 | 
			
		||||
            t.prevFocus.disconnect(t.prevFocusDestroyId);
 | 
			
		||||
        if (t.focus)
 | 
			
		||||
            t.focus.disconnect(t.focusDestroyId);
 | 
			
		||||
        // Remove from the middle, shift the focus chain up
 | 
			
		||||
        for (let i = modalActorFocusStack.length - 1; i > focusIndex; i--) {
 | 
			
		||||
            modalActorFocusStack[i].prevFocus = modalActorFocusStack[i - 1].prevFocus;
 | 
			
		||||
            modalActorFocusStack[i].prevFocusDestroyId = modalActorFocusStack[i - 1].prevFocusDestroyId;
 | 
			
		||||
            modalActorFocusStack[i].focus = modalActorFocusStack[i - 1].focus;
 | 
			
		||||
            modalActorFocusStack[i].focusDestroyId = modalActorFocusStack[i - 1].focusDestroyId;
 | 
			
		||||
            modalActorFocusStack[i].keybindingMode = modalActorFocusStack[i - 1].keybindingMode;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -457,8 +621,9 @@ function popModal(actor, timestamp) {
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    global.end_modal(timestamp);
 | 
			
		||||
    global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
 | 
			
		||||
    Meta.enable_unredirect_for_screen(global.screen);
 | 
			
		||||
    keybindingMode = Shell.KeyBindingMode.NORMAL;
 | 
			
		||||
    keybindingMode = KeybindingMode.NORMAL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function createLookingGlass() {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1542
									
								
								js/ui/messageTray.js
									
									
									
									
									
								
							
							
						
						@@ -14,7 +14,6 @@ const Atk = imports.gi.Atk;
 | 
			
		||||
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
 | 
			
		||||
const Animation = imports.ui.animation;
 | 
			
		||||
const Layout = imports.ui.layout;
 | 
			
		||||
const Lightbox = imports.ui.lightbox;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
@@ -23,10 +22,6 @@ const Tweener = imports.ui.tweener;
 | 
			
		||||
const OPEN_AND_CLOSE_TIME = 0.1;
 | 
			
		||||
const FADE_OUT_DIALOG_TIME = 1.0;
 | 
			
		||||
 | 
			
		||||
const WORK_SPINNER_ICON_SIZE = 24;
 | 
			
		||||
const WORK_SPINNER_ANIMATION_DELAY = 1.0;
 | 
			
		||||
const WORK_SPINNER_ANIMATION_TIME = 0.3;
 | 
			
		||||
 | 
			
		||||
const State = {
 | 
			
		||||
    OPENED: 0,
 | 
			
		||||
    CLOSED: 1,
 | 
			
		||||
@@ -42,16 +37,14 @@ const ModalDialog = new Lang.Class({
 | 
			
		||||
        params = Params.parse(params, { shellReactive: false,
 | 
			
		||||
                                        styleClass: null,
 | 
			
		||||
                                        parentActor: Main.uiGroup,
 | 
			
		||||
                                        keybindingMode: Shell.KeyBindingMode.SYSTEM_MODAL,
 | 
			
		||||
                                        shouldFadeIn: true,
 | 
			
		||||
                                        destroyOnClose: true });
 | 
			
		||||
                                        keybindingMode: Main.KeybindingMode.SYSTEM_MODAL,
 | 
			
		||||
                                        shouldFadeIn: true });
 | 
			
		||||
 | 
			
		||||
        this.state = State.CLOSED;
 | 
			
		||||
        this._hasModal = false;
 | 
			
		||||
        this._keybindingMode = params.keybindingMode;
 | 
			
		||||
        this._shellReactive = params.shellReactive;
 | 
			
		||||
        this._shouldFadeIn = params.shouldFadeIn;
 | 
			
		||||
        this._destroyOnClose = params.destroyOnClose;
 | 
			
		||||
 | 
			
		||||
        this._group = new St.Widget({ visible: false,
 | 
			
		||||
                                      x: 0,
 | 
			
		||||
@@ -65,59 +58,55 @@ const ModalDialog = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._group.connect('destroy', Lang.bind(this, this._onGroupDestroy));
 | 
			
		||||
 | 
			
		||||
        this._pressedKey = null;
 | 
			
		||||
        this._buttonKeys = {};
 | 
			
		||||
        this._group.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
 | 
			
		||||
        this._group.connect('key-release-event', Lang.bind(this, this._onKeyReleaseEvent));
 | 
			
		||||
 | 
			
		||||
        this.backgroundStack = new St.Widget({ layout_manager: new Clutter.BinLayout() });
 | 
			
		||||
        this._backgroundBin = new St.Bin({ child: this.backgroundStack,
 | 
			
		||||
                                           x_fill: true, y_fill: true });
 | 
			
		||||
        this._backgroundBin = new St.Bin();
 | 
			
		||||
        this._monitorConstraint = new Layout.MonitorConstraint();
 | 
			
		||||
        this._backgroundBin.add_constraint(this._monitorConstraint);
 | 
			
		||||
        this._group.add_actor(this._backgroundBin);
 | 
			
		||||
 | 
			
		||||
        this.dialogLayout = new St.BoxLayout({ style_class: 'modal-dialog',
 | 
			
		||||
                                               vertical:    true });
 | 
			
		||||
        // modal dialogs are fixed width and grow vertically; set the request
 | 
			
		||||
        // mode accordingly so wrapped labels are handled correctly during
 | 
			
		||||
        // size requests.
 | 
			
		||||
        this.dialogLayout.request_mode = Clutter.RequestMode.HEIGHT_FOR_WIDTH;
 | 
			
		||||
 | 
			
		||||
        if (params.styleClass != null)
 | 
			
		||||
        if (params.styleClass != null) {
 | 
			
		||||
            this.dialogLayout.add_style_class_name(params.styleClass);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!this._shellReactive) {
 | 
			
		||||
            this._lightbox = new Lightbox.Lightbox(this._group,
 | 
			
		||||
                                                   { inhibitEvents: true });
 | 
			
		||||
            this._lightbox.highlight(this._backgroundBin);
 | 
			
		||||
 | 
			
		||||
            this._eventBlocker = new Clutter.Actor({ reactive: true });
 | 
			
		||||
            this.backgroundStack.add_actor(this._eventBlocker);
 | 
			
		||||
            let stack = new Shell.Stack();
 | 
			
		||||
            this._backgroundBin.child = stack;
 | 
			
		||||
 | 
			
		||||
            this._eventBlocker = new Clutter.Group({ reactive: true });
 | 
			
		||||
            stack.add_actor(this._eventBlocker);
 | 
			
		||||
            stack.add_actor(this.dialogLayout);
 | 
			
		||||
        } else {
 | 
			
		||||
            this._backgroundBin.child = this.dialogLayout;
 | 
			
		||||
        }
 | 
			
		||||
        this.backgroundStack.add_actor(this.dialogLayout);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        this.contentLayout = new St.BoxLayout({ vertical: true });
 | 
			
		||||
        this.dialogLayout.add(this.contentLayout,
 | 
			
		||||
                              { expand:  true,
 | 
			
		||||
                                x_fill:  true,
 | 
			
		||||
                              { x_fill:  true,
 | 
			
		||||
                                y_fill:  true,
 | 
			
		||||
                                x_align: St.Align.MIDDLE,
 | 
			
		||||
                                y_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        this.buttonLayout = new St.BoxLayout({ style_class: 'modal-dialog-button-box',
 | 
			
		||||
                                               vertical: false });
 | 
			
		||||
                                                visible:     false,
 | 
			
		||||
                                                vertical:    false });
 | 
			
		||||
        this.dialogLayout.add(this.buttonLayout,
 | 
			
		||||
                              { x_align: St.Align.MIDDLE,
 | 
			
		||||
                              { expand:  true,
 | 
			
		||||
                                x_align: St.Align.MIDDLE,
 | 
			
		||||
                                y_align: St.Align.END });
 | 
			
		||||
 | 
			
		||||
        global.focus_manager.add_group(this.dialogLayout);
 | 
			
		||||
        this._initialKeyFocus = this.dialogLayout;
 | 
			
		||||
        this._initialKeyFocusDestroyId = 0;
 | 
			
		||||
        this._savedKeyFocus = null;
 | 
			
		||||
 | 
			
		||||
        this._workSpinner = null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroy: function() {
 | 
			
		||||
@@ -131,6 +120,7 @@ const ModalDialog = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    setButtons: function(buttons) {
 | 
			
		||||
        this.clearButtons();
 | 
			
		||||
        this.buttonLayout.visible = (buttons.length > 0);
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < buttons.length; i++) {
 | 
			
		||||
            let buttonInfo = buttons[i];
 | 
			
		||||
@@ -169,7 +159,6 @@ const ModalDialog = new Lang.Class({
 | 
			
		||||
            keys = [];
 | 
			
		||||
 | 
			
		||||
        let button = new St.Button({ style_class: 'modal-dialog-button',
 | 
			
		||||
                                     button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
 | 
			
		||||
                                     reactive:    true,
 | 
			
		||||
                                     can_focus:   true,
 | 
			
		||||
                                     label:       label });
 | 
			
		||||
@@ -191,55 +180,10 @@ const ModalDialog = new Lang.Class({
 | 
			
		||||
        return button;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    placeSpinner: function(layoutInfo) {
 | 
			
		||||
        let spinnerIcon = global.datadir + '/theme/process-working.svg';
 | 
			
		||||
        this._workSpinner = new Animation.AnimatedIcon(spinnerIcon, WORK_SPINNER_ICON_SIZE);
 | 
			
		||||
        this._workSpinner.actor.opacity = 0;
 | 
			
		||||
        this._workSpinner.actor.show();
 | 
			
		||||
 | 
			
		||||
        this.buttonLayout.add(this._workSpinner.actor, layoutInfo);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setWorking: function(working) {
 | 
			
		||||
        if (!this._workSpinner)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        Tweener.removeTweens(this._workSpinner.actor);
 | 
			
		||||
        if (working) {
 | 
			
		||||
            this._workSpinner.play();
 | 
			
		||||
            Tweener.addTween(this._workSpinner.actor,
 | 
			
		||||
                             { opacity: 255,
 | 
			
		||||
                               delay: WORK_SPINNER_ANIMATION_DELAY,
 | 
			
		||||
                               time: WORK_SPINNER_ANIMATION_TIME,
 | 
			
		||||
                               transition: 'linear'
 | 
			
		||||
                             });
 | 
			
		||||
        } else {
 | 
			
		||||
            Tweener.addTween(this._workSpinner.actor,
 | 
			
		||||
                             { opacity: 0,
 | 
			
		||||
                               time: WORK_SPINNER_ANIMATION_TIME,
 | 
			
		||||
                               transition: 'linear',
 | 
			
		||||
                               onCompleteScope: this,
 | 
			
		||||
                               onComplete: function() {
 | 
			
		||||
                                   if (this._workSpinner)
 | 
			
		||||
                                       this._workSpinner.stop();
 | 
			
		||||
                               }
 | 
			
		||||
                             });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onKeyPressEvent: function(object, event) {
 | 
			
		||||
        this._pressedKey = event.get_key_symbol();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onKeyReleaseEvent: function(object, event) {
 | 
			
		||||
        let pressedKey = this._pressedKey;
 | 
			
		||||
        this._pressedKey = null;
 | 
			
		||||
 | 
			
		||||
        let symbol = event.get_key_symbol();
 | 
			
		||||
        if (symbol != pressedKey)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        let buttonInfo = this._buttonKeys[symbol];
 | 
			
		||||
 | 
			
		||||
        if (!buttonInfo)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
@@ -322,10 +266,6 @@ const ModalDialog = new Lang.Class({
 | 
			
		||||
                               function() {
 | 
			
		||||
                                   this.state = State.CLOSED;
 | 
			
		||||
                                   this._group.hide();
 | 
			
		||||
                                   this.emit('closed');
 | 
			
		||||
 | 
			
		||||
                                   if (this._destroyOnClose)
 | 
			
		||||
                                       this.destroy();
 | 
			
		||||
                               })
 | 
			
		||||
                         });
 | 
			
		||||
    },
 | 
			
		||||
@@ -361,9 +301,8 @@ const ModalDialog = new Lang.Class({
 | 
			
		||||
        if (this._savedKeyFocus) {
 | 
			
		||||
            this._savedKeyFocus.grab_key_focus();
 | 
			
		||||
            this._savedKeyFocus = null;
 | 
			
		||||
        } else {
 | 
			
		||||
        } else
 | 
			
		||||
            this._initialKeyFocus.grab_key_focus();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!this._shellReactive)
 | 
			
		||||
            this._eventBlocker.lower_bottom();
 | 
			
		||||
 
 | 
			
		||||
@@ -124,45 +124,45 @@ const NotificationDaemon = new Lang.Class({
 | 
			
		||||
        Main.overview.connect('hidden',
 | 
			
		||||
            Lang.bind(this, this._onFocusAppChanged));
 | 
			
		||||
 | 
			
		||||
        this._trayManager.manage_screen(global.screen, Main.messageTray.actor);
 | 
			
		||||
        this._trayManager.manage_stage(global.stage, Main.messageTray.actor);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _imageForNotificationData: function(hints) {
 | 
			
		||||
        if (hints['image-data']) {
 | 
			
		||||
    _iconForNotificationData: function(icon, hints) {
 | 
			
		||||
        // If an icon is not specified, we use 'image-data' or 'image-path' hint for an icon
 | 
			
		||||
        // and don't show a large image. There are currently many applications that use
 | 
			
		||||
        // notify_notification_set_icon_from_pixbuf() from libnotify, which in turn sets
 | 
			
		||||
        // the 'image-data' hint. These applications don't typically pass in 'app_icon'
 | 
			
		||||
        // argument to Notify() and actually expect the pixbuf to be shown as an icon.
 | 
			
		||||
        // So the logic here does the right thing for this case. If both an icon and either
 | 
			
		||||
        // one of 'image-data' or 'image-path' are specified, we show both an icon and
 | 
			
		||||
        // a large image.
 | 
			
		||||
        if (icon) {
 | 
			
		||||
            if (icon.substr(0, 7) == 'file://')
 | 
			
		||||
                return new Gio.FileIcon({ file: Gio.File.new_for_uri(icon) });
 | 
			
		||||
            else if (icon[0] == '/') {
 | 
			
		||||
                return new Gio.FileIcon({ file: Gio.File.new_for_path(icon) });
 | 
			
		||||
            } else
 | 
			
		||||
                return new Gio.ThemedIcon({ name: icon });
 | 
			
		||||
        } else if (hints['image-data']) {
 | 
			
		||||
            let [width, height, rowStride, hasAlpha,
 | 
			
		||||
                 bitsPerSample, nChannels, data] = hints['image-data'];
 | 
			
		||||
            return Shell.util_create_pixbuf_from_data(data, GdkPixbuf.Colorspace.RGB, hasAlpha,
 | 
			
		||||
                                                      bitsPerSample, width, height, rowStride);
 | 
			
		||||
        } else if (hints['image-path']) {
 | 
			
		||||
            return new Gio.FileIcon({ file: Gio.File.new_for_path(hints['image-path']) });
 | 
			
		||||
        } else {
 | 
			
		||||
            let stockIcon;
 | 
			
		||||
            switch (hints.urgency) {
 | 
			
		||||
                case Urgency.LOW:
 | 
			
		||||
                case Urgency.NORMAL:
 | 
			
		||||
                    stockIcon = 'gtk-dialog-info';
 | 
			
		||||
                    break;
 | 
			
		||||
                case Urgency.CRITICAL:
 | 
			
		||||
                    stockIcon = 'gtk-dialog-error';
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
            return new Gio.ThemedIcon({ name: stockIcon });
 | 
			
		||||
        }
 | 
			
		||||
        return null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _fallbackIconForNotificationData: function(hints) {
 | 
			
		||||
        let stockIcon;
 | 
			
		||||
        switch (hints.urgency) {
 | 
			
		||||
            case Urgency.LOW:
 | 
			
		||||
            case Urgency.NORMAL:
 | 
			
		||||
                stockIcon = 'gtk-dialog-info';
 | 
			
		||||
                break;
 | 
			
		||||
            case Urgency.CRITICAL:
 | 
			
		||||
                stockIcon = 'gtk-dialog-error';
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
        return new Gio.ThemedIcon({ name: stockIcon });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _iconForNotificationData: function(icon) {
 | 
			
		||||
        if (icon) {
 | 
			
		||||
            if (icon.substr(0, 7) == 'file://')
 | 
			
		||||
                return new Gio.FileIcon({ file: Gio.File.new_for_uri(icon) });
 | 
			
		||||
            else if (icon[0] == '/')
 | 
			
		||||
                return new Gio.FileIcon({ file: Gio.File.new_for_path(icon) });
 | 
			
		||||
            else
 | 
			
		||||
                return new Gio.ThemedIcon({ name: icon });
 | 
			
		||||
        }
 | 
			
		||||
        return null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _lookupSource: function(title, pid, trayIcon) {
 | 
			
		||||
@@ -213,7 +213,7 @@ const NotificationDaemon = new Lang.Class({
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let source = new Source(title, pid, sender, trayIcon, ndata ? ndata.hints['desktop-entry'] : null);
 | 
			
		||||
        let source = new Source(title, pid, sender, trayIcon);
 | 
			
		||||
        source.setTransient(isForTransientNotification);
 | 
			
		||||
 | 
			
		||||
        if (!isForTransientNotification) {
 | 
			
		||||
@@ -276,13 +276,12 @@ const NotificationDaemon = new Lang.Class({
 | 
			
		||||
        if (!hints['image-path'] && hints['image_path'])
 | 
			
		||||
            hints['image-path'] = hints['image_path']; // version 1.1 of the spec
 | 
			
		||||
 | 
			
		||||
        if (!hints['image-data']) {
 | 
			
		||||
        if (!hints['image-data'])
 | 
			
		||||
            if (hints['image_data'])
 | 
			
		||||
                hints['image-data'] = hints['image_data']; // version 1.1 of the spec
 | 
			
		||||
            else if (hints['icon_data'] && !hints['image-path'])
 | 
			
		||||
                // early versions of the spec; 'icon_data' should only be used if 'image-path' is not available
 | 
			
		||||
                hints['image-data'] = hints['icon_data'];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let ndata = { appName: appName,
 | 
			
		||||
                      icon: icon,
 | 
			
		||||
@@ -356,8 +355,12 @@ const NotificationDaemon = new Lang.Class({
 | 
			
		||||
            [ndata.id, ndata.icon, ndata.summary, ndata.body,
 | 
			
		||||
             ndata.actions, ndata.hints, ndata.notification];
 | 
			
		||||
 | 
			
		||||
        let gicon = this._iconForNotificationData(icon, hints);
 | 
			
		||||
 | 
			
		||||
        if (notification == null) {
 | 
			
		||||
            notification = new MessageTray.Notification(source);
 | 
			
		||||
            notification = new MessageTray.Notification(source, summary, body,
 | 
			
		||||
                                                        { gicon: gicon,
 | 
			
		||||
                                                          bannerMarkup: true });
 | 
			
		||||
            ndata.notification = notification;
 | 
			
		||||
            notification.connect('destroy', Lang.bind(this,
 | 
			
		||||
                function(n, reason) {
 | 
			
		||||
@@ -380,38 +383,29 @@ const NotificationDaemon = new Lang.Class({
 | 
			
		||||
                function(n, actionId) {
 | 
			
		||||
                    this._emitActionInvoked(ndata.id, actionId);
 | 
			
		||||
                }));
 | 
			
		||||
        } else {
 | 
			
		||||
            notification.update(summary, body, { gicon: gicon,
 | 
			
		||||
                                                 bannerMarkup: true,
 | 
			
		||||
                                                 clear: true });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Mark music notifications so they can be shown in the screen shield
 | 
			
		||||
        notification.isMusic = (ndata.hints['category'] == 'x-gnome.music');
 | 
			
		||||
 | 
			
		||||
        let gicon = this._iconForNotificationData(icon, hints);
 | 
			
		||||
        let gimage = this._imageForNotificationData(hints);
 | 
			
		||||
 | 
			
		||||
        let image = null;
 | 
			
		||||
 | 
			
		||||
        // If an icon is not specified, we use 'image-data' or 'image-path' hint for an icon
 | 
			
		||||
        // and don't show a large image. There are currently many applications that use
 | 
			
		||||
        // notify_notification_set_icon_from_pixbuf() from libnotify, which in turn sets
 | 
			
		||||
        // the 'image-data' hint. These applications don't typically pass in 'app_icon'
 | 
			
		||||
        // argument to Notify() and actually expect the pixbuf to be shown as an icon.
 | 
			
		||||
        // So the logic here does the right thing for this case. If both an icon and either
 | 
			
		||||
        // one of 'image-data' or 'image-path' are specified, we show both an icon and
 | 
			
		||||
        // a large image.
 | 
			
		||||
        if (gicon && gimage)
 | 
			
		||||
            image = new St.Icon({ gicon: gimage,
 | 
			
		||||
                                  icon_size: notification.IMAGE_SIZE });
 | 
			
		||||
        else if (!gicon && gimage)
 | 
			
		||||
            gicon = gimage;
 | 
			
		||||
        else if (!gicon)
 | 
			
		||||
            gicon = this._fallbackIconForNotificationData(hints);
 | 
			
		||||
 | 
			
		||||
        notification.update(summary, body, { gicon: gicon,
 | 
			
		||||
                                             bannerMarkup: true,
 | 
			
		||||
                                             clear: true,
 | 
			
		||||
                                             soundFile: hints['sound-file'],
 | 
			
		||||
                                             soundName: hints['sound-name'] });
 | 
			
		||||
        notification.setImage(image);
 | 
			
		||||
        // We only display a large image if an icon is also specified.
 | 
			
		||||
        if (icon && (hints['image-data'] || hints['image-path'])) {
 | 
			
		||||
            let image = null;
 | 
			
		||||
            if (hints['image-data']) {
 | 
			
		||||
                let [width, height, rowStride, hasAlpha,
 | 
			
		||||
                 bitsPerSample, nChannels, data] = hints['image-data'];
 | 
			
		||||
                image = St.TextureCache.get_default().load_from_raw(data, hasAlpha,
 | 
			
		||||
                                                                    width, height, rowStride, notification.IMAGE_SIZE);
 | 
			
		||||
            } else if (hints['image-path']) {
 | 
			
		||||
                image = St.TextureCache.get_default().load_uri_async(GLib.filename_to_uri(hints['image-path'], null),
 | 
			
		||||
                                                                     notification.IMAGE_SIZE,
 | 
			
		||||
                                                                     notification.IMAGE_SIZE);
 | 
			
		||||
            }
 | 
			
		||||
            notification.setImage(image);
 | 
			
		||||
        } else {
 | 
			
		||||
            notification.unsetImage();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (actions.length) {
 | 
			
		||||
            notification.setUseActionIcons(hints['action-icons'] == true);
 | 
			
		||||
@@ -441,7 +435,7 @@ const NotificationDaemon = new Lang.Class({
 | 
			
		||||
        // of the 'transient' hint with hints['transient'] rather than hints.transient
 | 
			
		||||
        notification.setTransient(hints['transient'] == true);
 | 
			
		||||
 | 
			
		||||
        let sourceGIcon = source.useNotificationIcon ? gicon : null;
 | 
			
		||||
        let sourceGIcon = source.useNotificationIcon ? this._iconForNotificationData(icon, hints) : null;
 | 
			
		||||
        source.processNotification(notification, sourceGIcon);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -465,7 +459,7 @@ const NotificationDaemon = new Lang.Class({
 | 
			
		||||
            // 'icon-multi',
 | 
			
		||||
            'icon-static',
 | 
			
		||||
            'persistence',
 | 
			
		||||
            'sound',
 | 
			
		||||
            // 'sound',
 | 
			
		||||
        ];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -521,22 +515,12 @@ const Source = new Lang.Class({
 | 
			
		||||
    Name: 'NotificationDaemonSource',
 | 
			
		||||
    Extends: MessageTray.Source,
 | 
			
		||||
 | 
			
		||||
    _init: function(title, pid, sender, trayIcon, appId) {
 | 
			
		||||
        // Need to set the app before chaining up, so
 | 
			
		||||
        // methods called from the parent constructor can find it
 | 
			
		||||
        this.trayIcon = trayIcon;
 | 
			
		||||
        this.pid = pid;
 | 
			
		||||
        this.app = this._getApp(appId);
 | 
			
		||||
 | 
			
		||||
    _init: function(title, pid, sender, trayIcon) {
 | 
			
		||||
        this.parent(title);
 | 
			
		||||
 | 
			
		||||
        this.initialTitle = title;
 | 
			
		||||
 | 
			
		||||
        if (this.app)
 | 
			
		||||
            this.title = this.app.get_name();
 | 
			
		||||
        else
 | 
			
		||||
            this.useNotificationIcon = true;
 | 
			
		||||
 | 
			
		||||
        this.pid = pid;
 | 
			
		||||
        if (sender)
 | 
			
		||||
            this._nameWatcherId = Gio.DBus.session.watch_name(sender,
 | 
			
		||||
                                                              Gio.BusNameWatcherFlags.NONE,
 | 
			
		||||
@@ -545,19 +529,16 @@ const Source = new Lang.Class({
 | 
			
		||||
        else
 | 
			
		||||
            this._nameWatcherId = 0;
 | 
			
		||||
 | 
			
		||||
        if (this.trayIcon) {
 | 
			
		||||
            // Try again finding the app, using the WM_CLASS from the tray icon
 | 
			
		||||
            this._setSummaryIcon(this.trayIcon);
 | 
			
		||||
            this.useNotificationIcon = false;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
        this._setApp();
 | 
			
		||||
        if (this.app)
 | 
			
		||||
            this.title = this.app.get_name();
 | 
			
		||||
        else
 | 
			
		||||
            this.useNotificationIcon = true;
 | 
			
		||||
 | 
			
		||||
    _createPolicy: function() {
 | 
			
		||||
        if (this.app) {
 | 
			
		||||
            let id = this.app.get_id().replace(/\.desktop$/,'');
 | 
			
		||||
            return new MessageTray.NotificationApplicationPolicy(id);
 | 
			
		||||
        } else {
 | 
			
		||||
            return new MessageTray.NotificationGenericPolicy();
 | 
			
		||||
        this.trayIcon = trayIcon;
 | 
			
		||||
        if (this.trayIcon) {
 | 
			
		||||
           this._setSummaryIcon(this.trayIcon);
 | 
			
		||||
           this.useNotificationIcon = false;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -584,22 +565,24 @@ const Source = new Lang.Class({
 | 
			
		||||
            this.notify(notification);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    handleSummaryClick: function(button) {
 | 
			
		||||
    handleSummaryClick: function() {
 | 
			
		||||
        if (!this.trayIcon)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        let event = Clutter.get_current_event();
 | 
			
		||||
        if (event.type() != Clutter.EventType.BUTTON_RELEASE)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        // Left clicks are passed through only where there aren't unacknowledged
 | 
			
		||||
        // notifications, so it possible to open them in summary mode; right
 | 
			
		||||
        // clicks are always forwarded, as the right click menu is not useful for
 | 
			
		||||
        // tray icons
 | 
			
		||||
        if (button == 1 &&
 | 
			
		||||
        if (event.get_button() == 1 &&
 | 
			
		||||
            this.notifications.length > 0)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        let id = global.stage.connect('deactivate', Lang.bind(this, function () {
 | 
			
		||||
            global.stage.disconnect(id);
 | 
			
		||||
        let id = global.connect('notify::stage-input-mode', Lang.bind(this, function () {
 | 
			
		||||
            global.disconnect(id);
 | 
			
		||||
            this.trayIcon.click(event);
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
@@ -607,7 +590,7 @@ const Source = new Lang.Class({
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getApp: function(appId) {
 | 
			
		||||
    _getApp: function() {
 | 
			
		||||
        let app;
 | 
			
		||||
 | 
			
		||||
        app = Shell.WindowTracker.get_default().get_app_from_pid(this.pid);
 | 
			
		||||
@@ -615,17 +598,7 @@ const Source = new Lang.Class({
 | 
			
		||||
            return app;
 | 
			
		||||
 | 
			
		||||
        if (this.trayIcon) {
 | 
			
		||||
            app = Shell.AppSystem.get_default().lookup_startup_wmclass(this.trayIcon.wm_class);
 | 
			
		||||
            if (app != null)
 | 
			
		||||
                return app;
 | 
			
		||||
 | 
			
		||||
            app = Shell.AppSystem.get_default().lookup_desktop_wmclass(this.trayIcon.wm_class);
 | 
			
		||||
            if (app != null)
 | 
			
		||||
                return app;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (appId) {
 | 
			
		||||
            app = Shell.AppSystem.get_default().lookup_app(appId + '.desktop');
 | 
			
		||||
            app = Shell.AppSystem.get_default().lookup_wmclass(this.trayIcon.wmclass);
 | 
			
		||||
            if (app != null)
 | 
			
		||||
                return app;
 | 
			
		||||
        }
 | 
			
		||||
@@ -633,6 +606,22 @@ const Source = new Lang.Class({
 | 
			
		||||
        return null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _setApp: function() {
 | 
			
		||||
        if (this.app)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this.app = this._getApp();
 | 
			
		||||
        if (!this.app)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        // Only override the icon if we were previously using
 | 
			
		||||
        // notification-based icons (ie, not a trayicon) or if it was unset before
 | 
			
		||||
        if (!this.trayIcon) {
 | 
			
		||||
            this.useNotificationIcon = false;
 | 
			
		||||
            this.iconUpdated();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setTitle: function(title) {
 | 
			
		||||
        // Do nothing if .app is set, we don't want to override the
 | 
			
		||||
        // app name with whatever is provided through libnotify (usually
 | 
			
		||||
@@ -644,8 +633,8 @@ const Source = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    open: function(notification) {
 | 
			
		||||
        this.openApp();
 | 
			
		||||
        this.destroyNonResidentNotifications();
 | 
			
		||||
        this.openApp();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _lastNotificationRemoved: function() {
 | 
			
		||||
@@ -657,8 +646,11 @@ const Source = new Lang.Class({
 | 
			
		||||
        if (this.app == null)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this.app.activate();
 | 
			
		||||
        Main.overview.hide();
 | 
			
		||||
        let windows = this.app.get_windows();
 | 
			
		||||
        if (windows.length > 0) {
 | 
			
		||||
            let mostRecentWindow = windows[0];
 | 
			
		||||
            Main.activateWindow(mostRecentWindow);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroy: function() {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,210 +0,0 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Layout = imports.ui.layout;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const Mainloop = imports.mainloop;
 | 
			
		||||
const Tweener = imports.ui.tweener;
 | 
			
		||||
const Meta = imports.gi.Meta;
 | 
			
		||||
 | 
			
		||||
const HIDE_TIMEOUT = 1500;
 | 
			
		||||
const FADE_TIME = 0.1;
 | 
			
		||||
const LEVEL_ANIMATION_TIME = 0.1;
 | 
			
		||||
 | 
			
		||||
const LevelBar = new Lang.Class({
 | 
			
		||||
    Name: 'LevelBar',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._level = 0;
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.Bin({ style_class: 'level',
 | 
			
		||||
                                  x_fill: true, y_fill: true });
 | 
			
		||||
        this._bar = new St.DrawingArea();
 | 
			
		||||
        this._bar.connect('repaint', Lang.bind(this, this._repaint));
 | 
			
		||||
 | 
			
		||||
        this.actor.set_child(this._bar);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get level() {
 | 
			
		||||
        return this._level;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    set level(value) {
 | 
			
		||||
        let newValue = Math.max(0, Math.min(value, 100));
 | 
			
		||||
        if (newValue == this._level)
 | 
			
		||||
            return;
 | 
			
		||||
        this._level = newValue;
 | 
			
		||||
        this._bar.queue_repaint();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _repaint: function() {
 | 
			
		||||
        let cr = this._bar.get_context();
 | 
			
		||||
 | 
			
		||||
        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());
 | 
			
		||||
 | 
			
		||||
        let [w, h] = this._bar.get_surface_size();
 | 
			
		||||
        w *= (this._level / 100.);
 | 
			
		||||
 | 
			
		||||
        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();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const OsdWindow = new Lang.Class({
 | 
			
		||||
    Name: 'OsdWindow',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._popupSize = 0;
 | 
			
		||||
        this.actor = new St.Widget({ x_expand: true,
 | 
			
		||||
                                     y_expand: true,
 | 
			
		||||
                                     x_align: Clutter.ActorAlign.CENTER,
 | 
			
		||||
                                     y_align: Clutter.ActorAlign.CENTER });
 | 
			
		||||
        this.actor.add_constraint(new Layout.MonitorConstraint({ primary: true }));
 | 
			
		||||
        this._box = new St.BoxLayout({ style_class: 'osd-window',
 | 
			
		||||
                                       vertical: true });
 | 
			
		||||
        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 });
 | 
			
		||||
 | 
			
		||||
        this._label = new St.Label();
 | 
			
		||||
        this._box.add(this._label);
 | 
			
		||||
 | 
			
		||||
        this._level = new LevelBar();
 | 
			
		||||
        this._box.add(this._level.actor);
 | 
			
		||||
 | 
			
		||||
        this._hideTimeoutId = 0;
 | 
			
		||||
        this._reset();
 | 
			
		||||
 | 
			
		||||
        Main.layoutManager.connect('monitors-changed',
 | 
			
		||||
                                   Lang.bind(this, this._monitorsChanged));
 | 
			
		||||
        this._monitorsChanged();
 | 
			
		||||
 | 
			
		||||
        Main.uiGroup.add_child(this.actor);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setIcon: function(icon) {
 | 
			
		||||
        this._icon.gicon = icon;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setLabel: function(label) {
 | 
			
		||||
        this._label.visible = (label != undefined);
 | 
			
		||||
        if (label)
 | 
			
		||||
            this._label.text = label;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setLevel: function(level) {
 | 
			
		||||
        this._level.actor.visible = (level != undefined);
 | 
			
		||||
        if (this.actor.visible)
 | 
			
		||||
            Tweener.addTween(this._level,
 | 
			
		||||
                             { level: level,
 | 
			
		||||
                               time: LEVEL_ANIMATION_TIME,
 | 
			
		||||
                               transition: 'easeOutQuad' });
 | 
			
		||||
        else
 | 
			
		||||
            this._level.level = level;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    show: function() {
 | 
			
		||||
        if (!this._icon.gicon)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (!this.actor.visible) {
 | 
			
		||||
            Meta.disable_unredirect_for_screen(global.screen);
 | 
			
		||||
            this.actor.show();
 | 
			
		||||
            this.actor.opacity = 0;
 | 
			
		||||
            this.actor.get_parent().set_child_above_sibling(this.actor, null);
 | 
			
		||||
 | 
			
		||||
            Tweener.addTween(this.actor,
 | 
			
		||||
                             { opacity: 255,
 | 
			
		||||
                               time: FADE_TIME,
 | 
			
		||||
                               transition: 'easeOutQuad' });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this._hideTimeoutId)
 | 
			
		||||
            Mainloop.source_remove(this._hideTimeoutId);
 | 
			
		||||
        this._hideTimeoutId = Mainloop.timeout_add(HIDE_TIMEOUT,
 | 
			
		||||
                                                   Lang.bind(this, this._hide));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    cancel: function() {
 | 
			
		||||
        if (!this._hideTimeoutId)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        Mainloop.source_remove(this._hideTimeoutId);
 | 
			
		||||
        this._hide();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _hide: function() {
 | 
			
		||||
        this._hideTimeoutId = 0;
 | 
			
		||||
        Tweener.addTween(this.actor,
 | 
			
		||||
                         { opacity: 0,
 | 
			
		||||
                           time: FADE_TIME,
 | 
			
		||||
                           transition: 'easeOutQuad',
 | 
			
		||||
                           onComplete: Lang.bind(this, function() {
 | 
			
		||||
                              this._reset();
 | 
			
		||||
                              Meta.enable_unredirect_for_screen(global.screen);
 | 
			
		||||
                           })
 | 
			
		||||
                         });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _reset: function() {
 | 
			
		||||
        this.actor.hide();
 | 
			
		||||
        this.setLabel(null);
 | 
			
		||||
        this.setLevel(null);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _monitorsChanged: function() {
 | 
			
		||||
        /* assume 110x110 on a 640x480 display and scale from there */
 | 
			
		||||
        let monitor = Main.layoutManager.primaryMonitor;
 | 
			
		||||
        let scalew = monitor.width / 640.0;
 | 
			
		||||
        let scaleh = monitor.height / 480.0;
 | 
			
		||||
        let scale = Math.min(scalew, scaleh);
 | 
			
		||||
        this._popupSize = 110 * Math.max(1, scale);
 | 
			
		||||
 | 
			
		||||
        this._box.translation_y = monitor.height / 4;
 | 
			
		||||
        this._icon.icon_size = this._popupSize / 2;
 | 
			
		||||
        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;
 | 
			
		||||
 | 
			
		||||
        this._box.style = 'min-height: %dpx;'.format(Math.max(minWidth, minHeight));
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
@@ -10,28 +10,38 @@ const St = imports.gi.St;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Gdk = imports.gi.Gdk;
 | 
			
		||||
 | 
			
		||||
const Background = imports.ui.background;
 | 
			
		||||
const Dash = imports.ui.dash;
 | 
			
		||||
const DND = imports.ui.dnd;
 | 
			
		||||
const LayoutManager = imports.ui.layout;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const MessageTray = imports.ui.messageTray;
 | 
			
		||||
const OverviewControls = imports.ui.overviewControls;
 | 
			
		||||
const Panel = imports.ui.panel;
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
const Tweener = imports.ui.tweener;
 | 
			
		||||
const ViewSelector = imports.ui.viewSelector;
 | 
			
		||||
const WorkspaceThumbnail = imports.ui.workspaceThumbnail;
 | 
			
		||||
 | 
			
		||||
// Time for initial animation going into Overview mode
 | 
			
		||||
const ANIMATION_TIME = 0.25;
 | 
			
		||||
 | 
			
		||||
// Must be less than ANIMATION_TIME, since we switch to
 | 
			
		||||
// or from the overview completely after ANIMATION_TIME,
 | 
			
		||||
// and don't want the shading animation to get cut off
 | 
			
		||||
const SHADE_ANIMATION_TIME = .20;
 | 
			
		||||
const DND_WINDOW_SWITCH_TIMEOUT = 1250;
 | 
			
		||||
 | 
			
		||||
const DND_WINDOW_SWITCH_TIMEOUT = 750;
 | 
			
		||||
 | 
			
		||||
const OVERVIEW_ACTIVATION_TIMEOUT = 0.5;
 | 
			
		||||
const GLSL_DIM_EFFECT_DECLARATIONS = '';
 | 
			
		||||
const GLSL_DIM_EFFECT_CODE = '\
 | 
			
		||||
   vec2 dist = cogl_tex_coord_in[0].xy - vec2(0.5, 0.5); \
 | 
			
		||||
   float elipse_radius = 0.5; \
 | 
			
		||||
   /* from https://bugzilla.gnome.org/show_bug.cgi?id=669798: \
 | 
			
		||||
      the alpha on the gradient goes from 250 at its darkest to 180 at its most transparent. */ \
 | 
			
		||||
   float y = 250.0 / 255.0; \
 | 
			
		||||
   float x = 180.0 / 255.0; \
 | 
			
		||||
   /* interpolate darkening value, based on distance from screen center */ \
 | 
			
		||||
   float val = min(length(dist), elipse_radius); \
 | 
			
		||||
   float a = mix(x, y, val / elipse_radius); \
 | 
			
		||||
   /* dim_factor varies from [1.0 -> 0.5] when overview is showing \
 | 
			
		||||
      We use it to smooth value, then we clamp it to valid color interval */ \
 | 
			
		||||
   a = clamp(a - cogl_color_in.r + 0.5, 0.0, 1.0); \
 | 
			
		||||
   /* We\'re blending between: color and black color (obviously omitted in the equation) */ \
 | 
			
		||||
   cogl_color_out.xyz = cogl_color_out.xyz * (1.0 - a); \
 | 
			
		||||
   cogl_color_out.a = 1.0;';
 | 
			
		||||
 | 
			
		||||
const ShellInfo = new Lang.Class({
 | 
			
		||||
    Name: 'ShellInfo',
 | 
			
		||||
@@ -107,52 +117,52 @@ const Overview = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._overviewCreated = true;
 | 
			
		||||
 | 
			
		||||
        // The main Background actors are inside global.window_group which are
 | 
			
		||||
        // The main BackgroundActor is inside global.window_group which is
 | 
			
		||||
        // hidden when displaying the overview, so we create a new
 | 
			
		||||
        // 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.
 | 
			
		||||
        let monitor = Main.layoutManager.primaryMonitor;
 | 
			
		||||
        this._background = Meta.BackgroundActor.new_for_screen(global.screen);
 | 
			
		||||
        this._background.add_glsl_snippet(Meta.SnippetHook.FRAGMENT,
 | 
			
		||||
                                          GLSL_DIM_EFFECT_DECLARATIONS,
 | 
			
		||||
                                          GLSL_DIM_EFFECT_CODE,
 | 
			
		||||
                                          false);
 | 
			
		||||
        this._background.hide();
 | 
			
		||||
        global.overlay_group.add_actor(this._background);
 | 
			
		||||
 | 
			
		||||
        this._desktopFade = new St.Bin();
 | 
			
		||||
        Main.layoutManager.overviewGroup.add_child(this._desktopFade);
 | 
			
		||||
 | 
			
		||||
        let layout = new Clutter.BinLayout();
 | 
			
		||||
        this._stack = new Clutter.Actor({ layout_manager: layout });
 | 
			
		||||
        this._stack.add_constraint(new LayoutManager.MonitorConstraint({ primary: true }));
 | 
			
		||||
        global.overlay_group.add_actor(this._desktopFade);
 | 
			
		||||
 | 
			
		||||
        /* Translators: This is the main view to select
 | 
			
		||||
           activities. See also note for "Activities" string. */
 | 
			
		||||
        this._overview = new St.BoxLayout({ name: 'overview',
 | 
			
		||||
                                            accessible_name: _("Overview"),
 | 
			
		||||
                                            reactive: true,
 | 
			
		||||
                                            vertical: true,
 | 
			
		||||
                                            x_expand: true,
 | 
			
		||||
                                            y_expand: true });
 | 
			
		||||
                                            vertical: true });
 | 
			
		||||
        this._overview._delegate = this;
 | 
			
		||||
 | 
			
		||||
        this._backgroundGroup = new Meta.BackgroundGroup();
 | 
			
		||||
        Main.layoutManager.overviewGroup.add_child(this._backgroundGroup);
 | 
			
		||||
        this._bgManagers = [];
 | 
			
		||||
        this._group = new St.BoxLayout({ name: 'overview-group' });
 | 
			
		||||
 | 
			
		||||
        this._activationTime = 0;
 | 
			
		||||
        this._capturedEventId = 0;
 | 
			
		||||
        this._buttonPressId = 0;
 | 
			
		||||
 | 
			
		||||
        this.visible = false;           // animating to overview, in overview, animating out
 | 
			
		||||
        this._shown = false;            // show() and not hide()
 | 
			
		||||
        this._shownTemporarily = false; // showTemporarily() and not hideTemporarily()
 | 
			
		||||
        this._modal = false;            // have a modal grab
 | 
			
		||||
        this.animationInProgress = false;
 | 
			
		||||
        this.visibleTarget = false;
 | 
			
		||||
        this._hideInProgress = false;
 | 
			
		||||
 | 
			
		||||
        // During transitions, we raise this to the top to avoid having the overview
 | 
			
		||||
        // area be reactive; it causes too many issues such as double clicks on
 | 
			
		||||
        // Dash elements, or mouseover handlers in the workspaces.
 | 
			
		||||
        this._coverPane = new Clutter.Actor({ opacity: 0,
 | 
			
		||||
                                              reactive: true });
 | 
			
		||||
        Main.layoutManager.overviewGroup.add_child(this._coverPane);
 | 
			
		||||
        this._coverPane = new Clutter.Rectangle({ opacity: 0,
 | 
			
		||||
                                                  reactive: true });
 | 
			
		||||
        this._overview.add_actor(this._coverPane);
 | 
			
		||||
        this._coverPane.connect('event', Lang.bind(this, function (actor, event) { return true; }));
 | 
			
		||||
 | 
			
		||||
        this._stack.add_actor(this._overview);
 | 
			
		||||
        Main.layoutManager.overviewGroup.add_child(this._stack);
 | 
			
		||||
        this._overview.hide();
 | 
			
		||||
        global.overlay_group.add_actor(this._overview);
 | 
			
		||||
 | 
			
		||||
        this._coverPane.hide();
 | 
			
		||||
 | 
			
		||||
@@ -176,56 +186,6 @@ const Overview = new Lang.Class({
 | 
			
		||||
            this.init();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateBackgrounds: function() {
 | 
			
		||||
        for (let i = 0; i < this._bgManagers.length; i++)
 | 
			
		||||
            this._bgManagers[i].destroy();
 | 
			
		||||
 | 
			
		||||
        this._bgManagers = [];
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < Main.layoutManager.monitors.length; i++) {
 | 
			
		||||
            let bgManager = new Background.BackgroundManager({ container: this._backgroundGroup,
 | 
			
		||||
                                                               monitorIndex: i,
 | 
			
		||||
                                                               effects: Meta.BackgroundEffects.VIGNETTE });
 | 
			
		||||
            this._bgManagers.push(bgManager);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _unshadeBackgrounds: function() {
 | 
			
		||||
        let backgrounds = this._backgroundGroup.get_children();
 | 
			
		||||
        for (let i = 0; i < backgrounds.length; i++) {
 | 
			
		||||
            let background = backgrounds[i]._delegate;
 | 
			
		||||
 | 
			
		||||
            Tweener.addTween(background,
 | 
			
		||||
                             { brightness: 1.0,
 | 
			
		||||
                               time: SHADE_ANIMATION_TIME,
 | 
			
		||||
                               transition: 'easeOutQuad'
 | 
			
		||||
                             });
 | 
			
		||||
            Tweener.addTween(background,
 | 
			
		||||
                             { vignetteSharpness: 0.0,
 | 
			
		||||
                               time: SHADE_ANIMATION_TIME,
 | 
			
		||||
                               transition: 'easeOutQuad'
 | 
			
		||||
                             });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _shadeBackgrounds: function() {
 | 
			
		||||
        let backgrounds = this._backgroundGroup.get_children();
 | 
			
		||||
        for (let i = 0; i < backgrounds.length; i++) {
 | 
			
		||||
            let background = backgrounds[i]._delegate;
 | 
			
		||||
 | 
			
		||||
            Tweener.addTween(background,
 | 
			
		||||
                             { brightness: 0.8,
 | 
			
		||||
                               time: SHADE_ANIMATION_TIME,
 | 
			
		||||
                               transition: 'easeOutQuad'
 | 
			
		||||
                             });
 | 
			
		||||
            Tweener.addTween(background,
 | 
			
		||||
                             { vignetteSharpness: 0.7,
 | 
			
		||||
                               time: SHADE_ANIMATION_TIME,
 | 
			
		||||
                               transition: 'easeOutQuad'
 | 
			
		||||
                             });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _sessionUpdated: function() {
 | 
			
		||||
        this.isDummy = !Main.sessionMode.hasOverview;
 | 
			
		||||
        this._createOverview();
 | 
			
		||||
@@ -255,41 +215,53 @@ const Overview = new Lang.Class({
 | 
			
		||||
                                              in the search entry when no search is
 | 
			
		||||
                                              active; it should not exceed ~30
 | 
			
		||||
                                              characters. */
 | 
			
		||||
                                           hint_text: _("Type to search…"),
 | 
			
		||||
                                           hint_text: _("Type to search..."),
 | 
			
		||||
                                           track_hover: true,
 | 
			
		||||
                                           can_focus: true });
 | 
			
		||||
        this._searchEntryBin = new St.Bin({ child: this._searchEntry,
 | 
			
		||||
                                            x_align: St.Align.MIDDLE });
 | 
			
		||||
        this._overview.add_actor(this._searchEntryBin);
 | 
			
		||||
 | 
			
		||||
        // Create controls
 | 
			
		||||
        this._controls = new OverviewControls.ControlsManager(this._searchEntry);
 | 
			
		||||
        this._dash = this._controls.dash;
 | 
			
		||||
        this.viewSelector = this._controls.viewSelector;
 | 
			
		||||
 | 
			
		||||
        // Add our same-line elements after the search entry
 | 
			
		||||
        this._overview.add(this._controls.actor, { y_fill: true, expand: true });
 | 
			
		||||
        this._controls.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
 | 
			
		||||
 | 
			
		||||
        this._stack.add_actor(this._controls.indicatorActor);
 | 
			
		||||
 | 
			
		||||
        // TODO - recalculate everything when desktop size changes
 | 
			
		||||
        this._dash = new Dash.Dash();
 | 
			
		||||
        this._group.add_actor(this._dash.actor);
 | 
			
		||||
        this.dashIconSize = this._dash.iconSize;
 | 
			
		||||
        this._dash.connect('icon-size-changed',
 | 
			
		||||
                           Lang.bind(this, function() {
 | 
			
		||||
                               this.dashIconSize = this._dash.iconSize;
 | 
			
		||||
                           }));
 | 
			
		||||
 | 
			
		||||
        // Translators: this is the name of the dock/favorites area on
 | 
			
		||||
        // the left of the overview
 | 
			
		||||
        Main.ctrlAltTabManager.addGroup(this._dash.actor, _("Dash"), 'user-bookmarks-symbolic');
 | 
			
		||||
 | 
			
		||||
        this._viewSelector = new ViewSelector.ViewSelector(this._searchEntry,
 | 
			
		||||
                                                           this._dash.showAppsButton);
 | 
			
		||||
        this._group.add(this._viewSelector.actor, { x_fill: true,
 | 
			
		||||
                                                    expand: true });
 | 
			
		||||
 | 
			
		||||
        // Add our same-line elements after the search entry
 | 
			
		||||
        this._overview.add(this._group, { y_fill: true,
 | 
			
		||||
                                          expand: true });
 | 
			
		||||
 | 
			
		||||
        // Then account for message tray
 | 
			
		||||
        this._messageTrayGhost = new St.Bin({ style_class: 'message-tray-summary',
 | 
			
		||||
                                              reactive: false,
 | 
			
		||||
                                              opacity: 0,
 | 
			
		||||
                                              x_fill: true,
 | 
			
		||||
                                              y_fill: true });
 | 
			
		||||
        this._overview.add_actor(this._messageTrayGhost);
 | 
			
		||||
 | 
			
		||||
        Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._relayout));
 | 
			
		||||
        this._relayout();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addSearchProvider: function(provider) {
 | 
			
		||||
        this.viewSelector.addSearchProvider(provider);
 | 
			
		||||
        this._viewSelector.addSearchProvider(provider);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    removeSearchProvider: function(provider) {
 | 
			
		||||
        this.viewSelector.removeSearchProvider(provider);
 | 
			
		||||
        this._viewSelector.removeSearchProvider(provider);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
@@ -305,22 +277,18 @@ const Overview = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDragBegin: function() {
 | 
			
		||||
        this._inXdndDrag = true;
 | 
			
		||||
 | 
			
		||||
        DND.addDragMonitor(this._dragMonitor);
 | 
			
		||||
        // Remember the workspace we started from
 | 
			
		||||
        this._lastActiveWorkspaceIndex = global.screen.get_active_workspace_index();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDragEnd: function(time) {
 | 
			
		||||
        this._inXdndDrag = false;
 | 
			
		||||
 | 
			
		||||
        // In case the drag was canceled while in the overview
 | 
			
		||||
        // we have to go back to where we started and hide
 | 
			
		||||
        // the overview
 | 
			
		||||
        if (this._shown) {
 | 
			
		||||
        if (this._shownTemporarily)  {
 | 
			
		||||
            global.screen.get_workspace_by_index(this._lastActiveWorkspaceIndex).activate(time);
 | 
			
		||||
            this.hide();
 | 
			
		||||
            this.hideTemporarily();
 | 
			
		||||
        }
 | 
			
		||||
        this._resetWindowSwitchTimeout();
 | 
			
		||||
        this._lastHoveredWindow = null;
 | 
			
		||||
@@ -368,7 +336,7 @@ const Overview = new Lang.Class({
 | 
			
		||||
                                                this._needsFakePointerEvent = true;
 | 
			
		||||
                                                Main.activateWindow(dragEvent.targetActor._delegate.metaWindow,
 | 
			
		||||
                                                                    this._windowSwitchTimestamp);
 | 
			
		||||
                                                this.hide();
 | 
			
		||||
                                                this.hideTemporarily();
 | 
			
		||||
                                                this._lastHoveredWindow = null;
 | 
			
		||||
                                            }));
 | 
			
		||||
        }
 | 
			
		||||
@@ -376,10 +344,6 @@ const Overview = new Lang.Class({
 | 
			
		||||
        return DND.DragMotionResult.CONTINUE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onScrollEvent: function(actor, event) {
 | 
			
		||||
        this.emit('scroll-event', event);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addAction: function(action) {
 | 
			
		||||
        if (this.isDummy)
 | 
			
		||||
            return;
 | 
			
		||||
@@ -409,12 +373,16 @@ const Overview = new Lang.Class({
 | 
			
		||||
        // when it is next shown.
 | 
			
		||||
        this.hide();
 | 
			
		||||
 | 
			
		||||
        let workArea = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex);
 | 
			
		||||
        let primary = Main.layoutManager.primaryMonitor;
 | 
			
		||||
 | 
			
		||||
        this._coverPane.set_position(0, workArea.y);
 | 
			
		||||
        this._coverPane.set_size(workArea.width, workArea.height);
 | 
			
		||||
        let contentY = Main.panel.actor.height;
 | 
			
		||||
        let contentHeight = primary.height - contentY - Main.messageTray.actor.height;
 | 
			
		||||
 | 
			
		||||
        this._updateBackgrounds();
 | 
			
		||||
        this._overview.set_position(primary.x, primary.y);
 | 
			
		||||
        this._overview.set_size(primary.width, primary.height);
 | 
			
		||||
 | 
			
		||||
        this._coverPane.set_position(0, contentY);
 | 
			
		||||
        this._coverPane.set_size(primary.width, contentHeight);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onRestacked: function() {
 | 
			
		||||
@@ -433,7 +401,6 @@ const Overview = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    beginItemDrag: function(source) {
 | 
			
		||||
        this.emit('item-drag-begin');
 | 
			
		||||
        this._inDrag = true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    cancelledItemDrag: function(source) {
 | 
			
		||||
@@ -442,12 +409,10 @@ const Overview = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    endItemDrag: function(source) {
 | 
			
		||||
        this.emit('item-drag-end');
 | 
			
		||||
        this._inDrag = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    beginWindowDrag: function(source) {
 | 
			
		||||
        this.emit('window-drag-begin');
 | 
			
		||||
        this._inDrag = true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    cancelledWindowDrag: function(source) {
 | 
			
		||||
@@ -456,31 +421,23 @@ const Overview = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    endWindowDrag: function(source) {
 | 
			
		||||
        this.emit('window-drag-end');
 | 
			
		||||
        this._inDrag = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // show:
 | 
			
		||||
    //
 | 
			
		||||
    // Animates the overview visible and grabs mouse and keyboard input
 | 
			
		||||
    show: function() {
 | 
			
		||||
    show : function() {
 | 
			
		||||
        if (this.isDummy)
 | 
			
		||||
            return;
 | 
			
		||||
        if (this._shown)
 | 
			
		||||
            return;
 | 
			
		||||
        this._shown = true;
 | 
			
		||||
 | 
			
		||||
        if (!this._syncGrab())
 | 
			
		||||
        this._syncInputMode();
 | 
			
		||||
        if (!this._modal)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        Main.layoutManager.showOverview();
 | 
			
		||||
        this._animateVisible();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    focusSearch: function() {
 | 
			
		||||
        this.show();
 | 
			
		||||
        this._searchEntry.grab_key_focus();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    fadeInDesktop: function() {
 | 
			
		||||
            this._desktopFade.opacity = 0;
 | 
			
		||||
            this._desktopFade.show();
 | 
			
		||||
@@ -509,27 +466,60 @@ const Overview = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this.visible = true;
 | 
			
		||||
        this.animationInProgress = true;
 | 
			
		||||
        this.visibleTarget = true;
 | 
			
		||||
        this._activationTime = Date.now() / 1000;
 | 
			
		||||
 | 
			
		||||
        // All the the actors in the window group are completely obscured,
 | 
			
		||||
        // hiding the group holding them while the Overview is displayed greatly
 | 
			
		||||
        // increases performance of the Overview especially when there are many
 | 
			
		||||
        // windows visible.
 | 
			
		||||
        //
 | 
			
		||||
        // If we switched to displaying the actors in the Overview rather than
 | 
			
		||||
        // clones of them, this would obviously no longer be necessary.
 | 
			
		||||
        //
 | 
			
		||||
        // Disable unredirection while in the overview
 | 
			
		||||
        Meta.disable_unredirect_for_screen(global.screen);
 | 
			
		||||
        this.viewSelector.show();
 | 
			
		||||
        global.window_group.hide();
 | 
			
		||||
        this._overview.show();
 | 
			
		||||
        this._background.show();
 | 
			
		||||
        this._viewSelector.show();
 | 
			
		||||
 | 
			
		||||
        this._stack.opacity = 0;
 | 
			
		||||
        Tweener.addTween(this._stack,
 | 
			
		||||
        this._overview.opacity = 0;
 | 
			
		||||
        Tweener.addTween(this._overview,
 | 
			
		||||
                         { opacity: 255,
 | 
			
		||||
                           transition: 'easeOutQuad',
 | 
			
		||||
                           time: ANIMATION_TIME,
 | 
			
		||||
                           onComplete: this._showDone,
 | 
			
		||||
                           onCompleteScope: this
 | 
			
		||||
                         });
 | 
			
		||||
        this._shadeBackgrounds();
 | 
			
		||||
 | 
			
		||||
        Tweener.addTween(this._background,
 | 
			
		||||
                         { dim_factor: 0.8,
 | 
			
		||||
                           time: ANIMATION_TIME,
 | 
			
		||||
                           transition: 'easeOutQuad'
 | 
			
		||||
                         });
 | 
			
		||||
 | 
			
		||||
        this._coverPane.raise_top();
 | 
			
		||||
        this._coverPane.show();
 | 
			
		||||
        this.emit('showing');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // showTemporarily:
 | 
			
		||||
    //
 | 
			
		||||
    // Animates the overview visible without grabbing mouse and keyboard input;
 | 
			
		||||
    // if show() has already been called, this has no immediate effect, but
 | 
			
		||||
    // will result in the overview not being hidden until hideTemporarily() is
 | 
			
		||||
    // called.
 | 
			
		||||
    showTemporarily: function() {
 | 
			
		||||
        if (this.isDummy)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (this._shownTemporarily)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._syncInputMode();
 | 
			
		||||
        this._animateVisible();
 | 
			
		||||
        this._shownTemporarily = true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // hide:
 | 
			
		||||
    //
 | 
			
		||||
    // Reverses the effect of show()
 | 
			
		||||
@@ -540,20 +530,28 @@ const Overview = new Lang.Class({
 | 
			
		||||
        if (!this._shown)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let event = Clutter.get_current_event();
 | 
			
		||||
        if (event) {
 | 
			
		||||
            let type = event.type();
 | 
			
		||||
            let button = (type == Clutter.EventType.BUTTON_PRESS ||
 | 
			
		||||
                          type == Clutter.EventType.BUTTON_RELEASE);
 | 
			
		||||
            let ctrl = (event.get_state() & Clutter.ModifierType.CONTROL_MASK) != 0;
 | 
			
		||||
            if (button && ctrl)
 | 
			
		||||
                return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._animateNotVisible();
 | 
			
		||||
        if (!this._shownTemporarily)
 | 
			
		||||
            this._animateNotVisible();
 | 
			
		||||
 | 
			
		||||
        this._shown = false;
 | 
			
		||||
        this._syncGrab();
 | 
			
		||||
        this._syncInputMode();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // hideTemporarily:
 | 
			
		||||
    //
 | 
			
		||||
    // Reverses the effect of showTemporarily()
 | 
			
		||||
    hideTemporarily: function() {
 | 
			
		||||
        if (this.isDummy)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (!this._shownTemporarily)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (!this._shown)
 | 
			
		||||
            this._animateNotVisible();
 | 
			
		||||
 | 
			
		||||
        this._shownTemporarily = false;
 | 
			
		||||
        this._syncInputMode();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    toggle: function() {
 | 
			
		||||
@@ -566,51 +564,37 @@ const Overview = new Lang.Class({
 | 
			
		||||
            this.show();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Checks if the Activities button is currently sensitive to
 | 
			
		||||
    // clicks. The first call to this function within the
 | 
			
		||||
    // OVERVIEW_ACTIVATION_TIMEOUT time of the hot corner being
 | 
			
		||||
    // triggered will return false. This avoids opening and closing
 | 
			
		||||
    // the overview if the user both triggered the hot corner and
 | 
			
		||||
    // clicked the Activities button.
 | 
			
		||||
    shouldToggleByCornerOrButton: function() {
 | 
			
		||||
        if (this.animationInProgress)
 | 
			
		||||
            return false;
 | 
			
		||||
        if (this._inDrag)
 | 
			
		||||
            return false;
 | 
			
		||||
        if (this._activationTime == 0 || Date.now() / 1000 - this._activationTime > OVERVIEW_ACTIVATION_TIMEOUT)
 | 
			
		||||
            return true;
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    //// Private methods ////
 | 
			
		||||
 | 
			
		||||
    _syncGrab: function() {
 | 
			
		||||
        // We delay grab changes during animation so that when removing the
 | 
			
		||||
    _syncInputMode: function() {
 | 
			
		||||
        // We delay input mode changes during animation so that when removing the
 | 
			
		||||
        // overview we don't have a problem with the release of a press/release
 | 
			
		||||
        // going to an application.
 | 
			
		||||
        if (this.animationInProgress)
 | 
			
		||||
            return true;
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (this._shown) {
 | 
			
		||||
            let shouldBeModal = !this._inXdndDrag;
 | 
			
		||||
            if (shouldBeModal) {
 | 
			
		||||
                if (!this._modal) {
 | 
			
		||||
                    if (Main.pushModal(this._overview,
 | 
			
		||||
                                       { keybindingMode: Shell.KeyBindingMode.OVERVIEW })) {
 | 
			
		||||
                        this._modal = true;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        this.hide();
 | 
			
		||||
                        return false;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            if (!this._modal) {
 | 
			
		||||
                if (Main.pushModal(this._overview,
 | 
			
		||||
                                   { keybindingMode: Main.KeybindingMode.OVERVIEW }))
 | 
			
		||||
                    this._modal = true;
 | 
			
		||||
                else
 | 
			
		||||
                    this.hide();
 | 
			
		||||
            }
 | 
			
		||||
        } else if (this._shownTemporarily) {
 | 
			
		||||
            if (this._modal) {
 | 
			
		||||
                Main.popModal(this._overview);
 | 
			
		||||
                this._modal = false;
 | 
			
		||||
            }
 | 
			
		||||
            global.stage_input_mode = Shell.StageInputMode.FULLSCREEN;
 | 
			
		||||
        } else {
 | 
			
		||||
            if (this._modal) {
 | 
			
		||||
                Main.popModal(this._overview);
 | 
			
		||||
                this._modal = false;
 | 
			
		||||
            }
 | 
			
		||||
            else if (global.stage_input_mode == Shell.StageInputMode.FULLSCREEN)
 | 
			
		||||
                global.stage_input_mode = Shell.StageInputMode.NORMAL;
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _animateNotVisible: function() {
 | 
			
		||||
@@ -618,19 +602,24 @@ const Overview = new Lang.Class({
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this.animationInProgress = true;
 | 
			
		||||
        this.visibleTarget = false;
 | 
			
		||||
        this._hideInProgress = true;
 | 
			
		||||
 | 
			
		||||
        this.viewSelector.zoomFromOverview();
 | 
			
		||||
        this._viewSelector.zoomFromOverview();
 | 
			
		||||
 | 
			
		||||
        // Make other elements fade out.
 | 
			
		||||
        Tweener.addTween(this._stack,
 | 
			
		||||
        Tweener.addTween(this._overview,
 | 
			
		||||
                         { opacity: 0,
 | 
			
		||||
                           transition: 'easeOutQuad',
 | 
			
		||||
                           time: ANIMATION_TIME,
 | 
			
		||||
                           onComplete: this._hideDone,
 | 
			
		||||
                           onCompleteScope: this
 | 
			
		||||
                         });
 | 
			
		||||
        this._unshadeBackgrounds();
 | 
			
		||||
 | 
			
		||||
        Tweener.addTween(this._background,
 | 
			
		||||
                         { dim_factor: 1.0,
 | 
			
		||||
                           time: ANIMATION_TIME,
 | 
			
		||||
                           transition: 'easeOutQuad'
 | 
			
		||||
                         });
 | 
			
		||||
 | 
			
		||||
        this._coverPane.raise_top();
 | 
			
		||||
        this._coverPane.show();
 | 
			
		||||
@@ -644,10 +633,10 @@ const Overview = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this.emit('shown');
 | 
			
		||||
        // Handle any calls to hide* while we were showing
 | 
			
		||||
        if (!this._shown)
 | 
			
		||||
        if (!this._shown && !this._shownTemporarily)
 | 
			
		||||
            this._animateNotVisible();
 | 
			
		||||
 | 
			
		||||
        this._syncGrab();
 | 
			
		||||
        this._syncInputMode();
 | 
			
		||||
        global.sync_pointer();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -655,21 +644,25 @@ const Overview = new Lang.Class({
 | 
			
		||||
        // Re-enable unredirection
 | 
			
		||||
        Meta.enable_unredirect_for_screen(global.screen);
 | 
			
		||||
 | 
			
		||||
        this.viewSelector.hide();
 | 
			
		||||
        global.window_group.show();
 | 
			
		||||
 | 
			
		||||
        this._viewSelector.hide();
 | 
			
		||||
        this._desktopFade.hide();
 | 
			
		||||
        this._coverPane.hide();
 | 
			
		||||
        this._background.hide();
 | 
			
		||||
        this._overview.hide();
 | 
			
		||||
 | 
			
		||||
        this.visible = false;
 | 
			
		||||
        this.animationInProgress = false;
 | 
			
		||||
        this._hideInProgress = false;
 | 
			
		||||
 | 
			
		||||
        this._coverPane.hide();
 | 
			
		||||
 | 
			
		||||
        this.emit('hidden');
 | 
			
		||||
        // Handle any calls to show* while we were hiding
 | 
			
		||||
        if (this._shown)
 | 
			
		||||
        if (this._shown || this._shownTemporarily)
 | 
			
		||||
            this._animateVisible();
 | 
			
		||||
        else
 | 
			
		||||
            Main.layoutManager.hideOverview();
 | 
			
		||||
 | 
			
		||||
        this._syncGrab();
 | 
			
		||||
        this._syncInputMode();
 | 
			
		||||
 | 
			
		||||
        // Fake a pointer event if requested
 | 
			
		||||
        if (this._needsFakePointerEvent) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,629 +0,0 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const GObject = imports.gi.GObject;
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Meta = imports.gi.Meta;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
 | 
			
		||||
const Dash = imports.ui.dash;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
const Tweener = imports.ui.tweener;
 | 
			
		||||
const ViewSelector = imports.ui.viewSelector;
 | 
			
		||||
const WorkspaceThumbnail = imports.ui.workspaceThumbnail;
 | 
			
		||||
 | 
			
		||||
const SIDE_CONTROLS_ANIMATION_TIME = 0.16;
 | 
			
		||||
 | 
			
		||||
function getRtlSlideDirection(direction, actor) {
 | 
			
		||||
    let rtl = (actor.text_direction == Clutter.TextDirection.RTL);
 | 
			
		||||
    if (rtl)
 | 
			
		||||
        direction = (direction == SlideDirection.LEFT) ?
 | 
			
		||||
            SlideDirection.RIGHT : SlideDirection.LEFT;
 | 
			
		||||
 | 
			
		||||
    return direction;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const SlideDirection = {
 | 
			
		||||
    LEFT: 0,
 | 
			
		||||
    RIGHT: 1
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const SlideLayout = new Lang.Class({
 | 
			
		||||
    Name: 'SlideLayout',
 | 
			
		||||
    Extends: Clutter.FixedLayout,
 | 
			
		||||
 | 
			
		||||
    _init: function(params) {
 | 
			
		||||
        this._slideX = 1;
 | 
			
		||||
        this._direction = SlideDirection.LEFT;
 | 
			
		||||
 | 
			
		||||
        this.parent(params);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    vfunc_get_preferred_width: function(container, forHeight) {
 | 
			
		||||
        let child = container.get_first_child();
 | 
			
		||||
 | 
			
		||||
        let [minWidth, natWidth] = child.get_preferred_width(forHeight);
 | 
			
		||||
 | 
			
		||||
        minWidth *= this._slideX;
 | 
			
		||||
        natWidth *= this._slideX;
 | 
			
		||||
 | 
			
		||||
        return [minWidth, natWidth];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    vfunc_allocate: function(container, box, flags) {
 | 
			
		||||
        let child = container.get_first_child();
 | 
			
		||||
 | 
			
		||||
        let availWidth = Math.round(box.x2 - box.x1);
 | 
			
		||||
        let availHeight = Math.round(box.y2 - box.y1);
 | 
			
		||||
        let [, natWidth] = child.get_preferred_width(availHeight);
 | 
			
		||||
 | 
			
		||||
        // Align the actor inside the clipped box, as the actor's alignment
 | 
			
		||||
        // flags only determine what to do if the allocated box is bigger
 | 
			
		||||
        // than the actor's box.
 | 
			
		||||
        let realDirection = getRtlSlideDirection(this._direction, child);
 | 
			
		||||
        let alignX = (realDirection == SlideDirection.LEFT) ? (availWidth - natWidth) : 0;
 | 
			
		||||
 | 
			
		||||
        let actorBox = new Clutter.ActorBox();
 | 
			
		||||
        actorBox.x1 = alignX;
 | 
			
		||||
        actorBox.x2 = actorBox.x1 + child.x_expand ? availWidth : natWidth;
 | 
			
		||||
        actorBox.y1 = 0;
 | 
			
		||||
        actorBox.y2 = actorBox.y1 + availHeight;
 | 
			
		||||
 | 
			
		||||
        child.allocate(actorBox, flags);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    set slideX(value) {
 | 
			
		||||
        this._slideX = value;
 | 
			
		||||
        this.layout_changed();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get slideX() {
 | 
			
		||||
        return this._slideX;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    set slideDirection(direction) {
 | 
			
		||||
        this._direction = direction;
 | 
			
		||||
        this.layout_changed();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get slideDirection() {
 | 
			
		||||
        return this._direction;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const SlidingControl = new Lang.Class({
 | 
			
		||||
    Name: 'SlidingControl',
 | 
			
		||||
 | 
			
		||||
    _init: function(params) {
 | 
			
		||||
        params = Params.parse(params, { slideDirection: SlideDirection.LEFT });
 | 
			
		||||
 | 
			
		||||
        this.visible = true;
 | 
			
		||||
        this.inDrag = false;
 | 
			
		||||
 | 
			
		||||
        this.layout = new SlideLayout();
 | 
			
		||||
        this.layout.slideDirection = params.slideDirection;
 | 
			
		||||
        this.actor = new St.Widget({ layout_manager: this.layout,
 | 
			
		||||
                                     style_class: 'overview-controls',
 | 
			
		||||
                                     clip_to_allocation: true });
 | 
			
		||||
 | 
			
		||||
        Main.overview.connect('showing', Lang.bind(this, this._onOverviewShowing));
 | 
			
		||||
 | 
			
		||||
        Main.overview.connect('item-drag-begin', Lang.bind(this, this._onDragBegin));
 | 
			
		||||
        Main.overview.connect('item-drag-end', Lang.bind(this, this._onDragEnd));
 | 
			
		||||
        Main.overview.connect('item-drag-cancelled', Lang.bind(this, this._onDragEnd));
 | 
			
		||||
 | 
			
		||||
        Main.overview.connect('window-drag-begin', Lang.bind(this, this._onWindowDragBegin));
 | 
			
		||||
        Main.overview.connect('window-drag-cancelled', Lang.bind(this, this._onWindowDragEnd));
 | 
			
		||||
        Main.overview.connect('window-drag-end', Lang.bind(this, this._onWindowDragEnd));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getSlide: function() {
 | 
			
		||||
        throw new Error('getSlide() must be overridden');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    updateSlide: function() {
 | 
			
		||||
        Tweener.addTween(this.layout, { slideX: this.getSlide(),
 | 
			
		||||
                                        time: SIDE_CONTROLS_ANIMATION_TIME,
 | 
			
		||||
                                        transition: 'easeOutQuad' });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getVisibleWidth: function() {
 | 
			
		||||
        let child = this.actor.get_first_child();
 | 
			
		||||
        let [, , natWidth, ] = child.get_preferred_size();
 | 
			
		||||
        return natWidth;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getTranslation: function() {
 | 
			
		||||
        let child = this.actor.get_first_child();
 | 
			
		||||
        let direction = getRtlSlideDirection(this.layout.slideDirection, child);
 | 
			
		||||
        let visibleWidth = this.getVisibleWidth();
 | 
			
		||||
 | 
			
		||||
        if (direction == SlideDirection.LEFT)
 | 
			
		||||
            return - visibleWidth;
 | 
			
		||||
        else
 | 
			
		||||
            return visibleWidth;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateTranslation: function() {
 | 
			
		||||
        let translationStart = 0;
 | 
			
		||||
        let translationEnd = 0;
 | 
			
		||||
        let translation = this._getTranslation();
 | 
			
		||||
 | 
			
		||||
        if (this.visible) {
 | 
			
		||||
            translationStart = translation;
 | 
			
		||||
        } else {
 | 
			
		||||
            translationEnd = translation;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this.actor.translation_x == translationEnd)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this.actor.translation_x = translationStart;
 | 
			
		||||
        Tweener.addTween(this.actor, { translation_x: translationEnd,
 | 
			
		||||
                                       time: SIDE_CONTROLS_ANIMATION_TIME,
 | 
			
		||||
                                       transition: 'easeOutQuad'
 | 
			
		||||
                                     });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onOverviewShowing: function() {
 | 
			
		||||
        // reset any translation and make sure the actor is visible when
 | 
			
		||||
        // entering the overview
 | 
			
		||||
        this.visible = true;
 | 
			
		||||
        this.layout.slideX = this.getSlide();
 | 
			
		||||
        this.actor.translation_x = 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onWindowDragBegin: function() {
 | 
			
		||||
        this._onDragBegin();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onWindowDragEnd: function() {
 | 
			
		||||
        this._onDragEnd();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDragBegin: function() {
 | 
			
		||||
        this.inDrag = true;
 | 
			
		||||
        this.actor.translation_x = 0;
 | 
			
		||||
        this.updateSlide();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDragEnd: function() {
 | 
			
		||||
        this.inDrag = false;
 | 
			
		||||
        this.updateSlide();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    fadeIn: function() {
 | 
			
		||||
        Tweener.addTween(this.actor, { opacity: 255,
 | 
			
		||||
                                       time: SIDE_CONTROLS_ANIMATION_TIME / 2,
 | 
			
		||||
                                       transition: 'easeInQuad'
 | 
			
		||||
                                     });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    fadeHalf: function() {
 | 
			
		||||
        Tweener.addTween(this.actor, { opacity: 128,
 | 
			
		||||
                                       time: SIDE_CONTROLS_ANIMATION_TIME / 2,
 | 
			
		||||
                                       transition: 'easeOutQuad'
 | 
			
		||||
                                     });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    slideIn: function() {
 | 
			
		||||
        this.visible = true;
 | 
			
		||||
        this._updateTranslation();
 | 
			
		||||
        // we will update slideX and the translation from pageEmpty
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    slideOut: function() {
 | 
			
		||||
        this.visible = false;
 | 
			
		||||
        this._updateTranslation();
 | 
			
		||||
        // we will update slideX from pageEmpty
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    pageEmpty: function() {
 | 
			
		||||
        // When pageEmpty is received, there's no visible view in the
 | 
			
		||||
        // selector; this means we can now safely set the full slide for
 | 
			
		||||
        // the next page, since slideIn or slideOut might have been called,
 | 
			
		||||
        // changing the visiblity
 | 
			
		||||
        this.layout.slideX = this.getSlide();
 | 
			
		||||
        this._updateTranslation();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const ThumbnailsSlider = new Lang.Class({
 | 
			
		||||
    Name: 'ThumbnailsSlider',
 | 
			
		||||
    Extends: SlidingControl,
 | 
			
		||||
 | 
			
		||||
    _init: function(thumbnailsBox) {
 | 
			
		||||
        this.parent({ slideDirection: SlideDirection.RIGHT });
 | 
			
		||||
 | 
			
		||||
        this._thumbnailsBox = thumbnailsBox;
 | 
			
		||||
 | 
			
		||||
        this.actor.request_mode = Clutter.RequestMode.WIDTH_FOR_HEIGHT;
 | 
			
		||||
        this.actor.reactive = true;
 | 
			
		||||
        this.actor.track_hover = true;
 | 
			
		||||
        this.actor.add_actor(this._thumbnailsBox.actor);
 | 
			
		||||
 | 
			
		||||
        Main.layoutManager.connect('monitors-changed', Lang.bind(this, this.updateSlide));
 | 
			
		||||
        Main.overview.connect('hiding', Lang.bind(this, this.slideOut));
 | 
			
		||||
        this.actor.connect('notify::hover', Lang.bind(this, this.updateSlide));
 | 
			
		||||
        this._thumbnailsBox.actor.bind_property('visible', this.actor, 'visible', GObject.BindingFlags.SYNC_CREATE);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getAlwaysZoomOut: function() {
 | 
			
		||||
        // Always show the pager when hover, during a drag, or if workspaces are
 | 
			
		||||
        // actually used, e.g. there are windows on more than one
 | 
			
		||||
        let alwaysZoomOut = this.actor.hover || this.inDrag || !Meta.prefs_get_dynamic_workspaces() || global.screen.n_workspaces > 2;
 | 
			
		||||
 | 
			
		||||
        if (!alwaysZoomOut) {
 | 
			
		||||
            let monitors = Main.layoutManager.monitors;
 | 
			
		||||
            let primary = Main.layoutManager.primaryMonitor;
 | 
			
		||||
 | 
			
		||||
            /* Look for any monitor to the right of the primary, if there is
 | 
			
		||||
             * one, we always keep zoom out, otherwise its hard to reach
 | 
			
		||||
             * the thumbnail area without passing into the next monitor. */
 | 
			
		||||
            for (let i = 0; i < monitors.length; i++) {
 | 
			
		||||
                if (monitors[i].x >= primary.x + primary.width) {
 | 
			
		||||
                    alwaysZoomOut = true;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return alwaysZoomOut;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onOverviewShowing: function() {
 | 
			
		||||
        this.visible = true;
 | 
			
		||||
        this.layout.slideX = this.getSlide();
 | 
			
		||||
        this.actor.translation_x = this._getTranslation();
 | 
			
		||||
        this.slideIn();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getNonExpandedWidth: function() {
 | 
			
		||||
        let child = this.actor.get_first_child();
 | 
			
		||||
        return child.get_theme_node().get_length('visible-width');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getSlide: function() {
 | 
			
		||||
        if (!this.visible)
 | 
			
		||||
            return 0;
 | 
			
		||||
 | 
			
		||||
        let alwaysZoomOut = this._getAlwaysZoomOut();
 | 
			
		||||
        if (alwaysZoomOut)
 | 
			
		||||
            return 1;
 | 
			
		||||
 | 
			
		||||
        let child = this.actor.get_first_child();
 | 
			
		||||
        let preferredHeight = child.get_preferred_height(-1)[1];
 | 
			
		||||
        let expandedWidth = child.get_preferred_width(preferredHeight)[1];
 | 
			
		||||
 | 
			
		||||
        return this.getNonExpandedWidth() / expandedWidth;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getVisibleWidth: function() {
 | 
			
		||||
        let alwaysZoomOut = this._getAlwaysZoomOut();
 | 
			
		||||
        if (alwaysZoomOut)
 | 
			
		||||
            return this.parent();
 | 
			
		||||
        else
 | 
			
		||||
            return this.getNonExpandedWidth();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const DashSlider = new Lang.Class({
 | 
			
		||||
    Name: 'DashSlider',
 | 
			
		||||
    Extends: SlidingControl,
 | 
			
		||||
 | 
			
		||||
    _init: function(dash) {
 | 
			
		||||
        this.parent({ slideDirection: SlideDirection.LEFT });
 | 
			
		||||
 | 
			
		||||
        this._dash = dash;
 | 
			
		||||
 | 
			
		||||
        // SlideLayout reads the actor's expand flags to decide
 | 
			
		||||
        // whether to allocate the natural size to its child, or the whole
 | 
			
		||||
        // available allocation
 | 
			
		||||
        this._dash.actor.x_expand = true;
 | 
			
		||||
 | 
			
		||||
        this.actor.x_align = Clutter.ActorAlign.START;
 | 
			
		||||
        this.actor.y_expand = true;
 | 
			
		||||
 | 
			
		||||
        this.actor.add_actor(this._dash.actor);
 | 
			
		||||
 | 
			
		||||
        this._dash.connect('icon-size-changed', Lang.bind(this, this.updateSlide));
 | 
			
		||||
        Main.overview.connect('hiding', Lang.bind(this, this.slideOut));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getSlide: function() {
 | 
			
		||||
        if (this.visible || this.inDrag)
 | 
			
		||||
            return 1;
 | 
			
		||||
        else
 | 
			
		||||
            return 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onOverviewShowing: function() {
 | 
			
		||||
        this.visible = true;
 | 
			
		||||
        this.layout.slideX = this.getSlide();
 | 
			
		||||
        this.actor.translation_x = this._getTranslation();
 | 
			
		||||
        this.slideIn();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onWindowDragBegin: function() {
 | 
			
		||||
        this.fadeHalf();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onWindowDragEnd: function() {
 | 
			
		||||
        this.fadeIn();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const DashSpacer = new Lang.Class({
 | 
			
		||||
    Name: 'DashSpacer',
 | 
			
		||||
    Extends: St.Widget,
 | 
			
		||||
 | 
			
		||||
    _init: function(params) {
 | 
			
		||||
        this.parent(params);
 | 
			
		||||
 | 
			
		||||
        this._bindConstraint = null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setDashActor: function(dashActor) {
 | 
			
		||||
        if (this._bindConstraint) {
 | 
			
		||||
            this.remove_constraint(this._bindConstraint);
 | 
			
		||||
            this._bindConstraint = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (dashActor) {
 | 
			
		||||
            this._bindConstraint = new Clutter.BindConstraint({ source: dashActor,
 | 
			
		||||
                                                                coordinate: Clutter.BindCoordinate.SIZE });
 | 
			
		||||
            this.add_constraint(this._bindConstraint);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    vfunc_get_preferred_width: function(forHeight) {
 | 
			
		||||
        let box = this.get_allocation_box();
 | 
			
		||||
        let minWidth = this.parent(forHeight)[0];
 | 
			
		||||
        let natWidth = box.x2 - box.x1;
 | 
			
		||||
        return [minWidth, natWidth];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    vfunc_get_preferred_height: function(forWidth) {
 | 
			
		||||
        let box = this.get_allocation_box();
 | 
			
		||||
        let minHeight = this.parent(forWidth)[0];
 | 
			
		||||
        let natHeight = box.y2 - box.y1;
 | 
			
		||||
        return [minHeight, natHeight];
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const MessagesIndicator = new Lang.Class({
 | 
			
		||||
    Name: 'MessagesIndicator',
 | 
			
		||||
 | 
			
		||||
    _init: function(viewSelector) {
 | 
			
		||||
        this._count = 0;
 | 
			
		||||
        this._sources = [];
 | 
			
		||||
        this._viewSelector = viewSelector;
 | 
			
		||||
 | 
			
		||||
        this._container = new St.BoxLayout({ style_class: 'messages-indicator-contents',
 | 
			
		||||
                                             reactive: true,
 | 
			
		||||
                                             track_hover: true,
 | 
			
		||||
                                             x_expand: true,
 | 
			
		||||
                                             y_expand: true,
 | 
			
		||||
                                             x_align: Clutter.ActorAlign.CENTER });
 | 
			
		||||
 | 
			
		||||
        this._icon = new St.Icon({ icon_name: 'user-idle-symbolic',
 | 
			
		||||
                                   icon_size: 16 });
 | 
			
		||||
        this._container.add_actor(this._icon);
 | 
			
		||||
 | 
			
		||||
        this._label = new St.Label();
 | 
			
		||||
        this._container.add_actor(this._label);
 | 
			
		||||
 | 
			
		||||
        this._highlight = new St.Widget({ style_class: 'messages-indicator-highlight',
 | 
			
		||||
                                          x_expand: true,
 | 
			
		||||
                                          y_expand: true,
 | 
			
		||||
                                          y_align: Clutter.ActorAlign.END,
 | 
			
		||||
                                          visible: false });
 | 
			
		||||
 | 
			
		||||
        this._container.connect('notify::hover', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                this._highlight.visible = this._container.hover;
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
        let clickAction = new Clutter.ClickAction();
 | 
			
		||||
        this._container.add_action(clickAction);
 | 
			
		||||
        clickAction.connect('clicked', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                Main.messageTray.openTray();
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
        Main.messageTray.connect('showing', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                this._highlight.visible = false;
 | 
			
		||||
                this._container.hover = false;
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
        let layout = new Clutter.BinLayout();
 | 
			
		||||
        this.actor = new St.Widget({ layout_manager: layout,
 | 
			
		||||
                                     style_class: 'messages-indicator',
 | 
			
		||||
                                     y_expand: true,
 | 
			
		||||
                                     y_align: Clutter.ActorAlign.END,
 | 
			
		||||
                                     visible: false });
 | 
			
		||||
        this.actor.add_actor(this._container);
 | 
			
		||||
        this.actor.add_actor(this._highlight);
 | 
			
		||||
 | 
			
		||||
        Main.messageTray.connect('source-added', Lang.bind(this, this._onSourceAdded));
 | 
			
		||||
        Main.messageTray.connect('source-removed', Lang.bind(this, this._onSourceRemoved));
 | 
			
		||||
 | 
			
		||||
        let sources = Main.messageTray.getSources();
 | 
			
		||||
        sources.forEach(Lang.bind(this, function(source) { this._onSourceAdded(null, source); }));
 | 
			
		||||
 | 
			
		||||
        this._viewSelector.connect('page-changed', Lang.bind(this, this._updateVisibility));
 | 
			
		||||
        Main.overview.connect('showing', Lang.bind(this, this._updateVisibility));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onSourceAdded: function(tray, source) {
 | 
			
		||||
        if (source.trayIcon)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (source.isTransient)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        source.connect('count-updated', Lang.bind(this, this._updateCount));
 | 
			
		||||
        this._sources.push(source);
 | 
			
		||||
        this._updateCount();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onSourceRemoved: function(tray, source) {
 | 
			
		||||
        this._sources.splice(this._sources.indexOf(source), 1);
 | 
			
		||||
        this._updateCount();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateCount: function() {
 | 
			
		||||
        let count = 0;
 | 
			
		||||
        let hasChats = false;
 | 
			
		||||
        this._sources.forEach(Lang.bind(this,
 | 
			
		||||
            function(source) {
 | 
			
		||||
                count += source.indicatorCount;
 | 
			
		||||
                hasChats |= source.isChat;
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
        this._count = count;
 | 
			
		||||
        this._label.text = ngettext("%d new message",
 | 
			
		||||
                                    "%d new messages",
 | 
			
		||||
                                   count).format(count);
 | 
			
		||||
 | 
			
		||||
        this._icon.visible = hasChats;
 | 
			
		||||
        this._updateVisibility();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateVisibility: function() {
 | 
			
		||||
        let activePage = this._viewSelector.getActivePage();
 | 
			
		||||
        let visible = ((this._count > 0) && (activePage == ViewSelector.ViewPage.WINDOWS));
 | 
			
		||||
 | 
			
		||||
        this.actor.visible = visible;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const ControlsLayout = new Lang.Class({
 | 
			
		||||
    Name: 'ControlsLayout',
 | 
			
		||||
    Extends: Clutter.BinLayout,
 | 
			
		||||
    Signals: { 'allocation-changed': { flags: GObject.SignalFlags.RUN_LAST } },
 | 
			
		||||
 | 
			
		||||
    vfunc_allocate: function(container, box, flags) {
 | 
			
		||||
        this.parent(container, box, flags);
 | 
			
		||||
        this.emit('allocation-changed');
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const ControlsManager = new Lang.Class({
 | 
			
		||||
    Name: 'ControlsManager',
 | 
			
		||||
 | 
			
		||||
    _init: function(searchEntry) {
 | 
			
		||||
        this.dash = new Dash.Dash();
 | 
			
		||||
        this._dashSlider = new DashSlider(this.dash);
 | 
			
		||||
        this._dashSpacer = new DashSpacer();
 | 
			
		||||
        this._dashSpacer.setDashActor(this._dashSlider.actor);
 | 
			
		||||
 | 
			
		||||
        this._thumbnailsBox = new WorkspaceThumbnail.ThumbnailsBox();
 | 
			
		||||
        this._thumbnailsSlider = new ThumbnailsSlider(this._thumbnailsBox);
 | 
			
		||||
 | 
			
		||||
        this.viewSelector = new ViewSelector.ViewSelector(searchEntry,
 | 
			
		||||
                                                          this.dash.showAppsButton);
 | 
			
		||||
        this.viewSelector.connect('page-changed', Lang.bind(this, this._setVisibility));
 | 
			
		||||
        this.viewSelector.connect('page-empty', Lang.bind(this, this._onPageEmpty));
 | 
			
		||||
 | 
			
		||||
        this._indicator = new MessagesIndicator(this.viewSelector);
 | 
			
		||||
        this.indicatorActor = this._indicator.actor;
 | 
			
		||||
 | 
			
		||||
        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',
 | 
			
		||||
                                        x_expand: true, y_expand: true });
 | 
			
		||||
        this.actor.add_actor(this._group);
 | 
			
		||||
 | 
			
		||||
        this.actor.add_actor(this._dashSlider.actor);
 | 
			
		||||
 | 
			
		||||
        this._group.add_actor(this._dashSpacer);
 | 
			
		||||
        this._group.add(this.viewSelector.actor, { x_fill: true,
 | 
			
		||||
                                                   expand: true });
 | 
			
		||||
        this._group.add_actor(this._thumbnailsSlider.actor);
 | 
			
		||||
 | 
			
		||||
        layout.connect('allocation-changed', Lang.bind(this, this._updateWorkspacesGeometry));
 | 
			
		||||
 | 
			
		||||
        Main.overview.connect('showing', Lang.bind(this, this._updateSpacerVisibility));
 | 
			
		||||
        Main.overview.connect('item-drag-begin', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                let activePage = this.viewSelector.getActivePage();
 | 
			
		||||
                if (activePage != ViewSelector.ViewPage.WINDOWS)
 | 
			
		||||
                    this.viewSelector.fadeHalf();
 | 
			
		||||
            }));
 | 
			
		||||
        Main.overview.connect('item-drag-end', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                this.viewSelector.fadeIn();
 | 
			
		||||
            }));
 | 
			
		||||
        Main.overview.connect('item-drag-cancelled', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                this.viewSelector.fadeIn();
 | 
			
		||||
            }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateWorkspacesGeometry: function() {
 | 
			
		||||
        let [x, y] = this.actor.get_transformed_position();
 | 
			
		||||
        let [width, height] = this.actor.get_transformed_size();
 | 
			
		||||
        let geometry = { x: x, y: y, width: width, height: height };
 | 
			
		||||
 | 
			
		||||
        let spacing = this.actor.get_theme_node().get_length('spacing');
 | 
			
		||||
        let dashWidth = this._dashSlider.getVisibleWidth() + spacing;
 | 
			
		||||
        let thumbnailsWidth = this._thumbnailsSlider.getNonExpandedWidth() + spacing;
 | 
			
		||||
 | 
			
		||||
        geometry.width -= dashWidth;
 | 
			
		||||
        geometry.width -= thumbnailsWidth;
 | 
			
		||||
 | 
			
		||||
        if (this.actor.get_text_direction() == Clutter.TextDirection.LTR)
 | 
			
		||||
            geometry.x += dashWidth;
 | 
			
		||||
        else
 | 
			
		||||
            geometry.x += thumbnailsWidth;
 | 
			
		||||
 | 
			
		||||
        this.viewSelector.setWorkspacesFullGeometry(geometry);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _setVisibility: function() {
 | 
			
		||||
        // Ignore the case when we're leaving the overview, since
 | 
			
		||||
        // actors will be made visible again when entering the overview
 | 
			
		||||
        // next time, and animating them while doing so is just
 | 
			
		||||
        // unnecessary noise
 | 
			
		||||
        if (!Main.overview.visible ||
 | 
			
		||||
            (Main.overview.animationInProgress && !Main.overview.visibleTarget))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let activePage = this.viewSelector.getActivePage();
 | 
			
		||||
        let dashVisible = (activePage == ViewSelector.ViewPage.WINDOWS ||
 | 
			
		||||
                           activePage == ViewSelector.ViewPage.APPS);
 | 
			
		||||
        let thumbnailsVisible = (activePage == ViewSelector.ViewPage.WINDOWS);
 | 
			
		||||
 | 
			
		||||
        if (dashVisible)
 | 
			
		||||
            this._dashSlider.slideIn();
 | 
			
		||||
        else
 | 
			
		||||
            this._dashSlider.slideOut();
 | 
			
		||||
 | 
			
		||||
        if (thumbnailsVisible)
 | 
			
		||||
            this._thumbnailsSlider.slideIn();
 | 
			
		||||
        else
 | 
			
		||||
            this._thumbnailsSlider.slideOut();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateSpacerVisibility: function() {
 | 
			
		||||
        if (Main.overview.animationInProgress && !Main.overview.visibleTarget)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let activePage = this.viewSelector.getActivePage();
 | 
			
		||||
        this._dashSpacer.visible = (activePage == ViewSelector.ViewPage.WINDOWS);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onPageEmpty: function() {
 | 
			
		||||
        this._dashSlider.pageEmpty();
 | 
			
		||||
        this._thumbnailsSlider.pageEmpty();
 | 
			
		||||
 | 
			
		||||
        this._updateSpacerVisibility();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										564
									
								
								js/ui/panel.js
									
									
									
									
									
								
							
							
						
						@@ -15,14 +15,13 @@ const Signals = imports.signals;
 | 
			
		||||
const Atk = imports.gi.Atk;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const Animation = imports.ui.animation;
 | 
			
		||||
const Config = imports.misc.config;
 | 
			
		||||
const CtrlAltTab = imports.ui.ctrlAltTab;
 | 
			
		||||
const DND = imports.ui.dnd;
 | 
			
		||||
const Layout = imports.ui.layout;
 | 
			
		||||
const Overview = imports.ui.overview;
 | 
			
		||||
const PopupMenu = imports.ui.popupMenu;
 | 
			
		||||
const PanelMenu = imports.ui.panelMenu;
 | 
			
		||||
const RemoteMenu = imports.ui.remoteMenu;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const Tweener = imports.ui.tweener;
 | 
			
		||||
 | 
			
		||||
@@ -30,6 +29,7 @@ const PANEL_ICON_SIZE = 24;
 | 
			
		||||
 | 
			
		||||
const BUTTON_DND_ACTIVATION_TIMEOUT = 250;
 | 
			
		||||
 | 
			
		||||
const ANIMATED_ICON_UPDATE_TIMEOUT = 100;
 | 
			
		||||
const SPINNER_ANIMATION_TIME = 0.2;
 | 
			
		||||
 | 
			
		||||
// To make sure the panel corners blend nicely with the panel,
 | 
			
		||||
@@ -75,6 +75,81 @@ function _unpremultiply(color) {
 | 
			
		||||
                               blue: blue, alpha: color.alpha });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const Animation = new Lang.Class({
 | 
			
		||||
    Name: 'Animation',
 | 
			
		||||
 | 
			
		||||
    _init: function(filename, width, height, speed) {
 | 
			
		||||
        this.actor = new St.Bin();
 | 
			
		||||
        this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
 | 
			
		||||
        this._speed = speed;
 | 
			
		||||
 | 
			
		||||
        this._isLoaded = false;
 | 
			
		||||
        this._isPlaying = false;
 | 
			
		||||
        this._timeoutId = 0;
 | 
			
		||||
        this._frame = 0;
 | 
			
		||||
        this._animations = St.TextureCache.get_default().load_sliced_image (filename, width, height,
 | 
			
		||||
                                                                            Lang.bind(this, this._animationsLoaded));
 | 
			
		||||
        this.actor.set_child(this._animations);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    play: function() {
 | 
			
		||||
        if (this._isLoaded && this._timeoutId == 0) {
 | 
			
		||||
            if (this._frame == 0)
 | 
			
		||||
                this._showFrame(0);
 | 
			
		||||
 | 
			
		||||
            this._timeoutId = Mainloop.timeout_add(this._speed, Lang.bind(this, this._update));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._isPlaying = true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    stop: function() {
 | 
			
		||||
        if (this._timeoutId > 0) {
 | 
			
		||||
            Mainloop.source_remove(this._timeoutId);
 | 
			
		||||
            this._timeoutId = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._isPlaying = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _showFrame: function(frame) {
 | 
			
		||||
        let oldFrameActor = this._animations.get_child_at_index(this._frame);
 | 
			
		||||
        if (oldFrameActor)
 | 
			
		||||
            oldFrameActor.hide();
 | 
			
		||||
 | 
			
		||||
        this._frame = (frame % this._animations.get_n_children());
 | 
			
		||||
 | 
			
		||||
        let newFrameActor = this._animations.get_child_at_index(this._frame);
 | 
			
		||||
        if (newFrameActor)
 | 
			
		||||
            newFrameActor.show();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _update: function() {
 | 
			
		||||
        this._showFrame(this._frame + 1);
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _animationsLoaded: function() {
 | 
			
		||||
        this._isLoaded = true;
 | 
			
		||||
 | 
			
		||||
        if (this._isPlaying)
 | 
			
		||||
            this.play();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDestroy: function() {
 | 
			
		||||
        this.stop();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const AnimatedIcon = new Lang.Class({
 | 
			
		||||
    Name: 'AnimatedIcon',
 | 
			
		||||
    Extends: Animation,
 | 
			
		||||
 | 
			
		||||
    _init: function(name, size) {
 | 
			
		||||
        this.parent(global.datadir + '/theme/' + name, size, size, ANIMATED_ICON_UPDATE_TIMEOUT);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const TextShadower = new Lang.Class({
 | 
			
		||||
    Name: 'TextShadower',
 | 
			
		||||
 | 
			
		||||
@@ -128,7 +203,7 @@ const TextShadower = new Lang.Class({
 | 
			
		||||
            let child = children[i];
 | 
			
		||||
            let childBox = new Clutter.ActorBox();
 | 
			
		||||
            // The order of the labels here is arbitrary, except
 | 
			
		||||
            // we know the "real" label is at the end because Clutter.Actor
 | 
			
		||||
            // we know the "real" label is at the end because Clutter.Group
 | 
			
		||||
            // sorts by Z order
 | 
			
		||||
            switch (i) {
 | 
			
		||||
                case 0: // top
 | 
			
		||||
@@ -184,11 +259,11 @@ const AppMenuButton = new Lang.Class({
 | 
			
		||||
        this._actionGroupNotifyId = 0;
 | 
			
		||||
 | 
			
		||||
        let bin = new St.Bin({ name: 'appMenu' });
 | 
			
		||||
        bin.connect('style-changed', Lang.bind(this, this._onStyleChanged));
 | 
			
		||||
        this.actor.add_actor(bin);
 | 
			
		||||
 | 
			
		||||
        this.actor.bind_property("reactive", this.actor, "can-focus", 0);
 | 
			
		||||
        this.actor.reactive = false;
 | 
			
		||||
        this._targetIsCurrent = false;
 | 
			
		||||
 | 
			
		||||
        this._container = new Shell.GenericContainer();
 | 
			
		||||
        bin.set_child(this._container);
 | 
			
		||||
@@ -206,47 +281,51 @@ const AppMenuButton = new Lang.Class({
 | 
			
		||||
        this._iconBox.connect('notify::allocation',
 | 
			
		||||
                              Lang.bind(this, this._updateIconBoxClip));
 | 
			
		||||
        this._container.add_actor(this._iconBox);
 | 
			
		||||
 | 
			
		||||
        this._hbox = new St.BoxLayout({ style_class: 'panel-status-menu-box' });
 | 
			
		||||
        this._container.add_actor(this._hbox);
 | 
			
		||||
 | 
			
		||||
        this._label = new TextShadower();
 | 
			
		||||
        this._label.actor.y_align = Clutter.ActorAlign.CENTER;
 | 
			
		||||
        this._hbox.add_actor(this._label.actor);
 | 
			
		||||
        this._arrow = PopupMenu.unicodeArrow(St.Side.BOTTOM);
 | 
			
		||||
        this._hbox.add_actor(this._arrow);
 | 
			
		||||
        this._container.add_actor(this._label.actor);
 | 
			
		||||
 | 
			
		||||
        this._iconBottomClip = 0;
 | 
			
		||||
 | 
			
		||||
        this._visible = !Main.overview.visible;
 | 
			
		||||
        if (!this._visible)
 | 
			
		||||
            this.actor.hide();
 | 
			
		||||
        this._overviewHidingId = Main.overview.connect('hiding', Lang.bind(this, this._sync));
 | 
			
		||||
        this._overviewShowingId = Main.overview.connect('showing', Lang.bind(this, this._sync));
 | 
			
		||||
        Main.overview.connect('hiding', Lang.bind(this, function () {
 | 
			
		||||
            this.show();
 | 
			
		||||
        }));
 | 
			
		||||
        Main.overview.connect('showing', Lang.bind(this, function () {
 | 
			
		||||
            this.hide();
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        this._stop = true;
 | 
			
		||||
 | 
			
		||||
        this._spinner = null;
 | 
			
		||||
        this._spinner = new AnimatedIcon('process-working.svg',
 | 
			
		||||
                                         PANEL_ICON_SIZE);
 | 
			
		||||
        this._container.add_actor(this._spinner.actor);
 | 
			
		||||
        this._spinner.actor.hide();
 | 
			
		||||
        this._spinner.actor.lower_bottom();
 | 
			
		||||
 | 
			
		||||
        let tracker = Shell.WindowTracker.get_default();
 | 
			
		||||
        let appSys = Shell.AppSystem.get_default();
 | 
			
		||||
        this._focusAppNotifyId =
 | 
			
		||||
            tracker.connect('notify::focus-app', Lang.bind(this, this._focusAppChanged));
 | 
			
		||||
        this._appStateChangedSignalId =
 | 
			
		||||
            appSys.connect('app-state-changed', Lang.bind(this, this._onAppStateChanged));
 | 
			
		||||
        this._switchWorkspaceNotifyId =
 | 
			
		||||
            global.window_manager.connect('switch-workspace', Lang.bind(this, this._sync));
 | 
			
		||||
        tracker.connect('notify::focus-app', Lang.bind(this, this._focusAppChanged));
 | 
			
		||||
        appSys.connect('app-state-changed', Lang.bind(this, this._onAppStateChanged));
 | 
			
		||||
 | 
			
		||||
        global.window_manager.connect('switch-workspace', Lang.bind(this, this._sync));
 | 
			
		||||
 | 
			
		||||
        this._sync();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    show: function() {
 | 
			
		||||
        if (this._visible)
 | 
			
		||||
        if (this._visible || Main.screenShield.locked)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._visible = true;
 | 
			
		||||
        this.actor.reactive = true;
 | 
			
		||||
        this.actor.show();
 | 
			
		||||
 | 
			
		||||
        if (!this._targetIsCurrent)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this.actor.reactive = true;
 | 
			
		||||
 | 
			
		||||
        Tweener.removeTweens(this.actor);
 | 
			
		||||
        Tweener.addTween(this.actor,
 | 
			
		||||
                         { opacity: 255,
 | 
			
		||||
@@ -260,6 +339,11 @@ const AppMenuButton = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._visible = false;
 | 
			
		||||
        this.actor.reactive = false;
 | 
			
		||||
        if (!this._targetIsCurrent) {
 | 
			
		||||
            this.actor.hide();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Tweener.removeTweens(this.actor);
 | 
			
		||||
        Tweener.addTween(this.actor,
 | 
			
		||||
                         { opacity: 0,
 | 
			
		||||
@@ -271,36 +355,19 @@ const AppMenuButton = new Lang.Class({
 | 
			
		||||
                           onCompleteScope: this });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onStyleChanged: function(actor) {
 | 
			
		||||
        let node = actor.get_theme_node();
 | 
			
		||||
        let [success, icon] = node.lookup_url('spinner-image', false);
 | 
			
		||||
        if (!success || this._spinnerIcon == icon)
 | 
			
		||||
            return;
 | 
			
		||||
        this._spinnerIcon = icon;
 | 
			
		||||
        this._spinner = new Animation.AnimatedIcon(this._spinnerIcon, PANEL_ICON_SIZE);
 | 
			
		||||
        this._hbox.add_actor(this._spinner.actor);
 | 
			
		||||
        this._spinner.actor.hide();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onIconBoxStyleChanged: function() {
 | 
			
		||||
        let node = this._iconBox.get_theme_node();
 | 
			
		||||
        this._iconBottomClip = node.get_length('app-icon-bottom-clip');
 | 
			
		||||
        this._updateIconBoxClip();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _syncIcon: function() {
 | 
			
		||||
        if (!this._targetApp)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let icon = this._targetApp.get_faded_icon(2 * PANEL_ICON_SIZE, this._iconBox.text_direction);
 | 
			
		||||
        this._iconBox.set_child(icon);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onIconThemeChanged: function() {
 | 
			
		||||
        if (this._iconBox.child == null)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._syncIcon();
 | 
			
		||||
        this._iconBox.child.destroy();
 | 
			
		||||
        let icon = this._targetApp.get_faded_icon(2 * PANEL_ICON_SIZE);
 | 
			
		||||
        this._iconBox.set_child(icon);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateIconBoxClip: function() {
 | 
			
		||||
@@ -318,10 +385,7 @@ const AppMenuButton = new Lang.Class({
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._stop = true;
 | 
			
		||||
 | 
			
		||||
        if (this._spinner == null)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this.actor.reactive = true;
 | 
			
		||||
        Tweener.addTween(this._spinner.actor,
 | 
			
		||||
                         { opacity: 0,
 | 
			
		||||
                           time: SPINNER_ANIMATION_TIME,
 | 
			
		||||
@@ -337,10 +401,7 @@ const AppMenuButton = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    startAnimation: function() {
 | 
			
		||||
        this._stop = false;
 | 
			
		||||
 | 
			
		||||
        if (this._spinner == null)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this.actor.reactive = false;
 | 
			
		||||
        this._spinner.play();
 | 
			
		||||
        this._spinner.actor.show();
 | 
			
		||||
    },
 | 
			
		||||
@@ -349,7 +410,7 @@ const AppMenuButton = new Lang.Class({
 | 
			
		||||
        let [minSize, naturalSize] = this._iconBox.get_preferred_width(forHeight);
 | 
			
		||||
        alloc.min_size = minSize;
 | 
			
		||||
        alloc.natural_size = naturalSize;
 | 
			
		||||
        [minSize, naturalSize] = this._hbox.get_preferred_width(forHeight);
 | 
			
		||||
        [minSize, naturalSize] = this._label.actor.get_preferred_width(forHeight);
 | 
			
		||||
        alloc.min_size = alloc.min_size + Math.max(0, minSize - Math.floor(alloc.min_size / 2));
 | 
			
		||||
        alloc.natural_size = alloc.natural_size + Math.max(0, naturalSize - Math.floor(alloc.natural_size / 2));
 | 
			
		||||
    },
 | 
			
		||||
@@ -358,7 +419,7 @@ const AppMenuButton = new Lang.Class({
 | 
			
		||||
        let [minSize, naturalSize] = this._iconBox.get_preferred_height(forWidth);
 | 
			
		||||
        alloc.min_size = minSize;
 | 
			
		||||
        alloc.natural_size = naturalSize;
 | 
			
		||||
        [minSize, naturalSize] = this._hbox.get_preferred_height(forWidth);
 | 
			
		||||
        [minSize, naturalSize] = this._label.actor.get_preferred_height(forWidth);
 | 
			
		||||
        if (minSize > alloc.min_size)
 | 
			
		||||
            alloc.min_size = minSize;
 | 
			
		||||
        if (naturalSize > alloc.natural_size)
 | 
			
		||||
@@ -388,10 +449,11 @@ const AppMenuButton = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        let iconWidth = childBox.x2 - childBox.x1;
 | 
			
		||||
 | 
			
		||||
        [minWidth, naturalWidth] = this._hbox.get_preferred_width(-1);
 | 
			
		||||
        [minWidth, minHeight, naturalWidth, naturalHeight] = this._label.actor.get_preferred_size();
 | 
			
		||||
 | 
			
		||||
        childBox.y1 = 0;
 | 
			
		||||
        childBox.y2 = allocHeight;
 | 
			
		||||
        yPadding = Math.floor(Math.max(0, allocHeight - naturalHeight) / 2);
 | 
			
		||||
        childBox.y1 = yPadding;
 | 
			
		||||
        childBox.y2 = childBox.y1 + Math.min(naturalHeight, allocHeight);
 | 
			
		||||
 | 
			
		||||
        if (direction == Clutter.TextDirection.LTR) {
 | 
			
		||||
            childBox.x1 = Math.floor(iconWidth / 2);
 | 
			
		||||
@@ -400,7 +462,21 @@ const AppMenuButton = new Lang.Class({
 | 
			
		||||
            childBox.x2 = allocWidth - Math.floor(iconWidth / 2);
 | 
			
		||||
            childBox.x1 = Math.max(0, childBox.x2 - naturalWidth);
 | 
			
		||||
        }
 | 
			
		||||
        this._hbox.allocate(childBox, flags);
 | 
			
		||||
        this._label.actor.allocate(childBox, flags);
 | 
			
		||||
 | 
			
		||||
        if (direction == Clutter.TextDirection.LTR) {
 | 
			
		||||
            childBox.x1 = Math.floor(iconWidth / 2) + this._label.actor.width;
 | 
			
		||||
            childBox.x2 = childBox.x1 + this._spinner.actor.width;
 | 
			
		||||
            childBox.y1 = box.y1;
 | 
			
		||||
            childBox.y2 = box.y2 - 1;
 | 
			
		||||
            this._spinner.actor.allocate(childBox, flags);
 | 
			
		||||
        } else {
 | 
			
		||||
            childBox.x1 = -this._spinner.actor.width;
 | 
			
		||||
            childBox.x2 = childBox.x1 + this._spinner.actor.width;
 | 
			
		||||
            childBox.y1 = box.y1;
 | 
			
		||||
            childBox.y2 = box.y2 - 1;
 | 
			
		||||
            this._spinner.actor.allocate(childBox, flags);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onAppStateChanged: function(appSys, app) {
 | 
			
		||||
@@ -426,88 +502,104 @@ const AppMenuButton = new Lang.Class({
 | 
			
		||||
            // If the app has just lost focus to the panel, pretend
 | 
			
		||||
            // nothing happened; otherwise you can't keynav to the
 | 
			
		||||
            // app menu.
 | 
			
		||||
            if (global.stage.key_focus != null)
 | 
			
		||||
            if (global.stage_input_mode == Shell.StageInputMode.FOCUSED)
 | 
			
		||||
                return;
 | 
			
		||||
        }
 | 
			
		||||
        this._sync();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _findTargetApp: function() {
 | 
			
		||||
        let workspace = global.screen.get_active_workspace();
 | 
			
		||||
    _sync: function() {
 | 
			
		||||
        let tracker = Shell.WindowTracker.get_default();
 | 
			
		||||
        let focusedApp = tracker.focus_app;
 | 
			
		||||
        if (focusedApp && focusedApp.is_on_workspace(workspace))
 | 
			
		||||
            return focusedApp;
 | 
			
		||||
 | 
			
		||||
        let lastStartedApp = null;
 | 
			
		||||
        let workspace = global.screen.get_active_workspace();
 | 
			
		||||
        for (let i = 0; i < this._startingApps.length; i++)
 | 
			
		||||
            if (this._startingApps[i].is_on_workspace(workspace))
 | 
			
		||||
                return this._startingApps[i];
 | 
			
		||||
                lastStartedApp = this._startingApps[i];
 | 
			
		||||
 | 
			
		||||
        return null;
 | 
			
		||||
    },
 | 
			
		||||
        let targetApp = focusedApp != null ? focusedApp : lastStartedApp;
 | 
			
		||||
 | 
			
		||||
    _sync: function() {
 | 
			
		||||
        let targetApp = this._findTargetApp();
 | 
			
		||||
        if (targetApp == null) {
 | 
			
		||||
            if (!this._targetIsCurrent)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
        if (this._targetApp != targetApp) {
 | 
			
		||||
            if (this._appMenuNotifyId) {
 | 
			
		||||
                this._targetApp.disconnect(this._appMenuNotifyId);
 | 
			
		||||
                this._appMenuNotifyId = 0;
 | 
			
		||||
            }
 | 
			
		||||
            if (this._actionGroupNotifyId) {
 | 
			
		||||
                this._targetApp.disconnect(this._actionGroupNotifyId);
 | 
			
		||||
                this._actionGroupNotifyId = 0;
 | 
			
		||||
            }
 | 
			
		||||
            this.actor.reactive = false;
 | 
			
		||||
            this._targetIsCurrent = false;
 | 
			
		||||
 | 
			
		||||
            this._targetApp = targetApp;
 | 
			
		||||
 | 
			
		||||
            if (this._targetApp) {
 | 
			
		||||
                this._appMenuNotifyId = this._targetApp.connect('notify::menu', Lang.bind(this, this._sync));
 | 
			
		||||
                this._actionGroupNotifyId = this._targetApp.connect('notify::action-group', Lang.bind(this, this._sync));
 | 
			
		||||
                this._label.setText(this._targetApp.get_name());
 | 
			
		||||
                this.actor.set_accessible_name(this._targetApp.get_name());
 | 
			
		||||
            }
 | 
			
		||||
            Tweener.removeTweens(this.actor);
 | 
			
		||||
            Tweener.addTween(this.actor, { opacity: 0,
 | 
			
		||||
                                           time: Overview.ANIMATION_TIME,
 | 
			
		||||
                                           transition: 'easeOutQuad' });
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let visible = (this._targetApp != null && !Main.overview.visibleTarget);
 | 
			
		||||
        if (visible)
 | 
			
		||||
            this.show();
 | 
			
		||||
        else
 | 
			
		||||
            this.hide();
 | 
			
		||||
        if (!targetApp.is_on_workspace(workspace))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let isBusy = (this._targetApp != null &&
 | 
			
		||||
                      (this._targetApp.get_state() == Shell.AppState.STARTING ||
 | 
			
		||||
                       this._targetApp.get_state() == Shell.AppState.BUSY));
 | 
			
		||||
        if (isBusy)
 | 
			
		||||
        if (!this._targetIsCurrent) {
 | 
			
		||||
            this.actor.reactive = true;
 | 
			
		||||
            this._targetIsCurrent = true;
 | 
			
		||||
 | 
			
		||||
            Tweener.removeTweens(this.actor);
 | 
			
		||||
            Tweener.addTween(this.actor, { opacity: 255,
 | 
			
		||||
                                           time: Overview.ANIMATION_TIME,
 | 
			
		||||
                                           transition: 'easeOutQuad' });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (targetApp == this._targetApp) {
 | 
			
		||||
            if (targetApp && targetApp.get_state() != Shell.AppState.STARTING) {
 | 
			
		||||
                this.stopAnimation();
 | 
			
		||||
                this._maybeSetMenu();
 | 
			
		||||
            }
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._spinner.actor.hide();
 | 
			
		||||
        if (this._iconBox.child != null)
 | 
			
		||||
            this._iconBox.child.destroy();
 | 
			
		||||
        this._iconBox.hide();
 | 
			
		||||
        this._label.setText('');
 | 
			
		||||
 | 
			
		||||
        if (this._appMenuNotifyId)
 | 
			
		||||
            this._targetApp.disconnect(this._appMenuNotifyId);
 | 
			
		||||
        if (this._actionGroupNotifyId)
 | 
			
		||||
            this._targetApp.disconnect(this._actionGroupNotifyId);
 | 
			
		||||
        if (targetApp) {
 | 
			
		||||
            this._appMenuNotifyId = targetApp.connect('notify::menu', Lang.bind(this, this._sync));
 | 
			
		||||
            this._actionGroupNotifyId = targetApp.connect('notify::action-group', Lang.bind(this, this._sync));
 | 
			
		||||
        } else {
 | 
			
		||||
            this._appMenuNotifyId = 0;
 | 
			
		||||
            this._actionGroupNotifyId = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._targetApp = targetApp;
 | 
			
		||||
        let icon = targetApp.get_faded_icon(2 * PANEL_ICON_SIZE);
 | 
			
		||||
 | 
			
		||||
        this._label.setText(targetApp.get_name());
 | 
			
		||||
        this.setName(targetApp.get_name());
 | 
			
		||||
 | 
			
		||||
        this._iconBox.set_child(icon);
 | 
			
		||||
        this._iconBox.show();
 | 
			
		||||
 | 
			
		||||
        if (targetApp.get_state() == Shell.AppState.STARTING)
 | 
			
		||||
            this.startAnimation();
 | 
			
		||||
        else
 | 
			
		||||
            this.stopAnimation();
 | 
			
		||||
            this._maybeSetMenu();
 | 
			
		||||
 | 
			
		||||
        this.actor.reactive = (visible && !isBusy);
 | 
			
		||||
 | 
			
		||||
        this._syncIcon();
 | 
			
		||||
        this._maybeSetMenu();
 | 
			
		||||
        this.emit('changed');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _maybeSetMenu: function() {
 | 
			
		||||
        let menu;
 | 
			
		||||
 | 
			
		||||
        if (this._targetApp == null) {
 | 
			
		||||
            menu = null;
 | 
			
		||||
        } else if (this._targetApp.action_group && this._targetApp.menu) {
 | 
			
		||||
            if (this.menu instanceof RemoteMenu.RemoteMenu &&
 | 
			
		||||
        if (this._targetApp.action_group && this._targetApp.menu) {
 | 
			
		||||
            if (this.menu instanceof PopupMenu.RemoteMenu &&
 | 
			
		||||
                this.menu.actionGroup == this._targetApp.action_group)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            menu = new RemoteMenu.RemoteMenu(this.actor, this._targetApp.menu, this._targetApp.action_group);
 | 
			
		||||
            menu.connect('activate', Lang.bind(this, function() {
 | 
			
		||||
                let win = this._targetApp.get_windows()[0];
 | 
			
		||||
                win.check_alive(global.get_current_time());
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
            menu = new PopupMenu.RemoteMenu(this.actor, this._targetApp.menu, this._targetApp.action_group);
 | 
			
		||||
        } else {
 | 
			
		||||
            if (this.menu && this.menu.isDummyQuitMenu)
 | 
			
		||||
            if (this.menu.isDummyQuitMenu)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            // fallback to older menu
 | 
			
		||||
@@ -519,35 +611,7 @@ const AppMenuButton = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.setMenu(menu);
 | 
			
		||||
        if (menu)
 | 
			
		||||
            this._menuManager.addMenu(menu);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroy: function() {
 | 
			
		||||
        if (this._appStateChangedSignalId > 0) {
 | 
			
		||||
            let appSys = Shell.AppSystem.get_default();
 | 
			
		||||
            appSys.disconnect(this._appStateChangedSignalId);
 | 
			
		||||
            this._appStateChangedSignalId = 0;
 | 
			
		||||
        }
 | 
			
		||||
        if (this._focusAppNotifyId > 0) {
 | 
			
		||||
            let tracker = Shell.WindowTracker.get_default();
 | 
			
		||||
            tracker.disconnect(this._focusAppNotifyId);
 | 
			
		||||
            this._focusAppNotifyId = 0;
 | 
			
		||||
        }
 | 
			
		||||
        if (this._overviewHidingId > 0) {
 | 
			
		||||
            Main.overview.disconnect(this._overviewHidingId);
 | 
			
		||||
            this._overviewHidingId = 0;
 | 
			
		||||
        }
 | 
			
		||||
        if (this._overviewShowingId > 0) {
 | 
			
		||||
            Main.overview.disconnect(this._overviewShowingId);
 | 
			
		||||
            this._overviewShowingId = 0;
 | 
			
		||||
        }
 | 
			
		||||
        if (this._switchWorkspaceNotifyId > 0) {
 | 
			
		||||
            global.window_manager.disconnect(this._switchWorkspaceNotifyId);
 | 
			
		||||
            this._switchWorkspaceNotifyId = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.parent();
 | 
			
		||||
        this._menuManager.addMenu(menu);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@@ -561,16 +625,23 @@ const ActivitiesButton = new Lang.Class({
 | 
			
		||||
        this.parent(0.0, null, true);
 | 
			
		||||
        this.actor.accessible_role = Atk.Role.TOGGLE_BUTTON;
 | 
			
		||||
 | 
			
		||||
        let container = new Shell.GenericContainer();
 | 
			
		||||
        container.connect('get-preferred-width', Lang.bind(this, this._containerGetPreferredWidth));
 | 
			
		||||
        container.connect('get-preferred-height', Lang.bind(this, this._containerGetPreferredHeight));
 | 
			
		||||
        container.connect('allocate', Lang.bind(this, this._containerAllocate));
 | 
			
		||||
        this.actor.add_actor(container);
 | 
			
		||||
        this.actor.name = 'panelActivities';
 | 
			
		||||
 | 
			
		||||
        /* Translators: If there is no suitable word for "Activities"
 | 
			
		||||
           in your language, you can use the word for "Overview". */
 | 
			
		||||
        this._label = new St.Label({ text: _("Activities"),
 | 
			
		||||
                                     y_align: Clutter.ActorAlign.CENTER });
 | 
			
		||||
        this.actor.add_actor(this._label);
 | 
			
		||||
        this._label = new St.Label({ text: _("Activities") });
 | 
			
		||||
        container.add_actor(this._label);
 | 
			
		||||
 | 
			
		||||
        this.actor.label_actor = this._label;
 | 
			
		||||
 | 
			
		||||
        this.hotCorner = new Layout.HotCorner();
 | 
			
		||||
        container.add_actor(this.hotCorner.actor);
 | 
			
		||||
 | 
			
		||||
        this.actor.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
 | 
			
		||||
        this.actor.connect_after('button-release-event', Lang.bind(this, this._onButtonRelease));
 | 
			
		||||
        this.actor.connect_after('key-release-event', Lang.bind(this, this._onKeyRelease));
 | 
			
		||||
@@ -585,6 +656,44 @@ const ActivitiesButton = new Lang.Class({
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        this._xdndTimeOut = 0;
 | 
			
		||||
 | 
			
		||||
        // Since the hot corner uses stage coordinates, Clutter won't
 | 
			
		||||
        // queue relayouts for us when the panel moves. Queue a relayout
 | 
			
		||||
        // when that happens.
 | 
			
		||||
        Main.layoutManager.connect('panel-box-changed', Lang.bind(this, function() {
 | 
			
		||||
            container.queue_relayout();
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _containerGetPreferredWidth: function(actor, forHeight, alloc) {
 | 
			
		||||
        [alloc.min_size, alloc.natural_size] = this._label.get_preferred_width(forHeight);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _containerGetPreferredHeight: function(actor, forWidth, alloc) {
 | 
			
		||||
        [alloc.min_size, alloc.natural_size] = this._label.get_preferred_height(forWidth);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _containerAllocate: function(actor, box, flags) {
 | 
			
		||||
        this._label.allocate(box, flags);
 | 
			
		||||
 | 
			
		||||
        // The hot corner needs to be outside any padding/alignment
 | 
			
		||||
        // that has been imposed on us
 | 
			
		||||
        let primary = Main.layoutManager.primaryMonitor;
 | 
			
		||||
        let hotBox = new Clutter.ActorBox();
 | 
			
		||||
        let ok, x, y;
 | 
			
		||||
        if (actor.get_text_direction() == Clutter.TextDirection.LTR) {
 | 
			
		||||
            [ok, x, y] = actor.transform_stage_point(primary.x, primary.y)
 | 
			
		||||
        } else {
 | 
			
		||||
            [ok, x, y] = actor.transform_stage_point(primary.x + primary.width, primary.y);
 | 
			
		||||
            // hotCorner.actor has northeast gravity, so we don't need
 | 
			
		||||
            // to adjust x for its width
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        hotBox.x1 = Math.round(x);
 | 
			
		||||
        hotBox.x2 = hotBox.x1 + this.hotCorner.actor.width;
 | 
			
		||||
        hotBox.y1 = Math.round(y);
 | 
			
		||||
        hotBox.y2 = hotBox.y1 + this.hotCorner.actor.height;
 | 
			
		||||
        this.hotCorner.actor.allocate(hotBox, flags);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    handleDragOver: function(source, actor, x, y, time) {
 | 
			
		||||
@@ -594,14 +703,14 @@ const ActivitiesButton = new Lang.Class({
 | 
			
		||||
        if (this._xdndTimeOut != 0)
 | 
			
		||||
            Mainloop.source_remove(this._xdndTimeOut);
 | 
			
		||||
        this._xdndTimeOut = Mainloop.timeout_add(BUTTON_DND_ACTIVATION_TIMEOUT,
 | 
			
		||||
                                                 Lang.bind(this, this._xdndToggleOverview, actor));
 | 
			
		||||
                                                 Lang.bind(this, this._xdndShowOverview, actor));
 | 
			
		||||
 | 
			
		||||
        return DND.DragMotionResult.CONTINUE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onCapturedEvent: function(actor, event) {
 | 
			
		||||
        if (event.type() == Clutter.EventType.BUTTON_PRESS) {
 | 
			
		||||
            if (!Main.overview.shouldToggleByCornerOrButton())
 | 
			
		||||
            if (!this.hotCorner.shouldToggleOverviewOnClick())
 | 
			
		||||
                return true;
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
@@ -609,7 +718,6 @@ const ActivitiesButton = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _onButtonRelease: function() {
 | 
			
		||||
        Main.overview.toggle();
 | 
			
		||||
        this.menu.close();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onKeyRelease: function(actor, event) {
 | 
			
		||||
@@ -619,12 +727,16 @@ const ActivitiesButton = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _xdndToggleOverview: function(actor) {
 | 
			
		||||
    _xdndShowOverview: function(actor) {
 | 
			
		||||
        let [x, y, mask] = global.get_pointer();
 | 
			
		||||
        let pickedActor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y);
 | 
			
		||||
 | 
			
		||||
        if (pickedActor == this.actor && Main.overview.shouldToggleByCornerOrButton())
 | 
			
		||||
            Main.overview.toggle();
 | 
			
		||||
        if (pickedActor == this.actor) {
 | 
			
		||||
            if (!Main.overview.visible && !Main.overview.animationInProgress) {
 | 
			
		||||
                Main.overview.showTemporarily();
 | 
			
		||||
                Main.overview.beginItemDrag(actor);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Mainloop.source_remove(this._xdndTimeOut);
 | 
			
		||||
        this._xdndTimeOut = 0;
 | 
			
		||||
@@ -634,9 +746,12 @@ const ActivitiesButton = new Lang.Class({
 | 
			
		||||
const PanelCorner = new Lang.Class({
 | 
			
		||||
    Name: 'PanelCorner',
 | 
			
		||||
 | 
			
		||||
    _init: function(side) {
 | 
			
		||||
    _init: function(box, side) {
 | 
			
		||||
        this._side = side;
 | 
			
		||||
 | 
			
		||||
        this._box = box;
 | 
			
		||||
        this._box.connect('style-changed', Lang.bind(this, this._boxStyleChanged));
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.DrawingArea({ style_class: 'panel-corner' });
 | 
			
		||||
        this.actor.connect('style-changed', Lang.bind(this, this._styleChanged));
 | 
			
		||||
        this.actor.connect('repaint', Lang.bind(this, this._repaint));
 | 
			
		||||
@@ -692,12 +807,12 @@ const PanelCorner = new Lang.Class({
 | 
			
		||||
        return children[index];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setStyleParent: function(box) {
 | 
			
		||||
    _boxStyleChanged: function() {
 | 
			
		||||
        let side = this._side;
 | 
			
		||||
 | 
			
		||||
        let rtlAwareContainer = box instanceof St.BoxLayout;
 | 
			
		||||
        let rtlAwareContainer = this._box instanceof St.BoxLayout;
 | 
			
		||||
        if (rtlAwareContainer &&
 | 
			
		||||
            box.get_text_direction() == Clutter.TextDirection.RTL) {
 | 
			
		||||
            this._box.get_text_direction() == Clutter.TextDirection.RTL) {
 | 
			
		||||
            if (this._side == St.Side.LEFT)
 | 
			
		||||
                side = St.Side.RIGHT;
 | 
			
		||||
            else if (this._side == St.Side.RIGHT)
 | 
			
		||||
@@ -706,9 +821,9 @@ const PanelCorner = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        let button;
 | 
			
		||||
        if (side == St.Side.LEFT)
 | 
			
		||||
            button = this._findLeftmostButton(box);
 | 
			
		||||
            button = this._findLeftmostButton(this._box);
 | 
			
		||||
        else if (side == St.Side.RIGHT)
 | 
			
		||||
            button = this._findRightmostButton(box);
 | 
			
		||||
            button = this._findRightmostButton(this._box);
 | 
			
		||||
 | 
			
		||||
        if (button) {
 | 
			
		||||
            if (this._button && this._buttonStyleChangedSignalId) {
 | 
			
		||||
@@ -735,7 +850,7 @@ const PanelCorner = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
            // The corner doesn't support theme transitions, so override
 | 
			
		||||
            // the .panel-button default
 | 
			
		||||
            button.style = 'transition-duration: 0ms';
 | 
			
		||||
            button.style = 'transition-duration: 0';
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -748,8 +863,8 @@ const PanelCorner = new Lang.Class({
 | 
			
		||||
        let backgroundColor = node.get_color('-panel-corner-background-color');
 | 
			
		||||
        let borderColor = node.get_color('-panel-corner-border-color');
 | 
			
		||||
 | 
			
		||||
        let overlap = borderColor.alpha != 0;
 | 
			
		||||
        let offsetY = overlap ? 0 : borderWidth;
 | 
			
		||||
        let noOverlap = borderColor.alpha == 0;
 | 
			
		||||
        let offsetY = noOverlap ? borderWidth : 0;
 | 
			
		||||
 | 
			
		||||
        let cr = this.actor.get_context();
 | 
			
		||||
        cr.setOperator(Cairo.Operator.SOURCE);
 | 
			
		||||
@@ -773,18 +888,17 @@ const PanelCorner = new Lang.Class({
 | 
			
		||||
        Clutter.cairo_set_source_color(cr, over);
 | 
			
		||||
        cr.fill();
 | 
			
		||||
 | 
			
		||||
        if (overlap) {
 | 
			
		||||
            let offset = borderWidth;
 | 
			
		||||
            Clutter.cairo_set_source_color(cr, backgroundColor);
 | 
			
		||||
        if (noOverlap)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
            cr.save();
 | 
			
		||||
            cr.translate(xOffsetDirection * offset, - offset);
 | 
			
		||||
            cr.appendPath(savedPath);
 | 
			
		||||
            cr.fill();
 | 
			
		||||
            cr.restore();
 | 
			
		||||
        }
 | 
			
		||||
        let offset = borderWidth;
 | 
			
		||||
        Clutter.cairo_set_source_color(cr, backgroundColor);
 | 
			
		||||
 | 
			
		||||
        cr.$dispose();
 | 
			
		||||
        cr.save();
 | 
			
		||||
        cr.translate(xOffsetDirection * offset, - offset);
 | 
			
		||||
        cr.appendPath(savedPath);
 | 
			
		||||
        cr.fill();
 | 
			
		||||
        cr.restore();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _styleChanged: function() {
 | 
			
		||||
@@ -798,65 +912,31 @@ const PanelCorner = new Lang.Class({
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const AggregateMenu = new Lang.Class({
 | 
			
		||||
    Name: 'AggregateMenu',
 | 
			
		||||
    Extends: PanelMenu.Button,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.parent(0.0, _("Settings"), false);
 | 
			
		||||
        this.menu.actor.add_style_class_name('aggregate-menu');
 | 
			
		||||
 | 
			
		||||
        this._indicators = new St.BoxLayout({ style_class: 'panel-status-indicators-box' });
 | 
			
		||||
        this.actor.add_child(this._indicators);
 | 
			
		||||
 | 
			
		||||
        this._network = new imports.ui.status.network.NMApplet();
 | 
			
		||||
        if (Config.HAVE_BLUETOOTH) {
 | 
			
		||||
            this._bluetooth = new imports.ui.status.bluetooth.Indicator();
 | 
			
		||||
        } else {
 | 
			
		||||
            this._bluetooth = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._power = new imports.ui.status.power.Indicator();
 | 
			
		||||
        this._rfkill = new imports.ui.status.rfkill.Indicator();
 | 
			
		||||
        this._volume = new imports.ui.status.volume.Indicator();
 | 
			
		||||
        this._brightness = new imports.ui.status.brightness.Indicator();
 | 
			
		||||
        this._system = new imports.ui.status.system.Indicator();
 | 
			
		||||
        this._screencast = new imports.ui.status.screencast.Indicator();
 | 
			
		||||
 | 
			
		||||
        this._indicators.add_child(this._screencast.indicators);
 | 
			
		||||
        this._indicators.add_child(this._network.indicators);
 | 
			
		||||
        if (this._bluetooth) {
 | 
			
		||||
            this._indicators.add_child(this._bluetooth.indicators);
 | 
			
		||||
        }
 | 
			
		||||
        this._indicators.add_child(this._rfkill.indicators);
 | 
			
		||||
        this._indicators.add_child(this._volume.indicators);
 | 
			
		||||
        this._indicators.add_child(this._power.indicators);
 | 
			
		||||
        this._indicators.add_child(PopupMenu.unicodeArrow(St.Side.BOTTOM));
 | 
			
		||||
 | 
			
		||||
        this.menu.addMenuItem(this._volume.menu);
 | 
			
		||||
        this.menu.addMenuItem(this._brightness.menu);
 | 
			
		||||
        this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
 | 
			
		||||
        this.menu.addMenuItem(this._network.menu);
 | 
			
		||||
        if (this._bluetooth) {
 | 
			
		||||
            this.menu.addMenuItem(this._bluetooth.menu);
 | 
			
		||||
        }
 | 
			
		||||
        this.menu.addMenuItem(this._rfkill.menu);
 | 
			
		||||
        this.menu.addMenuItem(this._power.menu);
 | 
			
		||||
        this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
 | 
			
		||||
        this.menu.addMenuItem(this._system.menu);
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const PANEL_ITEM_IMPLEMENTATIONS = {
 | 
			
		||||
    'activities': ActivitiesButton,
 | 
			
		||||
    'aggregateMenu': AggregateMenu,
 | 
			
		||||
    'appMenu': AppMenuButton,
 | 
			
		||||
    'dateMenu': imports.ui.dateMenu.DateMenuButton,
 | 
			
		||||
    'a11y': imports.ui.status.accessibility.ATIndicator,
 | 
			
		||||
    'a11yGreeter': imports.ui.status.accessibility.ATGreeterIndicator,
 | 
			
		||||
    'volume': imports.ui.status.volume.Indicator,
 | 
			
		||||
    'battery': imports.ui.status.power.Indicator,
 | 
			
		||||
    'lockScreen': imports.ui.status.lockScreenMenu.Indicator,
 | 
			
		||||
    'logo': imports.gdm.loginDialog.LogoMenuButton,
 | 
			
		||||
    'keyboard': imports.ui.status.keyboard.InputSourceIndicator,
 | 
			
		||||
    'powerMenu': imports.gdm.powerMenu.PowerMenuButton,
 | 
			
		||||
    'userMenu': imports.ui.userMenu.UserMenuButton
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
if (Config.HAVE_BLUETOOTH)
 | 
			
		||||
    PANEL_ITEM_IMPLEMENTATIONS['bluetooth'] =
 | 
			
		||||
        imports.ui.status.bluetooth.Indicator;
 | 
			
		||||
 | 
			
		||||
try {
 | 
			
		||||
    PANEL_ITEM_IMPLEMENTATIONS['network'] =
 | 
			
		||||
        imports.ui.status.network.NMApplet;
 | 
			
		||||
} catch(e) {
 | 
			
		||||
    log('NMApplet is not supported. It is possible that your NetworkManager version is too old');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Panel = new Lang.Class({
 | 
			
		||||
    Name: 'Panel',
 | 
			
		||||
 | 
			
		||||
@@ -869,7 +949,7 @@ const Panel = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this.statusArea = {};
 | 
			
		||||
 | 
			
		||||
        this.menuManager = new PopupMenu.PopupMenuManager(this, { keybindingMode: Shell.KeyBindingMode.TOPBAR_POPUP });
 | 
			
		||||
        this.menuManager = new PopupMenu.PopupMenuManager(this);
 | 
			
		||||
 | 
			
		||||
        this._leftBox = new St.BoxLayout({ name: 'panelLeft' });
 | 
			
		||||
        this.actor.add_actor(this._leftBox);
 | 
			
		||||
@@ -878,10 +958,17 @@ const Panel = new Lang.Class({
 | 
			
		||||
        this._rightBox = new St.BoxLayout({ name: 'panelRight' });
 | 
			
		||||
        this.actor.add_actor(this._rightBox);
 | 
			
		||||
 | 
			
		||||
        this._leftCorner = new PanelCorner(St.Side.LEFT);
 | 
			
		||||
        if (this.actor.get_text_direction() == Clutter.TextDirection.RTL)
 | 
			
		||||
            this._leftCorner = new PanelCorner(this._rightBox, St.Side.LEFT);
 | 
			
		||||
        else
 | 
			
		||||
            this._leftCorner = new PanelCorner(this._leftBox, St.Side.LEFT);
 | 
			
		||||
 | 
			
		||||
        this.actor.add_actor(this._leftCorner.actor);
 | 
			
		||||
 | 
			
		||||
        this._rightCorner = new PanelCorner(St.Side.RIGHT);
 | 
			
		||||
        if (this.actor.get_text_direction() == Clutter.TextDirection.RTL)
 | 
			
		||||
            this._rightCorner = new PanelCorner(this._leftBox, St.Side.RIGHT);
 | 
			
		||||
        else
 | 
			
		||||
            this._rightCorner = new PanelCorner(this._rightBox, St.Side.RIGHT);
 | 
			
		||||
        this.actor.add_actor(this._rightCorner.actor);
 | 
			
		||||
 | 
			
		||||
        this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
 | 
			
		||||
@@ -889,13 +976,6 @@ const Panel = new Lang.Class({
 | 
			
		||||
        this.actor.connect('allocate', Lang.bind(this, this._allocate));
 | 
			
		||||
        this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
 | 
			
		||||
 | 
			
		||||
        Main.overview.connect('showing', Lang.bind(this, function () {
 | 
			
		||||
            this.actor.add_style_pseudo_class('overview');
 | 
			
		||||
        }));
 | 
			
		||||
        Main.overview.connect('hiding', Lang.bind(this, function () {
 | 
			
		||||
            this.actor.remove_style_pseudo_class('overview');
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        Main.layoutManager.panelBox.add(this.actor);
 | 
			
		||||
        Main.ctrlAltTabManager.addGroup(this.actor, _("Top Bar"), 'emblem-system-symbolic',
 | 
			
		||||
                                        { sortGroup: CtrlAltTab.SortGroup.TOP });
 | 
			
		||||
@@ -982,9 +1062,6 @@ const Panel = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onButtonPress: function(actor, event) {
 | 
			
		||||
        if (Main.modalCount > 0)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        if (event.get_source() != actor)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
@@ -1023,18 +1100,17 @@ const Panel = new Lang.Class({
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    toggleAppMenu: function() {
 | 
			
		||||
    openAppMenu: function() {
 | 
			
		||||
        let indicator = this.statusArea.appMenu;
 | 
			
		||||
        if (!indicator) // appMenu not supported by current session mode
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let menu = indicator.menu;
 | 
			
		||||
        if (!indicator.actor.reactive)
 | 
			
		||||
        if (!indicator.actor.reactive || menu.isOpen)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        menu.toggle();
 | 
			
		||||
        if (menu.isOpen)
 | 
			
		||||
            menu.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
 | 
			
		||||
        menu.open();
 | 
			
		||||
        menu.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    set boxOpacity(value) {
 | 
			
		||||
@@ -1065,13 +1141,17 @@ const Panel = new Lang.Class({
 | 
			
		||||
        this._sessionStyle = Main.sessionMode.panelStyle;
 | 
			
		||||
        if (this._sessionStyle)
 | 
			
		||||
            this._addStyleClassName(this._sessionStyle);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
        if (this.actor.get_text_direction() == Clutter.TextDirection.RTL) {
 | 
			
		||||
            this._leftCorner.setStyleParent(this._rightBox);
 | 
			
		||||
            this._rightCorner.setStyleParent(this._leftBox);
 | 
			
		||||
        } else {
 | 
			
		||||
            this._leftCorner.setStyleParent(this._leftBox);
 | 
			
		||||
            this._rightCorner.setStyleParent(this._rightBox);
 | 
			
		||||
    _initBox: function(elements, box) {
 | 
			
		||||
        for (let i = 0; i < elements.length; i++) {
 | 
			
		||||
            let role = elements[i];
 | 
			
		||||
            let constructor = PANEL_ITEM_IMPLEMENTATIONS[role];
 | 
			
		||||
            if (!constructor) {
 | 
			
		||||
                // panel icon is not supported (can happen for
 | 
			
		||||
                // bluetooth or network)
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -86,8 +86,13 @@ const ButtonBox = new Lang.Class({
 | 
			
		||||
            childBox.x2 = availWidth - this._minHPadding;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        childBox.y1 = 0;
 | 
			
		||||
        childBox.y2 = availHeight;
 | 
			
		||||
        if (natHeight <= availHeight) {
 | 
			
		||||
            childBox.y1 = Math.floor((availHeight - natHeight) / 2);
 | 
			
		||||
            childBox.y2 = childBox.y1 + natHeight;
 | 
			
		||||
        } else {
 | 
			
		||||
            childBox.y1 = 0;
 | 
			
		||||
            childBox.y2 = availHeight;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        child.allocate(childBox, flags);
 | 
			
		||||
    },
 | 
			
		||||
@@ -101,17 +106,17 @@ const Button = new Lang.Class({
 | 
			
		||||
        this.parent({ reactive: true,
 | 
			
		||||
                      can_focus: true,
 | 
			
		||||
                      track_hover: true,
 | 
			
		||||
                      accessible_name: nameText ? nameText : "",
 | 
			
		||||
                      accessible_role: Atk.Role.MENU });
 | 
			
		||||
 | 
			
		||||
        this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
 | 
			
		||||
        this.actor.connect('key-press-event', Lang.bind(this, this._onSourceKeyPress));
 | 
			
		||||
        this.actor.connect('notify::visible', Lang.bind(this, this._onVisibilityChanged));
 | 
			
		||||
 | 
			
		||||
        if (dontCreateMenu)
 | 
			
		||||
            this.menu = new PopupMenu.PopupDummyMenu(this.actor);
 | 
			
		||||
        else
 | 
			
		||||
            this.setMenu(new PopupMenu.PopupMenu(this.actor, menuAlignment, St.Side.TOP, 0));
 | 
			
		||||
 | 
			
		||||
        this.setName(nameText);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setSensitive: function(sensitive) {
 | 
			
		||||
@@ -120,6 +125,22 @@ const Button = new Lang.Class({
 | 
			
		||||
        this.actor.track_hover = sensitive;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setName: function(text) {
 | 
			
		||||
        if (text != null) {
 | 
			
		||||
            // This is the easiest way to provide a accessible name to
 | 
			
		||||
            // this widget. The label could be also used for other
 | 
			
		||||
            // purposes in the future.
 | 
			
		||||
            if (!this.label) {
 | 
			
		||||
                this.label = new St.Label({ text: text });
 | 
			
		||||
                this.actor.label_actor = this.label;
 | 
			
		||||
            } else
 | 
			
		||||
                this.label.text = text;
 | 
			
		||||
        } else {
 | 
			
		||||
            this.label = null;
 | 
			
		||||
            this.actor.label_actor = null;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setMenu: function(menu) {
 | 
			
		||||
        if (this.menu)
 | 
			
		||||
            this.menu.destroy();
 | 
			
		||||
@@ -162,18 +183,7 @@ const Button = new Lang.Class({
 | 
			
		||||
            return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onVisibilityChanged: function() {
 | 
			
		||||
        if (!this.menu)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (!this.actor.visible)
 | 
			
		||||
            this.menu.close();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onMenuKeyPress: function(actor, event) {
 | 
			
		||||
        if (global.focus_manager.navigate_from_event(event))
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        let symbol = event.get_key_symbol();
 | 
			
		||||
        if (symbol == Clutter.KEY_Left || symbol == Clutter.KEY_Right) {
 | 
			
		||||
            let group = global.focus_manager.get_group(this.actor);
 | 
			
		||||
@@ -195,8 +205,10 @@ const Button = new Lang.Class({
 | 
			
		||||
        // Setting the max-height won't do any good if the minimum height of the
 | 
			
		||||
        // 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);
 | 
			
		||||
        this.menu.actor.style = ('max-height: ' + Math.round(workArea.height) + 'px;');
 | 
			
		||||
        let monitor = Main.layoutManager.primaryMonitor;
 | 
			
		||||
        this.menu.actor.style = ('max-height: ' +
 | 
			
		||||
                                 Math.round(monitor.height - Main.panel.actor.height) +
 | 
			
		||||
                                 'px;');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroy: function() {
 | 
			
		||||
@@ -211,35 +223,48 @@ const Button = new Lang.Class({
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(Button.prototype);
 | 
			
		||||
 | 
			
		||||
/* SystemIndicator:
 | 
			
		||||
/* SystemStatusButton:
 | 
			
		||||
 *
 | 
			
		||||
 * This class manages one system indicator, which are the icons
 | 
			
		||||
 * that you see at the top right. A system indicator is composed
 | 
			
		||||
 * of an icon and a menu section, which will be composed into the
 | 
			
		||||
 * aggregate menu.
 | 
			
		||||
 * This class manages one System Status indicator (network, keyboard,
 | 
			
		||||
 * volume, bluetooth...), which is just a PanelMenuButton with an
 | 
			
		||||
 * icon.
 | 
			
		||||
 */
 | 
			
		||||
const SystemIndicator = new Lang.Class({
 | 
			
		||||
    Name: 'SystemIndicator',
 | 
			
		||||
const SystemStatusButton = new Lang.Class({
 | 
			
		||||
    Name: 'SystemStatusButton',
 | 
			
		||||
    Extends: Button,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.indicators = new St.BoxLayout({ style_class: 'panel-status-indicators-box',
 | 
			
		||||
                                             reactive: true });
 | 
			
		||||
        this.indicators.hide();
 | 
			
		||||
        this.menu = new PopupMenu.PopupMenuSection();
 | 
			
		||||
    _init: function(iconName, nameText) {
 | 
			
		||||
        this.parent(0.0, nameText);
 | 
			
		||||
        this.actor.add_style_class_name('panel-status-button');
 | 
			
		||||
 | 
			
		||||
        this._box = new St.BoxLayout({ style_class: 'panel-status-button-box' });
 | 
			
		||||
        this.actor.add_actor(this._box);
 | 
			
		||||
 | 
			
		||||
        if (iconName)
 | 
			
		||||
            this.setIcon(iconName);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _syncIndicatorsVisible: function() {
 | 
			
		||||
        this.indicators.visible = this.indicators.get_children().some(function(actor) {
 | 
			
		||||
            return actor.visible;
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
    addIcon: function(gicon) {
 | 
			
		||||
        let icon = new St.Icon({ gicon: gicon,
 | 
			
		||||
                                 style_class: 'system-status-icon' });
 | 
			
		||||
        this._box.add_actor(icon);
 | 
			
		||||
 | 
			
		||||
    _addIndicator: function() {
 | 
			
		||||
        let icon = new St.Icon({ style_class: 'system-status-icon' });
 | 
			
		||||
        this.indicators.add_actor(icon);
 | 
			
		||||
        icon.connect('notify::visible', Lang.bind(this, this._syncIndicatorsVisible));
 | 
			
		||||
        this._syncIndicatorsVisible();
 | 
			
		||||
        return icon;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setIcon: function(iconName) {
 | 
			
		||||
        // Need to first add a NULL GIcon and then set icon_name, to ensure
 | 
			
		||||
        // compatibility with -symbolic fallbacks
 | 
			
		||||
 | 
			
		||||
        if (!this.mainIcon)
 | 
			
		||||
            this.mainIcon = this.addIcon(null);
 | 
			
		||||
        this.mainIcon.icon_name = iconName;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setGIcon: function(gicon) {
 | 
			
		||||
        if (this.mainIcon)
 | 
			
		||||
            this.mainIcon.gicon = gicon;
 | 
			
		||||
        else
 | 
			
		||||
            this.mainIcon = this.addIcon(gicon);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(SystemIndicator.prototype);
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,6 @@
 | 
			
		||||
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Mainloop = imports.mainloop;
 | 
			
		||||
const Meta = imports.gi.Meta;
 | 
			
		||||
const GnomeDesktop = imports.gi.GnomeDesktop;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
 | 
			
		||||
@@ -42,9 +41,10 @@ const PointerWatcher = new Lang.Class({
 | 
			
		||||
    Name: 'PointerWatcher',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._idleMonitor = Meta.IdleMonitor.get_core();
 | 
			
		||||
        this._idleMonitor.add_idle_watch(IDLE_TIME, Lang.bind(this, this._onIdleMonitorBecameIdle));
 | 
			
		||||
        this._idle = this._idleMonitor.get_idletime() > IDLE_TIME;
 | 
			
		||||
        let idleMonitor = new GnomeDesktop.IdleMonitor();
 | 
			
		||||
        idleMonitor.connect('became-active', Lang.bind(this, this._onIdleMonitorBecameActive));
 | 
			
		||||
        idleMonitor.add_watch(IDLE_TIME, Lang.bind(this, this._onIdleMonitorBecameIdle));
 | 
			
		||||
        this._idle = idleMonitor.get_idletime() > IDLE_TIME;
 | 
			
		||||
        this._watches = [];
 | 
			
		||||
        this.pointerX = null;
 | 
			
		||||
        this.pointerY = null;
 | 
			
		||||
@@ -87,7 +87,6 @@ const PointerWatcher = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _onIdleMonitorBecameIdle: function(monitor) {
 | 
			
		||||
        this._idle = true;
 | 
			
		||||
        this._idleMonitor.add_user_active_watch(Lang.bind(this, this._onIdleMonitorBecameActive));
 | 
			
		||||
        this._updateTimeout();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1572
									
								
								js/ui/popupMenu.js
									
									
									
									
									
								
							
							
						
						@@ -1,199 +0,0 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Atk = imports.gi.Atk;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const GObject = imports.gi.GObject;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const ShellMenu = imports.gi.ShellMenu;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
const PopupMenu = imports.ui.popupMenu;
 | 
			
		||||
 | 
			
		||||
function stripMnemonics(label) {
 | 
			
		||||
    if (!label)
 | 
			
		||||
        return '';
 | 
			
		||||
 | 
			
		||||
    // remove all underscores that are not followed by another underscore
 | 
			
		||||
    return label.replace(/_([^_])/, '$1');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _insertItem(menu, trackerItem, position) {
 | 
			
		||||
    let mapper;
 | 
			
		||||
 | 
			
		||||
    if (trackerItem.get_is_separator())
 | 
			
		||||
        mapper = new RemoteMenuSeparatorItemMapper(trackerItem);
 | 
			
		||||
    else if (trackerItem.get_has_submenu())
 | 
			
		||||
        mapper = new RemoteMenuSubmenuItemMapper(trackerItem);
 | 
			
		||||
    else
 | 
			
		||||
        mapper = new RemoteMenuItemMapper(trackerItem);
 | 
			
		||||
 | 
			
		||||
    let item = mapper.menuItem;
 | 
			
		||||
    menu.addMenuItem(item, position);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _removeItem(menu, position) {
 | 
			
		||||
    let items = menu._getMenuItems();
 | 
			
		||||
    items[position].destroy();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const RemoteMenuSeparatorItemMapper = new Lang.Class({
 | 
			
		||||
    Name: 'RemoteMenuSeparatorItemMapper',
 | 
			
		||||
 | 
			
		||||
    _init: function(trackerItem) {
 | 
			
		||||
        this._trackerItem = trackerItem;
 | 
			
		||||
        this.menuItem = new PopupMenu.PopupSeparatorMenuItem();
 | 
			
		||||
        this._trackerItem.connect('notify::label', Lang.bind(this, this._updateLabel));
 | 
			
		||||
        this._updateLabel();
 | 
			
		||||
 | 
			
		||||
        this.menuItem.connect('destroy', function() {
 | 
			
		||||
            trackerItem.run_dispose();
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateLabel: function() {
 | 
			
		||||
        this.menuItem.label.text = stripMnemonics(this._trackerItem.label);
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const RequestSubMenu = new Lang.Class({
 | 
			
		||||
    Name: 'RequestSubMenu',
 | 
			
		||||
    Extends: PopupMenu.PopupSubMenuMenuItem,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.parent('');
 | 
			
		||||
        this._requestOpen = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _setOpenState: function(open) {
 | 
			
		||||
        this.emit('request-open', open);
 | 
			
		||||
        this._requestOpen = open;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getOpenState: function() {
 | 
			
		||||
        return this._requestOpen;
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const RemoteMenuSubmenuItemMapper = new Lang.Class({
 | 
			
		||||
    Name: 'RemoteMenuSubmenuItemMapper',
 | 
			
		||||
 | 
			
		||||
    _init: function(trackerItem) {
 | 
			
		||||
        this._trackerItem = trackerItem;
 | 
			
		||||
        this.menuItem = new RequestSubMenu();
 | 
			
		||||
        this._trackerItem.connect('notify::label', Lang.bind(this, this._updateLabel));
 | 
			
		||||
        this._updateLabel();
 | 
			
		||||
 | 
			
		||||
        this._tracker = Shell.MenuTracker.new_for_item_submenu(this._trackerItem,
 | 
			
		||||
                                                               _insertItem.bind(null, this.menuItem.menu),
 | 
			
		||||
                                                               _removeItem.bind(null, this.menuItem.menu));
 | 
			
		||||
 | 
			
		||||
        this.menuItem.connect('request-open', Lang.bind(this, function(menu, open) {
 | 
			
		||||
            this._trackerItem.request_submenu_shown(open);
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        this._trackerItem.connect('notify::submenu-shown', Lang.bind(this, function() {
 | 
			
		||||
            this.menuItem.setSubmenuShown(this._trackerItem.get_submenu_shown());
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        this.menuItem.connect('destroy', function() {
 | 
			
		||||
            trackerItem.run_dispose();
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroy: function() {
 | 
			
		||||
        this._tracker.destroy();
 | 
			
		||||
        this.parent();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateLabel: function() {
 | 
			
		||||
        this.menuItem.label.text = stripMnemonics(this._trackerItem.label);
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const RemoteMenuItemMapper = new Lang.Class({
 | 
			
		||||
    Name: 'RemoteMenuItemMapper',
 | 
			
		||||
 | 
			
		||||
    _init: function(trackerItem) {
 | 
			
		||||
        this._trackerItem = trackerItem;
 | 
			
		||||
 | 
			
		||||
        this.menuItem = new PopupMenu.PopupBaseMenuItem();
 | 
			
		||||
        this._label = new St.Label();
 | 
			
		||||
        this.menuItem.actor.add_child(this._label);
 | 
			
		||||
        this.menuItem.actor.label_actor = this._label;
 | 
			
		||||
 | 
			
		||||
        this.menuItem.connect('activate', Lang.bind(this, function() {
 | 
			
		||||
            this._trackerItem.activated();
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        this._trackerItem.bind_property('visible', this.menuItem.actor, 'visible', GObject.BindingFlags.SYNC_CREATE);
 | 
			
		||||
 | 
			
		||||
        this._trackerItem.connect('notify::label', Lang.bind(this, this._updateLabel));
 | 
			
		||||
        this._trackerItem.connect('notify::sensitive', Lang.bind(this, this._updateSensitivity));
 | 
			
		||||
        this._trackerItem.connect('notify::role', Lang.bind(this, this._updateRole));
 | 
			
		||||
        this._trackerItem.connect('notify::toggled', Lang.bind(this, this._updateDecoration));
 | 
			
		||||
 | 
			
		||||
        this._updateLabel();
 | 
			
		||||
        this._updateSensitivity();
 | 
			
		||||
        this._updateRole();
 | 
			
		||||
 | 
			
		||||
        this.menuItem.connect('destroy', function() {
 | 
			
		||||
            trackerItem.run_dispose();
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateLabel: function() {
 | 
			
		||||
        this._label.text = stripMnemonics(this._trackerItem.label);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateSensitivity: function() {
 | 
			
		||||
        this.menuItem.setSensitive(this._trackerItem.sensitive);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateDecoration: function() {
 | 
			
		||||
        let ornamentForRole = {};
 | 
			
		||||
        ornamentForRole[ShellMenu.MenuTrackerItemRole.RADIO] = PopupMenu.Ornament.DOT;
 | 
			
		||||
        ornamentForRole[ShellMenu.MenuTrackerItemRole.CHECK] = PopupMenu.Ornament.CHECK;
 | 
			
		||||
 | 
			
		||||
        let ornament = PopupMenu.Ornament.NONE;
 | 
			
		||||
        if (this._trackerItem.toggled)
 | 
			
		||||
            ornament = ornamentForRole[this._trackerItem.role];
 | 
			
		||||
 | 
			
		||||
        this.menuItem.setOrnament(ornament);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateRole: function() {
 | 
			
		||||
        let a11yRoles = {};
 | 
			
		||||
        a11yRoles[ShellMenu.MenuTrackerItemRole.NORMAL] = Atk.Role.MENU_ITEM;
 | 
			
		||||
        a11yRoles[ShellMenu.MenuTrackerItemRole.RADIO] = Atk.Role.RADIO_MENU_ITEM;
 | 
			
		||||
        a11yRoles[ShellMenu.MenuTrackerItemRole.CHECK] = Atk.Role.CHECK_MENU_ITEM;
 | 
			
		||||
 | 
			
		||||
        let a11yRole = a11yRoles[this._trackerItem.role];
 | 
			
		||||
        this.menuItem.actor.accessible_role = a11yRole;
 | 
			
		||||
 | 
			
		||||
        this._updateDecoration();
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const RemoteMenu = new Lang.Class({
 | 
			
		||||
    Name: 'RemoteMenu',
 | 
			
		||||
    Extends: PopupMenu.PopupMenu,
 | 
			
		||||
 | 
			
		||||
    _init: function(sourceActor, model, actionGroup) {
 | 
			
		||||
        this.parent(sourceActor, 0.0, St.Side.TOP);
 | 
			
		||||
 | 
			
		||||
        this._model = model;
 | 
			
		||||
        this._actionGroup = actionGroup;
 | 
			
		||||
        this._tracker = Shell.MenuTracker.new(this._actionGroup,
 | 
			
		||||
                                              this._model,
 | 
			
		||||
                                              null, /* action namespace */
 | 
			
		||||
                                              _insertItem.bind(null, this),
 | 
			
		||||
                                              _removeItem.bind(null, this));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroy: function() {
 | 
			
		||||
        this._tracker.destroy();
 | 
			
		||||
        this.parent();
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
@@ -1,12 +1,11 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const GdkPixbuf = imports.gi.GdkPixbuf;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
 | 
			
		||||
const FileUtils = imports.misc.fileUtils;
 | 
			
		||||
const Search = imports.ui.search;
 | 
			
		||||
 | 
			
		||||
const KEY_FILE_GROUP = 'Shell Search Provider';
 | 
			
		||||
@@ -59,114 +58,111 @@ var SearchProviderProxy = Gio.DBusProxy.makeProxyWrapper(SearchProviderIface);
 | 
			
		||||
var SearchProvider2Proxy = Gio.DBusProxy.makeProxyWrapper(SearchProvider2Iface);
 | 
			
		||||
 | 
			
		||||
function loadRemoteSearchProviders(addProviderCallback) {
 | 
			
		||||
    let objectPaths = {};
 | 
			
		||||
    let loadedProviders = [];
 | 
			
		||||
    let data = { loadedProviders: [],
 | 
			
		||||
                 objectPaths: {},
 | 
			
		||||
                 addProviderCallback: addProviderCallback };
 | 
			
		||||
    FileUtils.collectFromDatadirsAsync('search-providers',
 | 
			
		||||
                                       { loadedCallback: remoteProvidersLoaded,
 | 
			
		||||
                                         processFile: loadRemoteSearchProvider,
 | 
			
		||||
                                         data: data
 | 
			
		||||
                                       });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    function loadRemoteSearchProvider(file) {
 | 
			
		||||
        let keyfile = new GLib.KeyFile();
 | 
			
		||||
        let path = file.get_path();
 | 
			
		||||
function loadRemoteSearchProvider(file, info, data) {
 | 
			
		||||
    let keyfile = new GLib.KeyFile();
 | 
			
		||||
    let path = file.get_path();
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            keyfile.load_from_file(path, 0);
 | 
			
		||||
        } catch(e) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!keyfile.has_group(KEY_FILE_GROUP))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let remoteProvider;
 | 
			
		||||
        try {
 | 
			
		||||
            let group = KEY_FILE_GROUP;
 | 
			
		||||
            let busName = keyfile.get_string(group, 'BusName');
 | 
			
		||||
            let objectPath = keyfile.get_string(group, 'ObjectPath');
 | 
			
		||||
 | 
			
		||||
            if (objectPaths[objectPath])
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            let appInfo = null;
 | 
			
		||||
            try {
 | 
			
		||||
                let desktopId = keyfile.get_string(group, 'DesktopId');
 | 
			
		||||
                appInfo = Gio.DesktopAppInfo.new(desktopId);
 | 
			
		||||
            } catch (e) {
 | 
			
		||||
                log('Ignoring search provider ' + path + ': missing DesktopId');
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let version = '1';
 | 
			
		||||
            try {
 | 
			
		||||
                version = keyfile.get_string(group, 'Version');
 | 
			
		||||
            } catch (e) {
 | 
			
		||||
                // ignore error
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (version >= 2)
 | 
			
		||||
                remoteProvider = new RemoteSearchProvider2(appInfo, busName, objectPath);
 | 
			
		||||
            else
 | 
			
		||||
                remoteProvider = new RemoteSearchProvider(appInfo, busName, objectPath);
 | 
			
		||||
 | 
			
		||||
            objectPaths[objectPath] = remoteProvider;
 | 
			
		||||
            loadedProviders.push(remoteProvider);
 | 
			
		||||
        } catch(e) {
 | 
			
		||||
            log('Failed to add search provider %s: %s'.format(path, e.toString()));
 | 
			
		||||
        }
 | 
			
		||||
    try {
 | 
			
		||||
        keyfile.load_from_file(path, 0);
 | 
			
		||||
    } catch(e) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let dataDirs = GLib.get_system_data_dirs();
 | 
			
		||||
    dataDirs.forEach(function(dataDir) {
 | 
			
		||||
        let path = GLib.build_filenamev([dataDir, 'gnome-shell', 'search-providers']);
 | 
			
		||||
        let dir = Gio.File.new_for_path(path);
 | 
			
		||||
        let fileEnum;
 | 
			
		||||
        try {
 | 
			
		||||
            fileEnum = dir.enumerate_children('standard::name,standard::type',
 | 
			
		||||
                                              Gio.FileQueryInfoFlags.NONE, null);
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
            fileEnum = null;
 | 
			
		||||
        }
 | 
			
		||||
        if (fileEnum != null) {
 | 
			
		||||
            let info;
 | 
			
		||||
            while ((info = fileEnum.next_file(null)))
 | 
			
		||||
                loadRemoteSearchProvider(fileEnum.get_child(info));
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    if (!keyfile.has_group(KEY_FILE_GROUP))
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    let remoteProvider;
 | 
			
		||||
    try {
 | 
			
		||||
        let group = KEY_FILE_GROUP;
 | 
			
		||||
        let busName = keyfile.get_string(group, 'BusName');
 | 
			
		||||
        let objectPath = keyfile.get_string(group, 'ObjectPath');
 | 
			
		||||
 | 
			
		||||
        if (data.objectPaths[objectPath])
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let appInfo = null;
 | 
			
		||||
        try {
 | 
			
		||||
            let desktopId = keyfile.get_string(group, 'DesktopId');
 | 
			
		||||
            appInfo = Gio.DesktopAppInfo.new(desktopId);
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
            log('Ignoring search provider ' + path + ': missing DesktopId');
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let version = '1';
 | 
			
		||||
        try {
 | 
			
		||||
            version = keyfile.get_string(group, 'Version');
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
            // ignore error
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (version >= 2)
 | 
			
		||||
            remoteProvider = new RemoteSearchProvider2(appInfo, busName, objectPath);
 | 
			
		||||
        else
 | 
			
		||||
            remoteProvider = new RemoteSearchProvider(appInfo, busName, objectPath);
 | 
			
		||||
 | 
			
		||||
        data.objectPaths[objectPath] = remoteProvider;
 | 
			
		||||
        data.loadedProviders.push(remoteProvider);
 | 
			
		||||
    } catch(e) {
 | 
			
		||||
        log('Failed to add search provider %s: %s'.format(path, e.toString()));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function remoteProvidersLoaded(loadState) {
 | 
			
		||||
    let searchSettings = new Gio.Settings({ schema: Search.SEARCH_PROVIDERS_SCHEMA });
 | 
			
		||||
    let sortOrder = searchSettings.get_strv('sort-order');
 | 
			
		||||
    let numSorted = sortOrder.length;
 | 
			
		||||
 | 
			
		||||
    // Special case gnome-control-center to be always active and always first
 | 
			
		||||
    sortOrder.unshift('gnome-control-center.desktop');
 | 
			
		||||
    loadState.loadedProviders.sort(
 | 
			
		||||
        function(providerA, providerB) {
 | 
			
		||||
            let idxA, idxB;
 | 
			
		||||
            let appIdA, appIdB;
 | 
			
		||||
 | 
			
		||||
    loadedProviders.sort(function(providerA, providerB) {
 | 
			
		||||
        let idxA, idxB;
 | 
			
		||||
        let appIdA, appIdB;
 | 
			
		||||
            appIdA = providerA.appInfo.get_id();
 | 
			
		||||
            appIdB = providerB.appInfo.get_id();
 | 
			
		||||
 | 
			
		||||
        appIdA = providerA.appInfo.get_id();
 | 
			
		||||
        appIdB = providerB.appInfo.get_id();
 | 
			
		||||
            idxA = sortOrder.indexOf(appIdA);
 | 
			
		||||
            idxB = sortOrder.indexOf(appIdB);
 | 
			
		||||
 | 
			
		||||
        idxA = sortOrder.indexOf(appIdA);
 | 
			
		||||
        idxB = sortOrder.indexOf(appIdB);
 | 
			
		||||
            // if no provider is found in the order, use alphabetical order
 | 
			
		||||
            if ((idxA == -1) && (idxB == -1))
 | 
			
		||||
                return GLib.utf8_collate(providerA.title, providerB.title);
 | 
			
		||||
 | 
			
		||||
        // if no provider is found in the order, use alphabetical order
 | 
			
		||||
        if ((idxA == -1) && (idxB == -1)) {
 | 
			
		||||
            let nameA = providerA.appInfo.get_name();
 | 
			
		||||
            let nameB = providerB.appInfo.get_name();
 | 
			
		||||
            if (numSorted > 1) {
 | 
			
		||||
                // if providerA is the last, it goes after everything
 | 
			
		||||
                if ((idxA + 1) == numSorted)
 | 
			
		||||
                    return 1;
 | 
			
		||||
                // if providerB is the last, it goes after everything
 | 
			
		||||
                else if ((idxB + 1) == numSorted)
 | 
			
		||||
                    return -1;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return GLib.utf8_collate(nameA, nameB);
 | 
			
		||||
        }
 | 
			
		||||
            // if providerA isn't found, it's sorted after providerB
 | 
			
		||||
            if (idxA == -1)
 | 
			
		||||
                return 1;
 | 
			
		||||
 | 
			
		||||
        // if providerA isn't found, it's sorted after providerB
 | 
			
		||||
        if (idxA == -1)
 | 
			
		||||
            return 1;
 | 
			
		||||
            // if providerB isn't found, it's sorted after providerA
 | 
			
		||||
            if (idxB == -1)
 | 
			
		||||
                return -1;
 | 
			
		||||
 | 
			
		||||
        // if providerB isn't found, it's sorted after providerA
 | 
			
		||||
        if (idxB == -1)
 | 
			
		||||
            return -1;
 | 
			
		||||
            // finally, if both providers are found, return their order in the list
 | 
			
		||||
            return (idxA - idxB);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        // finally, if both providers are found, return their order in the list
 | 
			
		||||
        return (idxA - idxB);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    loadedProviders.forEach(addProviderCallback);
 | 
			
		||||
    loadState.loadedProviders.forEach(
 | 
			
		||||
        function(provider) {
 | 
			
		||||
            loadState.addProviderCallback(provider);
 | 
			
		||||
        });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const RemoteSearchProvider = new Lang.Class({
 | 
			
		||||
@@ -191,40 +187,24 @@ const RemoteSearchProvider = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    createIcon: function(size, meta) {
 | 
			
		||||
        let gicon = null;
 | 
			
		||||
        let icon = null;
 | 
			
		||||
 | 
			
		||||
        if (meta['icon']) {
 | 
			
		||||
            gicon = Gio.icon_deserialize(meta['icon']);
 | 
			
		||||
        } else if (meta['gicon']) {
 | 
			
		||||
            gicon = Gio.icon_new_for_string(meta['gicon']);
 | 
			
		||||
        if (meta['gicon']) {
 | 
			
		||||
            return new St.Icon({ gicon: Gio.icon_new_for_string(meta['gicon']),
 | 
			
		||||
                                 icon_size: size });
 | 
			
		||||
        } else if (meta['icon-data']) {
 | 
			
		||||
            let [width, height, rowStride, hasAlpha,
 | 
			
		||||
                 bitsPerSample, nChannels, data] = meta['icon-data'];
 | 
			
		||||
            gicon = Shell.util_create_pixbuf_from_data(data, GdkPixbuf.Colorspace.RGB, hasAlpha,
 | 
			
		||||
                                                       bitsPerSample, width, height, rowStride);
 | 
			
		||||
            let textureCache = St.TextureCache.get_default();
 | 
			
		||||
            return textureCache.load_from_raw(data, hasAlpha,
 | 
			
		||||
                                              width, height, rowStride, size);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (gicon)
 | 
			
		||||
            icon = new St.Icon({ gicon: gicon,
 | 
			
		||||
                                 icon_size: size });
 | 
			
		||||
        return icon;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    filterResults: function(results, maxNumber) {
 | 
			
		||||
        if (results.length <= maxNumber)
 | 
			
		||||
            return results;
 | 
			
		||||
 | 
			
		||||
        let regularResults = results.filter(function(r) { return !r.startsWith('special:'); });
 | 
			
		||||
        let specialResults = results.filter(function(r) { return r.startsWith('special:'); });
 | 
			
		||||
 | 
			
		||||
        return regularResults.slice(0, maxNumber).concat(specialResults.slice(0, maxNumber));
 | 
			
		||||
        return null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getResultsFinished: function(results, error) {
 | 
			
		||||
        if (error)
 | 
			
		||||
            return;
 | 
			
		||||
        this.searchSystem.setResults(this, results[0]);
 | 
			
		||||
        this.searchSystem.pushResults(this, results[0]);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getInitialResultSet: function(terms) {
 | 
			
		||||
@@ -235,8 +215,8 @@ const RemoteSearchProvider = new Lang.Class({
 | 
			
		||||
                                                 Lang.bind(this, this._getResultsFinished),
 | 
			
		||||
                                                 this._cancellable);
 | 
			
		||||
        } catch(e) {
 | 
			
		||||
            log('Error calling GetInitialResultSet for provider %s: %s'.format(this.id, e.toString()));
 | 
			
		||||
            this.searchSystem.setResults(this, []);
 | 
			
		||||
            log('Error calling GetInitialResultSet for provider %s: %s'.format( this.title, e.toString()));
 | 
			
		||||
            this.searchSystem.pushResults(this, []);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -248,8 +228,8 @@ const RemoteSearchProvider = new Lang.Class({
 | 
			
		||||
                                                   Lang.bind(this, this._getResultsFinished),
 | 
			
		||||
                                                   this._cancellable);
 | 
			
		||||
        } catch(e) {
 | 
			
		||||
            log('Error calling GetSubsearchResultSet for provider %s: %s'.format(this.id, e.toString()));
 | 
			
		||||
            this.searchSystem.setResults(this, []);
 | 
			
		||||
            log('Error calling GetSubsearchResultSet for provider %s: %s'.format(this.title, e.toString()));
 | 
			
		||||
            this.searchSystem.pushResults(this, []);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -261,15 +241,10 @@ const RemoteSearchProvider = new Lang.Class({
 | 
			
		||||
        let metas = results[0];
 | 
			
		||||
        let resultMetas = [];
 | 
			
		||||
        for (let i = 0; i < metas.length; i++) {
 | 
			
		||||
            for (let prop in metas[i]) {
 | 
			
		||||
                // we can use the serialized icon variant directly
 | 
			
		||||
                if (prop != 'icon')
 | 
			
		||||
                    metas[i][prop] = metas[i][prop].deep_unpack();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            for (let prop in metas[i])
 | 
			
		||||
                metas[i][prop] = metas[i][prop].deep_unpack();
 | 
			
		||||
            resultMetas.push({ id: metas[i]['id'],
 | 
			
		||||
                               name: metas[i]['name'],
 | 
			
		||||
                               description: metas[i]['description'],
 | 
			
		||||
                               createIcon: Lang.bind(this,
 | 
			
		||||
                                                     this.createIcon, metas[i]) });
 | 
			
		||||
        }
 | 
			
		||||
@@ -284,7 +259,7 @@ const RemoteSearchProvider = new Lang.Class({
 | 
			
		||||
                                            Lang.bind(this, this._getResultMetasFinished, callback),
 | 
			
		||||
                                            this._cancellable);
 | 
			
		||||
        } catch(e) {
 | 
			
		||||
            log('Error calling GetResultMetas for provider %s: %s'.format(this.id, e.toString()));
 | 
			
		||||
            log('Error calling GetResultMetas for provider %s: %s'.format(this.title, e.toString()));
 | 
			
		||||
            callback([]);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 
 | 
			
		||||
@@ -30,13 +30,144 @@ const EXEC_ARG_KEY = 'exec-arg';
 | 
			
		||||
 | 
			
		||||
const DIALOG_GROW_TIME = 0.1;
 | 
			
		||||
 | 
			
		||||
const CommandCompleter = new Lang.Class({
 | 
			
		||||
    Name: 'CommandCompleter',
 | 
			
		||||
 | 
			
		||||
    _init : function() {
 | 
			
		||||
        this._changedCount = 0;
 | 
			
		||||
        this._paths = GLib.getenv('PATH').split(':');
 | 
			
		||||
        this._paths.push(GLib.get_home_dir());
 | 
			
		||||
        this._valid = false;
 | 
			
		||||
        this._updateInProgress = false;
 | 
			
		||||
        this._childs = new Array(this._paths.length);
 | 
			
		||||
        this._monitors = new Array(this._paths.length);
 | 
			
		||||
        for (let i = 0; i < this._paths.length; i++) {
 | 
			
		||||
            this._childs[i] = [];
 | 
			
		||||
            let file = Gio.file_new_for_path(this._paths[i]);
 | 
			
		||||
            let info;
 | 
			
		||||
            try {
 | 
			
		||||
                info = file.query_info(Gio.FILE_ATTRIBUTE_STANDARD_TYPE, Gio.FileQueryInfoFlags.NONE, null);
 | 
			
		||||
            } catch (e) {
 | 
			
		||||
                // FIXME catchall
 | 
			
		||||
                this._paths[i] = null;
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (info.get_attribute_uint32(Gio.FILE_ATTRIBUTE_STANDARD_TYPE) != Gio.FileType.DIRECTORY)
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            this._paths[i] = file.get_path();
 | 
			
		||||
            this._monitors[i] = file.monitor_directory(Gio.FileMonitorFlags.NONE, null);
 | 
			
		||||
            if (this._monitors[i] != null) {
 | 
			
		||||
                this._monitors[i].connect('changed', Lang.bind(this, this._onChanged));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        this._paths = this._paths.filter(function(a) {
 | 
			
		||||
            return a != null;
 | 
			
		||||
        });
 | 
			
		||||
        this._update(0);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    update : function() {
 | 
			
		||||
        if (this._valid)
 | 
			
		||||
            return;
 | 
			
		||||
        this._update(0);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _update : function(i) {
 | 
			
		||||
        if (i == 0 && this._updateInProgress)
 | 
			
		||||
            return;
 | 
			
		||||
        this._updateInProgress = true;
 | 
			
		||||
        this._changedCount = 0;
 | 
			
		||||
        this._i = i;
 | 
			
		||||
        if (i >= this._paths.length) {
 | 
			
		||||
            this._valid = true;
 | 
			
		||||
            this._updateInProgress = false;
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        let file = Gio.file_new_for_path(this._paths[i]);
 | 
			
		||||
        this._childs[this._i] = [];
 | 
			
		||||
        FileUtils.listDirAsync(file, Lang.bind(this, function (files) {
 | 
			
		||||
            for (let i = 0; i < files.length; i++) {
 | 
			
		||||
                this._childs[this._i].push(files[i].get_name());
 | 
			
		||||
            }
 | 
			
		||||
            this._update(this._i + 1);
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onChanged : function(m, f, of, type) {
 | 
			
		||||
        if (!this._valid)
 | 
			
		||||
            return;
 | 
			
		||||
        let path = f.get_parent().get_path();
 | 
			
		||||
        let k = undefined;
 | 
			
		||||
        for (let i = 0; i < this._paths.length; i++) {
 | 
			
		||||
            if (this._paths[i] == path)
 | 
			
		||||
                k = i;
 | 
			
		||||
        }
 | 
			
		||||
        if (k === undefined) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        if (type == Gio.FileMonitorEvent.CREATED) {
 | 
			
		||||
            this._childs[k].push(f.get_basename());
 | 
			
		||||
        }
 | 
			
		||||
        if (type == Gio.FileMonitorEvent.DELETED) {
 | 
			
		||||
            this._changedCount++;
 | 
			
		||||
            if (this._changedCount > MAX_FILE_DELETED_BEFORE_INVALID) {
 | 
			
		||||
                this._valid = false;
 | 
			
		||||
            }
 | 
			
		||||
            let name = f.get_basename();
 | 
			
		||||
            this._childs[k] = this._childs[k].filter(function(e) {
 | 
			
		||||
                return e != name;
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        if (type == Gio.FileMonitorEvent.UNMOUNTED) {
 | 
			
		||||
            this._childs[k] = [];
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getCompletion: function(text) {
 | 
			
		||||
        let common = '';
 | 
			
		||||
        let notInit = true;
 | 
			
		||||
        if (!this._valid) {
 | 
			
		||||
            this._update(0);
 | 
			
		||||
            return common;
 | 
			
		||||
        }
 | 
			
		||||
        function _getCommon(s1, s2) {
 | 
			
		||||
            let k = 0;
 | 
			
		||||
            for (; k < s1.length && k < s2.length; k++) {
 | 
			
		||||
                if (s1[k] != s2[k])
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
            if (k == 0)
 | 
			
		||||
                return '';
 | 
			
		||||
            return s1.substr(0, k);
 | 
			
		||||
        }
 | 
			
		||||
        function _hasPrefix(s1, prefix) {
 | 
			
		||||
            return s1.indexOf(prefix) == 0;
 | 
			
		||||
        }
 | 
			
		||||
        for (let i = 0; i < this._childs.length; i++) {
 | 
			
		||||
            for (let k = 0; k < this._childs[i].length; k++) {
 | 
			
		||||
                if (!_hasPrefix(this._childs[i][k], text))
 | 
			
		||||
                    continue;
 | 
			
		||||
                if (notInit) {
 | 
			
		||||
                    common = this._childs[i][k];
 | 
			
		||||
                    notInit = false;
 | 
			
		||||
                }
 | 
			
		||||
                common = _getCommon(common, this._childs[i][k]);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (common.length)
 | 
			
		||||
            return common.substr(text.length);
 | 
			
		||||
        return common;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const RunDialog = new Lang.Class({
 | 
			
		||||
    Name: 'RunDialog',
 | 
			
		||||
    Extends: ModalDialog.ModalDialog,
 | 
			
		||||
 | 
			
		||||
    _init : function() {
 | 
			
		||||
        this.parent({ styleClass: 'run-dialog',
 | 
			
		||||
                      destroyOnClose: false });
 | 
			
		||||
        this.parent({ styleClass: 'run-dialog' });
 | 
			
		||||
 | 
			
		||||
        this._lockdownSettings = new Gio.Settings({ schema: LOCKDOWN_SCHEMA });
 | 
			
		||||
        this._terminalSettings = new Gio.Settings({ schema: TERMINAL_SCHEMA });
 | 
			
		||||
@@ -111,6 +242,8 @@ const RunDialog = new Lang.Class({
 | 
			
		||||
                           key: Clutter.Escape }]);
 | 
			
		||||
 | 
			
		||||
        this._pathCompleter = new Gio.FilenameCompleter();
 | 
			
		||||
        this._commandCompleter = new CommandCompleter();
 | 
			
		||||
        this._group.connect('notify::visible', Lang.bind(this._commandCompleter, this._commandCompleter.update));
 | 
			
		||||
 | 
			
		||||
        this._history = new History.HistoryManager({ gsettingsKey: HISTORY_KEY,
 | 
			
		||||
                                                     entry: this._entryText });
 | 
			
		||||
@@ -126,6 +259,18 @@ const RunDialog = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
            if (symbol == Clutter.slash) {
 | 
			
		||||
                // Need preload data before get completion. GFilenameCompleter load content of parent directory.
 | 
			
		||||
                // Parent directory for /usr/include/ is /usr/. So need to add fake name('a').
 | 
			
		||||
                let text = o.get_text().concat('/a');
 | 
			
		||||
                let prefix;
 | 
			
		||||
                if (text.lastIndexOf(' ') == -1)
 | 
			
		||||
                    prefix = text;
 | 
			
		||||
                else
 | 
			
		||||
                    prefix = text.substr(text.lastIndexOf(' ') + 1);
 | 
			
		||||
                this._getCompletion(prefix);
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
            if (symbol == Clutter.Tab) {
 | 
			
		||||
                let text = o.get_text();
 | 
			
		||||
                let prefix;
 | 
			
		||||
@@ -137,6 +282,8 @@ const RunDialog = new Lang.Class({
 | 
			
		||||
                if (postfix != null && postfix.length > 0) {
 | 
			
		||||
                    o.insert_text(postfix, -1);
 | 
			
		||||
                    o.set_cursor_position(text.length + postfix.length);
 | 
			
		||||
                    if (postfix[postfix.length - 1] == '/')
 | 
			
		||||
                        this._getCompletion(text + postfix + 'a');
 | 
			
		||||
                }
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
@@ -144,53 +291,11 @@ const RunDialog = new Lang.Class({
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getCommandCompletion: function(text) {
 | 
			
		||||
        function _getCommon(s1, s2) {
 | 
			
		||||
            if (s1 == null)
 | 
			
		||||
                return s2;
 | 
			
		||||
 | 
			
		||||
            let k = 0;
 | 
			
		||||
            for (; k < s1.length && k < s2.length; k++) {
 | 
			
		||||
                if (s1[k] != s2[k])
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
            if (k == 0)
 | 
			
		||||
                return '';
 | 
			
		||||
            return s1.substr(0, k);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let paths = GLib.getenv('PATH').split(':');
 | 
			
		||||
        paths.push(GLib.get_home_dir());
 | 
			
		||||
        let someResults = paths.map(function(path) {
 | 
			
		||||
            let results = [];
 | 
			
		||||
            try {
 | 
			
		||||
                let file = Gio.File.new_for_path(path);
 | 
			
		||||
                let fileEnum = file.enumerate_children('standard::name', Gio.FileQueryInfoFlags.NONE, null);
 | 
			
		||||
                let info;
 | 
			
		||||
                while ((info = fileEnum.next_file(null))) {
 | 
			
		||||
                    let name = info.get_name();
 | 
			
		||||
                    if (name.slice(0, text.length) == text)
 | 
			
		||||
                        results.push(name);
 | 
			
		||||
                }
 | 
			
		||||
            } catch (e if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND) &&
 | 
			
		||||
                           !e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_DIRECTORY))) {
 | 
			
		||||
                log(e);
 | 
			
		||||
            } finally {
 | 
			
		||||
                return results;
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        let results = someResults.reduce(function(a, b) {
 | 
			
		||||
            return a.concat(b);
 | 
			
		||||
        }, []);
 | 
			
		||||
        let common = results.reduce(_getCommon, null);
 | 
			
		||||
        return common.substr(text.length);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getCompletion : function(text) {
 | 
			
		||||
        if (text.indexOf('/') != -1) {
 | 
			
		||||
            return this._pathCompleter.get_completion_suffix(text);
 | 
			
		||||
        } else {
 | 
			
		||||
            return this._getCommandCompletion(text);
 | 
			
		||||
            return this._commandCompleter.getCompletion(text);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,163 +0,0 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
 | 
			
		||||
const Hash = imports.misc.hash;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
 | 
			
		||||
const ScreencastIface = <interface name="org.gnome.Shell.Screencast">
 | 
			
		||||
<method name="Screencast">
 | 
			
		||||
    <arg type="s" direction="in" name="file_template"/>
 | 
			
		||||
    <arg type="a{sv}" direction="in" name="options"/>
 | 
			
		||||
    <arg type="b" direction="out" name="success"/>
 | 
			
		||||
    <arg type="s" direction="out" name="filename_used"/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name="ScreencastArea">
 | 
			
		||||
    <arg type="i" direction="in" name="x"/>
 | 
			
		||||
    <arg type="i" direction="in" name="y"/>
 | 
			
		||||
    <arg type="i" direction="in" name="width"/>
 | 
			
		||||
    <arg type="i" direction="in" name="height"/>
 | 
			
		||||
    <arg type="s" direction="in" name="file_template"/>
 | 
			
		||||
    <arg type="a{sv}" direction="in" name="options"/>
 | 
			
		||||
    <arg type="b" direction="out" name="success"/>
 | 
			
		||||
    <arg type="s" direction="out" name="filename_used"/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name="StopScreencast">
 | 
			
		||||
    <arg type="b" direction="out" name="success"/>
 | 
			
		||||
</method>
 | 
			
		||||
</interface>;
 | 
			
		||||
 | 
			
		||||
const ScreencastService = new Lang.Class({
 | 
			
		||||
    Name: 'ScreencastService',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(ScreencastIface, this);
 | 
			
		||||
        this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell/Screencast');
 | 
			
		||||
 | 
			
		||||
        Gio.DBus.session.own_name('org.gnome.Shell.Screencast', Gio.BusNameOwnerFlags.REPLACE, null, null);
 | 
			
		||||
 | 
			
		||||
        this._recorders = new Hash.Map();
 | 
			
		||||
 | 
			
		||||
        Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get isRecording() {
 | 
			
		||||
        return this._recorders.size() > 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _ensureRecorderForSender: function(sender) {
 | 
			
		||||
        let recorder = this._recorders.get(sender);
 | 
			
		||||
        if (!recorder) {
 | 
			
		||||
            recorder = new Shell.Recorder({ stage: global.stage,
 | 
			
		||||
                                            screen: global.screen });
 | 
			
		||||
            recorder._watchNameId =
 | 
			
		||||
                Gio.bus_watch_name(Gio.BusType.SESSION, sender, 0, null,
 | 
			
		||||
                                   Lang.bind(this, this._onNameVanished));
 | 
			
		||||
            this._recorders.set(sender, recorder);
 | 
			
		||||
            this.emit('updated');
 | 
			
		||||
        }
 | 
			
		||||
        return recorder;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _sessionUpdated: function() {
 | 
			
		||||
        if (Main.sessionMode.allowScreencast)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        for (let sender in this._recorders.keys())
 | 
			
		||||
            this._recorders.delete(sender);
 | 
			
		||||
        this.emit('updated');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onNameVanished: function(connection, name) {
 | 
			
		||||
        this._stopRecordingForSender(name);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _stopRecordingForSender: function(sender) {
 | 
			
		||||
        let recorder = this._recorders.get(sender);
 | 
			
		||||
        if (!recorder)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        Gio.bus_unwatch_name(recorder._watchNameId);
 | 
			
		||||
        recorder.close();
 | 
			
		||||
        this._recorders.delete(sender);
 | 
			
		||||
        this.emit('updated');
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _applyOptionalParameters: function(recorder, options) {
 | 
			
		||||
        for (let option in options)
 | 
			
		||||
            options[option] = options[option].deep_unpack();
 | 
			
		||||
 | 
			
		||||
        if (options['pipeline'])
 | 
			
		||||
            recorder.set_pipeline(options['pipeline']);
 | 
			
		||||
        if (options['framerate'])
 | 
			
		||||
            recorder.set_framerate(options['framerate']);
 | 
			
		||||
        if (options['draw-cursor'])
 | 
			
		||||
            recorder.set_draw_cursor(options['draw-cursor']);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    ScreencastAsync: function(params, invocation) {
 | 
			
		||||
        let returnValue = [false, ''];
 | 
			
		||||
        if (!Main.sessionMode.allowScreencast) {
 | 
			
		||||
            invocation.return_value(GLib.Variant.new('(bs)', returnValue));
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let sender = invocation.get_sender();
 | 
			
		||||
        let recorder = this._ensureRecorderForSender(sender);
 | 
			
		||||
        if (!recorder.is_recording()) {
 | 
			
		||||
            let [fileTemplate, options] = params;
 | 
			
		||||
 | 
			
		||||
            recorder.set_file_template(fileTemplate);
 | 
			
		||||
            this._applyOptionalParameters(recorder, options);
 | 
			
		||||
            let [success, fileName] = recorder.record();
 | 
			
		||||
            returnValue = [success, fileName ? fileName : ''];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        invocation.return_value(GLib.Variant.new('(bs)', returnValue));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    ScreencastAreaAsync: function(params, invocation) {
 | 
			
		||||
        let returnValue = [false, ''];
 | 
			
		||||
        if (!Main.sessionMode.allowScreencast) {
 | 
			
		||||
            invocation.return_value(GLib.Variant.new('(bs)', returnValue));
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let sender = invocation.get_sender();
 | 
			
		||||
        let recorder = this._ensureRecorderForSender(sender);
 | 
			
		||||
 | 
			
		||||
        if (!recorder.is_recording()) {
 | 
			
		||||
            let [x, y, width, height, fileTemplate, options] = params;
 | 
			
		||||
 | 
			
		||||
            if (x < 0 || y < 0 ||
 | 
			
		||||
                width <= 0 || height <= 0 ||
 | 
			
		||||
                x + width > global.screen_width ||
 | 
			
		||||
                y + height > global.screen_height) {
 | 
			
		||||
                invocation.return_error_literal(Gio.IOErrorEnum,
 | 
			
		||||
                                                Gio.IOErrorEnum.CANCELLED,
 | 
			
		||||
                                                "Invalid params");
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            recorder.set_file_template(fileTemplate);
 | 
			
		||||
            recorder.set_area(x, y, width, height);
 | 
			
		||||
            this._applyOptionalParameters(recorder, options);
 | 
			
		||||
            let [success, fileName] = recorder.record();
 | 
			
		||||
            returnValue = [success, fileName ? fileName : ''];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        invocation.return_value(GLib.Variant.new('(bs)', returnValue));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    StopScreencastAsync: function(params, invocation) {
 | 
			
		||||
        let success = this._stopRecordingForSender(invocation.get_sender());
 | 
			
		||||
        invocation.return_value(GLib.Variant.new('(b)', [success]));
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(ScreencastService.prototype);
 | 
			
		||||
@@ -1,280 +0,0 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Gdk = imports.gi.Gdk;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Gtk = imports.gi.Gtk;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Meta = imports.gi.Meta;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
const Lightbox = imports.ui.lightbox;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const Tweener = imports.ui.tweener;
 | 
			
		||||
 | 
			
		||||
const ScreenshotIface = <interface name="org.gnome.Shell.Screenshot">
 | 
			
		||||
<method name="ScreenshotArea">
 | 
			
		||||
    <arg type="i" direction="in" name="x"/>
 | 
			
		||||
    <arg type="i" direction="in" name="y"/>
 | 
			
		||||
    <arg type="i" direction="in" name="width"/>
 | 
			
		||||
    <arg type="i" direction="in" name="height"/>
 | 
			
		||||
    <arg type="b" direction="in" name="flash"/>
 | 
			
		||||
    <arg type="s" direction="in" name="filename"/>
 | 
			
		||||
    <arg type="b" direction="out" name="success"/>
 | 
			
		||||
    <arg type="s" direction="out" name="filename_used"/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name="ScreenshotWindow">
 | 
			
		||||
    <arg type="b" direction="in" name="include_frame"/>
 | 
			
		||||
    <arg type="b" direction="in" name="include_cursor"/>
 | 
			
		||||
    <arg type="b" direction="in" name="flash"/>
 | 
			
		||||
    <arg type="s" direction="in" name="filename"/>
 | 
			
		||||
    <arg type="b" direction="out" name="success"/>
 | 
			
		||||
    <arg type="s" direction="out" name="filename_used"/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name="Screenshot">
 | 
			
		||||
    <arg type="b" direction="in" name="include_cursor"/>
 | 
			
		||||
    <arg type="b" direction="in" name="flash"/>
 | 
			
		||||
    <arg type="s" direction="in" name="filename"/>
 | 
			
		||||
    <arg type="b" direction="out" name="success"/>
 | 
			
		||||
    <arg type="s" direction="out" name="filename_used"/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name="SelectArea">
 | 
			
		||||
    <arg type="i" direction="out" name="x"/>
 | 
			
		||||
    <arg type="i" direction="out" name="y"/>
 | 
			
		||||
    <arg type="i" direction="out" name="width"/>
 | 
			
		||||
    <arg type="i" direction="out" name="height"/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name="FlashArea">
 | 
			
		||||
    <arg type="i" direction="in" name="x"/>
 | 
			
		||||
    <arg type="i" direction="in" name="y"/>
 | 
			
		||||
    <arg type="i" direction="in" name="width"/>
 | 
			
		||||
    <arg type="i" direction="in" name="height"/>
 | 
			
		||||
</method>
 | 
			
		||||
</interface>;
 | 
			
		||||
 | 
			
		||||
const ScreenshotService = new Lang.Class({
 | 
			
		||||
    Name: 'ScreenshotService',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(ScreenshotIface, this);
 | 
			
		||||
        this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell/Screenshot');
 | 
			
		||||
 | 
			
		||||
        Gio.DBus.session.own_name('org.gnome.Shell.Screenshot', Gio.BusNameOwnerFlags.REPLACE, null, null);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onScreenshotComplete: function(obj, result, area, filenameUsed, flash, invocation) {
 | 
			
		||||
        if (flash && result) {
 | 
			
		||||
            let flashspot = new Flashspot(area);
 | 
			
		||||
            flashspot.fire();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let retval = GLib.Variant.new('(bs)', [result, filenameUsed]);
 | 
			
		||||
        invocation.return_value(retval);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    ScreenshotAreaAsync : function (params, invocation) {
 | 
			
		||||
        let [x, y, width, height, flash, filename, callback] = params;
 | 
			
		||||
        if (x < 0 || y < 0 ||
 | 
			
		||||
            width <= 0 || height <= 0 ||
 | 
			
		||||
            x + width > global.screen_width || y + height > global.screen_height) {
 | 
			
		||||
            invocation.return_error_literal(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED,
 | 
			
		||||
                        "Invalid params");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        let screenshot = new Shell.Screenshot();
 | 
			
		||||
        screenshot.screenshot_area (x, y, width, height, filename,
 | 
			
		||||
                                Lang.bind(this, this._onScreenshotComplete,
 | 
			
		||||
                                          flash, invocation));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    ScreenshotWindowAsync : function (params, invocation) {
 | 
			
		||||
        let [include_frame, include_cursor, flash, filename] = params;
 | 
			
		||||
        let screenshot = new Shell.Screenshot();
 | 
			
		||||
        screenshot.screenshot_window (include_frame, include_cursor, filename,
 | 
			
		||||
                                  Lang.bind(this, this._onScreenshotComplete,
 | 
			
		||||
                                            flash, invocation));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    ScreenshotAsync : function (params, invocation) {
 | 
			
		||||
        let [include_cursor, flash, filename] = params;
 | 
			
		||||
        let screenshot = new Shell.Screenshot();
 | 
			
		||||
        screenshot.screenshot(include_cursor, filename,
 | 
			
		||||
                          Lang.bind(this, this._onScreenshotComplete,
 | 
			
		||||
                                    flash, invocation));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    SelectAreaAsync: function (params, invocation) {
 | 
			
		||||
        let selectArea = new SelectArea();
 | 
			
		||||
        selectArea.show();
 | 
			
		||||
        selectArea.connect('finished', Lang.bind(this,
 | 
			
		||||
            function(selectArea, areaRectangle) {
 | 
			
		||||
                if (areaRectangle) {
 | 
			
		||||
                    let retval = GLib.Variant.new('(iiii)',
 | 
			
		||||
                        [areaRectangle.x, areaRectangle.y,
 | 
			
		||||
                         areaRectangle.width, areaRectangle.height]);
 | 
			
		||||
                    invocation.return_value(retval);
 | 
			
		||||
                } else {
 | 
			
		||||
                    invocation.return_error_literal(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED,
 | 
			
		||||
                        "Operation was cancelled");
 | 
			
		||||
                }
 | 
			
		||||
            }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    FlashArea: function(x, y, width, height) {
 | 
			
		||||
        let flashspot = new Flashspot({ x : x, y : y, width: width, height: height});
 | 
			
		||||
        flashspot.fire();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const SelectArea = new Lang.Class({
 | 
			
		||||
    Name: 'SelectArea',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._startX = -1;
 | 
			
		||||
        this._startY = -1;
 | 
			
		||||
        this._lastX = 0;
 | 
			
		||||
        this._lastY = 0;
 | 
			
		||||
 | 
			
		||||
        this._initRubberbandColors();
 | 
			
		||||
 | 
			
		||||
        this._group = new St.Widget({ visible: false,
 | 
			
		||||
                                      reactive: true,
 | 
			
		||||
                                      x: 0,
 | 
			
		||||
                                      y: 0 });
 | 
			
		||||
        Main.uiGroup.add_actor(this._group);
 | 
			
		||||
 | 
			
		||||
        this._group.connect('button-press-event',
 | 
			
		||||
                            Lang.bind(this, this._onButtonPress));
 | 
			
		||||
        this._group.connect('button-release-event',
 | 
			
		||||
                            Lang.bind(this, this._onButtonRelease));
 | 
			
		||||
        this._group.connect('key-press-event',
 | 
			
		||||
                            Lang.bind(this, this._onKeyPress));
 | 
			
		||||
        this._group.connect('motion-event',
 | 
			
		||||
                            Lang.bind(this, this._onMotionEvent));
 | 
			
		||||
 | 
			
		||||
        let constraint = new Clutter.BindConstraint({ source: global.stage,
 | 
			
		||||
                                                      coordinate: Clutter.BindCoordinate.ALL });
 | 
			
		||||
        this._group.add_constraint(constraint);
 | 
			
		||||
 | 
			
		||||
        this._rubberband = new Clutter.Rectangle({ color: this._background,
 | 
			
		||||
                                                   has_border: true,
 | 
			
		||||
                                                   border_width: 1,
 | 
			
		||||
                                                   border_color: this._border });
 | 
			
		||||
        this._group.add_actor(this._rubberband);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    show: function() {
 | 
			
		||||
        if (!Main.pushModal(this._group) || this._group.visible)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        global.screen.set_cursor(Meta.Cursor.CROSSHAIR);
 | 
			
		||||
        this._group.visible = true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _initRubberbandColors: function() {
 | 
			
		||||
        function colorFromRGBA(rgba) {
 | 
			
		||||
            return new Clutter.Color({ red: rgba.red * 255,
 | 
			
		||||
                                       green: rgba.green * 255,
 | 
			
		||||
                                       blue: rgba.blue * 255,
 | 
			
		||||
                                       alpha: rgba.alpha * 255 });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let path = new Gtk.WidgetPath();
 | 
			
		||||
        path.append_type(Gtk.IconView);
 | 
			
		||||
 | 
			
		||||
        let context = new Gtk.StyleContext();
 | 
			
		||||
        context.set_path(path);
 | 
			
		||||
        context.add_class('rubberband');
 | 
			
		||||
 | 
			
		||||
        this._background = colorFromRGBA(context.get_background_color(Gtk.StateFlags.NORMAL));
 | 
			
		||||
        this._border = colorFromRGBA(context.get_border_color(Gtk.StateFlags.NORMAL));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getGeometry: function() {
 | 
			
		||||
        return { x: Math.min(this._startX, this._lastX),
 | 
			
		||||
                 y: Math.min(this._startY, this._lastY),
 | 
			
		||||
                 width: Math.abs(this._startX - this._lastX),
 | 
			
		||||
                 height: Math.abs(this._startY - this._lastY) };
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onKeyPress: function(actor, event) {
 | 
			
		||||
        if (event.get_key_symbol() == Clutter.Escape)
 | 
			
		||||
            this._destroy(null, false);
 | 
			
		||||
 | 
			
		||||
        return;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onMotionEvent: function(actor, event) {
 | 
			
		||||
        if (this._startX == -1 || this._startY == -1)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        [this._lastX, this._lastY] = event.get_coords();
 | 
			
		||||
        let geometry = this._getGeometry();
 | 
			
		||||
 | 
			
		||||
        this._rubberband.set_position(geometry.x, geometry.y);
 | 
			
		||||
        this._rubberband.set_size(geometry.width, geometry.height);
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onButtonPress: function(actor, event) {
 | 
			
		||||
        [this._startX, this._startY] = event.get_coords();
 | 
			
		||||
        this._rubberband.set_position(this._startX, this._startY);
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onButtonRelease: function(actor, event) {
 | 
			
		||||
        this._destroy(this._getGeometry(), true);
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _destroy: function(geometry, fade) {
 | 
			
		||||
        Tweener.addTween(this._group,
 | 
			
		||||
                         { opacity: 0,
 | 
			
		||||
                           time: fade ? 0.2 : 0,
 | 
			
		||||
                           transition: 'easeOutQuad',
 | 
			
		||||
                           onComplete: Lang.bind(this,
 | 
			
		||||
                               function() {
 | 
			
		||||
                                   Main.popModal(this._group);
 | 
			
		||||
                                   this._group.destroy();
 | 
			
		||||
                                   global.screen.set_cursor(Meta.Cursor.DEFAULT);
 | 
			
		||||
 | 
			
		||||
                                   this.emit('finished', geometry);
 | 
			
		||||
                               })
 | 
			
		||||
                         });
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(SelectArea.prototype);
 | 
			
		||||
 | 
			
		||||
const FLASHSPOT_ANIMATION_OUT_TIME = 0.5; // seconds
 | 
			
		||||
 | 
			
		||||
const Flashspot = new Lang.Class({
 | 
			
		||||
    Name: 'Flashspot',
 | 
			
		||||
    Extends: Lightbox.Lightbox,
 | 
			
		||||
 | 
			
		||||
    _init: function(area) {
 | 
			
		||||
        this.parent(Main.uiGroup, { inhibitEvents: true,
 | 
			
		||||
                                    width: area.width,
 | 
			
		||||
                                    height: area.height });
 | 
			
		||||
 | 
			
		||||
        this.actor.style_class = 'flashspot';
 | 
			
		||||
        this.actor.set_position(area.x, area.y);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    fire: function() {
 | 
			
		||||
        this.actor.show();
 | 
			
		||||
        this.actor.opacity = 255;
 | 
			
		||||
        Tweener.addTween(this.actor,
 | 
			
		||||
                         { opacity: 0,
 | 
			
		||||
                           time: FLASHSPOT_ANIMATION_OUT_TIME,
 | 
			
		||||
                           transition: 'easeOutQuad',
 | 
			
		||||
                           onComplete: Lang.bind(this, function() {
 | 
			
		||||
                               this.destroy();
 | 
			
		||||
                           })
 | 
			
		||||
                         });
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
@@ -31,7 +31,7 @@ const SearchSystem = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        let remoteIndex = this._remoteProviders.indexOf(provider);
 | 
			
		||||
        if (remoteIndex != -1)
 | 
			
		||||
            this._remoteProviders.splice(remoteIndex, 1);
 | 
			
		||||
            this._remoteProviders.splice(index, 1);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getProviders: function() {
 | 
			
		||||
@@ -51,7 +51,7 @@ const SearchSystem = new Lang.Class({
 | 
			
		||||
        this._previousResults = [];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setResults: function(provider, results) {
 | 
			
		||||
    pushResults: function(provider, results) {
 | 
			
		||||
        let i = this._providers.indexOf(provider);
 | 
			
		||||
        if (i == -1)
 | 
			
		||||
            return;
 | 
			
		||||
@@ -60,18 +60,28 @@ const SearchSystem = new Lang.Class({
 | 
			
		||||
        this.emit('search-updated', this._previousResults[i]);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    updateSearch: function(searchString) {
 | 
			
		||||
        searchString = searchString.replace(/^\s+/g, '').replace(/\s+$/g, '');
 | 
			
		||||
        if (searchString == '')
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let terms = searchString.split(/\s+/);
 | 
			
		||||
        this.updateSearchResults(terms);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    updateSearchResults: function(terms) {
 | 
			
		||||
        if (!terms)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let searchString = terms.join(' ');
 | 
			
		||||
        let previousSearchString = this._previousTerms.join(' ');
 | 
			
		||||
        if (searchString == previousSearchString)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let isSubSearch = false;
 | 
			
		||||
        if (this._previousTerms.length > 0)
 | 
			
		||||
            isSubSearch = searchString.indexOf(previousSearchString) == 0;
 | 
			
		||||
        let isSubSearch = terms.length == this._previousTerms.length;
 | 
			
		||||
        if (isSubSearch) {
 | 
			
		||||
            for (let i = 0; i < terms.length; i++) {
 | 
			
		||||
                if (terms[i].indexOf(this._previousTerms[i]) != 0) {
 | 
			
		||||
                    isSubSearch = false;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let previousResultsArr = this._previousResults;
 | 
			
		||||
 | 
			
		||||
@@ -100,6 +110,6 @@ const SearchSystem = new Lang.Class({
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(SearchSystem.prototype);
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,6 @@ const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Gtk = imports.gi.Gtk;
 | 
			
		||||
const Meta = imports.gi.Meta;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Atk = imports.gi.Atk;
 | 
			
		||||
 | 
			
		||||
@@ -14,31 +13,10 @@ const Main = imports.ui.main;
 | 
			
		||||
const Overview = imports.ui.overview;
 | 
			
		||||
const Separator = imports.ui.separator;
 | 
			
		||||
const Search = imports.ui.search;
 | 
			
		||||
const Util = imports.misc.util;
 | 
			
		||||
 | 
			
		||||
const MAX_LIST_SEARCH_RESULTS_ROWS = 3;
 | 
			
		||||
const MAX_GRID_SEARCH_RESULTS_ROWS = 1;
 | 
			
		||||
 | 
			
		||||
const MaxWidthBin = new Lang.Class({
 | 
			
		||||
    Name: 'MaxWidthBin',
 | 
			
		||||
    Extends: St.Bin,
 | 
			
		||||
 | 
			
		||||
    vfunc_allocate: function(box, flags) {
 | 
			
		||||
        let themeNode = this.get_theme_node();
 | 
			
		||||
        let maxWidth = themeNode.get_max_width();
 | 
			
		||||
        let availWidth = box.x2 - box.x1;
 | 
			
		||||
        let adjustedBox = box;
 | 
			
		||||
 | 
			
		||||
        if (availWidth > maxWidth) {
 | 
			
		||||
            let excessWidth = availWidth - maxWidth;
 | 
			
		||||
            adjustedBox.x1 += Math.floor(excessWidth / 2);
 | 
			
		||||
            adjustedBox.x2 -= Math.floor(excessWidth / 2);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.parent(adjustedBox, flags);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const SearchResult = new Lang.Class({
 | 
			
		||||
    Name: 'SearchResult',
 | 
			
		||||
 | 
			
		||||
@@ -104,11 +82,11 @@ const ListSearchResult = new Lang.Class({
 | 
			
		||||
                             y_fill: false,
 | 
			
		||||
                             x_align: St.Align.START,
 | 
			
		||||
                             y_align: St.Align.START });
 | 
			
		||||
        this.actor.label_actor = title;
 | 
			
		||||
 | 
			
		||||
        // TODO: should highlight terms in the description here
 | 
			
		||||
        if (this.metaInfo['description']) {
 | 
			
		||||
            let description = new St.Label({ style_class: 'list-search-result-description' });
 | 
			
		||||
            description.clutter_text.set_markup(this.metaInfo['description']);
 | 
			
		||||
            let description = new St.Label({ style_class: 'list-search-result-description',
 | 
			
		||||
                                             text: '"' + this.metaInfo['description'] + '"' });
 | 
			
		||||
            details.add(description, { x_fill: false,
 | 
			
		||||
                                       y_fill: false,
 | 
			
		||||
                                       x_align: St.Align.START,
 | 
			
		||||
@@ -126,25 +104,22 @@ const GridSearchResult = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this.actor.style_class = 'grid-search-result';
 | 
			
		||||
 | 
			
		||||
        let content = provider.createResultObject(metaInfo, terms);
 | 
			
		||||
        let content = provider.createResultActor(metaInfo, terms);
 | 
			
		||||
        let dragSource = null;
 | 
			
		||||
 | 
			
		||||
        if (content == null) {
 | 
			
		||||
            let actor = new St.Bin();
 | 
			
		||||
            content = new St.Bin();
 | 
			
		||||
            let icon = new IconGrid.BaseIcon(this.metaInfo['name'],
 | 
			
		||||
                                             { createIcon: this.metaInfo['createIcon'] });
 | 
			
		||||
            actor.set_child(icon.actor);
 | 
			
		||||
            actor.label_actor = icon.label;
 | 
			
		||||
            content.set_child(icon.actor);
 | 
			
		||||
            content.label_actor = icon.label;
 | 
			
		||||
            dragSource = icon.icon;
 | 
			
		||||
            content = { actor: actor, icon: icon };
 | 
			
		||||
        } else {
 | 
			
		||||
            if (content._delegate && content._delegate.getDragActorSource)
 | 
			
		||||
                dragSource = content._delegate.getDragActorSource();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.actor.set_child(content.actor);
 | 
			
		||||
        this.actor.label_actor = content.actor.label_actor;
 | 
			
		||||
        this.icon = content.icon;
 | 
			
		||||
        this.actor.set_child(content);
 | 
			
		||||
 | 
			
		||||
        let draggable = DND.makeDraggable(this.actor);
 | 
			
		||||
        draggable.connect('drag-begin',
 | 
			
		||||
@@ -182,170 +157,137 @@ const GridSearchResult = new Lang.Class({
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const SearchResultsBase = new Lang.Class({
 | 
			
		||||
    Name: 'SearchResultsBase',
 | 
			
		||||
const ListSearchResults = new Lang.Class({
 | 
			
		||||
    Name: 'ListSearchResults',
 | 
			
		||||
 | 
			
		||||
    _init: function(provider) {
 | 
			
		||||
        this.provider = provider;
 | 
			
		||||
 | 
			
		||||
        this._terms = [];
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.BoxLayout({ style_class: 'search-section',
 | 
			
		||||
                                        vertical: true });
 | 
			
		||||
 | 
			
		||||
        this._resultDisplayBin = new St.Bin({ x_fill: true,
 | 
			
		||||
                                              y_fill: true });
 | 
			
		||||
        this.actor.add(this._resultDisplayBin, { expand: true });
 | 
			
		||||
 | 
			
		||||
        let separator = new Separator.HorizontalSeparator({ style_class: 'search-section-separator' });
 | 
			
		||||
        this.actor.add(separator.actor);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroy: function() {
 | 
			
		||||
        this.actor.destroy();
 | 
			
		||||
        this._terms = [];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _clearResultDisplay: function() {
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    clear: function() {
 | 
			
		||||
        this._clearResultDisplay();
 | 
			
		||||
        this.actor.hide();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _keyFocusIn: function(actor) {
 | 
			
		||||
        this.emit('key-focus-in', actor);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _setMoreIconVisible: function(visible) {
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    updateSearch: function(providerResults, terms, callback) {
 | 
			
		||||
        this._terms = terms;
 | 
			
		||||
 | 
			
		||||
        if (providerResults.length == 0) {
 | 
			
		||||
            this._clearResultDisplay();
 | 
			
		||||
            this.actor.hide();
 | 
			
		||||
            callback();
 | 
			
		||||
        } else {
 | 
			
		||||
            let maxResults = this._getMaxDisplayedResults();
 | 
			
		||||
            let results = this.provider.filterResults(providerResults, maxResults);
 | 
			
		||||
            let hasMoreResults = results.length < providerResults.length;
 | 
			
		||||
 | 
			
		||||
            this.provider.getResultMetas(results, Lang.bind(this, function(metas) {
 | 
			
		||||
                this.clear();
 | 
			
		||||
 | 
			
		||||
                // To avoid CSS transitions causing flickering when
 | 
			
		||||
                // the first search result stays the same, we hide the
 | 
			
		||||
                // content while filling in the results.
 | 
			
		||||
                this.actor.hide();
 | 
			
		||||
                this._clearResultDisplay();
 | 
			
		||||
                this._renderResults(metas);
 | 
			
		||||
                this._setMoreIconVisible(hasMoreResults && this.provider.canLaunchSearch);
 | 
			
		||||
                this.actor.show();
 | 
			
		||||
                callback();
 | 
			
		||||
            }));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const ListSearchResults = new Lang.Class({
 | 
			
		||||
    Name: 'ListSearchResults',
 | 
			
		||||
    Extends: SearchResultsBase,
 | 
			
		||||
 | 
			
		||||
    _init: function(provider) {
 | 
			
		||||
        this.parent(provider);
 | 
			
		||||
 | 
			
		||||
        this._container = new St.BoxLayout({ style_class: 'search-section-content' });
 | 
			
		||||
        this.actor = new St.BoxLayout({ style_class: 'search-section-content' });
 | 
			
		||||
        this.providerIcon = new ProviderIcon(provider);
 | 
			
		||||
        this.providerIcon.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
 | 
			
		||||
        this.providerIcon.connect('clicked', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                provider.launchSearch(this._terms);
 | 
			
		||||
                Main.overview.toggle();
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
        this._container.add(this.providerIcon, { x_fill: false,
 | 
			
		||||
                                                 y_fill: false,
 | 
			
		||||
                                                 x_align: St.Align.START,
 | 
			
		||||
                                                 y_align: St.Align.START });
 | 
			
		||||
        this.actor.add(this.providerIcon, { x_fill: false,
 | 
			
		||||
                                            y_fill: false,
 | 
			
		||||
                                            x_align: St.Align.START,
 | 
			
		||||
                                            y_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        this._content = new St.BoxLayout({ style_class: 'list-search-results',
 | 
			
		||||
                                           vertical: true });
 | 
			
		||||
        this._container.add(this._content, { expand: true });
 | 
			
		||||
        this.actor.add_actor(this._content);
 | 
			
		||||
 | 
			
		||||
        this._resultDisplayBin.set_child(this._container);
 | 
			
		||||
        this._notDisplayedResult = [];
 | 
			
		||||
        this._terms = [];
 | 
			
		||||
        this._pendingClear = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _setMoreIconVisible: function(visible) {
 | 
			
		||||
        this.providerIcon.moreIcon.visible = true;
 | 
			
		||||
    getResultsForDisplay: function() {
 | 
			
		||||
        let alreadyVisible = this._pendingClear ? 0 : this.getVisibleResultCount();
 | 
			
		||||
        let canDisplay = MAX_LIST_SEARCH_RESULTS_ROWS - alreadyVisible;
 | 
			
		||||
 | 
			
		||||
        let newResults = this._notDisplayedResult.splice(0, canDisplay);
 | 
			
		||||
        return newResults;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getMaxDisplayedResults: function() {
 | 
			
		||||
        return MAX_LIST_SEARCH_RESULTS_ROWS;
 | 
			
		||||
    getVisibleResultCount: function() {
 | 
			
		||||
        return this._content.get_n_children();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _renderResults: function(metas) {
 | 
			
		||||
    hasMoreResults: function() {
 | 
			
		||||
        return this._notDisplayedResult.length > 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setResults: function(results, terms) {
 | 
			
		||||
        // copy the lists
 | 
			
		||||
        this._notDisplayedResult = results.slice(0);
 | 
			
		||||
        this._terms = terms.slice(0);
 | 
			
		||||
        this._pendingClear = true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    renderResults: function(metas) {
 | 
			
		||||
        for (let i = 0; i < metas.length; i++) {
 | 
			
		||||
            let display = new ListSearchResult(this.provider, metas[i], this._terms);
 | 
			
		||||
            display.actor.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
 | 
			
		||||
            this._content.add_actor(display.actor);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _clearResultDisplay: function () {
 | 
			
		||||
    clear: function () {
 | 
			
		||||
        this._content.destroy_all_children();
 | 
			
		||||
        this._pendingClear = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getFirstResult: function() {
 | 
			
		||||
        if (this._content.get_n_children() > 0)
 | 
			
		||||
        if (this.getVisibleResultCount() > 0)
 | 
			
		||||
            return this._content.get_child_at_index(0)._delegate;
 | 
			
		||||
        else
 | 
			
		||||
            return null;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(ListSearchResults.prototype);
 | 
			
		||||
 | 
			
		||||
const GridSearchResults = new Lang.Class({
 | 
			
		||||
    Name: 'GridSearchResults',
 | 
			
		||||
    Extends: SearchResultsBase,
 | 
			
		||||
 | 
			
		||||
    _init: function(provider) {
 | 
			
		||||
        this.parent(provider);
 | 
			
		||||
        this.provider = provider;
 | 
			
		||||
 | 
			
		||||
        this._grid = new IconGrid.IconGrid({ rowLimit: MAX_GRID_SEARCH_RESULTS_ROWS,
 | 
			
		||||
                                             xAlign: St.Align.START });
 | 
			
		||||
        this._bin = new St.Bin({ x_align: St.Align.MIDDLE });
 | 
			
		||||
        this._bin.set_child(this._grid.actor);
 | 
			
		||||
        this.actor = new St.Bin({ x_align: St.Align.MIDDLE });
 | 
			
		||||
 | 
			
		||||
        this._resultDisplayBin.set_child(this._bin);
 | 
			
		||||
        this.actor.set_child(this._grid.actor);
 | 
			
		||||
 | 
			
		||||
        this._notDisplayedResult = [];
 | 
			
		||||
        this._terms = [];
 | 
			
		||||
        this._pendingClear = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getMaxDisplayedResults: function() {
 | 
			
		||||
        return this._grid.columnsForWidth(this._bin.width) * this._grid.getRowLimit();
 | 
			
		||||
    getResultsForDisplay: function() {
 | 
			
		||||
        let alreadyVisible = this._pendingClear ? 0 : this._grid.visibleItemsCount();
 | 
			
		||||
        let canDisplay = this._grid.childrenInRow(this.actor.width) * this._grid.getRowLimit()
 | 
			
		||||
                         - alreadyVisible;
 | 
			
		||||
 | 
			
		||||
        let newResults = this._notDisplayedResult.splice(0, canDisplay);
 | 
			
		||||
        return newResults;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _renderResults: function(metas) {
 | 
			
		||||
    getVisibleResultCount: function() {
 | 
			
		||||
        return this._grid.visibleItemsCount();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    hasMoreResults: function() {
 | 
			
		||||
        return this._notDisplayedResult.length > 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setResults: function(results, terms) {
 | 
			
		||||
        // copy the lists
 | 
			
		||||
        this._notDisplayedResult = results.slice(0);
 | 
			
		||||
        this._terms = terms.slice(0);
 | 
			
		||||
        this._pendingClear = true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    renderResults: function(metas) {
 | 
			
		||||
        for (let i = 0; i < metas.length; i++) {
 | 
			
		||||
            let display = new GridSearchResult(this.provider, metas[i], this._terms);
 | 
			
		||||
            display.actor.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
 | 
			
		||||
            this._grid.addItem(display);
 | 
			
		||||
            this._grid.addItem(display.actor);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _clearResultDisplay: function () {
 | 
			
		||||
    clear: function () {
 | 
			
		||||
        this._grid.removeAll();
 | 
			
		||||
        this._pendingClear = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getFirstResult: function() {
 | 
			
		||||
        if (this._grid.visibleItemsCount() > 0)
 | 
			
		||||
        if (this.getVisibleResultCount() > 0)
 | 
			
		||||
            return this._grid.getItemAtIndex(0)._delegate;
 | 
			
		||||
        else
 | 
			
		||||
            return null;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(GridSearchResults.prototype);
 | 
			
		||||
 | 
			
		||||
const SearchResults = new Lang.Class({
 | 
			
		||||
    Name: 'SearchResults',
 | 
			
		||||
@@ -359,20 +301,12 @@ const SearchResults = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._content = new St.BoxLayout({ name: 'searchResultsContent',
 | 
			
		||||
                                           vertical: true });
 | 
			
		||||
        this._contentBin = new MaxWidthBin({ name: 'searchResultsBin',
 | 
			
		||||
                                             x_fill: true,
 | 
			
		||||
                                             y_fill: true,
 | 
			
		||||
                                             child: this._content });
 | 
			
		||||
 | 
			
		||||
        let scrollChild = new St.BoxLayout();
 | 
			
		||||
        scrollChild.add(this._contentBin, { expand: true });
 | 
			
		||||
 | 
			
		||||
        this._scrollView = new St.ScrollView({ x_fill: true,
 | 
			
		||||
                                               y_fill: false,
 | 
			
		||||
                                               overlay_scrollbars: true,
 | 
			
		||||
                                               style_class: 'search-display vfade' });
 | 
			
		||||
                                               style_class: 'vfade' });
 | 
			
		||||
        this._scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
 | 
			
		||||
        this._scrollView.add_actor(scrollChild);
 | 
			
		||||
        this._scrollView.add_actor(this._content);
 | 
			
		||||
        let action = new Clutter.PanAction({ interpolate: true });
 | 
			
		||||
        action.connect('pan', Lang.bind(this, this._onPan));
 | 
			
		||||
        this._scrollView.add_action(action);
 | 
			
		||||
@@ -389,9 +323,9 @@ const SearchResults = new Lang.Class({
 | 
			
		||||
        this._content.add(this._statusBin, { expand: true });
 | 
			
		||||
        this._statusBin.add_actor(this._statusText);
 | 
			
		||||
        this._providers = this._searchSystem.getProviders();
 | 
			
		||||
        this._providerDisplays = {};
 | 
			
		||||
        this._providerMeta = [];
 | 
			
		||||
        for (let i = 0; i < this._providers.length; i++) {
 | 
			
		||||
            this.createProviderDisplay(this._providers[i]);
 | 
			
		||||
            this.createProviderMeta(this._providers[i]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._highlightDefault = false;
 | 
			
		||||
@@ -405,61 +339,89 @@ const SearchResults = new Lang.Class({
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _keyFocusIn: function(provider, actor) {
 | 
			
		||||
        Util.ensureActorVisibleInScrollView(this._scrollView, actor);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    createProviderDisplay: function(provider) {
 | 
			
		||||
        let providerDisplay = null;
 | 
			
		||||
    createProviderMeta: function(provider) {
 | 
			
		||||
        let providerBox = new St.BoxLayout({ style_class: 'search-section',
 | 
			
		||||
                                             vertical: true });
 | 
			
		||||
        let providerIcon = null;
 | 
			
		||||
        let resultDisplay = null;
 | 
			
		||||
 | 
			
		||||
        if (provider.appInfo) {
 | 
			
		||||
            providerDisplay = new ListSearchResults(provider);
 | 
			
		||||
            resultDisplay = new ListSearchResults(provider);
 | 
			
		||||
            providerIcon = resultDisplay.providerIcon;
 | 
			
		||||
        } else {
 | 
			
		||||
            providerDisplay = new GridSearchResults(provider);
 | 
			
		||||
            resultDisplay = new GridSearchResults(provider);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        providerDisplay.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
 | 
			
		||||
        this._providerDisplays[provider.id] = providerDisplay;
 | 
			
		||||
        this._content.add(providerDisplay.actor);
 | 
			
		||||
        let resultDisplayBin = new St.Bin({ child: resultDisplay.actor,
 | 
			
		||||
                                            x_fill: true,
 | 
			
		||||
                                            y_fill: true });
 | 
			
		||||
        providerBox.add(resultDisplayBin, { expand: true });
 | 
			
		||||
 | 
			
		||||
        let separator = new Separator.HorizontalSeparator({ style_class: 'search-section-separator' });
 | 
			
		||||
        providerBox.add(separator.actor);
 | 
			
		||||
 | 
			
		||||
        this._providerMeta.push({ provider: provider,
 | 
			
		||||
                                  actor: providerBox,
 | 
			
		||||
                                  icon: providerIcon,
 | 
			
		||||
                                  resultDisplay: resultDisplay });
 | 
			
		||||
        this._content.add(providerBox);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroyProviderDisplay: function(provider) {
 | 
			
		||||
        this._providerDisplays[provider.id].destroy();
 | 
			
		||||
        delete this._providerDisplays[provider.id];
 | 
			
		||||
    destroyProviderMeta: function(provider) {
 | 
			
		||||
        for (let i=0; i < this._providerMeta.length; i++) {
 | 
			
		||||
            let meta = this._providerMeta[i];
 | 
			
		||||
            if (meta.provider == provider) {
 | 
			
		||||
                meta.actor.destroy();
 | 
			
		||||
                this._providerMeta.splice(i, 1);
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _clearDisplay: function() {
 | 
			
		||||
        for (let i = 0; i < this._providers.length; i++) {
 | 
			
		||||
            let provider = this._providers[i];
 | 
			
		||||
            let providerDisplay = this._providerDisplays[provider.id];
 | 
			
		||||
            providerDisplay.clear();
 | 
			
		||||
        for (let i = 0; i < this._providerMeta.length; i++) {
 | 
			
		||||
            let meta = this._providerMeta[i];
 | 
			
		||||
            meta.resultDisplay.clear();
 | 
			
		||||
            meta.actor.hide();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _clearDisplayForProvider: function(provider) {
 | 
			
		||||
        let meta = this._metaForProvider(provider);
 | 
			
		||||
        meta.resultDisplay.clear();
 | 
			
		||||
        meta.actor.hide();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    reset: function() {
 | 
			
		||||
        this._searchSystem.reset();
 | 
			
		||||
        this._statusBin.hide();
 | 
			
		||||
        this._clearDisplay();
 | 
			
		||||
        this._defaultResult = null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    startingSearch: function() {
 | 
			
		||||
        this.reset();
 | 
			
		||||
        this._statusText.set_text(_("Searching…"));
 | 
			
		||||
        this._statusText.set_text(_("Searching..."));
 | 
			
		||||
        this._statusBin.show();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    doSearch: function (searchString) {
 | 
			
		||||
        this._searchSystem.updateSearch(searchString);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _metaForProvider: function(provider) {
 | 
			
		||||
        return this._providerMeta[this._providers.indexOf(provider)];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _maybeSetInitialSelection: function() {
 | 
			
		||||
        let newDefaultResult = null;
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < this._providers.length; i++) {
 | 
			
		||||
            let provider = this._providers[i];
 | 
			
		||||
            let display = this._providerDisplays[provider.id];
 | 
			
		||||
        for (let i = 0; i < this._providerMeta.length; i++) {
 | 
			
		||||
            let meta = this._providerMeta[i];
 | 
			
		||||
 | 
			
		||||
            if (!display.actor.visible)
 | 
			
		||||
            if (!meta.actor.visible)
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            let firstResult = display.getFirstResult();
 | 
			
		||||
            let firstResult = meta.resultDisplay.getFirstResult();
 | 
			
		||||
            if (firstResult) {
 | 
			
		||||
                newDefaultResult = firstResult;
 | 
			
		||||
                break; // select this one!
 | 
			
		||||
@@ -479,14 +441,11 @@ const SearchResults = new Lang.Class({
 | 
			
		||||
    _updateStatusText: function () {
 | 
			
		||||
        let haveResults = false;
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < this._providers.length; i++) {
 | 
			
		||||
            let provider = this._providers[i];
 | 
			
		||||
            let display = this._providerDisplays[provider.id];
 | 
			
		||||
            if (display.getFirstResult()) {
 | 
			
		||||
        for (let i = 0; i < this._providerMeta.length; ++i)
 | 
			
		||||
            if (this._providerMeta[i].resultDisplay.getFirstResult()) {
 | 
			
		||||
                haveResults = true;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!haveResults) {
 | 
			
		||||
            this._statusText.set_text(_("No results."));
 | 
			
		||||
@@ -499,12 +458,42 @@ const SearchResults = new Lang.Class({
 | 
			
		||||
    _updateResults: function(searchSystem, results) {
 | 
			
		||||
        let terms = searchSystem.getTerms();
 | 
			
		||||
        let [provider, providerResults] = results;
 | 
			
		||||
        let display = this._providerDisplays[provider.id];
 | 
			
		||||
        let meta = this._metaForProvider(provider);
 | 
			
		||||
 | 
			
		||||
        display.updateSearch(providerResults, terms, Lang.bind(this, function() {
 | 
			
		||||
        if (providerResults.length == 0) {
 | 
			
		||||
            this._clearDisplayForProvider(provider);
 | 
			
		||||
            meta.resultDisplay.setResults([], []);
 | 
			
		||||
            this._maybeSetInitialSelection();
 | 
			
		||||
            this._updateStatusText();
 | 
			
		||||
        }));
 | 
			
		||||
        } else {
 | 
			
		||||
            meta.resultDisplay.setResults(providerResults, terms);
 | 
			
		||||
            let results = meta.resultDisplay.getResultsForDisplay();
 | 
			
		||||
 | 
			
		||||
            if (meta.icon)
 | 
			
		||||
                meta.icon.moreIcon.visible =
 | 
			
		||||
                    meta.resultDisplay.hasMoreResults() &&
 | 
			
		||||
                    provider.canLaunchSearch;
 | 
			
		||||
 | 
			
		||||
            provider.getResultMetas(results, Lang.bind(this, function(metas) {
 | 
			
		||||
                this._clearDisplayForProvider(provider);
 | 
			
		||||
                meta.actor.show();
 | 
			
		||||
 | 
			
		||||
                // Hiding drops the key focus if we have it
 | 
			
		||||
                let focus = global.stage.get_key_focus();
 | 
			
		||||
                // To avoid CSS transitions causing flickering when
 | 
			
		||||
                // the first search result stays the same, we hide the
 | 
			
		||||
                // content while filling in the results.
 | 
			
		||||
                this._content.hide();
 | 
			
		||||
 | 
			
		||||
                meta.resultDisplay.renderResults(metas);
 | 
			
		||||
                this._maybeSetInitialSelection();
 | 
			
		||||
                this._updateStatusText();
 | 
			
		||||
 | 
			
		||||
                this._content.show();
 | 
			
		||||
                if (this._content.contains(focus))
 | 
			
		||||
                    global.stage.set_key_focus(focus);
 | 
			
		||||
            }));
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    activateDefault: function() {
 | 
			
		||||
@@ -544,7 +533,6 @@ const ProviderIcon = new Lang.Class({
 | 
			
		||||
        this.parent({ style_class: 'search-provider-icon',
 | 
			
		||||
                      reactive: true,
 | 
			
		||||
                      can_focus: true,
 | 
			
		||||
                      accessible_name: provider.appInfo.get_name(),
 | 
			
		||||
                      track_hover: true });
 | 
			
		||||
 | 
			
		||||
        this._content = new St.Widget({ layout_manager: new Clutter.BinLayout() });
 | 
			
		||||
 
 | 
			
		||||
@@ -30,6 +30,5 @@ const HorizontalSeparator = new Lang.Class({
 | 
			
		||||
        cr.setSource(pattern);
 | 
			
		||||
        cr.rectangle(margin, gradientOffset, gradientWidth, gradientHeight);
 | 
			
		||||
        cr.fill();
 | 
			
		||||
        cr.$dispose();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -15,13 +15,10 @@ const DEFAULT_MODE = 'restrictive';
 | 
			
		||||
const _modes = {
 | 
			
		||||
    'restrictive': {
 | 
			
		||||
        parentMode: null,
 | 
			
		||||
        stylesheetName: 'gnome-shell.css',
 | 
			
		||||
        overridesSchema: 'org.gnome.shell.overrides',
 | 
			
		||||
        hasOverview: false,
 | 
			
		||||
        showCalendarEvents: false,
 | 
			
		||||
        allowSettings: false,
 | 
			
		||||
        allowExtensions: false,
 | 
			
		||||
        allowScreencast: false,
 | 
			
		||||
        enabledExtensions: [],
 | 
			
		||||
        hasRunDialog: false,
 | 
			
		||||
        hasWorkspaces: false,
 | 
			
		||||
@@ -47,9 +44,10 @@ const _modes = {
 | 
			
		||||
        unlockDialog: imports.gdm.loginDialog.LoginDialog,
 | 
			
		||||
        components: ['polkitAgent'],
 | 
			
		||||
        panel: {
 | 
			
		||||
            left: [],
 | 
			
		||||
            left: ['logo'],
 | 
			
		||||
            center: ['dateMenu'],
 | 
			
		||||
            right: ['a11yGreeter', 'keyboard', 'aggregateMenu'],
 | 
			
		||||
            right: ['a11y', 'display', 'keyboard',
 | 
			
		||||
                    'volume', 'battery', 'powerMenu']
 | 
			
		||||
        },
 | 
			
		||||
        panelStyle: 'login-screen'
 | 
			
		||||
    },
 | 
			
		||||
@@ -60,9 +58,9 @@ const _modes = {
 | 
			
		||||
        unlockDialog: undefined,
 | 
			
		||||
        components: ['polkitAgent', 'telepathyClient'],
 | 
			
		||||
        panel: {
 | 
			
		||||
            left: [],
 | 
			
		||||
            left: ['userMenu'],
 | 
			
		||||
            center: [],
 | 
			
		||||
            right: ['aggregateMenu']
 | 
			
		||||
            right: ['lockScreen']
 | 
			
		||||
        },
 | 
			
		||||
        panelStyle: 'lock-screen'
 | 
			
		||||
    },
 | 
			
		||||
@@ -72,19 +70,28 @@ const _modes = {
 | 
			
		||||
        unlockDialog: undefined,
 | 
			
		||||
        components: ['polkitAgent', 'telepathyClient'],
 | 
			
		||||
        panel: {
 | 
			
		||||
            left: [],
 | 
			
		||||
            left: ['userMenu'],
 | 
			
		||||
            center: [],
 | 
			
		||||
            right: ['a11y', 'keyboard', 'aggregateMenu']
 | 
			
		||||
            right: ['a11y', 'keyboard', 'lockScreen']
 | 
			
		||||
        },
 | 
			
		||||
        panelStyle: 'unlock-screen'
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    'initial-setup': {
 | 
			
		||||
        isPrimary: true,
 | 
			
		||||
        components: ['keyring'],
 | 
			
		||||
        panel: {
 | 
			
		||||
            left: [],
 | 
			
		||||
            center: ['dateMenu'],
 | 
			
		||||
            right: ['a11y', 'keyboard', 'volume']
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    'user': {
 | 
			
		||||
        hasOverview: true,
 | 
			
		||||
        showCalendarEvents: true,
 | 
			
		||||
        allowSettings: true,
 | 
			
		||||
        allowExtensions: true,
 | 
			
		||||
        allowScreencast: true,
 | 
			
		||||
        hasRunDialog: true,
 | 
			
		||||
        hasWorkspaces: true,
 | 
			
		||||
        hasWindows: true,
 | 
			
		||||
@@ -93,11 +100,12 @@ const _modes = {
 | 
			
		||||
        isPrimary: true,
 | 
			
		||||
        unlockDialog: imports.ui.unlockDialog.UnlockDialog,
 | 
			
		||||
        components: ['networkAgent', 'polkitAgent', 'telepathyClient',
 | 
			
		||||
                     'keyring', 'autorunManager', 'automountManager'],
 | 
			
		||||
                     'keyring', 'recorder', 'autorunManager', 'automountManager'],
 | 
			
		||||
        panel: {
 | 
			
		||||
            left: ['activities', 'appMenu'],
 | 
			
		||||
            center: ['dateMenu'],
 | 
			
		||||
            right: ['a11y', 'keyboard', 'aggregateMenu']
 | 
			
		||||
            right: ['a11y', 'keyboard', 'volume', 'bluetooth',
 | 
			
		||||
                    'network', 'battery', 'userMenu']
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
@@ -149,7 +157,12 @@ function listModes() {
 | 
			
		||||
const SessionMode = new Lang.Class({
 | 
			
		||||
    Name: 'SessionMode',
 | 
			
		||||
 | 
			
		||||
    init: function() {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        global.connect('notify::session-mode', Lang.bind(this, this._sync));
 | 
			
		||||
        this._modes = _modes;
 | 
			
		||||
        this._modeStack = [DEFAULT_MODE];
 | 
			
		||||
        this._sync();
 | 
			
		||||
 | 
			
		||||
        _getModes(Lang.bind(this, function(modes) {
 | 
			
		||||
            this._modes = modes;
 | 
			
		||||
            let primary = modes[global.session_mode] &&
 | 
			
		||||
@@ -157,8 +170,6 @@ const SessionMode = new Lang.Class({
 | 
			
		||||
            let mode = primary ? global.session_mode : 'user';
 | 
			
		||||
            this._modeStack = [mode];
 | 
			
		||||
            this._sync();
 | 
			
		||||
 | 
			
		||||
            this.emit('sessions-loaded');
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,19 +1,16 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
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 Config = imports.misc.config;
 | 
			
		||||
const ExtensionSystem = imports.ui.extensionSystem;
 | 
			
		||||
const ExtensionDownloader = imports.ui.extensionDownloader;
 | 
			
		||||
const ExtensionUtils = imports.misc.extensionUtils;
 | 
			
		||||
const Hash = imports.misc.hash;
 | 
			
		||||
const Flashspot = imports.ui.flashspot;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const Screenshot = imports.ui.screenshot;
 | 
			
		||||
const ViewSelector = imports.ui.viewSelector;
 | 
			
		||||
 | 
			
		||||
const GnomeShellIface = <interface name="org.gnome.Shell">
 | 
			
		||||
<method name="Eval">
 | 
			
		||||
@@ -21,32 +18,34 @@ const GnomeShellIface = <interface name="org.gnome.Shell">
 | 
			
		||||
    <arg type="b" direction="out" name="success" />
 | 
			
		||||
    <arg type="s" direction="out" name="result" />
 | 
			
		||||
</method>
 | 
			
		||||
<method name="FocusSearch"/>
 | 
			
		||||
<method name="ShowOSD">
 | 
			
		||||
    <arg type="a{sv}" direction="in" name="params"/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name="FocusApp">
 | 
			
		||||
    <arg type="s" direction="in" name="id"/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name="ShowApplications" />
 | 
			
		||||
<method name="GrabAccelerator">
 | 
			
		||||
    <arg type="s" direction="in" name="accelerator"/>
 | 
			
		||||
    <arg type="u" direction="in" name="flags"/>
 | 
			
		||||
    <arg type="u" direction="out" name="action"/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name="GrabAccelerators">
 | 
			
		||||
    <arg type="a(su)" direction="in" name="accelerators"/>
 | 
			
		||||
    <arg type="au" direction="out" name="actions"/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name="UngrabAccelerator">
 | 
			
		||||
    <arg type="u" direction="in" name="action"/>
 | 
			
		||||
<method name="ScreenshotArea">
 | 
			
		||||
    <arg type="i" direction="in" name="x"/>
 | 
			
		||||
    <arg type="i" direction="in" name="y"/>
 | 
			
		||||
    <arg type="i" direction="in" name="width"/>
 | 
			
		||||
    <arg type="i" direction="in" name="height"/>
 | 
			
		||||
    <arg type="b" direction="in" name="flash"/>
 | 
			
		||||
    <arg type="s" direction="in" name="filename"/>
 | 
			
		||||
    <arg type="b" direction="out" name="success"/>
 | 
			
		||||
</method>
 | 
			
		||||
<signal name="AcceleratorActivated">
 | 
			
		||||
    <arg name="action" type="u" />
 | 
			
		||||
    <arg name="deviceid" type="u" />
 | 
			
		||||
    <arg name="timestamp" type="u" />
 | 
			
		||||
</signal>
 | 
			
		||||
<method name="ScreenshotWindow">
 | 
			
		||||
    <arg type="b" direction="in" name="include_frame"/>
 | 
			
		||||
    <arg type="b" direction="in" name="include_cursor"/>
 | 
			
		||||
    <arg type="b" direction="in" name="flash"/>
 | 
			
		||||
    <arg type="s" direction="in" name="filename"/>
 | 
			
		||||
    <arg type="b" direction="out" name="success"/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name="Screenshot">
 | 
			
		||||
    <arg type="b" direction="in" name="include_cursor"/>
 | 
			
		||||
    <arg type="b" direction="in" name="flash"/>
 | 
			
		||||
    <arg type="s" direction="in" name="filename"/>
 | 
			
		||||
    <arg type="b" direction="out" name="success"/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name="FlashArea">
 | 
			
		||||
    <arg type="i" direction="in" name="x"/>
 | 
			
		||||
    <arg type="i" direction="in" name="y"/>
 | 
			
		||||
    <arg type="i" direction="in" name="width"/>
 | 
			
		||||
    <arg type="i" direction="in" name="height"/>
 | 
			
		||||
</method>
 | 
			
		||||
<property name="Mode" type="s" access="read" />
 | 
			
		||||
<property name="OverviewActive" type="b" access="readwrite" />
 | 
			
		||||
<property name="ShellVersion" type="s" access="read" />
 | 
			
		||||
@@ -76,16 +75,7 @@ const GnomeShell = new Lang.Class({
 | 
			
		||||
        this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(GnomeShellIface, this);
 | 
			
		||||
        this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell');
 | 
			
		||||
 | 
			
		||||
        this._extensionsService = new GnomeShellExtensions();
 | 
			
		||||
        this._screenshotService = new Screenshot.ScreenshotService();
 | 
			
		||||
 | 
			
		||||
        this._grabbedAccelerators = new Hash.Map();
 | 
			
		||||
        this._grabbers = new Hash.Map();
 | 
			
		||||
 | 
			
		||||
        global.display.connect('accelerator-activated', Lang.bind(this,
 | 
			
		||||
            function(display, action, deviceid, timestamp) {
 | 
			
		||||
                this._emitAcceleratorActivated(action, deviceid, timestamp);
 | 
			
		||||
            }));
 | 
			
		||||
        this._extensionsSerivce = new GnomeShellExtensions();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -104,7 +94,7 @@ const GnomeShell = new Lang.Class({
 | 
			
		||||
     */
 | 
			
		||||
    Eval: function(code) {
 | 
			
		||||
        if (!global.settings.get_boolean('development-tools'))
 | 
			
		||||
            return [false, ''];
 | 
			
		||||
            return [false, null];
 | 
			
		||||
 | 
			
		||||
        let returnValue;
 | 
			
		||||
        let success;
 | 
			
		||||
@@ -121,114 +111,81 @@ const GnomeShell = new Lang.Class({
 | 
			
		||||
        return [success, returnValue];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    FocusSearch: function() {
 | 
			
		||||
        Main.overview.focusSearch();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    ShowOSD: function(params) {
 | 
			
		||||
        for (let param in params)
 | 
			
		||||
            params[param] = params[param].deep_unpack();
 | 
			
		||||
 | 
			
		||||
        let icon = null;
 | 
			
		||||
        if (params['icon'])
 | 
			
		||||
            icon = Gio.Icon.new_for_string(params['icon']);
 | 
			
		||||
 | 
			
		||||
        Main.osdWindow.setIcon(icon);
 | 
			
		||||
        Main.osdWindow.setLabel(params['label']);
 | 
			
		||||
        Main.osdWindow.setLevel(params['level']);
 | 
			
		||||
 | 
			
		||||
        Main.osdWindow.show();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    FocusApp: function(id) {
 | 
			
		||||
        this.ShowApplications();
 | 
			
		||||
        Main.overview.viewSelector.appDisplay.selectApp(id);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    ShowApplications: function() {
 | 
			
		||||
        Main.overview.viewSelector.showApps();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    GrabAcceleratorAsync: function(params, invocation) {
 | 
			
		||||
        let [accel, flags] = params;
 | 
			
		||||
        let sender = invocation.get_sender();
 | 
			
		||||
        let bindingAction = this._grabAcceleratorForSender(accel, flags, sender);
 | 
			
		||||
        return invocation.return_value(GLib.Variant.new('(u)', [bindingAction]));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    GrabAcceleratorsAsync: function(params, invocation) {
 | 
			
		||||
        let [accels] = params;
 | 
			
		||||
        let sender = invocation.get_sender();
 | 
			
		||||
        let bindingActions = [];
 | 
			
		||||
        for (let i = 0; i < accels.length; i++) {
 | 
			
		||||
            let [accel, flags] = accels[i];
 | 
			
		||||
            bindingActions.push(this._grabAcceleratorForSender(accel, flags, sender));
 | 
			
		||||
        }
 | 
			
		||||
        return invocation.return_value(GLib.Variant.new('(au)', [bindingActions]));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    UngrabAcceleratorAsync: function(params, invocation) {
 | 
			
		||||
        let [action] = params;
 | 
			
		||||
        let grabbedBy = this._grabbedAccelerators.get(action);
 | 
			
		||||
        if (invocation.get_sender() != grabbedBy)
 | 
			
		||||
            return invocation.return_value(GLib.Variant.new('(b)', [false]));
 | 
			
		||||
 | 
			
		||||
        let ungrabSucceeded = global.display.ungrab_accelerator(action);
 | 
			
		||||
        if (ungrabSucceeded)
 | 
			
		||||
            this._grabbedAccelerators.delete(action);
 | 
			
		||||
        return invocation.return_value(GLib.Variant.new('(b)', [ungrabSucceeded]));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _emitAcceleratorActivated: function(action, deviceid, timestamp) {
 | 
			
		||||
        let destination = this._grabbedAccelerators.get(action);
 | 
			
		||||
        if (!destination)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let connection = this._dbusImpl.get_connection();
 | 
			
		||||
        let info = this._dbusImpl.get_info();
 | 
			
		||||
        connection.emit_signal(destination,
 | 
			
		||||
                               this._dbusImpl.get_object_path(),
 | 
			
		||||
                               info ? info.name : null,
 | 
			
		||||
                               'AcceleratorActivated',
 | 
			
		||||
                               GLib.Variant.new('(uuu)', [action, deviceid, timestamp]));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _grabAcceleratorForSender: function(accelerator, flags, sender) {
 | 
			
		||||
        let bindingAction = global.display.grab_accelerator(accelerator);
 | 
			
		||||
        if (bindingAction == Meta.KeyBindingAction.NONE)
 | 
			
		||||
            return Meta.KeyBindingAction.NONE;
 | 
			
		||||
 | 
			
		||||
        let bindingName = Meta.external_binding_name_for_action(bindingAction);
 | 
			
		||||
        Main.wm.allowKeybinding(bindingName, flags);
 | 
			
		||||
 | 
			
		||||
        this._grabbedAccelerators.set(bindingAction, sender);
 | 
			
		||||
 | 
			
		||||
        if (!this._grabbers.has(sender)) {
 | 
			
		||||
            let id = Gio.bus_watch_name(Gio.BusType.SESSION, sender, 0, null,
 | 
			
		||||
                                        Lang.bind(this, this._onGrabberBusNameVanished));
 | 
			
		||||
            this._grabbers.set(sender, id);
 | 
			
		||||
    _onScreenshotComplete: function(obj, result, area, flash, invocation) {
 | 
			
		||||
        if (flash && result) {
 | 
			
		||||
            let flashspot = new Flashspot.Flashspot(area);
 | 
			
		||||
            flashspot.fire();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return bindingAction;
 | 
			
		||||
        let retval = GLib.Variant.new('(b)', [result]);
 | 
			
		||||
        invocation.return_value(retval);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _ungrabAccelerator: function(action) {
 | 
			
		||||
        let ungrabSucceeded = global.display.ungrab_accelerator(action);
 | 
			
		||||
        if (ungrabSucceeded)
 | 
			
		||||
            this._grabbedAccelerators.delete(action);
 | 
			
		||||
    /**
 | 
			
		||||
     * ScreenshotArea:
 | 
			
		||||
     * @x: The X coordinate of the area
 | 
			
		||||
     * @y: The Y coordinate of the area
 | 
			
		||||
     * @width: The width of the area
 | 
			
		||||
     * @height: The height of the area
 | 
			
		||||
     * @flash: Whether to flash the area or not
 | 
			
		||||
     * @filename: The filename for the screenshot
 | 
			
		||||
     *
 | 
			
		||||
     * Takes a screenshot of the passed in area and saves it
 | 
			
		||||
     * in @filename as png image, it returns a boolean
 | 
			
		||||
     * indicating whether the operation was successful or not.
 | 
			
		||||
     *
 | 
			
		||||
     */
 | 
			
		||||
    ScreenshotAreaAsync : function (params, invocation) {
 | 
			
		||||
        let [x, y, width, height, flash, filename, callback] = params;
 | 
			
		||||
        let screenshot = new Shell.Screenshot();
 | 
			
		||||
        screenshot.screenshot_area (x, y, width, height, filename,
 | 
			
		||||
                                Lang.bind(this, this._onScreenshotComplete,
 | 
			
		||||
                                          flash, invocation));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onGrabberBusNameVanished: function(connection, name) {
 | 
			
		||||
        let grabs = this._grabbedAccelerators.items();
 | 
			
		||||
        for (let i = 0; i < grabs.length; i++) {
 | 
			
		||||
            let [action, sender] = grabs[i];
 | 
			
		||||
            if (sender == name)
 | 
			
		||||
                this._ungrabAccelerator(action);
 | 
			
		||||
        }
 | 
			
		||||
        Gio.bus_unwatch_name(this._grabbers.get(name));
 | 
			
		||||
        this._grabbers.delete(name);
 | 
			
		||||
    /**
 | 
			
		||||
     * ScreenshotWindow:
 | 
			
		||||
     * @include_frame: Whether to include the frame or not
 | 
			
		||||
     * @include_cursor: Whether to include the cursor image or not
 | 
			
		||||
     * @flash: Whether to flash the window area or not
 | 
			
		||||
     * @filename: The filename for the screenshot
 | 
			
		||||
     *
 | 
			
		||||
     * Takes a screenshot of the focused window (optionally omitting the frame)
 | 
			
		||||
     * and saves it in @filename as png image, it returns a boolean
 | 
			
		||||
     * indicating whether the operation was successful or not.
 | 
			
		||||
     *
 | 
			
		||||
     */
 | 
			
		||||
    ScreenshotWindowAsync : function (params, invocation) {
 | 
			
		||||
        let [include_frame, include_cursor, flash, filename] = params;
 | 
			
		||||
        let screenshot = new Shell.Screenshot();
 | 
			
		||||
        screenshot.screenshot_window (include_frame, include_cursor, filename,
 | 
			
		||||
                                  Lang.bind(this, this._onScreenshotComplete,
 | 
			
		||||
                                            flash, invocation));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Screenshot:
 | 
			
		||||
     * @filename: The filename for the screenshot
 | 
			
		||||
     * @include_cursor: Whether to include the cursor image or not
 | 
			
		||||
     * @flash: Whether to flash the screen or not
 | 
			
		||||
     *
 | 
			
		||||
     * Takes a screenshot of the whole screen and saves it
 | 
			
		||||
     * in @filename as png image, it returns a boolean
 | 
			
		||||
     * indicating whether the operation was successful or not.
 | 
			
		||||
     *
 | 
			
		||||
     */
 | 
			
		||||
    ScreenshotAsync : function (params, invocation) {
 | 
			
		||||
        let [include_cursor, flash, filename] = params;
 | 
			
		||||
        let screenshot = new Shell.Screenshot();
 | 
			
		||||
        screenshot.screenshot(include_cursor, filename,
 | 
			
		||||
                          Lang.bind(this, this._onScreenshotComplete,
 | 
			
		||||
                                    flash, invocation));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    FlashArea: function(x, y, width, height) {
 | 
			
		||||
        let flashspot = new Flashspot.Flashspot({ x : x, y : y, width: width, height: height});
 | 
			
		||||
        flashspot.fire();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    Mode: global.session_mode,
 | 
			
		||||
 | 
			
		||||
@@ -393,8 +350,8 @@ const ScreenSaverDBus = new Lang.Class({
 | 
			
		||||
        this.parent();
 | 
			
		||||
 | 
			
		||||
        this._screenShield = screenShield;
 | 
			
		||||
        screenShield.connect('active-changed', Lang.bind(this, function(shield) {
 | 
			
		||||
            this._dbusImpl.emit_signal('ActiveChanged', GLib.Variant.new('(b)', [shield.active]));
 | 
			
		||||
        screenShield.connect('lock-status-changed', Lang.bind(this, function(shield) {
 | 
			
		||||
            this._dbusImpl.emit_signal('ActiveChanged', GLib.Variant.new('(b)', [shield.locked]));
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(ScreenSaverIface, this);
 | 
			
		||||
@@ -415,13 +372,13 @@ const ScreenSaverDBus = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    SetActive: function(active) {
 | 
			
		||||
        if (active)
 | 
			
		||||
            this._screenShield.activate(true);
 | 
			
		||||
            this._screenShield.lock(true);
 | 
			
		||||
        else
 | 
			
		||||
            this._screenShield.deactivate(false);
 | 
			
		||||
            this._screenShield.unlock();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    GetActive: function() {
 | 
			
		||||
        return this._screenShield.active;
 | 
			
		||||
        return this._screenShield.locked;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    GetActiveTime: function() {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,8 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Gtk = imports.gi.Gtk;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
const BoxPointer = imports.ui.boxpointer;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
const PopupMenu = imports.ui.popupMenu;
 | 
			
		||||
@@ -14,7 +11,9 @@ const EntryMenu = new Lang.Class({
 | 
			
		||||
    Name: 'ShellEntryMenu',
 | 
			
		||||
    Extends: PopupMenu.PopupMenu,
 | 
			
		||||
 | 
			
		||||
    _init: function(entry) {
 | 
			
		||||
    _init: function(entry, params) {
 | 
			
		||||
        params = Params.parse (params, { isPassword: false });
 | 
			
		||||
 | 
			
		||||
        this.parent(entry, 0, St.Side.TOP);
 | 
			
		||||
 | 
			
		||||
        this.actor.add_style_class_name('entry-context-menu');
 | 
			
		||||
@@ -35,6 +34,8 @@ const EntryMenu = new Lang.Class({
 | 
			
		||||
        this._pasteItem = item;
 | 
			
		||||
 | 
			
		||||
        this._passwordItem = null;
 | 
			
		||||
        if (params.isPassword)
 | 
			
		||||
	    this._makePasswordItem();
 | 
			
		||||
 | 
			
		||||
        Main.uiGroup.add_actor(this.actor);
 | 
			
		||||
        this.actor.hide();
 | 
			
		||||
@@ -49,45 +50,47 @@ const EntryMenu = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get isPassword() {
 | 
			
		||||
        return this._passwordItem != null;
 | 
			
		||||
	return this._passwordItem != null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    set isPassword(v) {
 | 
			
		||||
        if (v == this.isPassword)
 | 
			
		||||
            return;
 | 
			
		||||
	if (v == this.isPassword)
 | 
			
		||||
	    return;
 | 
			
		||||
 | 
			
		||||
        if (v) {
 | 
			
		||||
            this._makePasswordItem();
 | 
			
		||||
            this._entry.input_purpose = Gtk.InputPurpose.PASSWORD;
 | 
			
		||||
        } else {
 | 
			
		||||
            this._passwordItem.destroy();
 | 
			
		||||
            this._passwordItem = null;
 | 
			
		||||
            this._entry.input_purpose = Gtk.InputPurpose.FREE_FORM;
 | 
			
		||||
        }
 | 
			
		||||
	if (v)
 | 
			
		||||
	    this._makePasswordItem();
 | 
			
		||||
	else {
 | 
			
		||||
	    this._passwordItem.destroy();
 | 
			
		||||
	    this._passwordItem = null;
 | 
			
		||||
	}
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    open: function(animate) {
 | 
			
		||||
    open: function() {
 | 
			
		||||
        this._updatePasteItem();
 | 
			
		||||
        this._updateCopyItem();
 | 
			
		||||
        if (this._passwordItem)
 | 
			
		||||
            this._updatePasswordItem();
 | 
			
		||||
 | 
			
		||||
        this.parent(animate);
 | 
			
		||||
        this._entry.add_style_pseudo_class('focus');
 | 
			
		||||
 | 
			
		||||
        let direction = Gtk.DirectionType.TAB_FORWARD;
 | 
			
		||||
        if (!this.actor.navigate_focus(null, direction, false))
 | 
			
		||||
            this.actor.grab_key_focus();
 | 
			
		||||
 | 
			
		||||
        this.parent();
 | 
			
		||||
        this._entry.add_style_pseudo_class('focus');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    close: function() {
 | 
			
		||||
        this._entry.grab_key_focus();
 | 
			
		||||
        this.parent();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateCopyItem: function() {
 | 
			
		||||
        let selection = this._entry.clutter_text.get_selection();
 | 
			
		||||
        this._copyItem.setSensitive(!this._entry.clutter_text.password_char &&
 | 
			
		||||
                                    selection && selection != '');
 | 
			
		||||
        this._copyItem.setSensitive(selection && selection != '');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updatePasteItem: function() {
 | 
			
		||||
        this._clipboard.get_text(St.ClipboardType.CLIPBOARD, Lang.bind(this,
 | 
			
		||||
        this._clipboard.get_text(Lang.bind(this,
 | 
			
		||||
            function(clipboard, text) {
 | 
			
		||||
                this._pasteItem.setSensitive(text && text != '');
 | 
			
		||||
            }));
 | 
			
		||||
@@ -103,11 +106,11 @@ const EntryMenu = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _onCopyActivated: function() {
 | 
			
		||||
        let selection = this._entry.clutter_text.get_selection();
 | 
			
		||||
        this._clipboard.set_text(St.ClipboardType.CLIPBOARD, selection);
 | 
			
		||||
        this._clipboard.set_text(selection);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onPasteActivated: function() {
 | 
			
		||||
        this._clipboard.get_text(St.ClipboardType.CLIPBOARD, Lang.bind(this,
 | 
			
		||||
        this._clipboard.get_text(Lang.bind(this,
 | 
			
		||||
            function(clipboard, text) {
 | 
			
		||||
                if (!text)
 | 
			
		||||
                    return;
 | 
			
		||||
@@ -131,12 +134,12 @@ function _setMenuAlignment(entry, stageX) {
 | 
			
		||||
 | 
			
		||||
function _onButtonPressEvent(actor, event, entry) {
 | 
			
		||||
    if (entry.menu.isOpen) {
 | 
			
		||||
        entry.menu.close(BoxPointer.PopupAnimation.FULL);
 | 
			
		||||
        entry.menu.close();
 | 
			
		||||
        return true;
 | 
			
		||||
    } else if (event.get_button() == 3) {
 | 
			
		||||
        let [stageX, stageY] = event.get_coords();
 | 
			
		||||
        _setMenuAlignment(entry, stageX);
 | 
			
		||||
        entry.menu.open(BoxPointer.PopupAnimation.FULL);
 | 
			
		||||
        entry.menu.open();
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
@@ -146,17 +149,14 @@ function _onPopup(actor, entry) {
 | 
			
		||||
    let [success, textX, textY, lineHeight] = entry.clutter_text.position_to_coords(-1);
 | 
			
		||||
    if (success)
 | 
			
		||||
        entry.menu.setSourceAlignment(textX / entry.width);
 | 
			
		||||
    entry.menu.open(BoxPointer.PopupAnimation.FULL);
 | 
			
		||||
    entry.menu.open();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function addContextMenu(entry, params) {
 | 
			
		||||
    if (entry.menu)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    params = Params.parse (params, { isPassword: false });
 | 
			
		||||
 | 
			
		||||
    entry.menu = new EntryMenu(entry);
 | 
			
		||||
    entry.menu.isPassword = params.isPassword;
 | 
			
		||||
    entry.menu = new EntryMenu(entry, params);
 | 
			
		||||
    entry._menuManager = new PopupMenu.PopupMenuManager({ actor: entry });
 | 
			
		||||
    entry._menuManager.addMenu(entry.menu);
 | 
			
		||||
 | 
			
		||||
@@ -167,10 +167,4 @@ function addContextMenu(entry, params) {
 | 
			
		||||
    entry.connect('button-press-event', Lang.bind(null, _onButtonPressEvent, entry));
 | 
			
		||||
 | 
			
		||||
    entry.connect('popup-menu', Lang.bind(null, _onPopup, entry));
 | 
			
		||||
 | 
			
		||||
    entry.connect('destroy', function() {
 | 
			
		||||
        entry.menu.destroy();
 | 
			
		||||
        entry.menu = null;
 | 
			
		||||
        entry._menuManager = null;
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										247
									
								
								js/ui/slider.js
									
									
									
									
									
								
							
							
						
						@@ -1,247 +0,0 @@
 | 
			
		||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
 | 
			
		||||
 | 
			
		||||
const Cairo = imports.cairo;
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const Atk = imports.gi.Atk;
 | 
			
		||||
 | 
			
		||||
const SLIDER_SCROLL_STEP = 0.05; /* Slider scrolling step in % */
 | 
			
		||||
 | 
			
		||||
const Slider = new Lang.Class({
 | 
			
		||||
    Name: "Slider",
 | 
			
		||||
 | 
			
		||||
    _init: function(value) {
 | 
			
		||||
        if (isNaN(value))
 | 
			
		||||
            // Avoid spreading NaNs around
 | 
			
		||||
            throw TypeError('The slider value must be a number');
 | 
			
		||||
        this._value = Math.max(Math.min(value, 1), 0);
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.DrawingArea({ style_class: 'slider',
 | 
			
		||||
                                          can_focus: true,
 | 
			
		||||
                                          reactive: true,
 | 
			
		||||
                                          accessible_role: Atk.Role.SLIDER });
 | 
			
		||||
        this.actor.connect('repaint', Lang.bind(this, this._sliderRepaint));
 | 
			
		||||
        this.actor.connect('button-press-event', Lang.bind(this, this._startDragging));
 | 
			
		||||
        this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
 | 
			
		||||
        this.actor.connect('key-press-event', Lang.bind(this, this.onKeyPressEvent));
 | 
			
		||||
 | 
			
		||||
        this._releaseId = this._motionId = 0;
 | 
			
		||||
        this._dragging = false;
 | 
			
		||||
 | 
			
		||||
        this._customAccessible = St.GenericAccessible.new_for_actor(this.actor);
 | 
			
		||||
        this.actor.set_accessible(this._customAccessible);
 | 
			
		||||
 | 
			
		||||
        this._customAccessible.connect('get-current-value', Lang.bind(this, this._getCurrentValue));
 | 
			
		||||
        this._customAccessible.connect('get-minimum-value', Lang.bind(this, this._getMinimumValue));
 | 
			
		||||
        this._customAccessible.connect('get-maximum-value', Lang.bind(this, this._getMaximumValue));
 | 
			
		||||
        this._customAccessible.connect('get-minimum-increment', Lang.bind(this, this._getMinimumIncrement));
 | 
			
		||||
        this._customAccessible.connect('set-current-value', Lang.bind(this, this._setCurrentValue));
 | 
			
		||||
 | 
			
		||||
        this.connect('value-changed', Lang.bind(this, this._valueChanged));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setValue: function(value) {
 | 
			
		||||
        if (isNaN(value))
 | 
			
		||||
            throw TypeError('The slider value must be a number');
 | 
			
		||||
 | 
			
		||||
        this._value = Math.max(Math.min(value, 1), 0);
 | 
			
		||||
        this.actor.queue_repaint();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _sliderRepaint: function(area) {
 | 
			
		||||
        let cr = area.get_context();
 | 
			
		||||
        let themeNode = area.get_theme_node();
 | 
			
		||||
        let [width, height] = area.get_surface_size();
 | 
			
		||||
 | 
			
		||||
        let handleRadius = themeNode.get_length('-slider-handle-radius');
 | 
			
		||||
 | 
			
		||||
        let handleBorderWidth = themeNode.get_length('-slider-handle-border-width');
 | 
			
		||||
        let [hasHandleColor, handleBorderColor] =
 | 
			
		||||
            themeNode.lookup_color('-slider-handle-border-color', false);
 | 
			
		||||
 | 
			
		||||
        let sliderHeight = themeNode.get_length('-slider-height');
 | 
			
		||||
 | 
			
		||||
        let sliderBorderWidth = themeNode.get_length('-slider-border-width');
 | 
			
		||||
        let sliderBorderRadius = Math.min(width, sliderHeight) / 2;
 | 
			
		||||
 | 
			
		||||
        let sliderBorderColor = themeNode.get_color('-slider-border-color');
 | 
			
		||||
        let sliderColor = themeNode.get_color('-slider-background-color');
 | 
			
		||||
 | 
			
		||||
        let sliderActiveBorderColor = themeNode.get_color('-slider-active-border-color');
 | 
			
		||||
        let sliderActiveColor = themeNode.get_color('-slider-active-background-color');
 | 
			
		||||
 | 
			
		||||
        const TAU = Math.PI * 2;
 | 
			
		||||
 | 
			
		||||
        let handleX = handleRadius + (width - 2 * handleRadius) * this._value;
 | 
			
		||||
 | 
			
		||||
        cr.arc(sliderBorderRadius + sliderBorderWidth, height / 2, sliderBorderRadius, TAU * 1/4, TAU * 3/4);
 | 
			
		||||
        cr.lineTo(handleX, (height - sliderHeight) / 2);
 | 
			
		||||
        cr.lineTo(handleX, (height + sliderHeight) / 2);
 | 
			
		||||
        cr.lineTo(sliderBorderRadius + sliderBorderWidth, (height + sliderHeight) / 2);
 | 
			
		||||
        Clutter.cairo_set_source_color(cr, sliderActiveColor);
 | 
			
		||||
        cr.fillPreserve();
 | 
			
		||||
        Clutter.cairo_set_source_color(cr, sliderActiveBorderColor);
 | 
			
		||||
        cr.setLineWidth(sliderBorderWidth);
 | 
			
		||||
        cr.stroke();
 | 
			
		||||
 | 
			
		||||
        cr.arc(width - sliderBorderRadius - sliderBorderWidth, height / 2, sliderBorderRadius, TAU * 3/4, TAU * 1/4);
 | 
			
		||||
        cr.lineTo(handleX, (height + sliderHeight) / 2);
 | 
			
		||||
        cr.lineTo(handleX, (height - sliderHeight) / 2);
 | 
			
		||||
        cr.lineTo(width - sliderBorderRadius - sliderBorderWidth, (height - sliderHeight) / 2);
 | 
			
		||||
        Clutter.cairo_set_source_color(cr, sliderColor);
 | 
			
		||||
        cr.fillPreserve();
 | 
			
		||||
        Clutter.cairo_set_source_color(cr, sliderBorderColor);
 | 
			
		||||
        cr.setLineWidth(sliderBorderWidth);
 | 
			
		||||
        cr.stroke();
 | 
			
		||||
 | 
			
		||||
        let handleY = height / 2;
 | 
			
		||||
 | 
			
		||||
        let color = themeNode.get_foreground_color();
 | 
			
		||||
        Clutter.cairo_set_source_color(cr, color);
 | 
			
		||||
        cr.arc(handleX, handleY, handleRadius, 0, 2 * Math.PI);
 | 
			
		||||
        cr.fillPreserve();
 | 
			
		||||
        if (hasHandleColor && handleBorderWidth) {
 | 
			
		||||
            Clutter.cairo_set_source_color(cr, handleBorderColor);
 | 
			
		||||
            cr.setLineWidth(handleBorderWidth);
 | 
			
		||||
            cr.stroke();
 | 
			
		||||
        }
 | 
			
		||||
        cr.$dispose();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _startDragging: function(actor, event) {
 | 
			
		||||
        this.startDragging(event);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    startDragging: function(event) {
 | 
			
		||||
        if (this._dragging)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        this._dragging = true;
 | 
			
		||||
 | 
			
		||||
        let device = event.get_device();
 | 
			
		||||
        device.grab(this.actor);
 | 
			
		||||
        this._grabbedDevice = device;
 | 
			
		||||
 | 
			
		||||
        this._releaseId = this.actor.connect('button-release-event', Lang.bind(this, this._endDragging));
 | 
			
		||||
        this._motionId = this.actor.connect('motion-event', Lang.bind(this, this._motionEvent));
 | 
			
		||||
        let absX, absY;
 | 
			
		||||
        [absX, absY] = event.get_coords();
 | 
			
		||||
        this._moveHandle(absX, absY);
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _endDragging: function() {
 | 
			
		||||
        if (this._dragging) {
 | 
			
		||||
            this.actor.disconnect(this._releaseId);
 | 
			
		||||
            this.actor.disconnect(this._motionId);
 | 
			
		||||
 | 
			
		||||
            this._grabbedDevice.ungrab();
 | 
			
		||||
            this._grabbedDevice = null;
 | 
			
		||||
            this._dragging = false;
 | 
			
		||||
 | 
			
		||||
            this.emit('drag-end');
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    scroll: function(event) {
 | 
			
		||||
        let direction = event.get_scroll_direction();
 | 
			
		||||
        let delta;
 | 
			
		||||
 | 
			
		||||
        if (event.is_pointer_emulated())
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (direction == Clutter.ScrollDirection.DOWN) {
 | 
			
		||||
            delta = -SLIDER_SCROLL_STEP;
 | 
			
		||||
        } else if (direction == Clutter.ScrollDirection.UP) {
 | 
			
		||||
            delta = +SLIDER_SCROLL_STEP;
 | 
			
		||||
        } else if (direction == Clutter.ScrollDirection.SMOOTH) {
 | 
			
		||||
            let [dx, dy] = event.get_scroll_delta();
 | 
			
		||||
            // Even though the slider is horizontal, use dy to match
 | 
			
		||||
            // the UP/DOWN above.
 | 
			
		||||
            delta = -dy / 10;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._value = Math.min(Math.max(0, this._value + delta), 1);
 | 
			
		||||
 | 
			
		||||
        this.actor.queue_repaint();
 | 
			
		||||
        this.emit('value-changed', this._value);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onScrollEvent: function(actor, event) {
 | 
			
		||||
        this.scroll(event);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _motionEvent: function(actor, event) {
 | 
			
		||||
        let absX, absY;
 | 
			
		||||
        [absX, absY] = event.get_coords();
 | 
			
		||||
        this._moveHandle(absX, absY);
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    onKeyPressEvent: function (actor, event) {
 | 
			
		||||
        let key = event.get_key_symbol();
 | 
			
		||||
        if (key == Clutter.KEY_Right || key == Clutter.KEY_Left) {
 | 
			
		||||
            let delta = key == Clutter.KEY_Right ? 0.1 : -0.1;
 | 
			
		||||
            this._value = Math.max(0, Math.min(this._value + delta, 1));
 | 
			
		||||
            this.actor.queue_repaint();
 | 
			
		||||
            this.emit('value-changed', this._value);
 | 
			
		||||
            this.emit('drag-end');
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _moveHandle: function(absX, absY) {
 | 
			
		||||
        let relX, relY, sliderX, sliderY;
 | 
			
		||||
        [sliderX, sliderY] = this.actor.get_transformed_position();
 | 
			
		||||
        relX = absX - sliderX;
 | 
			
		||||
        relY = absY - sliderY;
 | 
			
		||||
 | 
			
		||||
        let width = this.actor.width;
 | 
			
		||||
        let handleRadius = this.actor.get_theme_node().get_length('-slider-handle-radius');
 | 
			
		||||
 | 
			
		||||
        let newvalue;
 | 
			
		||||
        if (relX < handleRadius)
 | 
			
		||||
            newvalue = 0;
 | 
			
		||||
        else if (relX > width - handleRadius)
 | 
			
		||||
            newvalue = 1;
 | 
			
		||||
        else
 | 
			
		||||
            newvalue = (relX - handleRadius) / (width - 2 * handleRadius);
 | 
			
		||||
        this._value = newvalue;
 | 
			
		||||
        this.actor.queue_repaint();
 | 
			
		||||
        this.emit('value-changed', this._value);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getCurrentValue: function (actor) {
 | 
			
		||||
        return this._value;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getMinimumValue: function (actor) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getMaximumValue: function (actor) {
 | 
			
		||||
        return 1;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getMinimumIncrement: function (actor) {
 | 
			
		||||
        return 0.1;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _setCurrentValue: function (actor, value) {
 | 
			
		||||
        this._value = value;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _valueChanged: function (slider, value, property) {
 | 
			
		||||
        this._customAccessible.notify ("accessible-value");
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get value() {
 | 
			
		||||
        return this._value;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
Signals.addSignalMethods(Slider.prototype);
 | 
			
		||||
@@ -1,54 +1,43 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Mainloop = imports.mainloop;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
const PanelMenu = imports.ui.panelMenu;
 | 
			
		||||
const PopupMenu = imports.ui.popupMenu;
 | 
			
		||||
 | 
			
		||||
const A11Y_SCHEMA                   = 'org.gnome.desktop.a11y';
 | 
			
		||||
const KEY_ALWAYS_SHOW               = 'always-show-universal-access-status';
 | 
			
		||||
const A11Y_SCHEMA = 'org.gnome.desktop.a11y.keyboard';
 | 
			
		||||
const KEY_STICKY_KEYS_ENABLED = 'stickykeys-enable';
 | 
			
		||||
const KEY_BOUNCE_KEYS_ENABLED = 'bouncekeys-enable';
 | 
			
		||||
const KEY_SLOW_KEYS_ENABLED   = 'slowkeys-enable';
 | 
			
		||||
const KEY_MOUSE_KEYS_ENABLED  = 'mousekeys-enable';
 | 
			
		||||
 | 
			
		||||
const A11Y_KEYBOARD_SCHEMA          = 'org.gnome.desktop.a11y.keyboard';
 | 
			
		||||
const KEY_STICKY_KEYS_ENABLED       = 'stickykeys-enable';
 | 
			
		||||
const KEY_BOUNCE_KEYS_ENABLED       = 'bouncekeys-enable';
 | 
			
		||||
const KEY_SLOW_KEYS_ENABLED         = 'slowkeys-enable';
 | 
			
		||||
const KEY_MOUSE_KEYS_ENABLED        = 'mousekeys-enable';
 | 
			
		||||
const APPLICATIONS_SCHEMA = 'org.gnome.desktop.a11y.applications';
 | 
			
		||||
 | 
			
		||||
const APPLICATIONS_SCHEMA           = 'org.gnome.desktop.a11y.applications';
 | 
			
		||||
const DPI_LOW_REASONABLE_VALUE  = 50;
 | 
			
		||||
const DPI_HIGH_REASONABLE_VALUE = 500;
 | 
			
		||||
 | 
			
		||||
const DPI_FACTOR_LARGE              = 1.25;
 | 
			
		||||
const DPI_FACTOR_LARGE   = 1.25;
 | 
			
		||||
const DPI_FACTOR_LARGER  = 1.5;
 | 
			
		||||
const DPI_FACTOR_LARGEST = 2.0;
 | 
			
		||||
 | 
			
		||||
const WM_SCHEMA                     = 'org.gnome.desktop.wm.preferences';
 | 
			
		||||
const KEY_VISUAL_BELL               = 'visual-bell';
 | 
			
		||||
const WM_SCHEMA            = 'org.gnome.desktop.wm.preferences';
 | 
			
		||||
const KEY_VISUAL_BELL      = 'visual-bell';
 | 
			
		||||
 | 
			
		||||
const DESKTOP_INTERFACE_SCHEMA      = 'org.gnome.desktop.interface';
 | 
			
		||||
const KEY_GTK_THEME                 = 'gtk-theme';
 | 
			
		||||
const KEY_ICON_THEME                = 'icon-theme';
 | 
			
		||||
const KEY_WM_THEME                  = 'theme';
 | 
			
		||||
const KEY_TEXT_SCALING_FACTOR       = 'text-scaling-factor';
 | 
			
		||||
const DESKTOP_INTERFACE_SCHEMA = 'org.gnome.desktop.interface';
 | 
			
		||||
const KEY_GTK_THEME      = 'gtk-theme';
 | 
			
		||||
const KEY_ICON_THEME     = 'icon-theme';
 | 
			
		||||
const KEY_WM_THEME       = 'theme';
 | 
			
		||||
const KEY_TEXT_SCALING_FACTOR = 'text-scaling-factor';
 | 
			
		||||
 | 
			
		||||
const HIGH_CONTRAST_THEME           = 'HighContrast';
 | 
			
		||||
const HIGH_CONTRAST_THEME = 'HighContrast';
 | 
			
		||||
 | 
			
		||||
const ATIndicator = new Lang.Class({
 | 
			
		||||
    Name: 'ATIndicator',
 | 
			
		||||
    Extends: PanelMenu.Button,
 | 
			
		||||
    Extends: PanelMenu.SystemStatusButton,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.parent(0.0, _("Accessibility"));
 | 
			
		||||
 | 
			
		||||
        this._hbox = new St.BoxLayout({ style_class: 'panel-status-menu-box' });
 | 
			
		||||
        this._hbox.add_child(new St.Icon({ style_class: 'system-status-icon',
 | 
			
		||||
                                           icon_name: 'preferences-desktop-accessibility-symbolic' }));
 | 
			
		||||
        this._hbox.add_child(PopupMenu.unicodeArrow(St.Side.BOTTOM));
 | 
			
		||||
 | 
			
		||||
        this.actor.add_child(this._hbox);
 | 
			
		||||
 | 
			
		||||
        this._a11ySettings = new Gio.Settings({ schema: A11Y_SCHEMA });
 | 
			
		||||
        this._a11ySettings.connect('changed::' + KEY_ALWAYS_SHOW, Lang.bind(this, this._queueSyncMenuVisibility));
 | 
			
		||||
        this.parent('preferences-desktop-accessibility-symbolic', _("Accessibility"));
 | 
			
		||||
 | 
			
		||||
        let highContrast = this._buildHCItem();
 | 
			
		||||
        this.menu.addMenuItem(highContrast);
 | 
			
		||||
@@ -71,37 +60,20 @@ const ATIndicator = new Lang.Class({
 | 
			
		||||
        let visualBell = this._buildItem(_("Visual Alerts"), WM_SCHEMA, KEY_VISUAL_BELL);
 | 
			
		||||
        this.menu.addMenuItem(visualBell);
 | 
			
		||||
 | 
			
		||||
        let stickyKeys = this._buildItem(_("Sticky Keys"), A11Y_KEYBOARD_SCHEMA, KEY_STICKY_KEYS_ENABLED);
 | 
			
		||||
        let stickyKeys = this._buildItem(_("Sticky Keys"), A11Y_SCHEMA, KEY_STICKY_KEYS_ENABLED);
 | 
			
		||||
        this.menu.addMenuItem(stickyKeys);
 | 
			
		||||
 | 
			
		||||
        let slowKeys = this._buildItem(_("Slow Keys"), A11Y_KEYBOARD_SCHEMA, KEY_SLOW_KEYS_ENABLED);
 | 
			
		||||
        let slowKeys = this._buildItem(_("Slow Keys"), A11Y_SCHEMA, KEY_SLOW_KEYS_ENABLED);
 | 
			
		||||
        this.menu.addMenuItem(slowKeys);
 | 
			
		||||
 | 
			
		||||
        let bounceKeys = this._buildItem(_("Bounce Keys"), A11Y_KEYBOARD_SCHEMA, KEY_BOUNCE_KEYS_ENABLED);
 | 
			
		||||
        let bounceKeys = this._buildItem(_("Bounce Keys"), A11Y_SCHEMA, KEY_BOUNCE_KEYS_ENABLED);
 | 
			
		||||
        this.menu.addMenuItem(bounceKeys);
 | 
			
		||||
 | 
			
		||||
        let mouseKeys = this._buildItem(_("Mouse Keys"), A11Y_KEYBOARD_SCHEMA, KEY_MOUSE_KEYS_ENABLED);
 | 
			
		||||
        let mouseKeys = this._buildItem(_("Mouse Keys"), A11Y_SCHEMA, KEY_MOUSE_KEYS_ENABLED);
 | 
			
		||||
        this.menu.addMenuItem(mouseKeys);
 | 
			
		||||
 | 
			
		||||
        this._syncMenuVisibility();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _syncMenuVisibility: function() {
 | 
			
		||||
        this._syncMenuVisibilityIdle = 0;
 | 
			
		||||
 | 
			
		||||
        let alwaysShow = this._a11ySettings.get_boolean(KEY_ALWAYS_SHOW);
 | 
			
		||||
        let items = this.menu._getMenuItems();
 | 
			
		||||
 | 
			
		||||
        this.actor.visible = alwaysShow || items.some(function(f) { return !!f.state; });
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _queueSyncMenuVisibility: function() {
 | 
			
		||||
        if (this._syncMenuVisibilityIdle)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._syncMenuVisbilityIdle = Mainloop.idle_add(Lang.bind(this, this._syncMenuVisibility));
 | 
			
		||||
        this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
 | 
			
		||||
        this.menu.addSettingsAction(_("Universal Access Settings"), 'gnome-universal-access-panel.desktop');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _buildItemExtended: function(string, initial_value, writable, on_set) {
 | 
			
		||||
@@ -123,11 +95,9 @@ const ATIndicator = new Lang.Class({
 | 
			
		||||
            function(enabled) {
 | 
			
		||||
                return settings.set_boolean(key, enabled);
 | 
			
		||||
            });
 | 
			
		||||
        settings.connect('changed::'+key, Lang.bind(this, function() {
 | 
			
		||||
        settings.connect('changed::'+key, function() {
 | 
			
		||||
            widget.setToggleState(settings.get_boolean(key));
 | 
			
		||||
 | 
			
		||||
            this._queueSyncMenuVisibility();
 | 
			
		||||
        }));
 | 
			
		||||
        });
 | 
			
		||||
        return widget;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -159,7 +129,7 @@ const ATIndicator = new Lang.Class({
 | 
			
		||||
                    wmSettings.reset(KEY_WM_THEME);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        interfaceSettings.connect('changed::' + KEY_GTK_THEME, Lang.bind(this, function() {
 | 
			
		||||
        interfaceSettings.connect('changed::' + KEY_GTK_THEME, function() {
 | 
			
		||||
            let value = interfaceSettings.get_string(KEY_GTK_THEME);
 | 
			
		||||
            if (value == HIGH_CONTRAST_THEME) {
 | 
			
		||||
                highContrast.setToggleState(true);
 | 
			
		||||
@@ -167,9 +137,7 @@ const ATIndicator = new Lang.Class({
 | 
			
		||||
                highContrast.setToggleState(false);
 | 
			
		||||
                gtkTheme = value;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this._queueSyncMenuVisibility();
 | 
			
		||||
        }));
 | 
			
		||||
        });
 | 
			
		||||
        interfaceSettings.connect('changed::' + KEY_ICON_THEME, function() {
 | 
			
		||||
            let value = interfaceSettings.get_string(KEY_ICON_THEME);
 | 
			
		||||
            if (value != HIGH_CONTRAST_THEME)
 | 
			
		||||
@@ -198,22 +166,11 @@ const ATIndicator = new Lang.Class({
 | 
			
		||||
                else
 | 
			
		||||
                    settings.reset(KEY_TEXT_SCALING_FACTOR);
 | 
			
		||||
            });
 | 
			
		||||
        settings.connect('changed::' + KEY_TEXT_SCALING_FACTOR, Lang.bind(this, function() {
 | 
			
		||||
        settings.connect('changed::' + KEY_TEXT_SCALING_FACTOR, function() {
 | 
			
		||||
            let factor = settings.get_double(KEY_TEXT_SCALING_FACTOR);
 | 
			
		||||
            let active = (factor > 1.0);
 | 
			
		||||
            widget.setToggleState(active);
 | 
			
		||||
 | 
			
		||||
            this._queueSyncMenuVisibility();
 | 
			
		||||
        }));
 | 
			
		||||
        });
 | 
			
		||||
        return widget;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const ATGreeterIndicator = new Lang.Class({
 | 
			
		||||
    Name: 'ATGreeterIndicator',
 | 
			
		||||
    Extends: ATIndicator,
 | 
			
		||||
 | 
			
		||||
    // Override visibility handling to be always visible
 | 
			
		||||
    _syncMenuVisibility: function() { },
 | 
			
		||||
    _queueSyncMenuVisibility: function() { }
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||