Compare commits
	
		
			27 Commits
		
	
	
		
			3.8.3
			...
			wip/re-sea
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					3124e82838 | ||
| 
						 | 
					6c4daaaa71 | ||
| 
						 | 
					3768e85673 | ||
| 
						 | 
					d1c72e1e4c | ||
| 
						 | 
					2498497cc1 | ||
| 
						 | 
					7c3c6da368 | ||
| 
						 | 
					09e7ab5611 | ||
| 
						 | 
					53cff07eef | ||
| 
						 | 
					cfed6e401d | ||
| 
						 | 
					12b041a569 | ||
| 
						 | 
					f2dd94776c | ||
| 
						 | 
					c0f9c52ba6 | ||
| 
						 | 
					2ee8b0e427 | ||
| 
						 | 
					b250a72fcf | ||
| 
						 | 
					cf08745f6c | ||
| 
						 | 
					58023c81ad | ||
| 
						 | 
					7e8968da52 | ||
| 
						 | 
					fce1d8157e | ||
| 
						 | 
					3b5de01ed6 | ||
| 
						 | 
					a61da42aa7 | ||
| 
						 | 
					81acfdbfc3 | ||
| 
						 | 
					159b789443 | ||
| 
						 | 
					c07b715a3a | ||
| 
						 | 
					1bb09fd0f1 | ||
| 
						 | 
					4441541da6 | ||
| 
						 | 
					afee4d6925 | ||
| 
						 | 
					b62bd0e62f | 
@@ -16,7 +16,6 @@ EXTRA_DIST =		\
 | 
			
		||||
# These are files checked into Git that we don't want to distribute
 | 
			
		||||
DIST_EXCLUDE =					\
 | 
			
		||||
	.gitignore				\
 | 
			
		||||
	.gitmodules				\
 | 
			
		||||
	gnome-shell.doap			\
 | 
			
		||||
	HACKING					\
 | 
			
		||||
	MAINTAINERS				\
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										408
									
								
								NEWS
									
									
									
									
									
								
							
							
						
						
									
										408
									
								
								NEWS
									
									
									
									
									
								
							@@ -1,411 +1,3 @@
 | 
			
		||||
3.8.3
 | 
			
		||||
=====
 | 
			
		||||
* Fix child menu regression introduced in 3.8.2 [Florian; #699678]
 | 
			
		||||
* Fix alt-tab not always switching back to the previous window [Florian; #700356]
 | 
			
		||||
* Fix VPN network icon regression introduced in 3.8.2 [Florian; #700394]
 | 
			
		||||
* Allow switch-to-workspace-n keybindings in overview [Florian; #649977]
 | 
			
		||||
* Update man page [Matthias; #700339]
 | 
			
		||||
* Add FocusSearch DBus method [Florian; #700536]
 | 
			
		||||
* gdm: Update the session chooser style [Allan; #695742]
 | 
			
		||||
* Fix some app folders getting truncated at the top [Florian; #694371]
 | 
			
		||||
* Fix duplicate cursors in screenshots with magnification [Florian; #700488]
 | 
			
		||||
* popupMenu: Allow for an optional border for slider handle [Florian; #697917]
 | 
			
		||||
* 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]
 | 
			
		||||
* Tweak timeout for activating windows during XDND [Adel; #700150]
 | 
			
		||||
* Fix fullscreen windows not being unredirected when legacy tray icons
 | 
			
		||||
  are around [Adel; #701224]
 | 
			
		||||
* 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]
 | 
			
		||||
* Fix incomplete app menu if multiple actions only become available later
 | 
			
		||||
  [Xavier; #694612]
 | 
			
		||||
* Fix showing the OSD when a fullscreen app is unredirected [Adel, #701224]
 | 
			
		||||
* window-switcher: Only show windows from current workspace by default
 | 
			
		||||
  [Florian; #701214
 | 
			
		||||
* Misc bug fixes [Florian, Rui, Giovanni, Stef; #700409, #700625, #700807,
 | 
			
		||||
  #700842, #700900, #700944, #700190, #700972, #700877]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Giovanni Campagna, Xavier Claessens, Matthias Clasen, Allan Day,
 | 
			
		||||
  Adel Gadllah, Rui Matos, Florian Müllner, Carlos Soriano, Stef Walter
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Cheng-Chia Tseng [zh_HK, zh_TW], eternalhui [zh_CN]
 | 
			
		||||
 | 
			
		||||
3.8.2
 | 
			
		||||
=====
 | 
			
		||||
* 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]
 | 
			
		||||
* ctrlAltTab: Use symbolic icons for desktop windows [Matthias; #697914]
 | 
			
		||||
* gdm: Fix regression where domain login hint not shown [Stef; #698200]
 | 
			
		||||
* 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]
 | 
			
		||||
* Provide a DBus API for screencasting [Florian; #696247]
 | 
			
		||||
* Implement app folder keynav and shortcuts [Florian; #695314]
 | 
			
		||||
* polkitAgent: Allow retrying after mistyped passwords [Stef; #684431]
 | 
			
		||||
* Add input purpose and hints to StEntry and StIMText [Daiki; #691392]
 | 
			
		||||
* Set input-purpose property for password entries [Rui; #700043]
 | 
			
		||||
* Misc fixes and cleanups [Jasper, Florian, Giovanni, Tim, Rui; #697203,
 | 
			
		||||
  #698959, #696720, #698531, #676285, #698812, #699189]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Giovanni Campagna, Matthias Clasen, Lionel Landwerlin, Tim Lunn, Rui Matos,
 | 
			
		||||
  Simon McVittie, Marta Milakovic, Florian Müllner, Jasper St. Pierre,
 | 
			
		||||
  Daiki Ueno, Stef Walter
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Muhammet Kara [tr], Nik Kalach [ia], Žygimantas Beručka [lt],
 | 
			
		||||
  Kjartan Maraas [nb]
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
=======
 | 
			
		||||
* 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]
 | 
			
		||||
 | 
			
		||||
3.7.3
 | 
			
		||||
=====
 | 
			
		||||
* Add 'No Messages' label when message tray is empty [Victoria; #686738]
 | 
			
		||||
* Use better icons in Ctrl-Alt-Tab popup [Stéphane; #641303]
 | 
			
		||||
* Show the OSK on the monitor where the focused window lives [Giovanni; #685856]
 | 
			
		||||
* Highlight window clone and caption when hovered [Giovanni, Marc; #665310]
 | 
			
		||||
* Improve login process indication [Stéphane; #687113]
 | 
			
		||||
* Omit empty categories in apps view [Stéphane; #687970]
 | 
			
		||||
* Style panel differently according to mode [Florian; #684573]
 | 
			
		||||
* Make it possible to hide the user name [Matthias; #688577]
 | 
			
		||||
* Consolidate and improve chat connection notifications [Giovanni; #687213]
 | 
			
		||||
* Improve notification scrollbar appearance [Carlos; #688393]
 | 
			
		||||
* Fade scroll view fade near scrolling edges [Jasper; #689249]
 | 
			
		||||
* Add a read-only org.gnome.Shell.Mode property [Debarshi; #689300]
 | 
			
		||||
* Don't close message tray after using context menus [Giovanni; #689296]
 | 
			
		||||
* Port swipe-scrolling to ClutterPanAction [Jasper, Florian; #689062, #689552]
 | 
			
		||||
* Remember state of 'Remember Password' checkbox [Ron; #688039]
 | 
			
		||||
* Improve timestamp format in chat notifications [Carlos; #680989]
 | 
			
		||||
* Improve style of missed-messages counter [Carlos; #686472]
 | 
			
		||||
* Omit connection failure notifications if cancelled by user [Giovanni; #684823]
 | 
			
		||||
* Add window-based Alt-Tab popup [Florian; #688913]
 | 
			
		||||
* Support external session mode definitions [Florian; #689304]
 | 
			
		||||
* Support session-mode-specific extensions [Florian; #689305]
 | 
			
		||||
* Support 'parentMode' property in session modes [Florian; #689308]
 | 
			
		||||
* Support a new org.gnome.ShellSearchProvider2 DBus interface
 | 
			
		||||
  [Cosimo; #689735, #690009]
 | 
			
		||||
* Add "windows" to Ctrl-Alt-Tab popup [Jasper; #689653]
 | 
			
		||||
* Port PopupMenu to GrabHelper [Jasper; #689109, #689954]
 | 
			
		||||
* Show headphone icon when headphones are plugged in [Giovanni; #675902]
 | 
			
		||||
* Display (non-app) search results as list [Tanner, Cosimo; #681797]
 | 
			
		||||
* Skip diacritical marks in search terms [Aleksander; #648587]
 | 
			
		||||
* Expose all engine options in input sources [Giovanni, Rui; #682318]
 | 
			
		||||
* Add input source switcher popup [Rui; #682315]
 | 
			
		||||
* Add minimal support for InfiniBand in network menu [Dan; #677150]
 | 
			
		||||
* Misc bug fixes and cleanups [Sebastian, Aleksander, Giovanni, Tim, Cosimo,
 | 
			
		||||
  Florian, Matthias, Rui, Lionel, Colin, Piotr, Guillaume, Bastien, Tanner,
 | 
			
		||||
  Carlos, Stéphane, Jakub; #688422, #688379, #688750, #688771, #686800,
 | 
			
		||||
  #688133, #688895, #688966, #683986, #688004, #689108, #689029, #683449,
 | 
			
		||||
  #688196, #689304, #689243, #689295, #689325, #689400, #679168, #689568,
 | 
			
		||||
  #689537, #689528, #689749, #689789, #689353, #689820, #689868, #689778,
 | 
			
		||||
  #689959, #688589, #688589, #689955, #687250, #689965, #690046, #690049,
 | 
			
		||||
  #689884, #682286, #690173, #690174, #672941, #689876, #687881, #690171,
 | 
			
		||||
  #690241, #690312, #690175, #687955, #650843, #688234, #690427
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Giovanni Campagna, Cosimo Cecchi, Matthias Clasen, Stéphane Démurget,
 | 
			
		||||
  Guillaume Desmottes, Tanner Doshier, Piotr Drąg, Sebastian Keller,
 | 
			
		||||
  Lionel Landwerlin, Tim Lunn, Victoria Martínez de la Cruz, Aleksander Morgado,
 | 
			
		||||
  Florian Müllner, Bastien Nocera, Marc Plano-Lesay, Carlos Soriano Sánchez,
 | 
			
		||||
  Jakub Steiner, Jasper St. Pierre, Colin Walters, Dan Winship, Ron Yorston
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Yuri Myasoedov [ru], Wouter Bolsterlee [nl], Yaron Shahrabani [he],
 | 
			
		||||
  Nilamdyuti Goswami [as], Ani Peter [ml], Kjartan Maraas [nb],
 | 
			
		||||
  Dr.T.Vasudevan [ta], A S Alam [pa], Shankar Prasad [kn], Khaled Hosny [ar],
 | 
			
		||||
  Daniel Mustieles [es], Dušan Kazik [sk]
 | 
			
		||||
 | 
			
		||||
3.7.2
 | 
			
		||||
=====
 | 
			
		||||
* Enforce RTL in he for messages that might end up as LTR [Florian; #686630]
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										121
									
								
								configure.ac
									
									
									
									
									
								
							
							
						
						
									
										121
									
								
								configure.ac
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
AC_PREREQ(2.63)
 | 
			
		||||
AC_INIT([gnome-shell],[3.8.3],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
 | 
			
		||||
AC_INIT([gnome-shell],[3.7.2],[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])
 | 
			
		||||
@@ -53,29 +55,30 @@ 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 xfixes)
 | 
			
		||||
   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.35.4
 | 
			
		||||
MUTTER_MIN_VERSION=3.8.3
 | 
			
		||||
GTK_MIN_VERSION=3.7.9
 | 
			
		||||
GJS_MIN_VERSION=1.33.2
 | 
			
		||||
MUTTER_MIN_VERSION=3.7.2
 | 
			
		||||
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.6
 | 
			
		||||
PULSE_MIN_VERS=2.0
 | 
			
		||||
NETWORKMANAGER_MIN_VERSION=0.9.7
 | 
			
		||||
 | 
			
		||||
# Collect more than 20 libraries for a prize!
 | 
			
		||||
PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
 | 
			
		||||
@@ -87,26 +90,43 @@ PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_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 libcanberra-gtk3
 | 
			
		||||
			       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 >= $NETWORKMANAGER_MIN_VERSION
 | 
			
		||||
                               libnm-gtk >= $NETWORKMANAGER_MIN_VERSION
 | 
			
		||||
                               libsecret-unstable gcr-base-3 >= $GCR_MIN_VERSION)
 | 
			
		||||
                               gnome-keyring-1 gcr-3 >= $GCR_MIN_VERSION)
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
 | 
			
		||||
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(GVC, libpulse libpulse-mainloop-glib gobject-2.0)
 | 
			
		||||
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.7.2)
 | 
			
		||||
 | 
			
		||||
AC_MSG_CHECKING([for bluetooth support])
 | 
			
		||||
PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.1.0],
 | 
			
		||||
@@ -126,10 +146,32 @@ 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`
 | 
			
		||||
MUTTER_TYPELIB_DIR=`$PKG_CONFIG --variable=typelibdir libmutter`
 | 
			
		||||
@@ -156,6 +198,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])
 | 
			
		||||
 | 
			
		||||
@@ -171,7 +223,32 @@ 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)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,3 @@
 | 
			
		||||
wandadir = $(pkgdatadir)
 | 
			
		||||
dist_wanda_DATA = wanda.png
 | 
			
		||||
 | 
			
		||||
desktopdir=$(datadir)/applications
 | 
			
		||||
desktop_DATA = gnome-shell.desktop gnome-shell-extension-prefs.desktop
 | 
			
		||||
 | 
			
		||||
@@ -15,8 +12,6 @@ desktop_DATA = gnome-shell.desktop gnome-shell-extension-prefs.desktop
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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, 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). 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
 | 
			
		||||
    -->
 | 
			
		||||
 
 | 
			
		||||
@@ -39,14 +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="command-history" type="as">
 | 
			
		||||
      <default>[]</default>
 | 
			
		||||
      <_summary>History for command (Alt-F2) dialog</_summary>
 | 
			
		||||
@@ -73,6 +65,11 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
 | 
			
		||||
        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>
 | 
			
		||||
@@ -207,7 +204,7 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
 | 
			
		||||
      </_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.
 | 
			
		||||
 
 | 
			
		||||
@@ -38,6 +38,7 @@ stage {
 | 
			
		||||
 | 
			
		||||
/* small */
 | 
			
		||||
.app-well-menu,
 | 
			
		||||
.contact-details-status,
 | 
			
		||||
.run-dialog-error-label {
 | 
			
		||||
    font-size: 9pt;
 | 
			
		||||
}
 | 
			
		||||
@@ -125,10 +126,9 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
 | 
			
		||||
/* PopupMenu */
 | 
			
		||||
 | 
			
		||||
.popup-menu-boxpointer,
 | 
			
		||||
.candidate-popup-boxpointer {
 | 
			
		||||
.popup-menu-boxpointer {
 | 
			
		||||
    -arrow-border-radius: 8px;
 | 
			
		||||
    -arrow-background-color: rgba(0,0,0,0.9);
 | 
			
		||||
    -arrow-background-color: black;
 | 
			
		||||
    -arrow-border-width: 2px;
 | 
			
		||||
    -arrow-border-color: #a5a5a5;
 | 
			
		||||
    -arrow-base: 24px;
 | 
			
		||||
@@ -206,12 +206,11 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.popup-separator-menu-item {
 | 
			
		||||
    -gradient-height: 1px;
 | 
			
		||||
    -gradient-start: rgba(255,255,255,0.0);
 | 
			
		||||
    -gradient-end: rgba(255,255,255,0.3);
 | 
			
		||||
    -margin-horizontal: 24px;
 | 
			
		||||
    height: 1px;
 | 
			
		||||
    padding: 8px 0px;
 | 
			
		||||
    -gradient-height: 2px;
 | 
			
		||||
    -gradient-start: rgba(8,8,8,0);
 | 
			
		||||
    -gradient-end: #333333;
 | 
			
		||||
    -margin-horizontal: 1.5em;
 | 
			
		||||
    height: 1em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.popup-alternating-menu-item:alternate {
 | 
			
		||||
@@ -252,10 +251,6 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
    icon-size: 1.09em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.popup-battery-percentage {
 | 
			
		||||
    padding-left: 24px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Switches */
 | 
			
		||||
.toggle-switch {
 | 
			
		||||
    width: 65px;
 | 
			
		||||
@@ -286,24 +281,20 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
 | 
			
		||||
/* Buttons */
 | 
			
		||||
 | 
			
		||||
.candidate-page-button,
 | 
			
		||||
.dash-search-button,
 | 
			
		||||
.notification-button,
 | 
			
		||||
.notification-icon-button,
 | 
			
		||||
.hotplug-notification-item,
 | 
			
		||||
.hotplug-resident-eject-button,
 | 
			
		||||
.modal-dialog-button,
 | 
			
		||||
.app-view-control {
 | 
			
		||||
.modal-dialog-button {
 | 
			
		||||
    font-weight: bold;
 | 
			
		||||
    border: 1px solid #8b8b8b;
 | 
			
		||||
    background-gradient-direction: vertical;
 | 
			
		||||
    background-gradient-start: rgba(255, 255, 255, 0.2);
 | 
			
		||||
    background-gradient-end: rgba(255, 255, 255, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.modal-dialog-button {
 | 
			
		||||
    font-weight: bold;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.candidate-page-button:hover,
 | 
			
		||||
.dash-search-button:hover,
 | 
			
		||||
.notification-button:hover,
 | 
			
		||||
.notification-icon-button:hover,
 | 
			
		||||
.hotplug-notification-item:hover,
 | 
			
		||||
@@ -313,37 +304,27 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
    background-gradient-end: rgba(255, 255, 255, 0.1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.dash-search-button:selected,
 | 
			
		||||
.dash-search-button:focus,
 | 
			
		||||
.notification-button:focus,
 | 
			
		||||
.notification-icon-button:focus,
 | 
			
		||||
.hotplug-notification-item:focus,
 | 
			
		||||
.modal-dialog-button:focus,
 | 
			
		||||
.app-view-control:focus {
 | 
			
		||||
.modal-dialog-button:focus {
 | 
			
		||||
    border-width: 2px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.app-view-control:focus {
 | 
			
		||||
    padding: 3px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.app-view-control:first-child:ltr:focus,
 | 
			
		||||
.app-view-control:last-child:rtl:focus {
 | 
			
		||||
    border-right-width: 1px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.candidate-page-button:active,
 | 
			
		||||
.candidate-page-button:pressed,
 | 
			
		||||
.dash-search-button:active,
 | 
			
		||||
.dash-search-button:pressed,
 | 
			
		||||
.notification-button:active,
 | 
			
		||||
.notification-icon-button:active,
 | 
			
		||||
.hotplug-notification-item:active,
 | 
			
		||||
.hotplug-resident-eject-button:active,
 | 
			
		||||
.modal-dialog-button:active,
 | 
			
		||||
.modal-dialog-button:pressed,
 | 
			
		||||
.app-view-control:checked {
 | 
			
		||||
.modal-dialog-button:pressed {
 | 
			
		||||
    background-gradient-start: rgba(255, 255, 255, 0);
 | 
			
		||||
    background-gradient-end: rgba(255, 255, 255, 0.2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.candidate-page-button:insensitive,
 | 
			
		||||
.notification-button:insensitive,
 | 
			
		||||
.notification-icon-button:insensitive,
 | 
			
		||||
.modal-dialog-button:insensitive {
 | 
			
		||||
@@ -353,27 +334,6 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
    background-color: rgba(102, 102, 102, 0.15);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Common radii */
 | 
			
		||||
 | 
			
		||||
#searchEntry,
 | 
			
		||||
.modal-dialog-button,
 | 
			
		||||
.notification-button,
 | 
			
		||||
.hotplug-notification-item,
 | 
			
		||||
.app-view-controls {
 | 
			
		||||
    border-radius: 18px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.app-view-control:first-child:ltr,
 | 
			
		||||
.app-view-control:last-child:rtl {
 | 
			
		||||
    border-radius: 18px 0px 0px 18px;
 | 
			
		||||
    border-right-width: 0px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.app-view-control:last-child:ltr,
 | 
			
		||||
.app-view-control:first-child:rtl {
 | 
			
		||||
    border-radius: 0px 18px 18px 0px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Entries */
 | 
			
		||||
 | 
			
		||||
#searchEntry,
 | 
			
		||||
@@ -394,7 +354,7 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
    background-gradient-start: rgba(5,5,6,0.1);
 | 
			
		||||
    background-gradient-end: rgba(254,254,254,0.1);
 | 
			
		||||
    background-gradient-direction: vertical;
 | 
			
		||||
    transition-duration: 300ms;
 | 
			
		||||
    transition-duration: 300;
 | 
			
		||||
    box-shadow: inset 0px 2px 4px rgba(0,0,0,0.6);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -429,7 +389,7 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
    color: rgb(64, 64, 64);
 | 
			
		||||
    caret-color: rgb(64, 64, 64);
 | 
			
		||||
    font-weight: bold;
 | 
			
		||||
    transition-duration: 0ms;
 | 
			
		||||
    transition-duration: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.notification StEntry,
 | 
			
		||||
@@ -460,14 +420,17 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
    background-color: black;
 | 
			
		||||
    font-weight: bold;
 | 
			
		||||
    height: 1.86em;
 | 
			
		||||
    transition-duration: 250;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#panel.lock-screen {
 | 
			
		||||
    height: 2em;
 | 
			
		||||
    background-color: rgba(0,0,0,0.3);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#panel.unlock-screen,
 | 
			
		||||
#panel.login-screen {
 | 
			
		||||
    height: 2em;
 | 
			
		||||
    background-color: transparent;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -512,7 +475,6 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#appMenu {
 | 
			
		||||
    spinner-image: url("process-working.svg");
 | 
			
		||||
    spacing: 4px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -541,7 +503,7 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
    -minimum-hpadding: 6px;
 | 
			
		||||
    font-weight: bold;
 | 
			
		||||
    color: #ccc;
 | 
			
		||||
    transition-duration: 100ms;
 | 
			
		||||
    transition-duration: 100;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#panel.unlock-screen .panel-button,
 | 
			
		||||
@@ -589,17 +551,13 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.panel-menu {
 | 
			
		||||
    -boxpointer-gap: 4px;
 | 
			
		||||
    -boxpointer-gap: 4px
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.panel-status-button-box {
 | 
			
		||||
    spacing: 4px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.lock-screen-status-button-box {
 | 
			
		||||
    spacing: 8px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* User Menu */
 | 
			
		||||
 | 
			
		||||
#panelUserMenu {
 | 
			
		||||
@@ -655,11 +613,15 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
/* Overview */
 | 
			
		||||
 | 
			
		||||
#overview {
 | 
			
		||||
    spacing: 24px;
 | 
			
		||||
    spacing: 40px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.overview-controls {
 | 
			
		||||
    padding-bottom: 32px;
 | 
			
		||||
#overview-group {
 | 
			
		||||
    spacing: 32px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.window-caption {
 | 
			
		||||
    spacing: 25px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.workspace-thumbnails-background {
 | 
			
		||||
@@ -679,7 +641,7 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
 | 
			
		||||
.workspace-thumbnails {
 | 
			
		||||
    spacing: 11px;
 | 
			
		||||
    visible-width: 32px; /* Amount visible before hovering */
 | 
			
		||||
    visible-width: 96px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.workspace-thumbnail-indicator {
 | 
			
		||||
@@ -689,7 +651,6 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.window-caption {
 | 
			
		||||
    spacing: 25px;
 | 
			
		||||
    background: rgba(0,0,0,0.5);
 | 
			
		||||
    border-radius: 8px;
 | 
			
		||||
    padding: 4px 12px;
 | 
			
		||||
@@ -697,10 +658,6 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
    border: 2px solid rgba(0, 0, 0, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.window-caption:hover {
 | 
			
		||||
    border: 2px solid rgba(255, 255, 255, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.window-close, .notification-close {
 | 
			
		||||
    background-image: url("close-window.svg");
 | 
			
		||||
    background-size: 32px;
 | 
			
		||||
@@ -712,6 +669,10 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
    -shell-close-overlap: 16px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.window-caption:hover {
 | 
			
		||||
    border: 2px solid rgba(255, 255, 255, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.window-clone-border {
 | 
			
		||||
    border: 4px solid rgba(255, 255, 255, 0.5);
 | 
			
		||||
    border-radius: 4px;
 | 
			
		||||
@@ -742,38 +703,8 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.window-picker {
 | 
			
		||||
    -horizontal-spacing: 32px;
 | 
			
		||||
    -vertical-spacing: 32px;
 | 
			
		||||
    padding-left: 32px;
 | 
			
		||||
    padding-right: 32px;
 | 
			
		||||
    padding-bottom: 48px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.window-picker.external-monitor {
 | 
			
		||||
    padding: 32px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.messages-indicator {
 | 
			
		||||
    color: #999999;
 | 
			
		||||
    height: 32px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.messages-indicator-contents {
 | 
			
		||||
    spacing: 12px;
 | 
			
		||||
    padding-bottom: 12px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.messages-indicator-contents:hover {
 | 
			
		||||
    color: white;
 | 
			
		||||
    text-shadow: black 0px 2px 2px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.messages-indicator-highlight {
 | 
			
		||||
    background-gradient-start: transparent;
 | 
			
		||||
    background-gradient-end: #999999;
 | 
			
		||||
    background-gradient-direction: vertical;
 | 
			
		||||
 | 
			
		||||
    height: 6px;
 | 
			
		||||
    -horizontal-spacing: 40px;
 | 
			
		||||
    -vertical-spacing: 40px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Dash */
 | 
			
		||||
@@ -800,14 +731,14 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
    height: 24px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.empty-dash-drop-target {
 | 
			
		||||
    width: 24px;
 | 
			
		||||
    height: 24px;
 | 
			
		||||
#viewSelector {
 | 
			
		||||
    spacing: 1em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Search Box */
 | 
			
		||||
 | 
			
		||||
#searchEntry {
 | 
			
		||||
    border-radius: 17px;
 | 
			
		||||
    width: 320px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -824,18 +755,15 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
/* Search Results */
 | 
			
		||||
 | 
			
		||||
#searchResults {
 | 
			
		||||
    padding: 20px 10px 0px 10px;
 | 
			
		||||
    spacing: 18px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#searchResultsBin {
 | 
			
		||||
    max-width: 1000px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#searchResultsContent {
 | 
			
		||||
    spacing: 16px;
 | 
			
		||||
 | 
			
		||||
    /* for scrollbars */
 | 
			
		||||
    padding-left: 20px;
 | 
			
		||||
    padding-right: 20px;
 | 
			
		||||
    spacing: 16px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.search-section {
 | 
			
		||||
@@ -866,6 +794,24 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
    spacing: 3px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Text labels are an odd number of pixels tall. The uneven top and bottom
 | 
			
		||||
 * padding compensates for this and ensures that the label is vertically
 | 
			
		||||
 * centered */
 | 
			
		||||
.dash-search-button {
 | 
			
		||||
    border-radius: 16px;
 | 
			
		||||
    padding-top: 4px;
 | 
			
		||||
    padding-bottom: 5px;
 | 
			
		||||
    width: 300px;
 | 
			
		||||
    font-weight: bold;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.dash-search-button:focus,
 | 
			
		||||
.dash-search-button:selected {
 | 
			
		||||
    padding-top: 3px;
 | 
			
		||||
    padding-bottom: 4px;
 | 
			
		||||
    width: 298px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.dash-label {
 | 
			
		||||
    border-radius: 7px;
 | 
			
		||||
    padding: 4px 12px;
 | 
			
		||||
@@ -886,28 +832,35 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
    icon-size: 96px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.app-display {
 | 
			
		||||
    padding: 8px;
 | 
			
		||||
.all-app {
 | 
			
		||||
    padding: 16px;
 | 
			
		||||
    spacing: 20px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.app-view-controls {
 | 
			
		||||
    padding-bottom: 32px;
 | 
			
		||||
.app-filter {
 | 
			
		||||
    font-weight: bold;
 | 
			
		||||
    height: 2.85em;
 | 
			
		||||
    color: #aaa;
 | 
			
		||||
    width: 200px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.app-view-control {
 | 
			
		||||
    padding: 4px 32px;
 | 
			
		||||
.app-filter:hover {
 | 
			
		||||
    color: #eee;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.search-display > StBoxLayout,
 | 
			
		||||
.all-apps > StBoxLayout,
 | 
			
		||||
.frequent-apps > StBoxLayout {
 | 
			
		||||
    /* horizontal padding to make sure scrollbars or dash don't overlap content */
 | 
			
		||||
    padding: 0px 88px;
 | 
			
		||||
.app-filter:selected {
 | 
			
		||||
    color: #ffffff;
 | 
			
		||||
    background-image: url("filter-selected-ltr.svg");
 | 
			
		||||
    background-position: 190px 10px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.app-folder-icon {
 | 
			
		||||
    padding: 5px;
 | 
			
		||||
.app-filter:selected:rtl {
 | 
			
		||||
    background-image: url("filter-selected-rtl.svg");
 | 
			
		||||
    background-position: 10px 10px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.app-filter:focus {
 | 
			
		||||
    outline: 1px solid #aaa;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.dash-item-container > StButton {
 | 
			
		||||
@@ -926,6 +879,8 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.list-search-result-description {
 | 
			
		||||
    font-weight: bold;
 | 
			
		||||
    font-size: 12pt;
 | 
			
		||||
    color: #eeeeec;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -943,31 +898,16 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
    border-radius: 4px;
 | 
			
		||||
    padding: 3px;
 | 
			
		||||
    border: 1px rgba(0,0,0,0);
 | 
			
		||||
    transition-duration: 100ms;
 | 
			
		||||
    transition-duration: 100;
 | 
			
		||||
    text-align: center;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.app-folder-popup {
 | 
			
		||||
    -arrow-border-radius: 8px;
 | 
			
		||||
    -arrow-background-color: black;
 | 
			
		||||
    -arrow-base: 24px;
 | 
			
		||||
    -arrow-rise: 11px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.app-folder-popup-bin {
 | 
			
		||||
    padding: 15px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.app-well-app.running > .overview-icon {
 | 
			
		||||
    text-shadow: black 0px 2px 2px;
 | 
			
		||||
    background-image: url("running-indicator.svg");
 | 
			
		||||
    background-size: contain;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.app-well-app.app-folder > .overview-icon {
 | 
			
		||||
    background-color: rgba(0,0,0,0.5);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.app-well-app:hover > .overview-icon,
 | 
			
		||||
.show-apps:hover > .overview-icon,
 | 
			
		||||
.search-provider-icon:hover,
 | 
			
		||||
@@ -975,18 +915,10 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
.grid-search-result:hover .overview-icon {
 | 
			
		||||
    background-color: rgba(255,255,255,0.1);
 | 
			
		||||
    text-shadow: black 0px 2px 2px;
 | 
			
		||||
    transition-duration: 100ms;
 | 
			
		||||
    transition-duration: 100;
 | 
			
		||||
    color:white;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.app-display .app-well-app > .overview-icon {
 | 
			
		||||
    border-radius: 10px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.list-search-result:hover .list-search-result-description {
 | 
			
		||||
    text-shadow: rgba(0,0,0,0.8) 0px 1px 2px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.show-apps {
 | 
			
		||||
    padding: 4px 0;
 | 
			
		||||
}
 | 
			
		||||
@@ -999,22 +931,19 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
    color: white;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.app-well-app:checked > .overview-icon,
 | 
			
		||||
.app-well-app:active > .overview-icon,
 | 
			
		||||
.show-apps:checked > .overview-icon,
 | 
			
		||||
.show-apps:active > .overview-icon {
 | 
			
		||||
.show-apps:checked > .overview-icon {
 | 
			
		||||
    background-gradient-start: rgba(255, 255, 255, .05);
 | 
			
		||||
    background-gradient-end: rgba(255, 255, 255, .15);
 | 
			
		||||
    background-gradient-direction: vertical;
 | 
			
		||||
    border-radius: 4px;
 | 
			
		||||
    box-shadow: inset 0px 1px 2px 0px rgba(0, 0, 0, 1);
 | 
			
		||||
    transition-duration: 100ms;
 | 
			
		||||
    transition-duration: 100;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.show-apps:checked .show-apps-icon,
 | 
			
		||||
.show-apps:focus .show-apps-icon {
 | 
			
		||||
    color: white;
 | 
			
		||||
    transition-duration: 100ms;
 | 
			
		||||
    transition-duration: 100;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.app-well-app:focus > .overview-icon,
 | 
			
		||||
@@ -1053,7 +982,7 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
    -minimum-hpadding: 6px;
 | 
			
		||||
    font-weight: bold;
 | 
			
		||||
    color: #ccc;
 | 
			
		||||
    transition-duration: 100ms;
 | 
			
		||||
    transition-duration: 100;
 | 
			
		||||
    padding-left: .3em;
 | 
			
		||||
    padding-right: .3em;
 | 
			
		||||
}
 | 
			
		||||
@@ -1277,15 +1206,15 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
    font-weight: bold;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.calendar-other-month-day {
 | 
			
		||||
    color: #333333;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.calendar-day-with-events {
 | 
			
		||||
    font-weight: bold;
 | 
			
		||||
    color: white;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.calendar-other-month-day {
 | 
			
		||||
    color: #333333;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.events-header-vbox {
 | 
			
		||||
    spacing: 6pt;
 | 
			
		||||
    padding-right: .5em;
 | 
			
		||||
@@ -1356,11 +1285,17 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
#message-tray {
 | 
			
		||||
    background: #2e3436 url(message-tray-background.png);
 | 
			
		||||
    background-repeat: repeat;
 | 
			
		||||
    height: 72px;
 | 
			
		||||
    transition-duration: 250;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.message-tray-summary {
 | 
			
		||||
    height: 72px;
 | 
			
		||||
#message-tray:keyboard {
 | 
			
		||||
    /* Same as the OSK */
 | 
			
		||||
    background: rgba(0, 0, 0, 0.8);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#message-tray:overview {
 | 
			
		||||
    background: rgba(0, 0, 0, 0.1);
 | 
			
		||||
    outline: 1px solid rgba(128, 128, 128, 0.3);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.no-messages-label {
 | 
			
		||||
@@ -1371,9 +1306,9 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
 | 
			
		||||
.notification {
 | 
			
		||||
    border-radius: 10px 10px 0px 0px;
 | 
			
		||||
    background: rgba(0,0,0,0.9);
 | 
			
		||||
    background: rgba(0,0,0,0.8);
 | 
			
		||||
    padding: 8px 8px 4px 8px;
 | 
			
		||||
    spacing-rows: 4px;
 | 
			
		||||
    spacing-rows: 10px;
 | 
			
		||||
    spacing-columns: 10px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1403,7 +1338,7 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
 | 
			
		||||
.summary-boxpointer {
 | 
			
		||||
    -arrow-border-radius: 15px;
 | 
			
		||||
    -arrow-background-color: rgba(0,0,0,0.9);
 | 
			
		||||
    -arrow-background-color: rgba(0,0,0,0.8);
 | 
			
		||||
    -arrow-base: 36px;
 | 
			
		||||
    -arrow-rise: 18px;
 | 
			
		||||
    color: white;
 | 
			
		||||
@@ -1453,18 +1388,16 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.notification-actions {
 | 
			
		||||
    padding-top: 18px;
 | 
			
		||||
    spacing: 10px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.notification-button {
 | 
			
		||||
    -st-natural-width: 140px;
 | 
			
		||||
    padding: 4px 4px 5px;
 | 
			
		||||
    border-radius: 18px;
 | 
			
		||||
    padding: 4px 42px 5px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.notification-button:focus {
 | 
			
		||||
    -st-natural-width: 138px;
 | 
			
		||||
    padding: 3px 4px 4px;
 | 
			
		||||
    padding: 3px 41px 4px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.notification-icon-button {
 | 
			
		||||
@@ -1492,6 +1425,7 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
 | 
			
		||||
.hotplug-notification-item {
 | 
			
		||||
    padding: 2px 10px;
 | 
			
		||||
    border-radius: 18px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.hotplug-notification-item:focus {
 | 
			
		||||
@@ -1592,6 +1526,10 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
    border-radius: 4px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#summary-mode {
 | 
			
		||||
    height: 72px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.summary-source-button {
 | 
			
		||||
    padding: 6px 3px 6px 3px;
 | 
			
		||||
}
 | 
			
		||||
@@ -1616,7 +1554,7 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
.summary-source {
 | 
			
		||||
    border-radius: 4px;
 | 
			
		||||
    padding: 0 6px 0 6px;
 | 
			
		||||
    transition-duration: 100ms;
 | 
			
		||||
    transition-duration: 100;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.summary-source-counter {
 | 
			
		||||
@@ -1630,26 +1568,12 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
    -shell-counter-overlap-y: 13px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* OSD */
 | 
			
		||||
.osd-window {
 | 
			
		||||
    text-align: center;
 | 
			
		||||
    font-weight: bold;
 | 
			
		||||
    spacing: 1em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.osd-window .level {
 | 
			
		||||
    height: 0.6em;
 | 
			
		||||
    border-radius: 0.3em;
 | 
			
		||||
    background-color: rgba(190,190,190,0.2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* App Switcher */
 | 
			
		||||
.switcher-popup {
 | 
			
		||||
    padding: 8px;
 | 
			
		||||
    spacing: 16px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.osd-window,
 | 
			
		||||
.switcher-list {
 | 
			
		||||
    background: rgba(0,0,0,0.8);
 | 
			
		||||
    border: 1px solid rgba(128,128,128,0.40);
 | 
			
		||||
@@ -1801,6 +1725,8 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.modal-dialog-button {
 | 
			
		||||
    border-radius: 18px;
 | 
			
		||||
 | 
			
		||||
    margin-left: 10px;
 | 
			
		||||
    margin-right: 10px;
 | 
			
		||||
    padding: 4px 32px 5px;
 | 
			
		||||
@@ -2151,53 +2077,17 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* IBus Candidate Popup */
 | 
			
		||||
 | 
			
		||||
.candidate-popup-content {
 | 
			
		||||
    padding: 0.5em;
 | 
			
		||||
    spacing: 0.3em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.candidate-index {
 | 
			
		||||
    padding: 0 0.5em 0 0;
 | 
			
		||||
    color: #cccccc;
 | 
			
		||||
    padding: 0.5em 0.5em 0.5em 0.5em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.candidate-box {
 | 
			
		||||
    padding: 0.3em 0.5em 0.3em 0.5em;
 | 
			
		||||
.candidate-label {
 | 
			
		||||
    padding: 0.5em 0.5em 0.5em 0.5em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.candidate-box:selected {
 | 
			
		||||
.candidate-label:selected {
 | 
			
		||||
    border-radius: 4px;
 | 
			
		||||
    background-color: rgba(255,255,255,0.2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.candidate-box:hover {
 | 
			
		||||
    border-radius: 4px;
 | 
			
		||||
    background-color: rgba(255,255,255,0.1);
 | 
			
		||||
}
 | 
			
		||||
.candidate-page-button-box {
 | 
			
		||||
    height: 2em;
 | 
			
		||||
    width: 80px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.vertical .candidate-page-button-box {
 | 
			
		||||
    padding-top: 0.5em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.horizontal .candidate-page-button-box {
 | 
			
		||||
    padding-left: 0.5em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.candidate-page-button-previous {
 | 
			
		||||
    border-radius: 4px 0px 0px 4px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.candidate-page-button-next {
 | 
			
		||||
    border-radius: 0px 4px 4px 0px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.candidate-page-button-icon {
 | 
			
		||||
    icon-size: 1em;
 | 
			
		||||
    background-color: rgba(255,255,255,0.33);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Login Dialog */
 | 
			
		||||
@@ -2222,9 +2112,6 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
    border: none;
 | 
			
		||||
    background-color: transparent;
 | 
			
		||||
 | 
			
		||||
    padding-bottom: 80px;
 | 
			
		||||
    padding-top: 80px;
 | 
			
		||||
 | 
			
		||||
    border-radius: 16px;
 | 
			
		||||
    min-height: 150px;
 | 
			
		||||
    max-height: 700px;
 | 
			
		||||
@@ -2342,47 +2229,52 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
    width: 15em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.login-dialog-session-list,
 | 
			
		||||
.login-dialog-session-list-item {
 | 
			
		||||
    color: #babdb6;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.login-dialog-session-list-button:focus,
 | 
			
		||||
.login-dialog-session-list-button:active,
 | 
			
		||||
.login-dialog-session-list-button:hover,
 | 
			
		||||
.login-dialog-session-list-item:focus,
 | 
			
		||||
.login-dialog-session-list-item:hover {
 | 
			
		||||
    color: white;
 | 
			
		||||
.login-dialog-session-list {
 | 
			
		||||
    color: #ffffff;
 | 
			
		||||
    font-size: 10.5pt;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.login-dialog-session-list-button {
 | 
			
		||||
    padding: 4px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.login-dialog-session-list-scroll-view {
 | 
			
		||||
    padding: 6px;
 | 
			
		||||
.login-dialog-session-list-button:focus {
 | 
			
		||||
    background-color: #4c4c4c;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.login-dialog-session-list-item {
 | 
			
		||||
    padding-bottom: 6px;
 | 
			
		||||
.login-dialog-session-list-button:active {
 | 
			
		||||
    background-color: #4c4c4c;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.login-dialog-session-list-button:hover {
 | 
			
		||||
    font-weight: bold;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.login-dialog-session-list-scroll-view {
 | 
			
		||||
    background-gradient-start: rgba(80,80,80,0.3);
 | 
			
		||||
    background-gradient-end: rgba(80,80,80,0.7);
 | 
			
		||||
    background-gradient-direction: vertical;
 | 
			
		||||
    box-shadow: inset 0px 2px 4px rgba(0,0,0,0.9);
 | 
			
		||||
    border-radius: 8px;
 | 
			
		||||
    border: 1px solid rgba(80,80,80,1.0);
 | 
			
		||||
    padding: .5em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.login-dialog-session-list-item:focus {
 | 
			
		||||
    background-color: #666666;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.login-dialog-session-list-triangle {
 | 
			
		||||
    padding-right: 6px;
 | 
			
		||||
    padding-right: .5em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.login-dialog-session-list-item-box {
 | 
			
		||||
    padding-left: 6px;
 | 
			
		||||
    spacing: 6px;
 | 
			
		||||
    spacing: .25em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.login-dialog-session-list-item-dot {
 | 
			
		||||
    width: 10px;
 | 
			
		||||
    height: 10px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.login-dialog-logo-bin {
 | 
			
		||||
    padding: 24px 0px;
 | 
			
		||||
    width: .75em;
 | 
			
		||||
    height: .75em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.login-dialog .modal-dialog-button-box {
 | 
			
		||||
@@ -2431,22 +2323,15 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
    color: orange;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.user-widget {
 | 
			
		||||
.unlock-dialog-user-name-container {
 | 
			
		||||
    spacing: .4em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.user-widget-label {
 | 
			
		||||
    font-size: 16pt;
 | 
			
		||||
    font-weight: bold;
 | 
			
		||||
    text-align: left;
 | 
			
		||||
    padding-left: 15px;
 | 
			
		||||
    text-shadow: black 0px 4px 3px 0px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Screen shield */
 | 
			
		||||
 | 
			
		||||
.screen-shield-background {
 | 
			
		||||
    background: black;
 | 
			
		||||
    box-shadow: 0px 4px 8px rgba(0,0,0,0.9);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#lockDialogGroup {
 | 
			
		||||
@@ -2498,7 +2383,6 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
 | 
			
		||||
.screen-shield-notifications-box {
 | 
			
		||||
    spacing: 18px;
 | 
			
		||||
    max-width: 34em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.screen-shield-notification-source {
 | 
			
		||||
@@ -2532,14 +2416,3 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
    padding-bottom: 0px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.input-source-switcher-symbol {
 | 
			
		||||
   font-size: 34pt;
 | 
			
		||||
   width: 96px;
 | 
			
		||||
   height: 96px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Background menu */
 | 
			
		||||
 | 
			
		||||
.background-menu {
 | 
			
		||||
    -boxpointer-gap: 4px;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,7 @@
 | 
			
		||||
   height="16"
 | 
			
		||||
   id="svg12430"
 | 
			
		||||
   version="1.1"
 | 
			
		||||
   inkscape:version="0.48.3.1 r9886"
 | 
			
		||||
   inkscape:version="0.48+devel r11908 custom"
 | 
			
		||||
   sodipodi:docname="more-results.svg">
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs12432" />
 | 
			
		||||
@@ -26,8 +26,8 @@
 | 
			
		||||
     inkscape:pageopacity="1"
 | 
			
		||||
     inkscape:pageshadow="2"
 | 
			
		||||
     inkscape:zoom="1"
 | 
			
		||||
     inkscape:cx="8.3155237"
 | 
			
		||||
     inkscape:cy="0.89548874"
 | 
			
		||||
     inkscape:cx="7.4498765"
 | 
			
		||||
     inkscape:cy="9.9072581"
 | 
			
		||||
     inkscape:document-units="px"
 | 
			
		||||
     inkscape:current-layer="g14642-3-0"
 | 
			
		||||
     showgrid="false"
 | 
			
		||||
@@ -35,8 +35,8 @@
 | 
			
		||||
     inkscape:showpageshadow="false"
 | 
			
		||||
     inkscape:window-width="2560"
 | 
			
		||||
     inkscape:window-height="1376"
 | 
			
		||||
     inkscape:window-x="1200"
 | 
			
		||||
     inkscape:window-y="187"
 | 
			
		||||
     inkscape:window-x="1600"
 | 
			
		||||
     inkscape:window-y="27"
 | 
			
		||||
     inkscape:window-maximized="1">
 | 
			
		||||
    <inkscape:grid
 | 
			
		||||
       type="xygrid"
 | 
			
		||||
@@ -50,7 +50,7 @@
 | 
			
		||||
        <dc:format>image/svg+xml</dc:format>
 | 
			
		||||
        <dc:type
 | 
			
		||||
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
 | 
			
		||||
        <dc:title />
 | 
			
		||||
        <dc:title></dc:title>
 | 
			
		||||
      </cc:Work>
 | 
			
		||||
    </rdf:RDF>
 | 
			
		||||
  </metadata>
 | 
			
		||||
@@ -69,27 +69,11 @@
 | 
			
		||||
         inkscape:radius="0"
 | 
			
		||||
         inkscape:original="M 145.1875 400 C 144.5248 400 144 400.54899 144 401.21875 L 144 410.78125 C 144 411.45101 144.5248 412 145.1875 412 L 154.8125 412 C 155.4752 412 156 411.45101 156 410.78125 L 156 401.21875 C 156 400.54899 155.4752 400 154.8125 400 L 145.1875 400 z M 149 403 L 151 403 L 151 405 L 153 405 L 153 407 L 151 407 L 151 409 L 149 409 L 149 407 L 147 407 L 147 405 L 149 405 L 149 403 z "
 | 
			
		||||
         xlink:href="#rect11749-5-0-1-8"
 | 
			
		||||
         style="color:#bebebe;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;opacity:0.8"
 | 
			
		||||
         style="color:#bebebe;fill:#000000;fill-opacity:0.38207546;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible"
 | 
			
		||||
         id="path13004"
 | 
			
		||||
         inkscape:href="#rect11749-5-0-1-8"
 | 
			
		||||
         d="M 145.1875,400 C 144.5248,400 144,400.54899 144,401.21875 l 0,9.5625 c 0,0.66976 0.5248,1.21875 1.1875,1.21875 l 9.625,0 c 0.6627,0 1.1875,-0.54899 1.1875,-1.21875 l 0,-9.5625 C 156,400.54899 155.4752,400 154.8125,400 L 145.1875,400 z m 3.8125,3 2,0 0,2 2,0 0,2 -2,0 0,2 -2,0 0,-2 -2,0 0,-2 2,0 L 149,403 Z"
 | 
			
		||||
         transform="translate(0,1)" />
 | 
			
		||||
      <use
 | 
			
		||||
         x="0"
 | 
			
		||||
         y="0"
 | 
			
		||||
         xlink:href="#path13004"
 | 
			
		||||
         id="use11960"
 | 
			
		||||
         transform="translate(1,-1)"
 | 
			
		||||
         width="16"
 | 
			
		||||
         height="16" />
 | 
			
		||||
      <use
 | 
			
		||||
         x="0"
 | 
			
		||||
         y="0"
 | 
			
		||||
         xlink:href="#use11960"
 | 
			
		||||
         id="use11962"
 | 
			
		||||
         transform="translate(-2,0)"
 | 
			
		||||
         width="16"
 | 
			
		||||
         height="16" />
 | 
			
		||||
      <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.1 KiB After Width: | Height: | Size: 3.7 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								data/wanda.png
									
									
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								data/wanda.png
									
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 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
 | 
			
		||||
 
 | 
			
		||||
@@ -46,11 +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"/>
 | 
			
		||||
 
 | 
			
		||||
@@ -28,7 +28,6 @@ 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	\
 | 
			
		||||
@@ -39,11 +38,10 @@ nobase_dist_js_DATA = 	\
 | 
			
		||||
	ui/altTab.js		\
 | 
			
		||||
	ui/appDisplay.js	\
 | 
			
		||||
	ui/appFavorites.js	\
 | 
			
		||||
	ui/backgroundMenu.js	\
 | 
			
		||||
	ui/background.js	\
 | 
			
		||||
	ui/boxpointer.js	\
 | 
			
		||||
	ui/calendar.js		\
 | 
			
		||||
	ui/checkBox.js		\
 | 
			
		||||
	ui/centerLayout.js	\
 | 
			
		||||
	ui/ctrlAltTab.js	\
 | 
			
		||||
	ui/dash.js		\
 | 
			
		||||
	ui/dateMenu.js		\
 | 
			
		||||
@@ -52,6 +50,7 @@ nobase_dist_js_DATA = 	\
 | 
			
		||||
	ui/extensionSystem.js	\
 | 
			
		||||
	ui/extensionDownloader.js \
 | 
			
		||||
	ui/environment.js	\
 | 
			
		||||
	ui/flashspot.js		\
 | 
			
		||||
	ui/ibusCandidatePopup.js\
 | 
			
		||||
	ui/grabHelper.js	\
 | 
			
		||||
	ui/iconGrid.js		\
 | 
			
		||||
@@ -69,17 +68,13 @@ nobase_dist_js_DATA = 	\
 | 
			
		||||
	ui/shellEntry.js	\
 | 
			
		||||
	ui/shellMountOperation.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/runDialog.js		\
 | 
			
		||||
	ui/screencast.js	\
 | 
			
		||||
	ui/screenshot.js	\
 | 
			
		||||
        ui/screenShield.js	\
 | 
			
		||||
	ui/scripting.js		\
 | 
			
		||||
	ui/search.js		\
 | 
			
		||||
@@ -96,7 +91,6 @@ nobase_dist_js_DATA = 	\
 | 
			
		||||
	ui/tweener.js		\
 | 
			
		||||
	ui/unlockDialog.js	\
 | 
			
		||||
	ui/userMenu.js		\
 | 
			
		||||
	ui/userWidget.js	\
 | 
			
		||||
	ui/viewSelector.js	\
 | 
			
		||||
	ui/wanda.js		\
 | 
			
		||||
	ui/windowAttentionHandler.js	\
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -28,7 +28,6 @@ const Mainloop = imports.mainloop;
 | 
			
		||||
const Meta = imports.gi.Meta;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Pango = imports.gi.Pango;
 | 
			
		||||
const Realmd = imports.gdm.realmd;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
@@ -40,18 +39,85 @@ const GdmUtil = imports.gdm.util;
 | 
			
		||||
const Lightbox = imports.ui.lightbox;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const ModalDialog = imports.ui.modalDialog;
 | 
			
		||||
const Panel = imports.ui.panel;
 | 
			
		||||
const PanelMenu = imports.ui.panelMenu;
 | 
			
		||||
const Tweener = imports.ui.tweener;
 | 
			
		||||
const UserMenu = imports.ui.userMenu;
 | 
			
		||||
const UserWidget = imports.ui.userWidget;
 | 
			
		||||
 | 
			
		||||
const _FADE_ANIMATION_TIME = 0.25;
 | 
			
		||||
const _RESIZE_ANIMATION_TIME = 0.25;
 | 
			
		||||
const _SCROLL_ANIMATION_TIME = 0.5;
 | 
			
		||||
const _TIMED_LOGIN_IDLE_THRESHOLD = 5.0;
 | 
			
		||||
const _LOGO_ICON_HEIGHT = 48;
 | 
			
		||||
const _LOGO_ICON_HEIGHT = 16;
 | 
			
		||||
 | 
			
		||||
const WORK_SPINNER_ICON_SIZE = 24;
 | 
			
		||||
const WORK_SPINNER_ANIMATION_DELAY = 1.0;
 | 
			
		||||
const WORK_SPINNER_ANIMATION_TIME = 0.3;
 | 
			
		||||
 | 
			
		||||
let _loginDialog = null;
 | 
			
		||||
 | 
			
		||||
function _smoothlyResizeActor(actor, width, height) {
 | 
			
		||||
    let finalWidth;
 | 
			
		||||
    let finalHeight;
 | 
			
		||||
 | 
			
		||||
    if (width < 0)
 | 
			
		||||
        finalWidth = actor.width;
 | 
			
		||||
    else
 | 
			
		||||
        finalWidth = width;
 | 
			
		||||
 | 
			
		||||
    if (height < 0)
 | 
			
		||||
        finalHeight = actor.height;
 | 
			
		||||
    else
 | 
			
		||||
        finalHeight = height;
 | 
			
		||||
 | 
			
		||||
    actor.set_size(actor.width, actor.height);
 | 
			
		||||
 | 
			
		||||
    if (actor.width == finalWidth && actor.height == finalHeight)
 | 
			
		||||
        return null;
 | 
			
		||||
 | 
			
		||||
    let hold = new Batch.Hold();
 | 
			
		||||
 | 
			
		||||
    Tweener.addTween(actor,
 | 
			
		||||
                     { width: finalWidth,
 | 
			
		||||
                       height: finalHeight,
 | 
			
		||||
                       time: _RESIZE_ANIMATION_TIME,
 | 
			
		||||
                       transition: 'easeOutQuad',
 | 
			
		||||
                       onComplete: Lang.bind(this, function() {
 | 
			
		||||
                                       hold.release();
 | 
			
		||||
                                   })
 | 
			
		||||
                     });
 | 
			
		||||
    return hold;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const LogoMenuButton = new Lang.Class({
 | 
			
		||||
    Name: 'LogoMenuButton',
 | 
			
		||||
    Extends: PanelMenu.Button,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.parent(0.0, null, true);
 | 
			
		||||
 | 
			
		||||
        this._settings = new Gio.Settings({ schema: GdmUtil.LOGIN_SCREEN_SCHEMA });
 | 
			
		||||
        this._settings.connect('changed::' + GdmUtil.LOGO_KEY,
 | 
			
		||||
                               Lang.bind(this, this._updateLogo));
 | 
			
		||||
 | 
			
		||||
        this._iconBin = new St.Bin();
 | 
			
		||||
        this.actor.add_actor(this._iconBin);
 | 
			
		||||
 | 
			
		||||
        this._updateLogo();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateLogo: function() {
 | 
			
		||||
        let path = this._settings.get_string(GdmUtil.LOGO_KEY);
 | 
			
		||||
        let icon = null;
 | 
			
		||||
 | 
			
		||||
        if (path) {
 | 
			
		||||
            let file = Gio.file_new_for_path(path);
 | 
			
		||||
            let cache = St.TextureCache.get_default();
 | 
			
		||||
            icon = cache.load_uri_async(file.get_uri(), -1, _LOGO_ICON_HEIGHT);
 | 
			
		||||
        }
 | 
			
		||||
        this._iconBin.set_child(icon);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const UserListItem = new Lang.Class({
 | 
			
		||||
    Name: 'UserListItem',
 | 
			
		||||
 | 
			
		||||
@@ -62,7 +128,6 @@ const UserListItem = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        let layout = new St.BoxLayout({ vertical: false });
 | 
			
		||||
        this.actor = new St.Button({ style_class: 'login-dialog-user-list-item',
 | 
			
		||||
                                     button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
 | 
			
		||||
                                     can_focus: true,
 | 
			
		||||
                                     child: layout,
 | 
			
		||||
                                     reactive: true,
 | 
			
		||||
@@ -180,23 +245,155 @@ const UserList = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _showItem: function(item) {
 | 
			
		||||
        let tasks = [function() {
 | 
			
		||||
                         return GdmUtil.fadeInActor(item.actor);
 | 
			
		||||
                     }];
 | 
			
		||||
 | 
			
		||||
        let batch = new Batch.ConsecutiveBatch(this, tasks);
 | 
			
		||||
        return batch.run();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onItemActivated: function(activatedItem) {
 | 
			
		||||
        this.emit('activate', activatedItem);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    updateStyle: function(isExpanded) {
 | 
			
		||||
    giveUpWhitespace: function() {
 | 
			
		||||
        let container = this.actor.get_parent();
 | 
			
		||||
 | 
			
		||||
        container.child_set(this.actor, { expand: false });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    takeOverWhitespace: function() {
 | 
			
		||||
        let container = this.actor.get_parent();
 | 
			
		||||
 | 
			
		||||
        container.child_set(this.actor, { expand: true });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    pinInPlace: function() {
 | 
			
		||||
        this._box.set_size(this._box.width, this._box.height);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    shrinkToNaturalHeight: function() {
 | 
			
		||||
        let oldWidth = this._box.width;
 | 
			
		||||
        let oldHeight = this._box.height;
 | 
			
		||||
        this._box.set_size(-1, -1);
 | 
			
		||||
        let [minHeight, naturalHeight] = this._box.get_preferred_height(-1);
 | 
			
		||||
        this._box.set_size(oldWidth, oldHeight);
 | 
			
		||||
 | 
			
		||||
        let batch = new Batch.ConsecutiveBatch(this,
 | 
			
		||||
                                               [function() {
 | 
			
		||||
                                                    return _smoothlyResizeActor(this._box, -1, naturalHeight);
 | 
			
		||||
                                                },
 | 
			
		||||
 | 
			
		||||
                                                function() {
 | 
			
		||||
                                                    this._box.set_size(-1, -1);
 | 
			
		||||
                                                }
 | 
			
		||||
                                               ]);
 | 
			
		||||
 | 
			
		||||
        return batch.run();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    hideItemsExcept: function(exception) {
 | 
			
		||||
        let tasks = [];
 | 
			
		||||
 | 
			
		||||
        if (isExpanded)
 | 
			
		||||
            this._box.add_style_pseudo_class('expanded');
 | 
			
		||||
        else
 | 
			
		||||
            this._box.remove_style_pseudo_class('expanded');
 | 
			
		||||
        for (let userName in this._items) {
 | 
			
		||||
            let item = this._items[userName];
 | 
			
		||||
 | 
			
		||||
            item.actor.set_hover(false);
 | 
			
		||||
            item.actor.reactive = false;
 | 
			
		||||
            item.actor.can_focus = false;
 | 
			
		||||
            item.syncStyleClasses();
 | 
			
		||||
            item._timedLoginIndicator.scale_x = 0.;
 | 
			
		||||
            if (item != exception)
 | 
			
		||||
                tasks.push(function() {
 | 
			
		||||
                    return GdmUtil.fadeOutActor(item.actor);
 | 
			
		||||
                });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let batch = new Batch.ConsecutiveBatch(this,
 | 
			
		||||
                                               [function() {
 | 
			
		||||
                                                    return GdmUtil.fadeOutActor(this.actor.vscroll);
 | 
			
		||||
                                                },
 | 
			
		||||
 | 
			
		||||
                                                new Batch.ConcurrentBatch(this, tasks),
 | 
			
		||||
 | 
			
		||||
                                                function() {
 | 
			
		||||
                                                    this._box.remove_style_pseudo_class('expanded');
 | 
			
		||||
                                                }
 | 
			
		||||
                                               ]);
 | 
			
		||||
 | 
			
		||||
        return batch.run();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    hideItems: function() {
 | 
			
		||||
        return this.hideItemsExcept(null);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getExpandedHeight: function() {
 | 
			
		||||
        let hiddenActors = [];
 | 
			
		||||
        for (let userName in this._items) {
 | 
			
		||||
            let item = this._items[userName];
 | 
			
		||||
            if (!item.actor.visible) {
 | 
			
		||||
                item.actor.show();
 | 
			
		||||
                hiddenActors.push(item.actor);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!this._box.visible) {
 | 
			
		||||
            this._box.show();
 | 
			
		||||
            hiddenActors.push(this._box);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._box.set_size(-1, -1);
 | 
			
		||||
        let [minHeight, naturalHeight] = this._box.get_preferred_height(-1);
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < hiddenActors.length; i++) {
 | 
			
		||||
            let actor = hiddenActors[i];
 | 
			
		||||
            actor.hide();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return naturalHeight;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    showItems: function() {
 | 
			
		||||
        let tasks = [];
 | 
			
		||||
 | 
			
		||||
        for (let userName in this._items) {
 | 
			
		||||
            let item = this._items[userName];
 | 
			
		||||
            item.actor.sync_hover();
 | 
			
		||||
            item.actor.reactive = true;
 | 
			
		||||
            item.actor.can_focus = true;
 | 
			
		||||
            item.syncStyleClasses();
 | 
			
		||||
            tasks.push(function() {
 | 
			
		||||
                return this._showItem(item);
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let batch = new Batch.ConsecutiveBatch(this,
 | 
			
		||||
                                               [function() {
 | 
			
		||||
                                                    this.takeOverWhitespace();
 | 
			
		||||
                                                },
 | 
			
		||||
 | 
			
		||||
                                                function() {
 | 
			
		||||
                                                    let fullHeight = this._getExpandedHeight();
 | 
			
		||||
                                                    return _smoothlyResizeActor(this._box, -1, fullHeight);
 | 
			
		||||
                                                },
 | 
			
		||||
 | 
			
		||||
                                                function() {
 | 
			
		||||
                                                    this._box.add_style_pseudo_class('expanded');
 | 
			
		||||
                                                },
 | 
			
		||||
 | 
			
		||||
                                                new Batch.ConcurrentBatch(this, tasks),
 | 
			
		||||
 | 
			
		||||
                                                function() {
 | 
			
		||||
                                                    this.actor.set_size(-1, -1);
 | 
			
		||||
                                                },
 | 
			
		||||
 | 
			
		||||
                                                function() {
 | 
			
		||||
                                                    return GdmUtil.fadeInActor(this.actor.vscroll);
 | 
			
		||||
                                                }]);
 | 
			
		||||
        return batch.run();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    scrollToItem: function(item) {
 | 
			
		||||
@@ -295,7 +492,6 @@ const SessionListItem = new Lang.Class({
 | 
			
		||||
        this.id = id;
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.Button({ style_class: 'login-dialog-session-list-item',
 | 
			
		||||
                                     button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
 | 
			
		||||
                                     can_focus: true,
 | 
			
		||||
                                     reactive: true,
 | 
			
		||||
                                     x_fill: true,
 | 
			
		||||
@@ -336,7 +532,6 @@ const SessionListItem = new Lang.Class({
 | 
			
		||||
                          color.alpha / 255);
 | 
			
		||||
        cr.arc(width / 2, height / 2, width / 3, 0, 2 * Math.PI);
 | 
			
		||||
        cr.fill();
 | 
			
		||||
        cr.$dispose();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onClicked: function() {
 | 
			
		||||
@@ -356,7 +551,6 @@ const SessionList = new Lang.Class({
 | 
			
		||||
        this.actor.child = this._box;
 | 
			
		||||
 | 
			
		||||
        this._button = new St.Button({ style_class: 'login-dialog-session-list-button',
 | 
			
		||||
                                       button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
 | 
			
		||||
                                       can_focus: true,
 | 
			
		||||
                                       x_fill: true,
 | 
			
		||||
                                       y_fill: true });
 | 
			
		||||
@@ -368,7 +562,7 @@ const SessionList = new Lang.Class({
 | 
			
		||||
        box.add_actor(this._triangle);
 | 
			
		||||
 | 
			
		||||
        let label = new St.Label({ style_class: 'login-dialog-session-list-label',
 | 
			
		||||
                                   text: _("Session…") });
 | 
			
		||||
                                   text: _("Session...") });
 | 
			
		||||
        box.add_actor(label);
 | 
			
		||||
 | 
			
		||||
        this._button.connect('clicked',
 | 
			
		||||
@@ -479,7 +673,7 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
        this.parent({ shellReactive: true,
 | 
			
		||||
                      styleClass: 'login-dialog',
 | 
			
		||||
                      parentActor: parentActor,
 | 
			
		||||
                      keybindingMode: Shell.KeyBindingMode.LOGIN_SCREEN,
 | 
			
		||||
                      keybindingMode: Main.KeybindingMode.LOGIN_SCREEN,
 | 
			
		||||
                      shouldFadeIn: false });
 | 
			
		||||
        this.connect('destroy',
 | 
			
		||||
                     Lang.bind(this, this._onDestroy));
 | 
			
		||||
@@ -518,27 +712,25 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
                               Lang.bind(this, this._updateBanner));
 | 
			
		||||
        this._settings.connect('changed::' + GdmUtil.DISABLE_USER_LIST_KEY,
 | 
			
		||||
                               Lang.bind(this, this._updateDisableUserList));
 | 
			
		||||
        this._settings.connect('changed::' + GdmUtil.LOGO_KEY,
 | 
			
		||||
                               Lang.bind(this, this._updateLogo));
 | 
			
		||||
 | 
			
		||||
        this._textureCache = St.TextureCache.get_default();
 | 
			
		||||
        this._textureCache.connect('texture-file-changed',
 | 
			
		||||
                                   Lang.bind(this, this._updateLogoTexture));
 | 
			
		||||
 | 
			
		||||
        this._userSelectionBox = new St.BoxLayout({ style_class: 'login-dialog-user-selection-box',
 | 
			
		||||
                                                    vertical: true });
 | 
			
		||||
        this.contentLayout.add(this._userSelectionBox);
 | 
			
		||||
 | 
			
		||||
        this._bannerLabel = new St.Label({ style_class: 'login-dialog-banner',
 | 
			
		||||
                                           text: '' });
 | 
			
		||||
        this._userSelectionBox.add(this._bannerLabel);
 | 
			
		||||
        this.contentLayout.add(this._bannerLabel);
 | 
			
		||||
        this._updateBanner();
 | 
			
		||||
 | 
			
		||||
        this._titleLabel = new St.Label({ style_class: 'login-dialog-title',
 | 
			
		||||
                                          text: C_("title", "Sign In"),
 | 
			
		||||
                                          visible: false });
 | 
			
		||||
 | 
			
		||||
        this.contentLayout.add(this._titleLabel,
 | 
			
		||||
                              { y_fill: false,
 | 
			
		||||
                                y_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        this._userList = new UserList();
 | 
			
		||||
        this._userSelectionBox.add(this._userList.actor,
 | 
			
		||||
                                   { expand: true,
 | 
			
		||||
                                     x_fill: true,
 | 
			
		||||
                                     y_fill: true });
 | 
			
		||||
        this.contentLayout.add(this._userList.actor,
 | 
			
		||||
                               { expand: true,
 | 
			
		||||
                                 x_fill: true,
 | 
			
		||||
                                 y_fill: true });
 | 
			
		||||
 | 
			
		||||
        this.setInitialKeyFocus(this._userList.actor);
 | 
			
		||||
 | 
			
		||||
@@ -549,13 +741,6 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
                                 x_fill: true,
 | 
			
		||||
                                 y_fill: true,
 | 
			
		||||
                                 x_align: St.Align.START });
 | 
			
		||||
        this._promptUser = new St.Bin({ x_fill: true,
 | 
			
		||||
                                        x_align: St.Align.START });
 | 
			
		||||
        this._promptBox.add(this._promptUser,
 | 
			
		||||
                            { x_align: St.Align.START,
 | 
			
		||||
                              x_fill: true,
 | 
			
		||||
                              y_fill: true,
 | 
			
		||||
                              expand: true });
 | 
			
		||||
        this._promptLabel = new St.Label({ style_class: 'login-dialog-prompt-label' });
 | 
			
		||||
 | 
			
		||||
        this._promptBox.add(this._promptLabel,
 | 
			
		||||
@@ -566,7 +751,6 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
        this._promptEntry = new St.Entry({ style_class: 'login-dialog-prompt-entry',
 | 
			
		||||
                                           can_focus: true });
 | 
			
		||||
        this._promptEntryTextChangedId = 0;
 | 
			
		||||
        this._promptEntryActivateId = 0;
 | 
			
		||||
        this._promptBox.add(this._promptEntry,
 | 
			
		||||
                            { expand: true,
 | 
			
		||||
                              x_fill: true,
 | 
			
		||||
@@ -581,6 +765,7 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
        this._promptBox.add(this._promptLoginHint);
 | 
			
		||||
 | 
			
		||||
        this._signInButton = null;
 | 
			
		||||
        this._workSpinner = null;
 | 
			
		||||
 | 
			
		||||
        this._sessionList = new SessionList();
 | 
			
		||||
        this._sessionList.connect('session-activated',
 | 
			
		||||
@@ -601,7 +786,6 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
        let notListedLabel = new St.Label({ text: _("Not listed?"),
 | 
			
		||||
                                            style_class: 'login-dialog-not-listed-label' });
 | 
			
		||||
        this._notListedButton = new St.Button({ style_class: 'login-dialog-not-listed-button',
 | 
			
		||||
                                                button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
 | 
			
		||||
                                                can_focus: true,
 | 
			
		||||
                                                child: notListedLabel,
 | 
			
		||||
                                                reactive: true,
 | 
			
		||||
@@ -610,15 +794,10 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._notListedButton.connect('clicked', Lang.bind(this, this._hideUserListAndLogIn));
 | 
			
		||||
 | 
			
		||||
        this._userSelectionBox.add(this._notListedButton,
 | 
			
		||||
                                   { expand: false,
 | 
			
		||||
                                     x_align: St.Align.START,
 | 
			
		||||
                                     x_fill: true });
 | 
			
		||||
 | 
			
		||||
        this._logoBin = new St.Bin({ style_class: 'login-dialog-logo-bin', y_expand: true });
 | 
			
		||||
        this._logoBin.set_y_align(Clutter.ActorAlign.END);
 | 
			
		||||
        this.backgroundStack.add_actor(this._logoBin);
 | 
			
		||||
        this._updateLogo();
 | 
			
		||||
        this.contentLayout.add(this._notListedButton,
 | 
			
		||||
                               { expand: false,
 | 
			
		||||
                                 x_align: St.Align.START,
 | 
			
		||||
                                 x_fill: true });
 | 
			
		||||
 | 
			
		||||
        if (!this._userManager.is_loaded)
 | 
			
		||||
            this._userManagerLoadedId = this._userManager.connect('notify::is-loaded',
 | 
			
		||||
@@ -660,33 +839,13 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        if (enabled && text) {
 | 
			
		||||
            this._bannerLabel.set_text(text);
 | 
			
		||||
            this._bannerLabel.show();
 | 
			
		||||
            this._fadeInBanner();
 | 
			
		||||
        } else {
 | 
			
		||||
            this._bannerLabel.hide();
 | 
			
		||||
            this._fadeOutBanner();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateLogoTexture: function(cache, uri) {
 | 
			
		||||
        if (this._logoFileUri != uri)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let icon = null;
 | 
			
		||||
        if (this._logoFileUri)
 | 
			
		||||
            icon = this._textureCache.load_uri_async(this._logoFileUri,
 | 
			
		||||
                                                     -1, _LOGO_ICON_HEIGHT);
 | 
			
		||||
        this._logoBin.set_child(icon);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateLogo: function() {
 | 
			
		||||
        let path = this._settings.get_string(GdmUtil.LOGO_KEY);
 | 
			
		||||
 | 
			
		||||
        this._logoFileUri = path ? Gio.file_new_for_path(path).get_uri() : null;
 | 
			
		||||
        this._updateLogoTexture(this._textureCache, this._logoFileUri);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _reset: function() {
 | 
			
		||||
        this._userVerifier.clear();
 | 
			
		||||
 | 
			
		||||
        this._updateSensitivity(true);
 | 
			
		||||
        this._promptMessage.hide();
 | 
			
		||||
        this._user = null;
 | 
			
		||||
@@ -702,7 +861,7 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
        this._promptEntry.text = '';
 | 
			
		||||
 | 
			
		||||
        this._updateSensitivity(true);
 | 
			
		||||
        this.setWorking(false);
 | 
			
		||||
        this._setWorking(false);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDefaultSessionChanged: function(client, sessionId) {
 | 
			
		||||
@@ -713,50 +872,73 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
        if (message) {
 | 
			
		||||
            this._promptMessage.text = message;
 | 
			
		||||
            this._promptMessage.styleClass = styleClass;
 | 
			
		||||
            this._promptMessage.show();
 | 
			
		||||
            GdmUtil.fadeInActor(this._promptMessage);
 | 
			
		||||
        } else {
 | 
			
		||||
            this._promptMessage.hide();
 | 
			
		||||
            GdmUtil.fadeOutActor(this._promptMessage);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _showLoginHint: function(verifier, message) {
 | 
			
		||||
        this._promptLoginHint.set_text(message)
 | 
			
		||||
        this._promptLoginHint.show();
 | 
			
		||||
        this._promptLoginHint.opacity = 255;
 | 
			
		||||
        GdmUtil.fadeInActor(this._promptLoginHint);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _hideLoginHint: function() {
 | 
			
		||||
        this._promptLoginHint.hide();
 | 
			
		||||
        GdmUtil.fadeOutActor(this._promptLoginHint);
 | 
			
		||||
        this._promptLoginHint.set_text('');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    cancel: function() {
 | 
			
		||||
        if (this._verifyingUser)
 | 
			
		||||
            this._userVerifier.cancel();
 | 
			
		||||
        else
 | 
			
		||||
            this._reset();
 | 
			
		||||
        this._userVerifier.cancel();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _fadeInPrompt: function() {
 | 
			
		||||
        let tasks = [function() {
 | 
			
		||||
                         return GdmUtil.fadeInActor(this._promptLabel);
 | 
			
		||||
                     },
 | 
			
		||||
 | 
			
		||||
                     function() {
 | 
			
		||||
                         return GdmUtil.fadeInActor(this._promptEntry);
 | 
			
		||||
                     },
 | 
			
		||||
 | 
			
		||||
                     function() {
 | 
			
		||||
                         // Show it with 0 opacity so we preallocate space for it
 | 
			
		||||
                         // in the event we need to fade in the message
 | 
			
		||||
                         this._promptLoginHint.opacity = 0;
 | 
			
		||||
                         this._promptLoginHint.show();
 | 
			
		||||
                     },
 | 
			
		||||
 | 
			
		||||
                     function() {
 | 
			
		||||
                         return GdmUtil.fadeInActor(this._promptBox);
 | 
			
		||||
                     },
 | 
			
		||||
 | 
			
		||||
                     function() {
 | 
			
		||||
                         if (this._user && this._user.is_logged_in())
 | 
			
		||||
                             return null;
 | 
			
		||||
 | 
			
		||||
                         if (!this._verifyingUser)
 | 
			
		||||
                             return null;
 | 
			
		||||
 | 
			
		||||
                         return GdmUtil.fadeInActor(this._sessionList.actor);
 | 
			
		||||
                     },
 | 
			
		||||
 | 
			
		||||
                     function() {
 | 
			
		||||
                         this._promptEntry.grab_key_focus();
 | 
			
		||||
                     }];
 | 
			
		||||
 | 
			
		||||
        this._sessionList.actor.hide();
 | 
			
		||||
        let batch = new Batch.ConcurrentBatch(this, tasks);
 | 
			
		||||
        return batch.run();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _showPrompt: function(forSecret) {
 | 
			
		||||
        this._sessionList.actor.hide();
 | 
			
		||||
        this._promptLabel.show();
 | 
			
		||||
        this._promptEntry.show();
 | 
			
		||||
        this._promptLoginHint.opacity = 0;
 | 
			
		||||
        this._promptLoginHint.show();
 | 
			
		||||
        this._promptBox.opacity = 0;
 | 
			
		||||
        this._promptBox.show();
 | 
			
		||||
        Tweener.addTween(this._promptBox,
 | 
			
		||||
                         { opacity: 255,
 | 
			
		||||
                           time: _FADE_ANIMATION_TIME,
 | 
			
		||||
                           transition: 'easeOutQuad' });
 | 
			
		||||
 | 
			
		||||
        if ((this._user && !this._user.is_logged_in()) || this._verifyingUser)
 | 
			
		||||
            this._sessionList.actor.show();
 | 
			
		||||
 | 
			
		||||
        this._promptEntry.grab_key_focus();
 | 
			
		||||
 | 
			
		||||
        let hold = new Batch.Hold();
 | 
			
		||||
 | 
			
		||||
        let tasks = [function() {
 | 
			
		||||
                         return this._fadeInPrompt();
 | 
			
		||||
                     },
 | 
			
		||||
 | 
			
		||||
                     function() {
 | 
			
		||||
                         this._prepareDialog(forSecret, hold);
 | 
			
		||||
                     },
 | 
			
		||||
 | 
			
		||||
@@ -768,6 +950,10 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _prepareDialog: function(forSecret, hold) {
 | 
			
		||||
        this._workSpinner = new Panel.AnimatedIcon('process-working.svg', WORK_SPINNER_ICON_SIZE);
 | 
			
		||||
        this._workSpinner.actor.opacity = 0;
 | 
			
		||||
        this._workSpinner.actor.show();
 | 
			
		||||
 | 
			
		||||
        this.buttonLayout.visible = true;
 | 
			
		||||
        this.clearButtons();
 | 
			
		||||
 | 
			
		||||
@@ -780,11 +966,12 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
                             y_fill: false,
 | 
			
		||||
                             x_align: St.Align.START,
 | 
			
		||||
                             y_align: St.Align.MIDDLE });
 | 
			
		||||
        this.placeSpinner({ expand: false,
 | 
			
		||||
                            x_fill: false,
 | 
			
		||||
                            y_fill: false,
 | 
			
		||||
                            x_align: St.Align.END,
 | 
			
		||||
                            y_align: St.Align.MIDDLE });
 | 
			
		||||
        this.buttonLayout.add(this._workSpinner.actor,
 | 
			
		||||
                              { expand: false,
 | 
			
		||||
                                x_fill: false,
 | 
			
		||||
                                y_fill: false,
 | 
			
		||||
                                x_align: St.Align.END,
 | 
			
		||||
                                y_align: St.Align.MIDDLE });
 | 
			
		||||
        this._signInButton = this.addButton({ action: Lang.bind(this, function() {
 | 
			
		||||
                                                          hold.release();
 | 
			
		||||
                                                      }),
 | 
			
		||||
@@ -803,11 +990,6 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
                                                    Lang.bind(this, function() {
 | 
			
		||||
                                                        this._updateSignInButtonSensitivity(this._promptEntry.text.length > 0);
 | 
			
		||||
                                                    }));
 | 
			
		||||
 | 
			
		||||
        this._promptEntryActivateId =
 | 
			
		||||
            this._promptEntry.clutter_text.connect('activate', function() {
 | 
			
		||||
                hold.release();
 | 
			
		||||
            });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateSensitivity: function(sensitive) {
 | 
			
		||||
@@ -832,31 +1014,56 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
            this._promptEntryTextChangedId = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this._promptEntryActivateId > 0) {
 | 
			
		||||
            this._promptEntry.clutter_text.disconnect(this._promptEntryActivateId);
 | 
			
		||||
            this._promptEntryActivateId = 0;
 | 
			
		||||
        let tasks = [function() {
 | 
			
		||||
                         this._setWorking(false);
 | 
			
		||||
 | 
			
		||||
                         return GdmUtil.fadeOutActor(this._promptBox);
 | 
			
		||||
                     },
 | 
			
		||||
 | 
			
		||||
                     function() {
 | 
			
		||||
                         this._promptLoginHint.hide();
 | 
			
		||||
 | 
			
		||||
                         this._updateSensitivity(true);
 | 
			
		||||
                         this._promptEntry.set_text('');
 | 
			
		||||
 | 
			
		||||
                         this.clearButtons();
 | 
			
		||||
                         this._workSpinner = null;
 | 
			
		||||
                         this._signInButton = null;
 | 
			
		||||
                     }];
 | 
			
		||||
 | 
			
		||||
        let batch = new Batch.ConsecutiveBatch(this, tasks);
 | 
			
		||||
 | 
			
		||||
        return batch.run();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _setWorking: function(working) {
 | 
			
		||||
        if (!this._workSpinner)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        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() {
 | 
			
		||||
                                   this._workSpinner.stop();
 | 
			
		||||
                               }
 | 
			
		||||
                             });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.setWorking(false);
 | 
			
		||||
        this._promptBox.hide();
 | 
			
		||||
        this._promptLoginHint.hide();
 | 
			
		||||
 | 
			
		||||
        this._promptUser.set_child(null);
 | 
			
		||||
 | 
			
		||||
        this._updateSensitivity(true);
 | 
			
		||||
        this._promptEntry.set_text('');
 | 
			
		||||
 | 
			
		||||
        this._sessionList.close();
 | 
			
		||||
        this._promptLoginHint.hide();
 | 
			
		||||
 | 
			
		||||
        this.clearButtons();
 | 
			
		||||
        this._signInButton = null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _askQuestion: function(verifier, serviceName, question, passwordChar) {
 | 
			
		||||
        this._promptLabel.set_text(question);
 | 
			
		||||
 | 
			
		||||
        this._updateSensitivity(true);
 | 
			
		||||
        this._promptEntry.set_text('');
 | 
			
		||||
        this._promptEntry.clutter_text.set_password_char(passwordChar);
 | 
			
		||||
 | 
			
		||||
@@ -867,7 +1074,7 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
                     function() {
 | 
			
		||||
                         let text = this._promptEntry.get_text();
 | 
			
		||||
                         this._updateSensitivity(false);
 | 
			
		||||
                         this.setWorking(true);
 | 
			
		||||
                         this._setWorking(true);
 | 
			
		||||
                         this._userVerifier.answerQuery(serviceName, text);
 | 
			
		||||
                     }];
 | 
			
		||||
 | 
			
		||||
@@ -875,79 +1082,25 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
        return batch.run();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _showRealmLoginHint: function(realmManager, hint) {
 | 
			
		||||
        if (!hint)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        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._showLoginHint(null, _("(e.g., user or %s)").format(hint));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _askForUsernameAndLogIn: function() {
 | 
			
		||||
        this._promptLabel.set_text(_("Username: "));
 | 
			
		||||
        this._promptEntry.set_text('');
 | 
			
		||||
        this._promptEntry.clutter_text.set_password_char('');
 | 
			
		||||
 | 
			
		||||
        let realmManager = new Realmd.Manager();
 | 
			
		||||
        let signalId = realmManager.connect('login-format-changed',
 | 
			
		||||
	                                        Lang.bind(this, this._showRealmLoginHint));
 | 
			
		||||
        this._showRealmLoginHint(realmManager.loginFormat);
 | 
			
		||||
 | 
			
		||||
        let tasks = [this._showPrompt,
 | 
			
		||||
 | 
			
		||||
                     function() {
 | 
			
		||||
                         let userName = this._promptEntry.get_text();
 | 
			
		||||
                         this._promptEntry.reactive = false;
 | 
			
		||||
                         return this._beginVerificationForUser(userName);
 | 
			
		||||
                     },
 | 
			
		||||
 | 
			
		||||
                     function() {
 | 
			
		||||
                         realmManager.disconnect(signalId)
 | 
			
		||||
                         realmManager.release();
 | 
			
		||||
                     }];
 | 
			
		||||
 | 
			
		||||
        let batch = new Batch.ConsecutiveBatch(this, tasks);
 | 
			
		||||
        return batch.run();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _startSession: function(serviceName) {
 | 
			
		||||
        Tweener.addTween(this.dialogLayout,
 | 
			
		||||
                         { opacity: 0,
 | 
			
		||||
                           time: _FADE_ANIMATION_TIME,
 | 
			
		||||
                           transition: 'easeOutQuad',
 | 
			
		||||
                           onUpdate: function() {
 | 
			
		||||
                               let children = Main.layoutManager.uiGroup.get_children();
 | 
			
		||||
 | 
			
		||||
                               for (let i = 0; i < children.length; i++) {
 | 
			
		||||
                                   if (children[i] != Main.layoutManager.screenShieldGroup)
 | 
			
		||||
                                       children[i].opacity = this.dialogLayout.opacity;
 | 
			
		||||
                               }
 | 
			
		||||
                           },
 | 
			
		||||
                           onUpdateScope: this,
 | 
			
		||||
                           onComplete: function() {
 | 
			
		||||
                               Mainloop.idle_add(Lang.bind(this, function() {
 | 
			
		||||
                                   this._greeter.call_start_session_when_ready_sync(serviceName, true, null);
 | 
			
		||||
                                   return false;
 | 
			
		||||
                               }));
 | 
			
		||||
                           },
 | 
			
		||||
                           onCompleteScope: this });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onSessionOpened: function(client, serviceName) {
 | 
			
		||||
        if (!this._userVerifier.hasPendingMessages) {
 | 
			
		||||
            this._startSession(serviceName);
 | 
			
		||||
        } else {
 | 
			
		||||
            let signalId = this._userVerifier.connect('no-more-messages',
 | 
			
		||||
                                                      Lang.bind(this, function() {
 | 
			
		||||
                                                          this._userVerifier.disconnect(signalId);
 | 
			
		||||
                                                          this._startSession(serviceName);
 | 
			
		||||
                                                      }));
 | 
			
		||||
        }
 | 
			
		||||
        this._greeter.call_start_session_when_ready_sync(serviceName, true, null);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _waitForItemForUser: function(userName) {
 | 
			
		||||
@@ -1013,7 +1166,7 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
                     function() {
 | 
			
		||||
                         // If we're just starting out, start on the right
 | 
			
		||||
                         // item.
 | 
			
		||||
                         if (!this._userManager.is_loaded) {
 | 
			
		||||
                         if (!this.is_loaded) {
 | 
			
		||||
                             this._userList.jumpToItem(this._timedLoginItem);
 | 
			
		||||
                         }
 | 
			
		||||
                     },
 | 
			
		||||
@@ -1074,21 +1227,75 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
                             }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _setUserListExpanded: function(expanded) {
 | 
			
		||||
        this._userList.updateStyle(expanded);
 | 
			
		||||
        this._userSelectionBox.visible = expanded;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _hideUserListAndLogIn: function() {
 | 
			
		||||
        this._setUserListExpanded(false);
 | 
			
		||||
        GdmUtil.cloneAndFadeOutActor(this._userSelectionBox);
 | 
			
		||||
        this._askForUsernameAndLogIn();
 | 
			
		||||
        let tasks = [function() {
 | 
			
		||||
                         return this._userList.hideItems();
 | 
			
		||||
                     },
 | 
			
		||||
 | 
			
		||||
                     function() {
 | 
			
		||||
                         return this._userList.giveUpWhitespace();
 | 
			
		||||
                     },
 | 
			
		||||
 | 
			
		||||
                     function() {
 | 
			
		||||
                         this._userList.actor.hide();
 | 
			
		||||
                     },
 | 
			
		||||
 | 
			
		||||
                     new Batch.ConcurrentBatch(this, [this._fadeOutTitleLabel,
 | 
			
		||||
                                                      this._fadeOutNotListedButton]),
 | 
			
		||||
 | 
			
		||||
                     function() {
 | 
			
		||||
                         return this._askForUsernameAndLogIn();
 | 
			
		||||
                     }];
 | 
			
		||||
 | 
			
		||||
        let batch = new Batch.ConsecutiveBatch(this, tasks);
 | 
			
		||||
        batch.run();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _showUserList: function() {
 | 
			
		||||
        this._hidePrompt();
 | 
			
		||||
        this._setUserListExpanded(true);
 | 
			
		||||
        this._userList.actor.grab_key_focus();
 | 
			
		||||
        let tasks = [this._hidePrompt,
 | 
			
		||||
 | 
			
		||||
                     new Batch.ConcurrentBatch(this, [this._fadeInTitleLabel,
 | 
			
		||||
                                                      this._fadeInNotListedButton]),
 | 
			
		||||
 | 
			
		||||
                     function() {
 | 
			
		||||
                         this._sessionList.close();
 | 
			
		||||
                         this._promptLoginHint.hide();
 | 
			
		||||
                         this._userList.actor.show();
 | 
			
		||||
                         this._userList.actor.opacity = 255;
 | 
			
		||||
                         return this._userList.showItems();
 | 
			
		||||
                     },
 | 
			
		||||
 | 
			
		||||
                     function() {
 | 
			
		||||
                         this._userList.actor.reactive = true;
 | 
			
		||||
                         this._userList.actor.grab_key_focus();
 | 
			
		||||
                     }];
 | 
			
		||||
 | 
			
		||||
        let batch = new Batch.ConsecutiveBatch(this, tasks);
 | 
			
		||||
        batch.run();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _fadeInBanner: function() {
 | 
			
		||||
        return GdmUtil.fadeInActor(this._bannerLabel);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _fadeOutBanner: function() {
 | 
			
		||||
        return GdmUtil.fadeOutActor(this._bannerLabel);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _fadeInTitleLabel: function() {
 | 
			
		||||
        return GdmUtil.fadeInActor(this._titleLabel);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _fadeOutTitleLabel: function() {
 | 
			
		||||
        return GdmUtil.fadeOutActor(this._titleLabel);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _fadeInNotListedButton: function() {
 | 
			
		||||
        return GdmUtil.fadeInActor(this._notListedButton);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _fadeOutNotListedButton: function() {
 | 
			
		||||
        return GdmUtil.fadeOutActor(this._notListedButton);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _beginVerificationForUser: function(userName) {
 | 
			
		||||
@@ -1099,30 +1306,38 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
        return hold;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _beginVerificationForItem: function(item) {
 | 
			
		||||
        let userWidget = new UserWidget.UserWidget(item.user);
 | 
			
		||||
        this._promptUser.set_child(userWidget.actor);
 | 
			
		||||
 | 
			
		||||
        let tasks = [function() {
 | 
			
		||||
                         let userName = item.user.get_user_name();
 | 
			
		||||
                         return this._beginVerificationForUser(userName);
 | 
			
		||||
                     }];
 | 
			
		||||
        let batch = new Batch.ConsecutiveBatch(this, tasks);
 | 
			
		||||
        return batch.run();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onUserListActivated: function(activatedItem) {
 | 
			
		||||
        let userName;
 | 
			
		||||
 | 
			
		||||
        let tasks = [function() {
 | 
			
		||||
                         return GdmUtil.cloneAndFadeOutActor(this._userSelectionBox);
 | 
			
		||||
                         this._userList.actor.reactive = false;
 | 
			
		||||
                         return this._userList.pinInPlace();
 | 
			
		||||
                     },
 | 
			
		||||
 | 
			
		||||
                     function() {
 | 
			
		||||
                         this._setUserListExpanded(false);
 | 
			
		||||
                         return this._userList.hideItemsExcept(activatedItem);
 | 
			
		||||
                     },
 | 
			
		||||
 | 
			
		||||
                     function() {
 | 
			
		||||
                         return this._userList.giveUpWhitespace();
 | 
			
		||||
                     },
 | 
			
		||||
 | 
			
		||||
                     new Batch.ConcurrentBatch(this, [this._fadeOutTitleLabel,
 | 
			
		||||
                                                      this._fadeOutNotListedButton]),
 | 
			
		||||
 | 
			
		||||
                     function() {
 | 
			
		||||
                         return this._userList.shrinkToNaturalHeight();
 | 
			
		||||
                     },
 | 
			
		||||
 | 
			
		||||
                     function() {
 | 
			
		||||
                         userName = activatedItem.user.get_user_name();
 | 
			
		||||
 | 
			
		||||
                         return this._beginVerificationForUser(userName);
 | 
			
		||||
                     }];
 | 
			
		||||
 | 
			
		||||
        this._user = activatedItem.user;
 | 
			
		||||
 | 
			
		||||
        let batch = new Batch.ConcurrentBatch(this, [new Batch.ConsecutiveBatch(this, tasks),
 | 
			
		||||
                                                     this._beginVerificationForItem(activatedItem)]);
 | 
			
		||||
        let batch = new Batch.ConsecutiveBatch(this, tasks);
 | 
			
		||||
        batch.run();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -1151,6 +1366,15 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
                                  Lang.bind(this, function(userManager, user) {
 | 
			
		||||
                                      this._userList.removeUser(user);
 | 
			
		||||
                                  }));
 | 
			
		||||
 | 
			
		||||
        // emitted in idle so caller doesn't have to explicitly check if
 | 
			
		||||
        // it's loaded immediately after construction
 | 
			
		||||
        // (since there's no way the caller could be listening for
 | 
			
		||||
        // 'loaded' yet)
 | 
			
		||||
        Mainloop.idle_add(Lang.bind(this, function() {
 | 
			
		||||
            this.emit('loaded');
 | 
			
		||||
            this.is_loaded = true;
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onOpened: function() {
 | 
			
		||||
@@ -1165,9 +1389,5 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
        this.parent();
 | 
			
		||||
 | 
			
		||||
        Main.ctrlAltTabManager.removeGroup(this.dialogLayout);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addCharacter: function(unichar) {
 | 
			
		||||
        this._promptEntry.clutter_text.insert_unichar(unichar);
 | 
			
		||||
    },
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										172
									
								
								js/gdm/util.js
									
									
									
									
									
								
							
							
						
						
									
										172
									
								
								js/gdm/util.js
									
									
									
									
									
								
							@@ -1,14 +1,13 @@
 | 
			
		||||
// -*- 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 Batch = imports.gdm.batch;
 | 
			
		||||
const Fprint = imports.gdm.fingerprint;
 | 
			
		||||
const Realmd = imports.gdm.realmd;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
const Tweener = imports.ui.tweener;
 | 
			
		||||
@@ -16,7 +15,6 @@ const Tweener = imports.ui.tweener;
 | 
			
		||||
const PASSWORD_SERVICE_NAME = 'gdm-password';
 | 
			
		||||
const FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint';
 | 
			
		||||
const FADE_ANIMATION_TIME = 0.16;
 | 
			
		||||
const CLONE_FADE_ANIMATION_TIME = 0.25;
 | 
			
		||||
 | 
			
		||||
const LOGIN_SCREEN_SCHEMA = 'org.gnome.login-screen';
 | 
			
		||||
const FINGERPRINT_AUTHENTICATION_KEY = 'enable-fingerprint-authentication';
 | 
			
		||||
@@ -27,9 +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
 | 
			
		||||
 | 
			
		||||
function fadeInActor(actor) {
 | 
			
		||||
    if (actor.opacity == 255 && actor.visible)
 | 
			
		||||
        return null;
 | 
			
		||||
@@ -76,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',
 | 
			
		||||
 | 
			
		||||
@@ -116,9 +83,7 @@ const ShellUserVerifier = new Lang.Class({
 | 
			
		||||
        this._settings = new Gio.Settings({ schema: LOGIN_SCREEN_SCHEMA });
 | 
			
		||||
 | 
			
		||||
        this._fprintManager = new Fprint.FprintManager();
 | 
			
		||||
        this._messageQueue = [];
 | 
			
		||||
        this._messageQueueTimeoutId = 0;
 | 
			
		||||
        this.hasPendingMessages = false;
 | 
			
		||||
        this._realmManager = new Realmd.Manager();
 | 
			
		||||
 | 
			
		||||
        this._failCounter = 0;
 | 
			
		||||
    },
 | 
			
		||||
@@ -158,73 +123,13 @@ const ShellUserVerifier = new Lang.Class({
 | 
			
		||||
            this._userVerifier.run_dispose();
 | 
			
		||||
            this._userVerifier = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._clearMessageQueue();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    answerQuery: function(serviceName, answer) {
 | 
			
		||||
        if (!this._userVerifier.hasPendingMessages) {
 | 
			
		||||
            this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
 | 
			
		||||
        } else {
 | 
			
		||||
            let signalId = this._userVerifier.connect('no-more-messages',
 | 
			
		||||
                                                      Lang.bind(this, function() {
 | 
			
		||||
                                                          this._userVerifier.disconnect(signalId);
 | 
			
		||||
                                                          this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, 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.iconName);
 | 
			
		||||
 | 
			
		||||
        this._messageQueueTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
 | 
			
		||||
                                                       message.interval,
 | 
			
		||||
                                                       Lang.bind(this, function() {
 | 
			
		||||
                                                           this._messageQueueTimeoutId = 0;
 | 
			
		||||
                                                           this._queueMessageTimeout();
 | 
			
		||||
                                                       }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _queueMessage: function(message, iconName) {
 | 
			
		||||
        let interval = this._getIntervalForMessage(message);
 | 
			
		||||
 | 
			
		||||
        this.hasPendingMessages = true;
 | 
			
		||||
        this._messageQueue.push({ text: message, interval: interval, iconName: iconName });
 | 
			
		||||
        this._queueMessageTimeout();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _clearMessageQueue: function() {
 | 
			
		||||
        this.finishMessageQueue();
 | 
			
		||||
 | 
			
		||||
        if (this._messageQueueTimeoutId != 0) {
 | 
			
		||||
            GLib.source_remove(this._messageQueueTimeoutId);
 | 
			
		||||
            this._messageQueueTimeoutId = 0;
 | 
			
		||||
        }
 | 
			
		||||
        // Clear any previous message
 | 
			
		||||
        this.emit('show-message', null, null);
 | 
			
		||||
 | 
			
		||||
        this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _checkForFingerprintReader: function() {
 | 
			
		||||
@@ -244,7 +149,7 @@ const ShellUserVerifier = new Lang.Class({
 | 
			
		||||
        logError(error, where);
 | 
			
		||||
        this._hold.release();
 | 
			
		||||
 | 
			
		||||
        this._queueMessage(_("Authentication error"), 'login-dialog-message-warning');
 | 
			
		||||
        this.emit('show-message', _("Authentication error"), 'login-dialog-message-warning');
 | 
			
		||||
        this._verificationFailed(false);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -363,7 +268,7 @@ const ShellUserVerifier = new Lang.Class({
 | 
			
		||||
            // to indicate the user can swipe their finger instead
 | 
			
		||||
            this.emit('show-login-hint', _("(or swipe finger)"));
 | 
			
		||||
        } else if (serviceName == PASSWORD_SERVICE_NAME) {
 | 
			
		||||
            this._queueMessage(info, 'login-dialog-message-info');
 | 
			
		||||
            this.emit('show-message', info, 'login-dialog-message-info');
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -372,7 +277,22 @@ const ShellUserVerifier = new Lang.Class({
 | 
			
		||||
        // users who haven't enrolled their fingerprint.
 | 
			
		||||
        if (serviceName != PASSWORD_SERVICE_NAME)
 | 
			
		||||
            return;
 | 
			
		||||
        this._queueMessage(problem, 'login-dialog-message-warning');
 | 
			
		||||
        this.emit('show-message', problem, 'login-dialog-message-warning');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _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) {
 | 
			
		||||
@@ -380,6 +300,10 @@ const ShellUserVerifier = new Lang.Class({
 | 
			
		||||
        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, '');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -392,6 +316,8 @@ const ShellUserVerifier = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onReset: function() {
 | 
			
		||||
        this.clear();
 | 
			
		||||
 | 
			
		||||
        // Clear previous attempts to authenticate
 | 
			
		||||
        this._failCounter = 0;
 | 
			
		||||
 | 
			
		||||
@@ -402,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
 | 
			
		||||
@@ -422,25 +339,15 @@ const ShellUserVerifier = new Lang.Class({
 | 
			
		||||
            this._failCounter < this._settings.get_int(ALLOWED_FAILURES_KEY);
 | 
			
		||||
 | 
			
		||||
        if (canRetry) {
 | 
			
		||||
            if (!this._userVerifier.hasPendingMessages) {
 | 
			
		||||
                this._retry();
 | 
			
		||||
            } else {
 | 
			
		||||
                let signalId = this._userVerifier.connect('no-more-messages',
 | 
			
		||||
                                                          Lang.bind(this, function() {
 | 
			
		||||
                                                              this._userVerifier.disconnect(signalId);
 | 
			
		||||
                                                              this._retry();
 | 
			
		||||
                                                          }));
 | 
			
		||||
            }
 | 
			
		||||
            this.clear();
 | 
			
		||||
            this.begin(this._userName, new Batch.Hold());
 | 
			
		||||
        } else {
 | 
			
		||||
            if (!this._userVerifier.hasPendingMessages) {
 | 
			
		||||
                this._cancelAndReset();
 | 
			
		||||
            } else {
 | 
			
		||||
                let signalId = this._userVerifier.connect('no-more-messages',
 | 
			
		||||
                                                          Lang.bind(this, function() {
 | 
			
		||||
                                                              this._userVerifier.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');
 | 
			
		||||
@@ -455,6 +362,11 @@ const ShellUserVerifier = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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 });
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -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>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										141
									
								
								js/misc/hash.js
									
									
									
									
									
								
							
							
						
						
									
										141
									
								
								js/misc/hash.js
									
									
									
									
									
								
							@@ -1,141 +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 = { };
 | 
			
		||||
 | 
			
		||||
        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 };
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    delete: function(key) {
 | 
			
		||||
        let hash = this._hashKey(key);
 | 
			
		||||
        let node = this._pool[hash];
 | 
			
		||||
 | 
			
		||||
        if (node && _sameValue(node.key, key)) {
 | 
			
		||||
            delete this._pool[hash];
 | 
			
		||||
            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 Object.getOwnPropertyNames(this._pool).length;
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
@@ -5,7 +5,7 @@ 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'>
 | 
			
		||||
@@ -26,23 +26,6 @@ const SystemdLoginManagerIface = <interface name='org.freedesktop.login1.Manager
 | 
			
		||||
<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'>
 | 
			
		||||
@@ -68,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>;
 | 
			
		||||
@@ -76,36 +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++) {
 | 
			
		||||
        if (required[i] != reference[i])
 | 
			
		||||
            return required[i] < reference[i];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
@@ -133,30 +93,24 @@ 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) {
 | 
			
		||||
@@ -186,15 +140,6 @@ 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);
 | 
			
		||||
    },
 | 
			
		||||
@@ -205,33 +150,8 @@ const LoginManagerSystemd = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    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',
 | 
			
		||||
@@ -240,28 +160,34 @@ 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) {
 | 
			
		||||
@@ -283,11 +209,10 @@ const LoginManagerConsoleKit = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    canSuspend: function(asyncCallback) {
 | 
			
		||||
        asyncCallback(false);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    listSessions: function(asyncCallback) {
 | 
			
		||||
        asyncCallback([]);
 | 
			
		||||
        Mainloop.idle_add(Lang.bind(this, function() {
 | 
			
		||||
            asyncCallback(this._upClient.get_can_suspend());
 | 
			
		||||
            return false;
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    powerOff: function() {
 | 
			
		||||
@@ -299,12 +224,6 @@ const LoginManagerConsoleKit = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    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,13 +1,8 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
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()<>]+\\)))*\\)';
 | 
			
		||||
@@ -213,63 +208,3 @@ function insertSorted(array, val, cmp) {
 | 
			
		||||
 | 
			
		||||
    return pos;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function makeCloseButton() {
 | 
			
		||||
    let closeButton = new St.Button({ 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.
 | 
			
		||||
    closeButton.set_x_align(Clutter.ActorAlign.END);
 | 
			
		||||
    closeButton.set_y_align(Clutter.ActorAlign.START);
 | 
			
		||||
 | 
			
		||||
    // XXX Clutter 2.0 workaround: ClutterBinLayout needs expand
 | 
			
		||||
    // to respect the alignments.
 | 
			
		||||
    closeButton.set_x_expand(true);
 | 
			
		||||
    closeButton.set_y_expand(true);
 | 
			
		||||
 | 
			
		||||
    closeButton.connect('style-changed', function() {
 | 
			
		||||
        let themeNode = closeButton.get_theme_node();
 | 
			
		||||
        closeButton.translation_x = themeNode.get_length('-shell-close-overlap-x');
 | 
			
		||||
        closeButton.translation_y = themeNode.get_length('-shell-close-overlap-y');
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    return closeButton;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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' });
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										110
									
								
								js/ui/altTab.js
									
									
									
									
									
								
							
							
						
						
									
										110
									
								
								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() {
 | 
			
		||||
@@ -397,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);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@@ -429,26 +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 allWindows = global.display.get_tab_list(Meta.TabList.NORMAL,
 | 
			
		||||
                                                     global.screen, null);
 | 
			
		||||
 | 
			
		||||
        // 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;
 | 
			
		||||
            });
 | 
			
		||||
            this._addIcon(appIcon);
 | 
			
		||||
        }
 | 
			
		||||
        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;
 | 
			
		||||
@@ -474,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;
 | 
			
		||||
@@ -596,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 });
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,6 @@
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const GObject = imports.gi.GObject;
 | 
			
		||||
const Gtk = imports.gi.Gtk;
 | 
			
		||||
const GMenu = imports.gi.GMenu;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
@@ -15,12 +14,10 @@ const Mainloop = imports.mainloop;
 | 
			
		||||
const Atk = imports.gi.Atk;
 | 
			
		||||
 | 
			
		||||
const AppFavorites = imports.ui.appFavorites;
 | 
			
		||||
const BoxPointer = imports.ui.boxpointer;
 | 
			
		||||
const DND = imports.ui.dnd;
 | 
			
		||||
const IconGrid = imports.ui.iconGrid;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const Overview = imports.ui.overview;
 | 
			
		||||
const OverviewControls = imports.ui.overviewControls;
 | 
			
		||||
const PopupMenu = imports.ui.popupMenu;
 | 
			
		||||
const Tweener = imports.ui.tweener;
 | 
			
		||||
const Workspace = imports.ui.workspace;
 | 
			
		||||
@@ -29,469 +26,255 @@ const Util = imports.misc.util;
 | 
			
		||||
 | 
			
		||||
const MAX_APPLICATION_WORK_MILLIS = 75;
 | 
			
		||||
const MENU_POPUP_TIMEOUT = 600;
 | 
			
		||||
const MAX_COLUMNS = 6;
 | 
			
		||||
 | 
			
		||||
const INACTIVE_GRID_OPACITY = 77;
 | 
			
		||||
const FOLDER_SUBICON_FRACTION = .4;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// Recursively load a GMenuTreeDirectory; we could put this in ShellAppSystem too
 | 
			
		||||
function _loadCategory(dir, view) {
 | 
			
		||||
    let iter = dir.iter();
 | 
			
		||||
    let appSystem = Shell.AppSystem.get_default();
 | 
			
		||||
    let nextType;
 | 
			
		||||
    while ((nextType = iter.next()) != GMenu.TreeItemType.INVALID) {
 | 
			
		||||
        if (nextType == GMenu.TreeItemType.ENTRY) {
 | 
			
		||||
            let entry = iter.get_entry();
 | 
			
		||||
            let app = appSystem.lookup_app_by_tree_entry(entry);
 | 
			
		||||
            if (!entry.get_app_info().get_nodisplay())
 | 
			
		||||
                view.addApp(app);
 | 
			
		||||
        } else if (nextType == GMenu.TreeItemType.DIRECTORY) {
 | 
			
		||||
            let itemDir = iter.get_directory();
 | 
			
		||||
            if (!itemDir.get_is_nodisplay())
 | 
			
		||||
                _loadCategory(itemDir, view);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
const SCROLL_TIME = 0.1;
 | 
			
		||||
 | 
			
		||||
const AlphabeticalView = new Lang.Class({
 | 
			
		||||
    Name: 'AlphabeticalView',
 | 
			
		||||
    Abstract: true,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._grid = new IconGrid.IconGrid({ xAlign: St.Align.MIDDLE,
 | 
			
		||||
                                             columnLimit: MAX_COLUMNS });
 | 
			
		||||
        this._grid = new IconGrid.IconGrid({ xAlign: St.Align.START });
 | 
			
		||||
        this._appSystem = Shell.AppSystem.get_default();
 | 
			
		||||
 | 
			
		||||
        // Standard hack for ClutterBinLayout
 | 
			
		||||
        this._grid.actor.x_expand = true;
 | 
			
		||||
 | 
			
		||||
        this._items = {};
 | 
			
		||||
        this._allItems = [];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    removeAll: function() {
 | 
			
		||||
        this._grid.removeAll();
 | 
			
		||||
        this._items = {};
 | 
			
		||||
        this._allItems = [];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getItemId: function(item) {
 | 
			
		||||
        throw new Error('Not implemented');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createItemIcon: function(item) {
 | 
			
		||||
        throw new Error('Not implemented');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _compareItems: function(a, b) {
 | 
			
		||||
        throw new Error('Not implemented');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _addItem: function(item) {
 | 
			
		||||
        let id = this._getItemId(item);
 | 
			
		||||
        if (this._items[id] !== undefined)
 | 
			
		||||
            return null;
 | 
			
		||||
 | 
			
		||||
        let itemIcon = this._createItemIcon(item);
 | 
			
		||||
        this._allItems.push(item);
 | 
			
		||||
        this._items[id] = itemIcon;
 | 
			
		||||
 | 
			
		||||
        return itemIcon;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    loadGrid: function() {
 | 
			
		||||
        this._allItems.sort(this._compareItems);
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < this._allItems.length; i++) {
 | 
			
		||||
            let id = this._getItemId(this._allItems[i]);
 | 
			
		||||
            if (!id)
 | 
			
		||||
                continue;
 | 
			
		||||
            this._grid.addItem(this._items[id].actor);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const FolderView = new Lang.Class({
 | 
			
		||||
    Name: 'FolderView',
 | 
			
		||||
    Extends: AlphabeticalView,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.parent();
 | 
			
		||||
        this.actor = this._grid.actor;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getItemId: function(item) {
 | 
			
		||||
        return item.get_id();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createItemIcon: function(item) {
 | 
			
		||||
        return new AppIcon(item);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _compareItems: function(a, b) {
 | 
			
		||||
        return a.compare_by_name(b);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addApp: function(app) {
 | 
			
		||||
        this._addItem(app);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    createFolderIcon: function(size) {
 | 
			
		||||
        let icon = new St.Widget({ layout_manager: new Clutter.BinLayout(),
 | 
			
		||||
                                   style_class: 'app-folder-icon',
 | 
			
		||||
                                   width: size, height: size });
 | 
			
		||||
        let subSize = Math.floor(FOLDER_SUBICON_FRACTION * size);
 | 
			
		||||
 | 
			
		||||
        let aligns = [ Clutter.ActorAlign.START, Clutter.ActorAlign.END ];
 | 
			
		||||
        for (let i = 0; i < Math.min(this._allItems.length, 4); i++) {
 | 
			
		||||
            let texture = this._allItems[i].create_icon_texture(subSize);
 | 
			
		||||
            let bin = new St.Bin({ child: texture,
 | 
			
		||||
                                   x_expand: true, y_expand: true });
 | 
			
		||||
            bin.set_x_align(aligns[i % 2]);
 | 
			
		||||
            bin.set_y_align(aligns[Math.floor(i / 2)]);
 | 
			
		||||
            icon.add_actor(bin);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return icon;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const AllViewLayout = new Lang.Class({
 | 
			
		||||
    Name: 'AllViewLayout',
 | 
			
		||||
    Extends: Clutter.BinLayout,
 | 
			
		||||
 | 
			
		||||
    vfunc_get_preferred_height: function(container, forWidth) {
 | 
			
		||||
        let minBottom = 0;
 | 
			
		||||
        let naturalBottom = 0;
 | 
			
		||||
 | 
			
		||||
        for (let child = container.get_first_child();
 | 
			
		||||
             child;
 | 
			
		||||
             child = child.get_next_sibling()) {
 | 
			
		||||
            let childY = child.y;
 | 
			
		||||
            let [childMin, childNatural] = child.get_preferred_height(forWidth);
 | 
			
		||||
 | 
			
		||||
            if (childMin + childY > minBottom)
 | 
			
		||||
                minBottom = childMin + childY;
 | 
			
		||||
 | 
			
		||||
            if (childNatural + childY > naturalBottom)
 | 
			
		||||
                naturalBottom = childNatural + childY;
 | 
			
		||||
        }
 | 
			
		||||
        return [minBottom, naturalBottom];
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const AllView = new Lang.Class({
 | 
			
		||||
    Name: 'AllView',
 | 
			
		||||
    Extends: AlphabeticalView,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.parent();
 | 
			
		||||
 | 
			
		||||
        this._grid.actor.y_align = Clutter.ActorAlign.START;
 | 
			
		||||
        this._grid.actor.y_expand = true;
 | 
			
		||||
        this._pendingAppLaterId = 0;
 | 
			
		||||
        this._appIcons = {}; // desktop file id
 | 
			
		||||
        this._allApps = [];
 | 
			
		||||
 | 
			
		||||
        let box = new St.BoxLayout({ vertical: true });
 | 
			
		||||
        this._stack = new St.Widget({ layout_manager: new AllViewLayout() });
 | 
			
		||||
        this._stack.add_actor(this._grid.actor);
 | 
			
		||||
        this._eventBlocker = new St.Widget({ x_expand: true, y_expand: true });
 | 
			
		||||
        this._stack.add_actor(this._eventBlocker);
 | 
			
		||||
        box.add(this._stack, { y_align: St.Align.START, expand: true });
 | 
			
		||||
        box.add(this._grid.actor, { y_align: St.Align.START, expand: true });
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.ScrollView({ x_fill: true,
 | 
			
		||||
                                         y_fill: false,
 | 
			
		||||
                                         y_align: St.Align.START,
 | 
			
		||||
                                         x_expand: true,
 | 
			
		||||
                                         y_expand: true,
 | 
			
		||||
                                         overlay_scrollbars: true,
 | 
			
		||||
                                         style_class: 'all-apps vfade' });
 | 
			
		||||
                                         style_class: 'vfade' });
 | 
			
		||||
        this.actor.add_actor(box);
 | 
			
		||||
        this.actor.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
 | 
			
		||||
        let action = new Clutter.PanAction({ interpolate: true });
 | 
			
		||||
        action.connect('pan', Lang.bind(this, this._onPan));
 | 
			
		||||
        this.actor.add_action(action);
 | 
			
		||||
 | 
			
		||||
        this._clickAction = new Clutter.ClickAction();
 | 
			
		||||
        this._clickAction.connect('clicked', Lang.bind(this, function() {
 | 
			
		||||
            if (!this._currentPopup)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            let [x, y] = this._clickAction.get_coords();
 | 
			
		||||
            let actor = global.stage.get_actor_at_pos(Clutter.PickMode.ALL, x, y);
 | 
			
		||||
            if (!this._currentPopup.actor.contains(actor))
 | 
			
		||||
                this._currentPopup.popdown();
 | 
			
		||||
        }));
 | 
			
		||||
        this._eventBlocker.add_action(this._clickAction);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onPan: function(action) {
 | 
			
		||||
        this._clickAction.release();
 | 
			
		||||
 | 
			
		||||
        let [dist, dx, dy] = action.get_motion_delta(0);
 | 
			
		||||
        let adjustment = this.actor.vscroll.adjustment;
 | 
			
		||||
        adjustment.value -= (dy / this.actor.height) * adjustment.page_size;
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getItemId: function(item) {
 | 
			
		||||
        if (item instanceof Shell.App)
 | 
			
		||||
            return item.get_id();
 | 
			
		||||
        else if (item instanceof GMenu.TreeDirectory)
 | 
			
		||||
            return item.get_menu_id();
 | 
			
		||||
        else
 | 
			
		||||
            return null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createItemIcon: function(item) {
 | 
			
		||||
        if (item instanceof Shell.App)
 | 
			
		||||
            return new AppIcon(item);
 | 
			
		||||
        else if (item instanceof GMenu.TreeDirectory)
 | 
			
		||||
            return new FolderIcon(item, this);
 | 
			
		||||
        else
 | 
			
		||||
            return null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _compareItems: function(itemA, itemB) {
 | 
			
		||||
        // bit of a hack: rely on both ShellApp and GMenuTreeDirectory
 | 
			
		||||
        // having a get_name() method
 | 
			
		||||
        let nameA = GLib.utf8_collate_key(itemA.get_name(), -1);
 | 
			
		||||
        let nameB = GLib.utf8_collate_key(itemB.get_name(), -1);
 | 
			
		||||
        return (nameA > nameB) ? 1 : (nameA < nameB ? -1 : 0);
 | 
			
		||||
    removeAll: function() {
 | 
			
		||||
        this._grid.removeAll();
 | 
			
		||||
        this._appIcons = {};
 | 
			
		||||
        this._allApps = [];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addApp: function(app) {
 | 
			
		||||
        let appIcon = this._addItem(app);
 | 
			
		||||
        if (appIcon)
 | 
			
		||||
            appIcon.actor.connect('key-focus-in',
 | 
			
		||||
                                  Lang.bind(this, this._ensureIconVisible));
 | 
			
		||||
    },
 | 
			
		||||
        var id = app.get_id();
 | 
			
		||||
        if (this._appIcons[id] !== undefined)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
    addFolder: function(dir) {
 | 
			
		||||
        let folderIcon = this._addItem(dir);
 | 
			
		||||
        if (folderIcon)
 | 
			
		||||
            folderIcon.actor.connect('key-focus-in',
 | 
			
		||||
                                     Lang.bind(this, this._ensureIconVisible));
 | 
			
		||||
    },
 | 
			
		||||
        let appIcon = new AppWellIcon(app);
 | 
			
		||||
        let pos = Util.insertSorted(this._allApps, app, function(a, b) {
 | 
			
		||||
            return a.compare_by_name(b);
 | 
			
		||||
        });
 | 
			
		||||
        this._grid.addItem(appIcon.actor, pos);
 | 
			
		||||
        appIcon.actor.connect('key-focus-in', Lang.bind(this, this._ensureIconVisible));
 | 
			
		||||
 | 
			
		||||
    addFolderPopup: function(popup) {
 | 
			
		||||
        this._stack.add_actor(popup.actor);
 | 
			
		||||
        popup.connect('open-state-changed', Lang.bind(this,
 | 
			
		||||
            function(popup, isOpen) {
 | 
			
		||||
                this._eventBlocker.reactive = isOpen;
 | 
			
		||||
                this._currentPopup = isOpen ? popup : null;
 | 
			
		||||
                this._updateIconOpacities(isOpen);
 | 
			
		||||
                if (isOpen) {
 | 
			
		||||
                    this._ensureIconVisible(popup.actor);
 | 
			
		||||
                    this._grid.actor.y = popup.parentOffset;
 | 
			
		||||
                } else {
 | 
			
		||||
                    this._grid.actor.y = 0;
 | 
			
		||||
                }
 | 
			
		||||
            }));
 | 
			
		||||
        this._appIcons[id] = appIcon;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _ensureIconVisible: function(icon) {
 | 
			
		||||
        Util.ensureActorVisibleInScrollView(this.actor, icon);
 | 
			
		||||
        let adjustment = this.actor.vscroll.adjustment;
 | 
			
		||||
        let [value, lower, upper, stepIncrement, pageIncrement, pageSize] = adjustment.get_values();
 | 
			
		||||
 | 
			
		||||
        let offset = 0;
 | 
			
		||||
        let vfade = this.actor.get_effect("vfade");
 | 
			
		||||
        if (vfade)
 | 
			
		||||
            offset = vfade.fade_offset;
 | 
			
		||||
 | 
			
		||||
        // If this gets called as part of a right-click, the actor
 | 
			
		||||
        // will be needs_allocation, and so "icon.y" would return 0
 | 
			
		||||
        let box = icon.get_allocation_box();
 | 
			
		||||
 | 
			
		||||
        if (box.y1 < value + offset)
 | 
			
		||||
            value = Math.max(0, box.y1 - offset);
 | 
			
		||||
        else if (box.y2 > value + pageSize - offset)
 | 
			
		||||
            value = Math.min(upper, box.y2 + offset - pageSize);
 | 
			
		||||
        else
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        Tweener.addTween(adjustment,
 | 
			
		||||
                         { value: value,
 | 
			
		||||
                           time: SCROLL_TIME,
 | 
			
		||||
                           transition: 'easeOutQuad' });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateIconOpacities: function(folderOpen) {
 | 
			
		||||
        for (let id in this._items) {
 | 
			
		||||
            if (folderOpen && !this._items[id].actor.checked)
 | 
			
		||||
                this._items[id].actor.opacity = INACTIVE_GRID_OPACITY;
 | 
			
		||||
            else
 | 
			
		||||
                this._items[id].actor.opacity = 255;
 | 
			
		||||
    setVisibleApps: function(apps) {
 | 
			
		||||
        if (apps == null) { // null implies "all"
 | 
			
		||||
            for (var id in this._appIcons) {
 | 
			
		||||
                var icon = this._appIcons[id];
 | 
			
		||||
                icon.actor.visible = true;
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            // Set everything to not-visible, then set to visible what we should see
 | 
			
		||||
            for (var id in this._appIcons) {
 | 
			
		||||
                var icon = this._appIcons[id];
 | 
			
		||||
                icon.actor.visible = false;
 | 
			
		||||
            }
 | 
			
		||||
            for (var i = 0; i < apps.length; i++) {
 | 
			
		||||
                var app = apps[i];
 | 
			
		||||
                var id = app.get_id();
 | 
			
		||||
                var icon = this._appIcons[id];
 | 
			
		||||
                icon.actor.visible = true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const FrequentView = new Lang.Class({
 | 
			
		||||
    Name: 'FrequentView',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._grid = new IconGrid.IconGrid({ xAlign: St.Align.MIDDLE,
 | 
			
		||||
                                             fillParent: true,
 | 
			
		||||
                                             columnLimit: MAX_COLUMNS });
 | 
			
		||||
        this.actor = new St.Widget({ style_class: 'frequent-apps',
 | 
			
		||||
                                     x_expand: true, y_expand: true });
 | 
			
		||||
        this.actor.add_actor(this._grid.actor);
 | 
			
		||||
 | 
			
		||||
        this._usage = Shell.AppUsage.get_default();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    removeAll: function() {
 | 
			
		||||
        this._grid.removeAll();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    loadApps: function() {
 | 
			
		||||
        let mostUsed = this._usage.get_most_used ("");
 | 
			
		||||
        for (let i = 0; i < mostUsed.length; i++) {
 | 
			
		||||
            let appIcon = new AppIcon(mostUsed[i]);
 | 
			
		||||
            this._grid.addItem(appIcon.actor, -1);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const Views = {
 | 
			
		||||
    FREQUENT: 0,
 | 
			
		||||
    ALL: 1
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const ControlsBoxLayout = Lang.Class({
 | 
			
		||||
    Name: 'ControlsBoxLayout',
 | 
			
		||||
    Extends: Clutter.BoxLayout,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Override the BoxLayout behavior to use the maximum preferred width of all
 | 
			
		||||
     * buttons for each child
 | 
			
		||||
     */
 | 
			
		||||
    vfunc_get_preferred_width: function(container, forHeight) {
 | 
			
		||||
        let maxMinWidth = 0;
 | 
			
		||||
        let maxNaturalWidth = 0;
 | 
			
		||||
        for (let child = container.get_first_child();
 | 
			
		||||
             child;
 | 
			
		||||
             child = child.get_next_sibling()) {
 | 
			
		||||
             let [minWidth, natWidth] = child.get_preferred_width(forHeight);
 | 
			
		||||
             maxMinWidth = Math.max(maxMinWidth, minWidth);
 | 
			
		||||
             maxNaturalWidth = Math.max(maxNaturalWidth, natWidth);
 | 
			
		||||
        }
 | 
			
		||||
        let childrenCount = container.get_n_children();
 | 
			
		||||
        let totalSpacing = this.spacing * (childrenCount - 1);
 | 
			
		||||
        return [maxMinWidth * childrenCount + totalSpacing,
 | 
			
		||||
                maxNaturalWidth * childrenCount + totalSpacing];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    vfunc_set_container: function(container) {
 | 
			
		||||
        if(this._styleChangedId) {
 | 
			
		||||
            this._container.disconnect(this._styleChangedId);
 | 
			
		||||
            this._styleChangedId = 0;
 | 
			
		||||
        }
 | 
			
		||||
        if(container != null)
 | 
			
		||||
            this._styleChangedId = container.connect('style-changed', Lang.bind(this,
 | 
			
		||||
                    function() { this.spacing = this._container.get_theme_node().get_length('spacing'); }));
 | 
			
		||||
        this._container = container;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const AppDisplay = new Lang.Class({
 | 
			
		||||
    Name: 'AppDisplay',
 | 
			
		||||
const ViewByCategories = new Lang.Class({
 | 
			
		||||
    Name: 'ViewByCategories',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._appSystem = Shell.AppSystem.get_default();
 | 
			
		||||
        this._appSystem.connect('installed-changed', Lang.bind(this, function() {
 | 
			
		||||
            Main.queueDeferredWork(this._allAppsWorkId);
 | 
			
		||||
        }));
 | 
			
		||||
        Main.overview.connect('showing', Lang.bind(this, function() {
 | 
			
		||||
            Main.queueDeferredWork(this._frequentAppsWorkId);
 | 
			
		||||
        }));
 | 
			
		||||
        global.settings.connect('changed::app-folder-categories', Lang.bind(this, function() {
 | 
			
		||||
            Main.queueDeferredWork(this._allAppsWorkId);
 | 
			
		||||
        }));
 | 
			
		||||
        this.actor = new St.BoxLayout({ style_class: 'all-app' });
 | 
			
		||||
        this.actor._delegate = this;
 | 
			
		||||
 | 
			
		||||
        this._views = [];
 | 
			
		||||
        this._view = new AlphabeticalView();
 | 
			
		||||
 | 
			
		||||
        let view, button;
 | 
			
		||||
        view = new FrequentView();
 | 
			
		||||
        button = new St.Button({ label: _("Frequent"),
 | 
			
		||||
                                 style_class: 'app-view-control',
 | 
			
		||||
                                 can_focus: true,
 | 
			
		||||
                                 x_expand: true });
 | 
			
		||||
        this._views[Views.FREQUENT] = { 'view': view, 'control': button };
 | 
			
		||||
        // categories can be -1 (the All view) or 0...n-1, where n
 | 
			
		||||
        // is the number of sections
 | 
			
		||||
        // -2 is a flag to indicate that nothing is selected
 | 
			
		||||
        // (used only before the actor is mapped the first time)
 | 
			
		||||
        this._currentCategory = -2;
 | 
			
		||||
        this._categories = [];
 | 
			
		||||
 | 
			
		||||
        view = new AllView();
 | 
			
		||||
        button = new St.Button({ label: _("All"),
 | 
			
		||||
                                 style_class: 'app-view-control',
 | 
			
		||||
                                 can_focus: true,
 | 
			
		||||
                                 x_expand: true });
 | 
			
		||||
        this._views[Views.ALL] = { 'view': view, 'control': button };
 | 
			
		||||
        this._categoryBox = new St.BoxLayout({ vertical: true,
 | 
			
		||||
                                               reactive: true,
 | 
			
		||||
                                               accessible_role: Atk.Role.LIST });
 | 
			
		||||
        this._categoryScroll = new St.ScrollView({ x_fill: false,
 | 
			
		||||
                                                   y_fill: false,
 | 
			
		||||
                                                   style_class: 'vfade' });
 | 
			
		||||
        this._categoryScroll.add_actor(this._categoryBox);
 | 
			
		||||
        this.actor.add(this._view.actor, { expand: true, x_fill: true, y_fill: true });
 | 
			
		||||
        this.actor.add(this._categoryScroll, { expand: false, y_fill: false, y_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.BoxLayout({ style_class: 'app-display',
 | 
			
		||||
                                        vertical: true,
 | 
			
		||||
                                        x_expand: true, y_expand: true });
 | 
			
		||||
 | 
			
		||||
        this._viewStack = new St.Widget({ layout_manager: new Clutter.BinLayout(),
 | 
			
		||||
                                          x_expand: true, y_expand: true });
 | 
			
		||||
        this.actor.add(this._viewStack, { expand: true });
 | 
			
		||||
 | 
			
		||||
        let layout = new ControlsBoxLayout({ homogeneous: true });
 | 
			
		||||
        this._controls = new St.Widget({ style_class: 'app-view-controls' });
 | 
			
		||||
        this._controls.set_layout_manager(layout);
 | 
			
		||||
        this.actor.add(new St.Bin({ child: this._controls }));
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < this._views.length; i++) {
 | 
			
		||||
            this._viewStack.add_actor(this._views[i].view.actor);
 | 
			
		||||
            this._controls.add_actor(this._views[i].control);
 | 
			
		||||
 | 
			
		||||
            let viewIndex = i;
 | 
			
		||||
            this._views[i].control.connect('clicked', Lang.bind(this,
 | 
			
		||||
                function(actor) {
 | 
			
		||||
                    this._showView(viewIndex);
 | 
			
		||||
                }));
 | 
			
		||||
        }
 | 
			
		||||
        this._showView(Views.FREQUENT);
 | 
			
		||||
        // Always select the "All" filter when switching to the app view
 | 
			
		||||
        this.actor.connect('notify::mapped', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                if (this.actor.mapped && this._allCategoryButton)
 | 
			
		||||
                    this._selectCategory(-1);
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
        // We need a dummy actor to catch the keyboard focus if the
 | 
			
		||||
        // user Ctrl-Alt-Tabs here before the deferred work creates
 | 
			
		||||
        // our real contents
 | 
			
		||||
        this._focusDummy = new St.Bin({ can_focus: true });
 | 
			
		||||
        this._viewStack.add_actor(this._focusDummy);
 | 
			
		||||
 | 
			
		||||
        this._allAppsWorkId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplayAllApps));
 | 
			
		||||
        this._frequentAppsWorkId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplayFrequentApps));
 | 
			
		||||
        this.actor.add(this._focusDummy);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _showView: function(activeIndex) {
 | 
			
		||||
        for (let i = 0; i < this._views.length; i++) {
 | 
			
		||||
            let actor = this._views[i].view.actor;
 | 
			
		||||
            let params = { time: OverviewControls.SIDE_CONTROLS_ANIMATION_TIME,
 | 
			
		||||
                           opacity: (i == activeIndex) ? 255 : 0 };
 | 
			
		||||
            if (i == activeIndex)
 | 
			
		||||
                actor.visible = true;
 | 
			
		||||
            else
 | 
			
		||||
                params.onComplete = function() { actor.hide(); };
 | 
			
		||||
            Tweener.addTween(actor, params);
 | 
			
		||||
    _selectCategory: function(num) {
 | 
			
		||||
        if (this._currentCategory == num) // nothing to do
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
            if (i == activeIndex)
 | 
			
		||||
                this._views[i].control.add_style_pseudo_class('checked');
 | 
			
		||||
        this._currentCategory = num;
 | 
			
		||||
 | 
			
		||||
        if (num != -1) {
 | 
			
		||||
            var category = this._categories[num];
 | 
			
		||||
            this._allCategoryButton.remove_style_pseudo_class('selected');
 | 
			
		||||
            this._view.setVisibleApps(category.apps);
 | 
			
		||||
        } else {
 | 
			
		||||
            this._allCategoryButton.add_style_pseudo_class('selected');
 | 
			
		||||
            this._view.setVisibleApps(null);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (var i = 0; i < this._categories.length; i++) {
 | 
			
		||||
            if (i == num)
 | 
			
		||||
                this._categories[i].button.add_style_pseudo_class('selected');
 | 
			
		||||
            else
 | 
			
		||||
                this._views[i].control.remove_style_pseudo_class('checked');
 | 
			
		||||
                this._categories[i].button.remove_style_pseudo_class('selected');
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _redisplay: function() {
 | 
			
		||||
        this._redisplayFrequentApps();
 | 
			
		||||
        this._redisplayAllApps();
 | 
			
		||||
    // Recursively load a GMenuTreeDirectory; we could put this in ShellAppSystem too
 | 
			
		||||
    _loadCategory: function(dir, appList) {
 | 
			
		||||
        var iter = dir.iter();
 | 
			
		||||
        var nextType;
 | 
			
		||||
        while ((nextType = iter.next()) != GMenu.TreeItemType.INVALID) {
 | 
			
		||||
            if (nextType == GMenu.TreeItemType.ENTRY) {
 | 
			
		||||
                var entry = iter.get_entry();
 | 
			
		||||
                var app = this._appSystem.lookup_app_by_tree_entry(entry);
 | 
			
		||||
                if (!entry.get_app_info().get_nodisplay()) {
 | 
			
		||||
                    this._view.addApp(app);
 | 
			
		||||
                    appList.push(app);
 | 
			
		||||
                }
 | 
			
		||||
            } else if (nextType == GMenu.TreeItemType.DIRECTORY) {
 | 
			
		||||
                var itemDir = iter.get_directory();
 | 
			
		||||
                if (!itemDir.get_is_nodisplay())
 | 
			
		||||
                    this._loadCategory(itemDir, appList);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _redisplayFrequentApps: function() {
 | 
			
		||||
        let view = this._views[Views.FREQUENT].view;
 | 
			
		||||
    _addCategory: function(name, index, dir) {
 | 
			
		||||
        let apps;
 | 
			
		||||
 | 
			
		||||
        view.removeAll();
 | 
			
		||||
        view.loadApps();
 | 
			
		||||
        if (dir != null) {
 | 
			
		||||
            apps = [];
 | 
			
		||||
            this._loadCategory(dir, apps);
 | 
			
		||||
 | 
			
		||||
            if (apps.length == 0)
 | 
			
		||||
                return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let button = new St.Button({ label: GLib.markup_escape_text (name, -1),
 | 
			
		||||
                                     style_class: 'app-filter',
 | 
			
		||||
                                     x_align: St.Align.START,
 | 
			
		||||
                                     can_focus: true ,
 | 
			
		||||
                                     accessible_role: Atk.Role.LIST_ITEM });
 | 
			
		||||
        button.connect('clicked', Lang.bind(this, function() {
 | 
			
		||||
            this._selectCategory(index);
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        if (dir == null) {
 | 
			
		||||
            this._allCategoryButton = button;
 | 
			
		||||
        } else {
 | 
			
		||||
            this._categories.push({ apps: apps,
 | 
			
		||||
                                    name: name,
 | 
			
		||||
                                    button: button });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._categoryBox.add(button, { expand: true, x_fill: true, y_fill: false });
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _redisplayAllApps: function() {
 | 
			
		||||
        let view = this._views[Views.ALL].view;
 | 
			
		||||
    _removeAll: function() {
 | 
			
		||||
        this._view.removeAll();
 | 
			
		||||
        this._categories = [];
 | 
			
		||||
        this._categoryBox.destroy_all_children();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
        view.removeAll();
 | 
			
		||||
    refresh: function() {
 | 
			
		||||
        this._removeAll();
 | 
			
		||||
 | 
			
		||||
        let tree = this._appSystem.get_tree();
 | 
			
		||||
        let root = tree.get_root_directory();
 | 
			
		||||
        /* Translators: Filter to display all applications */
 | 
			
		||||
        this._addCategory(_("All"), -1, null);
 | 
			
		||||
 | 
			
		||||
        let iter = root.iter();
 | 
			
		||||
        let nextType;
 | 
			
		||||
        let folderCategories = global.settings.get_strv('app-folder-categories');
 | 
			
		||||
        var tree = this._appSystem.get_tree();
 | 
			
		||||
        var root = tree.get_root_directory();
 | 
			
		||||
 | 
			
		||||
        var iter = root.iter();
 | 
			
		||||
        var nextType;
 | 
			
		||||
        var i = 0;
 | 
			
		||||
        while ((nextType = iter.next()) != GMenu.TreeItemType.INVALID) {
 | 
			
		||||
            if (nextType == GMenu.TreeItemType.DIRECTORY) {
 | 
			
		||||
                let dir = iter.get_directory();
 | 
			
		||||
                var dir = iter.get_directory();
 | 
			
		||||
                if (dir.get_is_nodisplay())
 | 
			
		||||
                    continue;
 | 
			
		||||
 | 
			
		||||
                if (folderCategories.indexOf(dir.get_menu_id()) != -1)
 | 
			
		||||
                    view.addFolder(dir);
 | 
			
		||||
                else
 | 
			
		||||
                    _loadCategory(dir, view);
 | 
			
		||||
                if (this._addCategory(dir.get_name(), i, dir))
 | 
			
		||||
                    i++;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        view.loadGrid();
 | 
			
		||||
 | 
			
		||||
        this._selectCategory(-1);
 | 
			
		||||
 | 
			
		||||
        if (this._focusDummy) {
 | 
			
		||||
            let focused = this._focusDummy.has_key_focus();
 | 
			
		||||
@@ -503,6 +286,29 @@ const AppDisplay = new Lang.Class({
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
/* This class represents a display containing a collection of application items.
 | 
			
		||||
 * The applications are sorted based on their name.
 | 
			
		||||
 */
 | 
			
		||||
const AllAppDisplay = new Lang.Class({
 | 
			
		||||
    Name: 'AllAppDisplay',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._appSystem = Shell.AppSystem.get_default();
 | 
			
		||||
        this._appSystem.connect('installed-changed', Lang.bind(this, function() {
 | 
			
		||||
            Main.queueDeferredWork(this._workId);
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        this._appView = new ViewByCategories();
 | 
			
		||||
        this.actor = new St.Bin({ child: this._appView.actor, x_fill: true, y_fill: true });
 | 
			
		||||
 | 
			
		||||
        this._workId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplay));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _redisplay: function() {
 | 
			
		||||
        this._appView.refresh();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const AppSearchProvider = new Lang.Class({
 | 
			
		||||
    Name: 'AppSearchProvider',
 | 
			
		||||
 | 
			
		||||
@@ -554,183 +360,70 @@ const AppSearchProvider = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    createResultActor: function (resultMeta, terms) {
 | 
			
		||||
        let app = resultMeta['id'];
 | 
			
		||||
        let icon = new AppIcon(app);
 | 
			
		||||
        let icon = new AppWellIcon(app);
 | 
			
		||||
        return icon.actor;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const FolderIcon = new Lang.Class({
 | 
			
		||||
    Name: 'FolderIcon',
 | 
			
		||||
const SettingsSearchProvider = new Lang.Class({
 | 
			
		||||
    Name: 'SettingsSearchProvider',
 | 
			
		||||
 | 
			
		||||
    _init: function(dir, parentView) {
 | 
			
		||||
        this._dir = dir;
 | 
			
		||||
        this._parentView = parentView;
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.Button({ style_class: 'app-well-app app-folder',
 | 
			
		||||
                                     button_mask: St.ButtonMask.ONE,
 | 
			
		||||
                                     toggle_mode: true,
 | 
			
		||||
                                     can_focus: true,
 | 
			
		||||
                                     x_fill: true,
 | 
			
		||||
                                     y_fill: true });
 | 
			
		||||
        this.actor._delegate = this;
 | 
			
		||||
 | 
			
		||||
        let label = this._dir.get_name();
 | 
			
		||||
        this.icon = new IconGrid.BaseIcon(label,
 | 
			
		||||
                                          { createIcon: Lang.bind(this, this._createIcon) });
 | 
			
		||||
        this.actor.set_child(this.icon.actor);
 | 
			
		||||
        this.actor.label_actor = this.icon.label;
 | 
			
		||||
 | 
			
		||||
        this.view = new FolderView();
 | 
			
		||||
        this.view.actor.reactive = false;
 | 
			
		||||
        _loadCategory(dir, this.view);
 | 
			
		||||
        this.view.loadGrid();
 | 
			
		||||
 | 
			
		||||
        this.actor.connect('clicked', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                this._ensurePopup();
 | 
			
		||||
                this._popup.toggle();
 | 
			
		||||
            }));
 | 
			
		||||
        this.actor.connect('notify::mapped', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                if (!this.actor.mapped && this._popup)
 | 
			
		||||
                    this._popup.popdown();
 | 
			
		||||
            }));
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.appInfo = Gio.DesktopAppInfo.new('gnome-control-center.desktop');
 | 
			
		||||
        this._appSys = Shell.AppSystem.get_default();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createIcon: function(size) {
 | 
			
		||||
        return this.view.createFolderIcon(size);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _ensurePopup: function() {
 | 
			
		||||
        if (this._popup)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let spaceTop = this.actor.y;
 | 
			
		||||
        let spaceBottom = this._parentView.actor.height - (this.actor.y + this.actor.height);
 | 
			
		||||
        let side = spaceTop > spaceBottom ? St.Side.BOTTOM : St.Side.TOP;
 | 
			
		||||
 | 
			
		||||
        this._popup = new AppFolderPopup(this, side);
 | 
			
		||||
        this._parentView.addFolderPopup(this._popup);
 | 
			
		||||
 | 
			
		||||
        // Position the popup above or below the source icon
 | 
			
		||||
        if (side == St.Side.BOTTOM) {
 | 
			
		||||
            this._popup.actor.show();
 | 
			
		||||
            let closeButtonOffset = -this._popup.closeButton.translation_y;
 | 
			
		||||
            let y = this.actor.y - this._popup.actor.height;
 | 
			
		||||
            let yWithButton = y - closeButtonOffset;
 | 
			
		||||
            this._popup.parentOffset = yWithButton < 0 ? -yWithButton : 0;
 | 
			
		||||
            this._popup.actor.y = Math.max(y, closeButtonOffset);
 | 
			
		||||
            this._popup.actor.hide();
 | 
			
		||||
        } else {
 | 
			
		||||
            this._popup.actor.y = this.actor.y + this.actor.height;
 | 
			
		||||
    getResultMetas: function(prefs, callback) {
 | 
			
		||||
        let metas = [];
 | 
			
		||||
        for (let i = 0; i < prefs.length; i++) {
 | 
			
		||||
            let pref = prefs[i];
 | 
			
		||||
            metas.push({ 'id': pref,
 | 
			
		||||
                         'name': pref.get_name(),
 | 
			
		||||
                         'createIcon': function() { return null; }
 | 
			
		||||
                       });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._popup.connect('open-state-changed', Lang.bind(this,
 | 
			
		||||
            function(popup, isOpen) {
 | 
			
		||||
                if (!isOpen)
 | 
			
		||||
                    this.actor.checked = false;
 | 
			
		||||
            }));
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const AppFolderPopup = new Lang.Class({
 | 
			
		||||
    Name: 'AppFolderPopup',
 | 
			
		||||
 | 
			
		||||
    _init: function(source, side) {
 | 
			
		||||
        this._source = source;
 | 
			
		||||
        this._view = source.view;
 | 
			
		||||
        this._arrowSide = side;
 | 
			
		||||
 | 
			
		||||
        this._isOpen = false;
 | 
			
		||||
        this.parentOffset = 0;
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.Widget({ layout_manager: new Clutter.BinLayout(),
 | 
			
		||||
                                     visible: false,
 | 
			
		||||
                                     // We don't want to expand really, but look
 | 
			
		||||
                                     // at the layout manager of our parent...
 | 
			
		||||
                                     //
 | 
			
		||||
                                     // DOUBLE HACK: if you set one, you automatically
 | 
			
		||||
                                     // get the effect for the other direction too, so
 | 
			
		||||
                                     // we need to set the y_align
 | 
			
		||||
                                     x_expand: true,
 | 
			
		||||
                                     y_expand: true,
 | 
			
		||||
                                     x_align: Clutter.ActorAlign.CENTER,
 | 
			
		||||
                                     y_align: Clutter.ActorAlign.START });
 | 
			
		||||
        this._boxPointer = new BoxPointer.BoxPointer(this._arrowSide,
 | 
			
		||||
                                                     { style_class: 'app-folder-popup-bin',
 | 
			
		||||
                                                       x_fill: true,
 | 
			
		||||
                                                       y_fill: true,
 | 
			
		||||
                                                       x_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        this._boxPointer.actor.style_class = 'app-folder-popup';
 | 
			
		||||
        this.actor.add_actor(this._boxPointer.actor);
 | 
			
		||||
        this._boxPointer.bin.set_child(this._view.actor);
 | 
			
		||||
 | 
			
		||||
        this.closeButton = Util.makeCloseButton();
 | 
			
		||||
        this.closeButton.connect('clicked', Lang.bind(this, this.popdown));
 | 
			
		||||
        this.actor.add_actor(this.closeButton);
 | 
			
		||||
 | 
			
		||||
        this._boxPointer.actor.bind_property('opacity', this.closeButton, 'opacity',
 | 
			
		||||
                                             GObject.BindingFlags.SYNC_CREATE);
 | 
			
		||||
 | 
			
		||||
        global.focus_manager.add_group(this.actor);
 | 
			
		||||
 | 
			
		||||
        source.actor.connect('destroy', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                this.actor.destroy();
 | 
			
		||||
            }));
 | 
			
		||||
        this.actor.connect('key-press-event', Lang.bind(this, this._onKeyPress));
 | 
			
		||||
        callback(metas);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onKeyPress: function(actor, event) {
 | 
			
		||||
        if (!this._isOpen)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        if (event.get_key_symbol() != Clutter.KEY_Escape)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        this.popdown();
 | 
			
		||||
        return true;
 | 
			
		||||
    getInitialResultSet: function(terms) {
 | 
			
		||||
        this.searchSystem.pushResults(this, this._appSys.search_settings(terms));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    toggle: function() {
 | 
			
		||||
        if (this._isOpen)
 | 
			
		||||
            this.popdown();
 | 
			
		||||
        else
 | 
			
		||||
            this.popup();
 | 
			
		||||
    getSubsearchResultSet: function(previousResults, terms) {
 | 
			
		||||
        this.searchSystem.pushResults(this, this._appSys.search_settings(terms));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    popup: function() {
 | 
			
		||||
        if (this._isOpen)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this.actor.show();
 | 
			
		||||
        this.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
 | 
			
		||||
 | 
			
		||||
        this._boxPointer.setArrowActor(this._source.actor);
 | 
			
		||||
        this._boxPointer.show(BoxPointer.PopupAnimation.FADE |
 | 
			
		||||
                              BoxPointer.PopupAnimation.SLIDE);
 | 
			
		||||
 | 
			
		||||
        this._isOpen = true;
 | 
			
		||||
        this.emit('open-state-changed', true);
 | 
			
		||||
    activateResult: function(pref) {
 | 
			
		||||
        pref.activate();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    popdown: function() {
 | 
			
		||||
        if (!this._isOpen)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._boxPointer.hide(BoxPointer.PopupAnimation.FADE |
 | 
			
		||||
                              BoxPointer.PopupAnimation.SLIDE);
 | 
			
		||||
        this._isOpen = false;
 | 
			
		||||
        this.emit('open-state-changed', false);
 | 
			
		||||
    launchSearch: function(terms) {
 | 
			
		||||
        // FIXME: this should be a remote search provider
 | 
			
		||||
        this.appInfo.launch([], global.create_app_launch_context());
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(AppFolderPopup.prototype);
 | 
			
		||||
 | 
			
		||||
const AppIcon = new Lang.Class({
 | 
			
		||||
    Name: 'AppIcon',
 | 
			
		||||
    Extends: IconGrid.BaseIcon,
 | 
			
		||||
 | 
			
		||||
    _init : function(app, iconParams) {
 | 
			
		||||
    _init : function(app, params) {
 | 
			
		||||
        this.app = app;
 | 
			
		||||
 | 
			
		||||
        let label = this.app.get_name();
 | 
			
		||||
 | 
			
		||||
        this.parent(label, params);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    createIcon: function(iconSize) {
 | 
			
		||||
        return this.app.create_icon_texture(iconSize);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const AppWellIcon = new Lang.Class({
 | 
			
		||||
    Name: 'AppWellIcon',
 | 
			
		||||
 | 
			
		||||
    _init : function(app, iconParams, onActivateOverride) {
 | 
			
		||||
        this.app = app;
 | 
			
		||||
        this.actor = new St.Button({ style_class: 'app-well-app',
 | 
			
		||||
                                     reactive: true,
 | 
			
		||||
@@ -740,15 +433,13 @@ const AppIcon = new Lang.Class({
 | 
			
		||||
                                     y_fill: true });
 | 
			
		||||
        this.actor._delegate = this;
 | 
			
		||||
 | 
			
		||||
        if (!iconParams)
 | 
			
		||||
            iconParams = {};
 | 
			
		||||
 | 
			
		||||
        iconParams['createIcon'] = Lang.bind(this, this._createIcon);
 | 
			
		||||
        this.icon = new IconGrid.BaseIcon(app.get_name(), iconParams);
 | 
			
		||||
        this.icon = new AppIcon(app, iconParams);
 | 
			
		||||
        this.actor.set_child(this.icon.actor);
 | 
			
		||||
 | 
			
		||||
        this.actor.label_actor = this.icon.label;
 | 
			
		||||
 | 
			
		||||
        // A function callback to override the default "app.activate()"; used by preferences
 | 
			
		||||
        this._onActivateOverride = onActivateOverride;
 | 
			
		||||
        this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
 | 
			
		||||
        this.actor.connect('clicked', Lang.bind(this, this._onClicked));
 | 
			
		||||
        this.actor.connect('popup-menu', Lang.bind(this, this._onKeyboardPopupMenu));
 | 
			
		||||
@@ -787,10 +478,6 @@ const AppIcon = new Lang.Class({
 | 
			
		||||
        this._removeMenuTimeout();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createIcon: function(iconSize) {
 | 
			
		||||
        return this.app.create_icon_texture(iconSize);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _removeMenuTimeout: function() {
 | 
			
		||||
        if (this._menuTimeoutId > 0) {
 | 
			
		||||
            Mainloop.source_remove(this._menuTimeoutId);
 | 
			
		||||
@@ -848,7 +535,6 @@ const AppIcon = new Lang.Class({
 | 
			
		||||
    popupMenu: function() {
 | 
			
		||||
        this._removeMenuTimeout();
 | 
			
		||||
        this.actor.fake_release();
 | 
			
		||||
        this._draggable.fakeRelease();
 | 
			
		||||
 | 
			
		||||
        if (!this._menu) {
 | 
			
		||||
            this._menu = new AppIconMenu(this);
 | 
			
		||||
@@ -890,13 +576,16 @@ const AppIcon = new Lang.Class({
 | 
			
		||||
        this.emit('launching');
 | 
			
		||||
        let modifiers = event.get_state();
 | 
			
		||||
 | 
			
		||||
        if (modifiers & Clutter.ModifierType.CONTROL_MASK
 | 
			
		||||
            && this.app.state == Shell.AppState.RUNNING) {
 | 
			
		||||
            this.app.open_new_window(-1);
 | 
			
		||||
        if (this._onActivateOverride) {
 | 
			
		||||
            this._onActivateOverride(event);
 | 
			
		||||
        } else {
 | 
			
		||||
            this.app.activate();
 | 
			
		||||
            if (modifiers & Clutter.ModifierType.CONTROL_MASK
 | 
			
		||||
                && this.app.state == Shell.AppState.RUNNING) {
 | 
			
		||||
                this.app.open_new_window(-1);
 | 
			
		||||
            } else {
 | 
			
		||||
                this.app.activate();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Main.overview.hide();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -917,7 +606,7 @@ const AppIcon = new Lang.Class({
 | 
			
		||||
        return this.icon.icon;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(AppIcon.prototype);
 | 
			
		||||
Signals.addSignalMethods(AppWellIcon.prototype);
 | 
			
		||||
 | 
			
		||||
const AppIconMenu = new Lang.Class({
 | 
			
		||||
    Name: 'AppIconMenu',
 | 
			
		||||
 
 | 
			
		||||
@@ -1,798 +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 });
 | 
			
		||||
        this.actor = new Meta.BackgroundGroup();
 | 
			
		||||
        this.actor._delegate = this;
 | 
			
		||||
 | 
			
		||||
        this._destroySignalId = this.actor.connect('destroy',
 | 
			
		||||
                                                   Lang.bind(this, this._destroy));
 | 
			
		||||
 | 
			
		||||
        this._settings = new Gio.Settings({ schema: BACKGROUND_SCHEMA });
 | 
			
		||||
        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._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;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _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 = Gio.File.new_for_uri(uri).get_path();
 | 
			
		||||
 | 
			
		||||
        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 });
 | 
			
		||||
 | 
			
		||||
        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 });
 | 
			
		||||
        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();
 | 
			
		||||
                  });
 | 
			
		||||
}
 | 
			
		||||
@@ -38,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();
 | 
			
		||||
@@ -221,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');
 | 
			
		||||
@@ -402,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();
 | 
			
		||||
@@ -414,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) {
 | 
			
		||||
@@ -427,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());
 | 
			
		||||
@@ -551,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.
 | 
			
		||||
@@ -572,47 +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.y2 + 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.y1 - 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;
 | 
			
		||||
            }));
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    set xOffset(offset) {
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
@@ -12,6 +11,7 @@ const Mainloop = imports.mainloop;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
 | 
			
		||||
const MSECS_IN_DAY = 24 * 60 * 60 * 1000;
 | 
			
		||||
const WEEKDATE_HEADER_WIDTH_DIGITS = 3;
 | 
			
		||||
const SHOW_WEEKDATE_KEY = 'show-weekdate';
 | 
			
		||||
 | 
			
		||||
// in org.gnome.desktop.interface
 | 
			
		||||
@@ -63,18 +63,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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -98,6 +95,15 @@ function _getCalendarWeekForDate(date) {
 | 
			
		||||
    return weekNumber;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _getDigitWidth(actor){
 | 
			
		||||
    let context = actor.get_pango_context();
 | 
			
		||||
    let themeNode = actor.get_theme_node();
 | 
			
		||||
    let font = themeNode.get_font();
 | 
			
		||||
    let metrics = context.get_metrics(font, context.get_language());
 | 
			
		||||
    let width = metrics.get_approximate_digit_width();
 | 
			
		||||
    return width;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _getCalendarDayAbbreviation(dayNumber) {
 | 
			
		||||
    let abbreviations = [
 | 
			
		||||
        /* Translators: Calendar grid abbreviation for Sunday.
 | 
			
		||||
@@ -168,12 +174,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 +197,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 +238,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 +289,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 +307,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;
 | 
			
		||||
@@ -379,6 +345,8 @@ const Calendar = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._weekStart = Shell.util_get_week_start();
 | 
			
		||||
        this._weekdate = NaN;
 | 
			
		||||
        this._digitWidth = NaN;
 | 
			
		||||
        this._settings = new Gio.Settings({ schema: 'org.gnome.shell.calendar' });
 | 
			
		||||
 | 
			
		||||
        this._settings.connect('changed::' + SHOW_WEEKDATE_KEY, Lang.bind(this, this._onSettingsChange));
 | 
			
		||||
@@ -415,11 +383,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,6 +419,8 @@ const Calendar = new Lang.Class({
 | 
			
		||||
        this.actor.add(this._topBox,
 | 
			
		||||
                       { row: 0, col: 0, col_span: offsetCols + 7 });
 | 
			
		||||
 | 
			
		||||
        this.actor.connect('style-changed', Lang.bind(this, this._onStyleChange));
 | 
			
		||||
 | 
			
		||||
        let back = new St.Button({ style_class: 'calendar-change-month-back' });
 | 
			
		||||
        this._topBox.add(back);
 | 
			
		||||
        back.connect('clicked', Lang.bind(this, this._onPrevMonthButtonClicked));
 | 
			
		||||
@@ -479,6 +457,18 @@ const Calendar = new Lang.Class({
 | 
			
		||||
        this._firstDayIndex = this.actor.get_n_children();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onStyleChange: function(actor, event) {
 | 
			
		||||
        // width of a digit in pango units
 | 
			
		||||
        this._digitWidth = _getDigitWidth(this.actor) / Pango.SCALE;
 | 
			
		||||
        this._setWeekdateHeaderWidth();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _setWeekdateHeaderWidth: function() {
 | 
			
		||||
        if (this.digitWidth != NaN && this._useWeekdate && this._weekdateHeader) {
 | 
			
		||||
            this._weekdateHeader.set_width (this._digitWidth * WEEKDATE_HEADER_WIDTH_DIGITS);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onScroll : function(actor, event) {
 | 
			
		||||
        switch (event.get_scroll_direction()) {
 | 
			
		||||
        case Clutter.ScrollDirection.UP:
 | 
			
		||||
@@ -556,44 +546,20 @@ 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) {
 | 
			
		||||
        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();
 | 
			
		||||
@@ -602,9 +568,8 @@ const Calendar = new Lang.Class({
 | 
			
		||||
                this.setDate(newlySelectedDate, 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
 | 
			
		||||
@@ -644,13 +609,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);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@@ -668,8 +637,16 @@ 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(dayNameBox, timeBox, eventTitleBox, includeDayName, day, time, desc) {
 | 
			
		||||
@@ -686,6 +663,9 @@ const EventsList = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _addPeriod: function(header, begin, end, includeDayName, showNothingScheduled) {
 | 
			
		||||
        if (!this._eventSource)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let events = this._eventSource.getEvents(begin, end);
 | 
			
		||||
 | 
			
		||||
        let clockFormat = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);;
 | 
			
		||||
@@ -782,9 +762,6 @@ const EventsList = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _update: function() {
 | 
			
		||||
        if (this._eventSource.isLoading)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let today = new Date();
 | 
			
		||||
        if (_sameDay (this._date, today)) {
 | 
			
		||||
            this._showToday();
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										60
									
								
								js/ui/centerLayout.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								js/ui/centerLayout.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,60 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
 | 
			
		||||
const CenterLayout = new Lang.Class({
 | 
			
		||||
    Name: 'CenterLayout',
 | 
			
		||||
    Extends: Clutter.BoxLayout,
 | 
			
		||||
 | 
			
		||||
    vfunc_allocate: function(container, box, flags) {
 | 
			
		||||
        let rtl = container.get_text_direction() == Clutter.TextDirection.RTL;
 | 
			
		||||
 | 
			
		||||
        let availWidth = box.x2 - box.x1;
 | 
			
		||||
        let availHeight = box.y2 - box.y1;
 | 
			
		||||
 | 
			
		||||
        // Assume that these are the first three widgets and they are all visible.
 | 
			
		||||
        let [left, center, right] = container.get_children();
 | 
			
		||||
 | 
			
		||||
        // Only support horizontal layouts for now.
 | 
			
		||||
        let [leftMinWidth, leftNaturalWidth] = left.get_preferred_width(availHeight);
 | 
			
		||||
        let [centerMinWidth, centerNaturalWidth] = center.get_preferred_width(availHeight);
 | 
			
		||||
        let [rightMinWidth, rightNaturalWidth] = right.get_preferred_width(availHeight);
 | 
			
		||||
 | 
			
		||||
        let sideWidth = (availWidth - centerMinWidth) / 2;
 | 
			
		||||
 | 
			
		||||
        let childBox = new Clutter.ActorBox();
 | 
			
		||||
        childBox.y1 = box.y1;
 | 
			
		||||
        childBox.y2 = box.y1 + availHeight;
 | 
			
		||||
 | 
			
		||||
        let leftSide = Math.min(Math.floor(sideWidth), leftNaturalWidth);
 | 
			
		||||
        let rightSide = Math.min(Math.floor(sideWidth), rightNaturalWidth);
 | 
			
		||||
 | 
			
		||||
        if (rtl) {
 | 
			
		||||
            childBox.x1 = availWidth - leftSide;
 | 
			
		||||
            childBox.x2 = availWidth;
 | 
			
		||||
        } else {
 | 
			
		||||
            childBox.x1 = 0;
 | 
			
		||||
            childBox.x2 = leftSide;
 | 
			
		||||
        }
 | 
			
		||||
        childBox.x1 += box.x1;
 | 
			
		||||
        left.allocate(childBox, flags);
 | 
			
		||||
 | 
			
		||||
        let maxSide = Math.max(leftSide, rightSide);
 | 
			
		||||
        let sideWidth = Math.max((availWidth - centerNaturalWidth) / 2, maxSide);
 | 
			
		||||
 | 
			
		||||
        childBox.x1 = box.x1 + Math.ceil(sideWidth);
 | 
			
		||||
        childBox.x2 = box.x2 - Math.ceil(sideWidth);
 | 
			
		||||
        center.allocate(childBox, flags);
 | 
			
		||||
 | 
			
		||||
        if (rtl) {
 | 
			
		||||
            childBox.x1 = 0;
 | 
			
		||||
            childBox.x2 = rightSide;
 | 
			
		||||
        } else {
 | 
			
		||||
            childBox.x1 = availWidth - rightSide;
 | 
			
		||||
            childBox.x2 = availWidth;
 | 
			
		||||
        }
 | 
			
		||||
        childBox.x1 += box.x1;
 | 
			
		||||
        right.allocate(childBox, flags);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
@@ -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;
 | 
			
		||||
@@ -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;
 | 
			
		||||
    },
 | 
			
		||||
 
 | 
			
		||||
@@ -25,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 });
 | 
			
		||||
@@ -60,20 +60,18 @@ 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);
 | 
			
		||||
@@ -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();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -236,7 +215,7 @@ const KeyringPrompter = new Lang.Class({
 | 
			
		||||
    enable: function() {
 | 
			
		||||
        this._prompter.register(Gio.DBus.session);
 | 
			
		||||
        this._dbusId = Gio.DBus.session.own_name('org.gnome.keyring.SystemPrompter',
 | 
			
		||||
                                                 Gio.BusNameOwnerFlags.ALLOW_REPLACEMENT, null, null);
 | 
			
		||||
                                                 Gio.BusNameOwnerFlags.REPLACE, null, null);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    disable: function() {
 | 
			
		||||
 
 | 
			
		||||
@@ -587,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() {
 | 
			
		||||
@@ -613,15 +612,11 @@ const NetworkAgent = new Lang.Class({
 | 
			
		||||
            this._vpnRequests[requestId].cancel(true);
 | 
			
		||||
        this._vpnRequests = { };
 | 
			
		||||
 | 
			
		||||
        this._enabled = false;
 | 
			
		||||
        this._native.auto_register = false;
 | 
			
		||||
        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;
 | 
			
		||||
 
 | 
			
		||||
@@ -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 });
 | 
			
		||||
@@ -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;
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,8 @@ const Recorder = new Lang.Class({
 | 
			
		||||
        Main.wm.addKeybinding('toggle-recording',
 | 
			
		||||
                              this._bindingSettings,
 | 
			
		||||
                              Meta.KeyBindingFlags.NONE,
 | 
			
		||||
                              Shell.KeyBindingMode.ALL,
 | 
			
		||||
                              Main.KeybindingMode.NORMAL |
 | 
			
		||||
                              Main.KeybindingMode.OVERVIEW,
 | 
			
		||||
                              Lang.bind(this, this._toggleRecorder));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,6 @@ const Tp = imports.gi.TelepathyGLib;
 | 
			
		||||
const History = imports.misc.history;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const MessageTray = imports.ui.messageTray;
 | 
			
		||||
const NotificationDaemon = imports.ui.notificationDaemon;
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
const PopupMenu = imports.ui.popupMenu;
 | 
			
		||||
 | 
			
		||||
@@ -416,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 NotificationDaemon.NotificationApplicationPolicy('empathy');
 | 
			
		||||
 | 
			
		||||
            Main.messageTray.add(this._appSource);
 | 
			
		||||
            this._appSource.connect('destroy', Lang.bind(this, function () {
 | 
			
		||||
                this._appSource = null;
 | 
			
		||||
@@ -487,10 +484,6 @@ const ChatSource = new Lang.Class({
 | 
			
		||||
        return rightClickMenu;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createPolicy: function() {
 | 
			
		||||
        return new NotificationDaemon.NotificationApplicationPolicy('empathy');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateAlias: function() {
 | 
			
		||||
        let oldAlias = this.title;
 | 
			
		||||
        let newAlias = this._contact.get_alias();
 | 
			
		||||
@@ -640,10 +633,6 @@ const ChatSource = new Lang.Class({
 | 
			
		||||
        return this._pendingMessages.length;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get indicatorCount() {
 | 
			
		||||
        return this.count;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get unseenCount() {
 | 
			
		||||
        return this.count;
 | 
			
		||||
    },
 | 
			
		||||
@@ -1059,10 +1048,6 @@ const ApproverSource = new Lang.Class({
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createPolicy: function() {
 | 
			
		||||
        return new NotificationDaemon.NotificationApplicationPolicy('empathy');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroy: function() {
 | 
			
		||||
        if (this._invalidId != 0) {
 | 
			
		||||
            this._dispatchOp.disconnect(this._invalidId);
 | 
			
		||||
@@ -1361,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;
 | 
			
		||||
 
 | 
			
		||||
@@ -89,33 +89,22 @@ 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');
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                items.push({ name: windows[i].title,
 | 
			
		||||
                             proxy: windows[i].get_compositor_private(),
 | 
			
		||||
                             focusCallback: Lang.bind(windows[i],
 | 
			
		||||
                                 function(timestamp) {
 | 
			
		||||
                                     Main.activateWindow(this, timestamp);
 | 
			
		||||
                                 }),
 | 
			
		||||
                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({ window: windows[i],
 | 
			
		||||
                             name: windows[i].title,
 | 
			
		||||
                             iconActor: icon,
 | 
			
		||||
                             iconName: iconName,
 | 
			
		||||
                             sortGroup: SortGroup.MIDDLE });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										323
									
								
								js/ui/dash.js
									
									
									
									
									
								
							
							
						
						
									
										323
									
								
								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() {
 | 
			
		||||
@@ -326,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,
 | 
			
		||||
@@ -366,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];
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@@ -393,6 +371,7 @@ const Dash = new Lang.Class({
 | 
			
		||||
        this._maxHeight = -1;
 | 
			
		||||
        this.iconSize = 64;
 | 
			
		||||
        this._shownInitially = false;
 | 
			
		||||
        this._ignoreHeight = false;
 | 
			
		||||
 | 
			
		||||
        this._dragPlaceholder = null;
 | 
			
		||||
        this._dragPlaceholderPos = -1;
 | 
			
		||||
@@ -408,19 +387,20 @@ 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,
 | 
			
		||||
            function() {
 | 
			
		||||
                if (this._maxHeight != this.actor.height)
 | 
			
		||||
                if (this._ignoreHeight)
 | 
			
		||||
                    return;
 | 
			
		||||
 | 
			
		||||
                if (this._maxHeight != this.actor.height);
 | 
			
		||||
                    this._queueRedisplay();
 | 
			
		||||
                this._maxHeight = this.actor.height;
 | 
			
		||||
            }));
 | 
			
		||||
@@ -439,10 +419,14 @@ 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));
 | 
			
		||||
        Main.overview.connect('showing',
 | 
			
		||||
                              Lang.bind(this, this._onOverviewShowing));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDragBegin: function() {
 | 
			
		||||
@@ -451,12 +435,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() {
 | 
			
		||||
@@ -473,7 +451,6 @@ const Dash = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _endDrag: function() {
 | 
			
		||||
        this._clearDragPlaceholder();
 | 
			
		||||
        this._clearEmptyDropTarget();
 | 
			
		||||
        this._showAppsIcon.setDragApp(null);
 | 
			
		||||
        DND.removeDragMonitor(this._dragMonitor);
 | 
			
		||||
    },
 | 
			
		||||
@@ -484,7 +461,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();
 | 
			
		||||
@@ -520,9 +497,9 @@ const Dash = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _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;
 | 
			
		||||
@@ -539,7 +516,7 @@ 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());
 | 
			
		||||
@@ -599,13 +576,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;
 | 
			
		||||
@@ -618,18 +595,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) +
 | 
			
		||||
@@ -654,7 +636,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
 | 
			
		||||
@@ -674,11 +656,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;
 | 
			
		||||
                               }
 | 
			
		||||
                             });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
@@ -689,13 +675,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 = [];
 | 
			
		||||
@@ -761,7 +747,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);
 | 
			
		||||
 | 
			
		||||
@@ -778,11 +764,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
 | 
			
		||||
@@ -796,39 +782,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;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -852,22 +824,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;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@@ -876,7 +853,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;
 | 
			
		||||
@@ -885,16 +862,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;
 | 
			
		||||
 | 
			
		||||
@@ -925,10 +903,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)
 | 
			
		||||
@@ -951,6 +929,65 @@ const Dash = new Lang.Class({
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _computeTranslation: function() {
 | 
			
		||||
        let rtl = (this.actor.get_text_direction() == Clutter.TextDirection.RTL);
 | 
			
		||||
 | 
			
		||||
        if (rtl)
 | 
			
		||||
            return this.actor.width;
 | 
			
		||||
        else
 | 
			
		||||
            return - this.actor.width;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onOverviewShowing: function() {
 | 
			
		||||
        // reset any translation and make sure the actor is visible when
 | 
			
		||||
        // entering the overview
 | 
			
		||||
        this.slideX = 0;
 | 
			
		||||
        this.actor.show();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get slideX() {
 | 
			
		||||
        return this._slideX;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    set slideX(value) {
 | 
			
		||||
        this._slideX = value;
 | 
			
		||||
        this.actor.translation_x = this._slideX;
 | 
			
		||||
 | 
			
		||||
        if (this._slideX > 0) {
 | 
			
		||||
            let rect = new Clutter.Rect();
 | 
			
		||||
            rect.size.width = this.actor.width - this._slideX;
 | 
			
		||||
            rect.size.height = this.actor.height;
 | 
			
		||||
            this.actor.clip_rect = rect;
 | 
			
		||||
        } else {
 | 
			
		||||
            this.actor.clip_rect = null;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    show: function() {
 | 
			
		||||
        this.actor.show();
 | 
			
		||||
        Tweener.addTween(this, { slideX: 0,
 | 
			
		||||
                                 transition: 'easeOutQuad',
 | 
			
		||||
                                 time: DASH_ANIMATION_TIME,
 | 
			
		||||
                                 onComplete: Lang.bind(this,
 | 
			
		||||
                                     function() {
 | 
			
		||||
                                         this._ignoreHeight = false;
 | 
			
		||||
                                     })
 | 
			
		||||
                               });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    hide: function() {
 | 
			
		||||
        this._ignoreHeight = true;
 | 
			
		||||
        let hiddenX = this._computeTranslation();
 | 
			
		||||
        Tweener.addTween(this, { slideX: hiddenX,
 | 
			
		||||
                                 transition: 'easeOutQuad',
 | 
			
		||||
                                 time: DASH_ANIMATION_TIME,
 | 
			
		||||
                                 onComplete: Lang.bind(this,
 | 
			
		||||
                                 function() {
 | 
			
		||||
                                     this.actor.hide();
 | 
			
		||||
                                 })
 | 
			
		||||
                               });
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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({
 | 
			
		||||
@@ -84,25 +83,12 @@ const DateMenuButton = new Lang.Class({
 | 
			
		||||
                               }));
 | 
			
		||||
        vbox.add(this._calendar.actor);
 | 
			
		||||
 | 
			
		||||
        let separator = new PopupMenu.PopupSeparatorMenuItem();
 | 
			
		||||
        separator.setColumnWidths(1);
 | 
			
		||||
        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));
 | 
			
		||||
        this._openCalendarItem.actor.can_focus = false;
 | 
			
		||||
        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));
 | 
			
		||||
        this._openClocksItem.actor.can_focus = false;
 | 
			
		||||
        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);
 | 
			
		||||
@@ -122,6 +108,16 @@ const DateMenuButton = new Lang.Class({
 | 
			
		||||
        // 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) {
 | 
			
		||||
            if (isOpen) {
 | 
			
		||||
@@ -155,17 +151,14 @@ 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;
 | 
			
		||||
        if (visible) {
 | 
			
		||||
          let alignment = 0.25;
 | 
			
		||||
@@ -180,16 +173,8 @@ const DateMenuButton = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _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() {
 | 
			
		||||
@@ -198,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
 | 
			
		||||
@@ -218,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)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										52
									
								
								js/ui/dnd.js
									
									
									
									
									
								
							
							
						
						
									
										52
									
								
								js/ui/dnd.js
									
									
									
									
									
								
							@@ -85,13 +85,11 @@ const _Draggable = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this.actor.connect('destroy', Lang.bind(this, function() {
 | 
			
		||||
            this._actorDestroyed = true;
 | 
			
		||||
 | 
			
		||||
            // 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._dragCancellable)
 | 
			
		||||
            if (this._dragInProgress)
 | 
			
		||||
                this._cancelDrag(global.get_current_time());
 | 
			
		||||
            this.disconnectAll();
 | 
			
		||||
        }));
 | 
			
		||||
@@ -104,7 +102,6 @@ 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
 | 
			
		||||
@@ -139,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;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
@@ -207,19 +203,6 @@ const _Draggable = new Lang.Class({
 | 
			
		||||
        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
 | 
			
		||||
@@ -291,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;
 | 
			
		||||
@@ -442,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);
 | 
			
		||||
@@ -455,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) {
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,6 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Mainloop = imports.mainloop;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
 | 
			
		||||
const AccountsService = imports.gi.AccountsService;
 | 
			
		||||
@@ -51,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" />
 | 
			
		||||
@@ -225,8 +223,7 @@ 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._user = AccountsService.UserManager.get_default().get_user(GLib.get_user_name());
 | 
			
		||||
 | 
			
		||||
@@ -380,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 });
 | 
			
		||||
        }
 | 
			
		||||
@@ -393,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) {
 | 
			
		||||
@@ -418,34 +408,22 @@ const EndSessionDialog = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _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._updateDescription();
 | 
			
		||||
                    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;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -462,7 +440,7 @@ const EndSessionDialog = new Lang.Class({
 | 
			
		||||
            let item = new ListItem(app, reason);
 | 
			
		||||
            item.connect('activate',
 | 
			
		||||
                         Lang.bind(this, function() {
 | 
			
		||||
                             this.close();
 | 
			
		||||
                             this.close(global.get_current_time());
 | 
			
		||||
                         }));
 | 
			
		||||
            this._applicationList.add(item.actor, { x_fill: true });
 | 
			
		||||
            this._stopTimer();
 | 
			
		||||
@@ -510,9 +488,5 @@ const EndSessionDialog = new Lang.Class({
 | 
			
		||||
                                        invocation.return_value(null);
 | 
			
		||||
                                        this.disconnect(signalId);
 | 
			
		||||
                                    }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    Close: function(parameters, invocation) {
 | 
			
		||||
        this.close();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -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());
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -106,15 +106,11 @@ 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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    extension.stateObj.enable();
 | 
			
		||||
@@ -133,7 +129,6 @@ function logExtensionError(uuid, error) {
 | 
			
		||||
    extension.state = ExtensionState.ERROR;
 | 
			
		||||
    if (!extension.errors)
 | 
			
		||||
        extension.errors = [];
 | 
			
		||||
    extension.errors.push(message);
 | 
			
		||||
 | 
			
		||||
    log('Extension "%s" had error: %s'.format(uuid, message));
 | 
			
		||||
    _signals.emit('extension-state-changed', { uuid: uuid,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										45
									
								
								js/ui/flashspot.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										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,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,6 +10,17 @@ const St = imports.gi.St;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
 | 
			
		||||
function _navigateActor(actor) {
 | 
			
		||||
    if (!actor)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    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:
 | 
			
		||||
// @owner: the actor that owns the GrabHelper
 | 
			
		||||
// @params: optional parameters to pass to Main.pushModal()
 | 
			
		||||
@@ -35,7 +46,6 @@ const GrabHelper = new Lang.Class({
 | 
			
		||||
        this._keyFocusNotifyId = 0;
 | 
			
		||||
        this._focusWindowChangedId = 0;
 | 
			
		||||
        this._ignoreRelease = false;
 | 
			
		||||
        this._isUngrabbingCount = 0;
 | 
			
		||||
 | 
			
		||||
        this._modalCount = 0;
 | 
			
		||||
        this._grabFocusCount = 0;
 | 
			
		||||
@@ -67,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;
 | 
			
		||||
@@ -168,18 +178,12 @@ const GrabHelper = new Lang.Class({
 | 
			
		||||
        if (params.grabFocus && !this._takeFocusGrab(hadFocus))
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        this._grabStack.push(params);
 | 
			
		||||
 | 
			
		||||
        if (params.focus) {
 | 
			
		||||
        if (params.focus)
 | 
			
		||||
            params.focus.grab_key_focus();
 | 
			
		||||
        } else if (newFocus && (hadFocus || params.grabFocus)) {
 | 
			
		||||
            if (!newFocus.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false))
 | 
			
		||||
                newFocus.grab_key_focus();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ((params.grabFocus || params.modal) && !this._capturedEventId)
 | 
			
		||||
            this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
 | 
			
		||||
        else if (hadFocus || params.grabFocus)
 | 
			
		||||
            _navigateActor(newFocus);
 | 
			
		||||
 | 
			
		||||
        this._grabStack.push(params);
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -188,6 +192,8 @@ const GrabHelper = new Lang.Class({
 | 
			
		||||
        if (firstGrab) {
 | 
			
		||||
            if (!Main.pushModal(this._owner, this._modalParams))
 | 
			
		||||
                return false;
 | 
			
		||||
 | 
			
		||||
            this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._modalCount++;
 | 
			
		||||
@@ -199,6 +205,11 @@ const GrabHelper = new Lang.Class({
 | 
			
		||||
        if (this._modalCount > 0)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (this._capturedEventId > 0) {
 | 
			
		||||
            global.stage.disconnect(this._capturedEventId);
 | 
			
		||||
            this._capturedEventId = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Main.popModal(this._owner);
 | 
			
		||||
        global.sync_pointer();
 | 
			
		||||
    },
 | 
			
		||||
@@ -234,7 +245,7 @@ const GrabHelper = new Lang.Class({
 | 
			
		||||
            this._keyFocusNotifyId = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this._focusWindowChangedId > 0) {
 | 
			
		||||
        if (!this._focusWindowChanged > 0) {
 | 
			
		||||
            let metaDisplay = global.screen.get_display();
 | 
			
		||||
            metaDisplay.disconnect(this._focusWindowChangedId);
 | 
			
		||||
            this._focusWindowChangedId = 0;
 | 
			
		||||
@@ -248,7 +259,7 @@ const GrabHelper = new Lang.Class({
 | 
			
		||||
                global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        global.screen.focus_default_window(global.display.get_current_time_roundtrip());
 | 
			
		||||
        global.screen.focus_default_window(global.get_current_time());
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // ignoreRelease:
 | 
			
		||||
@@ -276,14 +287,6 @@ const GrabHelper = new Lang.Class({
 | 
			
		||||
        if (grabStackIndex < 0)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        // We may get key focus changes when calling onUngrab, which
 | 
			
		||||
        // would cause an extra ungrab() on the next actor in the
 | 
			
		||||
        // stack, which is wrong. Ignore key focus changes during the
 | 
			
		||||
        // ungrab, and restore the saved key focus ourselves afterwards.
 | 
			
		||||
        // We use a count as ungrab() may be re-entrant, as onUngrab()
 | 
			
		||||
        // may ungrab additional actors.
 | 
			
		||||
        this._isUngrabbingCount++;
 | 
			
		||||
 | 
			
		||||
        let focus = global.stage.key_focus;
 | 
			
		||||
        let hadFocus = focus && this._isWithinGrabbedActor(focus);
 | 
			
		||||
 | 
			
		||||
@@ -305,31 +308,14 @@ const GrabHelper = new Lang.Class({
 | 
			
		||||
                this._releaseFocusGrab();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!this.grabbed && this._capturedEventId > 0) {
 | 
			
		||||
            global.stage.disconnect(this._capturedEventId);
 | 
			
		||||
            this._capturedEventId = 0;
 | 
			
		||||
 | 
			
		||||
            this._ignoreRelease = false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (hadFocus) {
 | 
			
		||||
            let poppedGrab = poppedGrabs[0];
 | 
			
		||||
            if (poppedGrab.savedFocus)
 | 
			
		||||
                poppedGrab.savedFocus.grab_key_focus();
 | 
			
		||||
            _navigateActor(poppedGrab.savedFocus);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._isUngrabbingCount--;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _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;
 | 
			
		||||
@@ -342,6 +328,12 @@ const GrabHelper = new Lang.Class({
 | 
			
		||||
        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;
 | 
			
		||||
 | 
			
		||||
@@ -355,16 +347,12 @@ 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 this._modalCount > 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onKeyFocusChanged: function() {
 | 
			
		||||
        if (this._isUngrabbingCount > 0)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let focus = global.stage.key_focus;
 | 
			
		||||
        if (!focus || !this._isWithinGrabbedActor(focus))
 | 
			
		||||
            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) {
 | 
			
		||||
 
 | 
			
		||||
@@ -23,8 +23,6 @@ const BaseIcon = new Lang.Class({
 | 
			
		||||
        this.actor._delegate = this;
 | 
			
		||||
        this.actor.connect('style-changed',
 | 
			
		||||
                           Lang.bind(this, this._onStyleChanged));
 | 
			
		||||
        this.actor.connect('destroy',
 | 
			
		||||
                           Lang.bind(this, this._onDestroy));
 | 
			
		||||
 | 
			
		||||
        this._spacing = 0;
 | 
			
		||||
 | 
			
		||||
@@ -54,9 +52,6 @@ const BaseIcon = new Lang.Class({
 | 
			
		||||
        this._setSizeManually = params.setSizeManually;
 | 
			
		||||
 | 
			
		||||
        this.icon = null;
 | 
			
		||||
 | 
			
		||||
        let cache = St.TextureCache.get_default();
 | 
			
		||||
        this._iconThemeChangedId = cache.connect('icon-theme-changed', Lang.bind(this, this._onIconThemeChanged));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _allocate: function(actor, box, flags) {
 | 
			
		||||
@@ -151,22 +146,7 @@ const BaseIcon = new Lang.Class({
 | 
			
		||||
            size = found ? len : ICON_SIZE;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this.iconSize == size && this._iconBin.child)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._createIconTexture(size);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDestroy: function() {
 | 
			
		||||
        if (this._iconThemeChangedId > 0) {
 | 
			
		||||
            let cache = St.TextureCache.get_default();
 | 
			
		||||
            cache.disconnect(this._iconThemeChangedId);
 | 
			
		||||
            this._iconThemeChangedId = 0;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onIconThemeChanged: function() {
 | 
			
		||||
        this._createIconTexture(this.iconSize);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@@ -176,16 +156,13 @@ const IconGrid = new Lang.Class({
 | 
			
		||||
    _init: function(params) {
 | 
			
		||||
        params = Params.parse(params, { rowLimit: null,
 | 
			
		||||
                                        columnLimit: null,
 | 
			
		||||
                                        fillParent: false,
 | 
			
		||||
                                        xAlign: St.Align.MIDDLE });
 | 
			
		||||
        this._rowLimit = params.rowLimit;
 | 
			
		||||
        this._colLimit = params.columnLimit;
 | 
			
		||||
        this._xAlign = params.xAlign;
 | 
			
		||||
        this._fillParent = params.fillParent;
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.BoxLayout({ style_class: 'icon-grid',
 | 
			
		||||
                                        vertical: true });
 | 
			
		||||
 | 
			
		||||
        // Pulled from CSS, but hardcode some defaults here
 | 
			
		||||
        this._spacing = 0;
 | 
			
		||||
        this._hItemSize = this._vItemSize = ICON_SIZE;
 | 
			
		||||
@@ -199,11 +176,6 @@ 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 children = this._grid.get_children();
 | 
			
		||||
        let nColumns = this._colLimit ? Math.min(this._colLimit,
 | 
			
		||||
                                                 children.length)
 | 
			
		||||
@@ -225,20 +197,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, spacing;
 | 
			
		||||
        if (forWidth < 0) {
 | 
			
		||||
        let nColumns;
 | 
			
		||||
        if (forWidth < 0)
 | 
			
		||||
            nColumns = children.length;
 | 
			
		||||
            spacing = this._spacing;
 | 
			
		||||
        } else {
 | 
			
		||||
            [nColumns, , spacing] = this._computeLayout(forWidth);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        else
 | 
			
		||||
            nColumns = this._computeLayout(forWidth)[0];
 | 
			
		||||
        let nRows;
 | 
			
		||||
        if (nColumns > 0)
 | 
			
		||||
            nRows = Math.ceil(children.length / nColumns);
 | 
			
		||||
@@ -246,25 +210,18 @@ const IconGrid = new Lang.Class({
 | 
			
		||||
            nRows = 0;
 | 
			
		||||
        if (this._rowLimit)
 | 
			
		||||
            nRows = Math.min(nRows, this._rowLimit);
 | 
			
		||||
        let totalSpacing = Math.max(0, nRows - 1) * spacing;
 | 
			
		||||
        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 [nColumns, usedWidth, spacing] = this._computeLayout(availWidth);
 | 
			
		||||
        let [nColumns, usedWidth] = this._computeLayout(availWidth);
 | 
			
		||||
 | 
			
		||||
        let leftPadding;
 | 
			
		||||
        switch(this._xAlign) {
 | 
			
		||||
@@ -303,8 +260,7 @@ const IconGrid = new Lang.Class({
 | 
			
		||||
            childBox.x2 = childBox.x1 + width;
 | 
			
		||||
            childBox.y2 = childBox.y1 + height;
 | 
			
		||||
 | 
			
		||||
            if (this._rowLimit && rowIndex >= this._rowLimit ||
 | 
			
		||||
                this._fillParent && childBox.y2 > availHeight) {
 | 
			
		||||
            if (this._rowLimit && rowIndex >= this._rowLimit) {
 | 
			
		||||
                this._grid.set_skip_paint(children[i], true);
 | 
			
		||||
            } else {
 | 
			
		||||
                children[i].allocate(childBox, flags);
 | 
			
		||||
@@ -318,10 +274,10 @@ const IconGrid = new Lang.Class({
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (columnIndex == 0) {
 | 
			
		||||
                y += this._vItemSize + spacing;
 | 
			
		||||
                y += this._vItemSize + this._spacing;
 | 
			
		||||
                x = box.x1 + leftPadding;
 | 
			
		||||
            } else {
 | 
			
		||||
                x += this._hItemSize + spacing;
 | 
			
		||||
                x += this._hItemSize + this._spacing;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
@@ -337,25 +293,16 @@ const IconGrid = new Lang.Class({
 | 
			
		||||
    _computeLayout: function (forWidth) {
 | 
			
		||||
        let nColumns = 0;
 | 
			
		||||
        let usedWidth = 0;
 | 
			
		||||
        let spacing = this._spacing;
 | 
			
		||||
 | 
			
		||||
        if (this._colLimit) {
 | 
			
		||||
            let itemWidth = this._hItemSize * this._colLimit;
 | 
			
		||||
            let emptyArea = forWidth - itemWidth;
 | 
			
		||||
            spacing = Math.max(this._spacing, emptyArea / (2 * this._colLimit));
 | 
			
		||||
            spacing = Math.round(spacing);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        while ((this._colLimit == null || nColumns < this._colLimit) &&
 | 
			
		||||
               (usedWidth + this._hItemSize <= forWidth)) {
 | 
			
		||||
            usedWidth += this._hItemSize + spacing;
 | 
			
		||||
            usedWidth += this._hItemSize + this._spacing;
 | 
			
		||||
            nColumns += 1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (nColumns > 0)
 | 
			
		||||
            usedWidth -= spacing;
 | 
			
		||||
            usedWidth -= this._spacing;
 | 
			
		||||
 | 
			
		||||
        return [nColumns, usedWidth, spacing];
 | 
			
		||||
        return [nColumns, usedWidth];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onStyleChanged: function() {
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
    },
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1457
									
								
								js/ui/layout.js
									
									
									
									
									
								
							
							
						
						
									
										1457
									
								
								js/ui/layout.js
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -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;
 | 
			
		||||
@@ -102,7 +101,6 @@ const Lightbox = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    show: function() {
 | 
			
		||||
        Tweener.removeTweens(this.actor);
 | 
			
		||||
        if (this._fadeInTime) {
 | 
			
		||||
            this.shown = false;
 | 
			
		||||
            this.actor.opacity = 0;
 | 
			
		||||
@@ -112,20 +110,17 @@ const Lightbox = new Lang.Class({
 | 
			
		||||
                               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() {
 | 
			
		||||
        this.shown = false;
 | 
			
		||||
        Tweener.removeTweens(this.actor);
 | 
			
		||||
        if (this._fadeOutTime) {
 | 
			
		||||
            Tweener.addTween(this.actor,
 | 
			
		||||
                             { opacity: 0,
 | 
			
		||||
@@ -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); ';
 | 
			
		||||
 | 
			
		||||
@@ -685,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',
 | 
			
		||||
@@ -873,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();
 | 
			
		||||
@@ -913,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) {
 | 
			
		||||
@@ -951,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 =
 | 
			
		||||
@@ -1056,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;
 | 
			
		||||
    },
 | 
			
		||||
@@ -1132,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);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										216
									
								
								js/ui/main.js
									
									
									
									
									
								
							
							
						
						
									
										216
									
								
								js/ui/main.js
									
									
									
									
									
								
							@@ -18,13 +18,11 @@ 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;
 | 
			
		||||
@@ -33,13 +31,27 @@ 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 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
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
let componentManager = null;
 | 
			
		||||
let panel = null;
 | 
			
		||||
let overview = null;
 | 
			
		||||
@@ -51,13 +63,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 modalCount = 0;
 | 
			
		||||
let keybindingMode = Shell.KeyBindingMode.NONE;
 | 
			
		||||
let keybindingMode = KeybindingMode.NORMAL;
 | 
			
		||||
let modalActorFocusStack = [];
 | 
			
		||||
let uiGroup = null;
 | 
			
		||||
let magnifier = null;
 | 
			
		||||
@@ -67,25 +78,24 @@ let layoutManager = null;
 | 
			
		||||
let _startDate;
 | 
			
		||||
let _defaultCssStylesheet = null;
 | 
			
		||||
let _cssStylesheet = null;
 | 
			
		||||
let _workspacesSettings = 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 && lookingGlass)
 | 
			
		||||
        lookingGlass.close();
 | 
			
		||||
    if (sessionMode.isGreeter)
 | 
			
		||||
        screenShield.showDialog();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function start() {
 | 
			
		||||
@@ -99,37 +109,9 @@ function start() {
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
    let workspacesSchema;
 | 
			
		||||
    if (keys.indexOf('dynamic-workspaces') > -1)
 | 
			
		||||
        workspacesSchema = sessionMode.overridesSchema;
 | 
			
		||||
    else
 | 
			
		||||
        workspacesSchema = 'org.gnome.mutter';
 | 
			
		||||
 | 
			
		||||
     _workspacesSettings = new Gio.Settings({ schema: workspacesSchema });
 | 
			
		||||
     _workspacesSettings.connect('changed::dynamic-workspaces', _queueCheckWorkspaces);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _initializeUI() {
 | 
			
		||||
    // Ensure ShellWindowTracker and ShellAppUsage are initialized; this will
 | 
			
		||||
    // also initialize ShellAppSystem first.  ShellAppSystem
 | 
			
		||||
    // needs to load all the .desktop files, and ShellWindowTracker
 | 
			
		||||
@@ -143,26 +125,48 @@ function _initializeUI() {
 | 
			
		||||
 | 
			
		||||
    tracker.connect('startup-sequence-changed', _queueCheckWorkspaces);
 | 
			
		||||
 | 
			
		||||
    _loadDefaultStylesheet();
 | 
			
		||||
    // 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;
 | 
			
		||||
 | 
			
		||||
    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();
 | 
			
		||||
    wm = new WindowManager.WindowManager();
 | 
			
		||||
    messageTray = new MessageTray.MessageTray();
 | 
			
		||||
    keyboard = new Keyboard.Keyboard();
 | 
			
		||||
    notificationDaemon = new NotificationDaemon.NotificationDaemon();
 | 
			
		||||
@@ -170,19 +174,19 @@ function _initializeUI() {
 | 
			
		||||
    componentManager = new Components.ComponentManager();
 | 
			
		||||
 | 
			
		||||
    layoutManager.init();
 | 
			
		||||
    keyboard.init();
 | 
			
		||||
    overview.init();
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
@@ -194,6 +198,9 @@ 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);
 | 
			
		||||
@@ -204,18 +211,6 @@ function _initializeUI() {
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
                              }
 | 
			
		||||
                          });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
let _workspaces = [];
 | 
			
		||||
@@ -399,18 +394,6 @@ function _nWorkspacesChanged() {
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _loadDefaultStylesheet() {
 | 
			
		||||
    if (!sessionMode.isPrimary)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    let stylesheet = global.datadir + '/theme/' + sessionMode.stylesheetName;
 | 
			
		||||
    if (_defaultCssStylesheet == stylesheet)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    _defaultCssStylesheet = stylesheet;
 | 
			
		||||
    loadTheme();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * getThemeStylesheet:
 | 
			
		||||
 *
 | 
			
		||||
@@ -491,6 +474,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)
 | 
			
		||||
@@ -499,6 +493,10 @@ function _findModal(actor) {
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function isInModalStack(actor) {
 | 
			
		||||
    return _findModal(actor) != -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * pushModal:
 | 
			
		||||
 * @actor: #ClutterActor which will be given keyboard focus
 | 
			
		||||
@@ -521,7 +519,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
 | 
			
		||||
 *
 | 
			
		||||
@@ -530,7 +528,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)) {
 | 
			
		||||
@@ -548,20 +546,19 @@ function pushModal(actor, params) {
 | 
			
		||||
        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;
 | 
			
		||||
@@ -591,7 +588,7 @@ function popModal(actor, timestamp) {
 | 
			
		||||
        global.stage.set_key_focus(null);
 | 
			
		||||
        global.end_modal(timestamp);
 | 
			
		||||
        global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
 | 
			
		||||
        keybindingMode = Shell.KeyBindingMode.NORMAL;
 | 
			
		||||
        keybindingMode = KeybindingMode.NORMAL;
 | 
			
		||||
 | 
			
		||||
        throw new Error('incorrect pop');
 | 
			
		||||
    }
 | 
			
		||||
@@ -602,33 +599,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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -640,7 +622,7 @@ function popModal(actor, timestamp) {
 | 
			
		||||
    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() {
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -22,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,
 | 
			
		||||
@@ -41,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,
 | 
			
		||||
@@ -64,14 +58,10 @@ 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);
 | 
			
		||||
@@ -87,10 +77,15 @@ const ModalDialog = new Lang.Class({
 | 
			
		||||
                                                   { 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 });
 | 
			
		||||
@@ -101,7 +96,8 @@ const ModalDialog = new Lang.Class({
 | 
			
		||||
                                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,
 | 
			
		||||
                              { expand:  true,
 | 
			
		||||
                                x_align: St.Align.MIDDLE,
 | 
			
		||||
@@ -111,8 +107,6 @@ const ModalDialog = new Lang.Class({
 | 
			
		||||
        this._initialKeyFocus = this.dialogLayout;
 | 
			
		||||
        this._initialKeyFocusDestroyId = 0;
 | 
			
		||||
        this._savedKeyFocus = null;
 | 
			
		||||
 | 
			
		||||
        this._workSpinner = null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroy: function() {
 | 
			
		||||
@@ -126,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];
 | 
			
		||||
@@ -164,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 });
 | 
			
		||||
@@ -186,57 +180,10 @@ const ModalDialog = new Lang.Class({
 | 
			
		||||
        return button;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    placeSpinner: function(layoutInfo) {
 | 
			
		||||
        /* This is here because of recursive imports */
 | 
			
		||||
        const Panel = imports.ui.panel;
 | 
			
		||||
        let spinnerIcon = global.datadir + '/theme/process-working.svg';
 | 
			
		||||
        this._workSpinner = new Panel.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;
 | 
			
		||||
 | 
			
		||||
@@ -319,10 +266,6 @@ const ModalDialog = new Lang.Class({
 | 
			
		||||
                               function() {
 | 
			
		||||
                                   this.state = State.CLOSED;
 | 
			
		||||
                                   this._group.hide();
 | 
			
		||||
                                   this.emit('closed');
 | 
			
		||||
 | 
			
		||||
                                   if (this._destroyOnClose)
 | 
			
		||||
                                       this.destroy();
 | 
			
		||||
                               })
 | 
			
		||||
                         });
 | 
			
		||||
    },
 | 
			
		||||
 
 | 
			
		||||
@@ -103,126 +103,6 @@ const STANDARD_TRAY_ICON_IMPLEMENTATIONS = {
 | 
			
		||||
    'ibus-ui-gtk': 'keyboard'
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const NotificationGenericPolicy = new Lang.Class({
 | 
			
		||||
    Name: 'NotificationGenericPolicy',
 | 
			
		||||
    Extends: MessageTray.NotificationPolicy,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        // Don't chain to parent, it would try setting
 | 
			
		||||
        // our properties to the defaults
 | 
			
		||||
 | 
			
		||||
        this.id = 'generic';
 | 
			
		||||
 | 
			
		||||
        this._masterSettings = new Gio.Settings({ schema: 'org.gnome.desktop.notifications' });
 | 
			
		||||
        this._masterSettings.connect('changed', Lang.bind(this, this._changed));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    store: function() { },
 | 
			
		||||
 | 
			
		||||
    destroy: function() {
 | 
			
		||||
        this._masterSettings.run_dispose();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _changed: function(settings, key) {
 | 
			
		||||
        this.emit('policy-changed', key);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get enable() {
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get enableSound() {
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get showBanners() {
 | 
			
		||||
        return this._masterSettings.get_boolean('show-banners');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get forceExpanded() {
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get showInLockScreen() {
 | 
			
		||||
        return this._masterSettings.get_boolean('show-in-lock-screen');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get detailsInLockScreen() {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const NotificationApplicationPolicy = new Lang.Class({
 | 
			
		||||
    Name: 'NotificationApplicationPolicy',
 | 
			
		||||
    Extends: MessageTray.NotificationPolicy,
 | 
			
		||||
 | 
			
		||||
    _init: function(id) {
 | 
			
		||||
        // Don't chain to parent, it would try setting
 | 
			
		||||
        // our properties to the defaults
 | 
			
		||||
 | 
			
		||||
        this.id = id;
 | 
			
		||||
        this._canonicalId = this._canonicalizeId(id)
 | 
			
		||||
 | 
			
		||||
        this._masterSettings = new Gio.Settings({ schema: 'org.gnome.desktop.notifications' });
 | 
			
		||||
        this._settings = new Gio.Settings({ schema: 'org.gnome.desktop.notifications.application',
 | 
			
		||||
                                            path: '/org/gnome/desktop/notifications/application/' + this._canonicalId + '/' });
 | 
			
		||||
 | 
			
		||||
        this._masterSettings.connect('changed', Lang.bind(this, this._changed));
 | 
			
		||||
        this._settings.connect('changed', Lang.bind(this, this._changed));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    store: function() {
 | 
			
		||||
        this._settings.set_string('application-id', this.id + '.desktop');
 | 
			
		||||
 | 
			
		||||
        let apps = this._masterSettings.get_strv('application-children');
 | 
			
		||||
        if (apps.indexOf(this._canonicalId) < 0) {
 | 
			
		||||
            apps.push(this._canonicalId);
 | 
			
		||||
            this._masterSettings.set_strv('application-children', apps);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroy: function() {
 | 
			
		||||
        this._masterSettings.run_dispose();
 | 
			
		||||
        this._settings.run_dispose();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _changed: function(settings, key) {
 | 
			
		||||
        this.emit('policy-changed', key);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _canonicalizeId: function(id) {
 | 
			
		||||
        // Keys are restricted to lowercase alphanumeric characters and dash,
 | 
			
		||||
        // and two dashes cannot be in succession
 | 
			
		||||
        return id.toLowerCase().replace(/[^a-z0-9\-]/g, '-').replace(/--+/g, '-');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get enable() {
 | 
			
		||||
        return this._settings.get_boolean('enable');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get enableSound() {
 | 
			
		||||
        return this._settings.get_boolean('enable-sound-alerts');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get showBanners() {
 | 
			
		||||
        return this._masterSettings.get_boolean('show-banners') &&
 | 
			
		||||
            this._settings.get_boolean('show-banners');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get forceExpanded() {
 | 
			
		||||
        return this._settings.get_boolean('force-expanded');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get showInLockScreen() {
 | 
			
		||||
        return this._masterSettings.get_boolean('show-in-lock-screen') &&
 | 
			
		||||
            this._settings.get_boolean('show-in-lock-screen');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get detailsInLockScreen() {
 | 
			
		||||
        return this._settings.get_boolean('details-in-lock-screen');
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const NotificationDaemon = new Lang.Class({
 | 
			
		||||
    Name: 'NotificationDaemon',
 | 
			
		||||
 | 
			
		||||
@@ -244,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) {
 | 
			
		||||
@@ -333,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) {
 | 
			
		||||
@@ -475,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) {
 | 
			
		||||
@@ -499,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);
 | 
			
		||||
@@ -560,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);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -584,7 +459,7 @@ const NotificationDaemon = new Lang.Class({
 | 
			
		||||
            // 'icon-multi',
 | 
			
		||||
            'icon-static',
 | 
			
		||||
            'persistence',
 | 
			
		||||
            'sound',
 | 
			
		||||
            // 'sound',
 | 
			
		||||
        ];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -640,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,
 | 
			
		||||
@@ -664,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 NotificationApplicationPolicy(id);
 | 
			
		||||
        } else {
 | 
			
		||||
            return new NotificationGenericPolicy();
 | 
			
		||||
        this.trayIcon = trayIcon;
 | 
			
		||||
        if (this.trayIcon) {
 | 
			
		||||
           this._setSummaryIcon(this.trayIcon);
 | 
			
		||||
           this.useNotificationIcon = false;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -703,17 +565,19 @@ 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;
 | 
			
		||||
 | 
			
		||||
@@ -726,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);
 | 
			
		||||
@@ -734,13 +598,7 @@ const Source = new Lang.Class({
 | 
			
		||||
            return app;
 | 
			
		||||
 | 
			
		||||
        if (this.trayIcon) {
 | 
			
		||||
            app = Shell.AppSystem.get_default().lookup_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;
 | 
			
		||||
        }
 | 
			
		||||
@@ -748,11 +606,11 @@ const Source = new Lang.Class({
 | 
			
		||||
        return null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _setApp: function(appId) {
 | 
			
		||||
    _setApp: function() {
 | 
			
		||||
        if (this.app)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this.app = this._getApp(appId);
 | 
			
		||||
        this.app = this._getApp();
 | 
			
		||||
        if (!this.app)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,207 +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.layoutManager.addChrome(this.actor, { affectsInputRegion: false });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    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._hideTimeoutId = 0;
 | 
			
		||||
        this._hide();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _hide: function() {
 | 
			
		||||
        Tweener.addTween(this.actor,
 | 
			
		||||
                         { opacity: 0,
 | 
			
		||||
                           time: FADE_TIME,
 | 
			
		||||
                           transition: 'easeOutQuad',
 | 
			
		||||
                           onComplete: Lang.bind(this, this._reset) });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _reset: function() {
 | 
			
		||||
        this.actor.hide();
 | 
			
		||||
        this.setLabel(null);
 | 
			
		||||
        this.setLevel(null);
 | 
			
		||||
        Meta.enable_unredirect_for_screen(global.screen);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _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,13 +10,11 @@ const St = imports.gi.St;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Gdk = imports.gi.Gdk;
 | 
			
		||||
 | 
			
		||||
const Background = imports.ui.background;
 | 
			
		||||
const CenterLayout = imports.ui.centerLayout;
 | 
			
		||||
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;
 | 
			
		||||
@@ -26,14 +24,25 @@ 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 165 at its darkest to 98 at its most transparent. */ \
 | 
			
		||||
   float y = 165.0 / 255.0; \
 | 
			
		||||
   float x = 98.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',
 | 
			
		||||
@@ -89,6 +98,13 @@ const ShellInfo = new Lang.Class({
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const ControlsChange = {
 | 
			
		||||
    BEFORE_PAGE: 1,
 | 
			
		||||
    AFTER_PAGE: 2,
 | 
			
		||||
    DND_START: 3,
 | 
			
		||||
    DND_END: 4
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const Overview = new Lang.Class({
 | 
			
		||||
    Name: 'Overview',
 | 
			
		||||
 | 
			
		||||
@@ -109,50 +125,53 @@ 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();
 | 
			
		||||
        global.overlay_group.add_actor(this._desktopFade);
 | 
			
		||||
 | 
			
		||||
        let layout = new Clutter.BinLayout();
 | 
			
		||||
        this._stack = new Clutter.Actor({ layout_manager: layout });
 | 
			
		||||
        this._stack.add_constraint(new LayoutManager.MonitorConstraint({ primary: true }));
 | 
			
		||||
 | 
			
		||||
        /* Translators: This is the main view to select
 | 
			
		||||
           activities. See also note for "Activities" string. */
 | 
			
		||||
        this._overview = new St.BoxLayout({ name: 'overview',
 | 
			
		||||
                                            accessible_name: _("Overview"),
 | 
			
		||||
                                            reactive: true,
 | 
			
		||||
                                            vertical: true,
 | 
			
		||||
                                            x_expand: true,
 | 
			
		||||
                                            y_expand: true });
 | 
			
		||||
                                            vertical: true });
 | 
			
		||||
        this._overview._delegate = this;
 | 
			
		||||
 | 
			
		||||
        this._groupStack = new St.Widget({ layout_manager: new Clutter.BinLayout(),
 | 
			
		||||
                                           x_expand: true, y_expand: true,
 | 
			
		||||
                                           clip_to_allocation: true });
 | 
			
		||||
        this._group = new St.BoxLayout({ name: 'overview-group',
 | 
			
		||||
                                         reactive: true,
 | 
			
		||||
                                         x_expand: true, y_expand: true });
 | 
			
		||||
        this._groupStack.add_actor(this._group);
 | 
			
		||||
        let layout = new CenterLayout.CenterLayout();
 | 
			
		||||
        this._group = new St.Widget({ name: 'overview-group',
 | 
			
		||||
                                      layout_manager: layout });
 | 
			
		||||
 | 
			
		||||
        this._backgroundGroup = new Meta.BackgroundGroup();
 | 
			
		||||
        global.overlay_group.add_child(this._backgroundGroup);
 | 
			
		||||
        this._backgroundGroup.hide();
 | 
			
		||||
        this._bgManagers = [];
 | 
			
		||||
        this._spacing = 0;
 | 
			
		||||
        this._group.connect('style-changed',
 | 
			
		||||
            Lang.bind(this, function() {
 | 
			
		||||
                let node = this._group.get_theme_node();
 | 
			
		||||
                let spacing = node.get_length('spacing');
 | 
			
		||||
                if (spacing != this._spacing) {
 | 
			
		||||
                    this._spacing = spacing;
 | 
			
		||||
                }
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
        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
 | 
			
		||||
@@ -162,9 +181,8 @@ const Overview = new Lang.Class({
 | 
			
		||||
        this._overview.add_actor(this._coverPane);
 | 
			
		||||
        this._coverPane.connect('event', Lang.bind(this, function (actor, event) { return true; }));
 | 
			
		||||
 | 
			
		||||
        this._stack.hide();
 | 
			
		||||
        this._stack.add_actor(this._overview);
 | 
			
		||||
        global.overlay_group.add_actor(this._stack);
 | 
			
		||||
        this._overview.hide();
 | 
			
		||||
        global.overlay_group.add_actor(this._overview);
 | 
			
		||||
 | 
			
		||||
        this._coverPane.hide();
 | 
			
		||||
 | 
			
		||||
@@ -177,7 +195,6 @@ const Overview = new Lang.Class({
 | 
			
		||||
        Main.xdndHandler.connect('drag-end', Lang.bind(this, this._onDragEnd));
 | 
			
		||||
 | 
			
		||||
        global.screen.connect('restacked', Lang.bind(this, this._onRestacked));
 | 
			
		||||
        this._group.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
 | 
			
		||||
 | 
			
		||||
        this._windowSwitchTimeoutId = 0;
 | 
			
		||||
        this._windowSwitchTimestamp = 0;
 | 
			
		||||
@@ -189,56 +206,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();
 | 
			
		||||
@@ -268,50 +235,119 @@ 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._dash = new Dash.Dash();
 | 
			
		||||
        this._viewSelector = new ViewSelector.ViewSelector(this._searchEntry,
 | 
			
		||||
                                                           this._dash.showAppsButton);
 | 
			
		||||
        this._thumbnailsBox = new WorkspaceThumbnail.ThumbnailsBox();
 | 
			
		||||
        this._controls = new OverviewControls.ControlsManager(this._dash,
 | 
			
		||||
                                                              this._thumbnailsBox,
 | 
			
		||||
                                                              this._viewSelector);
 | 
			
		||||
 | 
			
		||||
        this._controls.dashActor.x_align = Clutter.ActorAlign.START;
 | 
			
		||||
        this._controls.dashActor.y_expand = true;
 | 
			
		||||
 | 
			
		||||
        // Put the dash in a separate layer to allow content to be centered
 | 
			
		||||
        this._groupStack.add_actor(this._controls.dashActor);
 | 
			
		||||
 | 
			
		||||
        // Pack all the actors into the group
 | 
			
		||||
        this._group.add_actor(this._controls.dashSpacer);
 | 
			
		||||
        this._group.add(this._viewSelector.actor, { x_fill: true,
 | 
			
		||||
                                                    expand: true });
 | 
			
		||||
        this._group.add_actor(this._controls.thumbnailsActor);
 | 
			
		||||
 | 
			
		||||
        // Add our same-line elements after the search entry
 | 
			
		||||
        this._overview.add(this._groupStack, { y_fill: true, expand: true });
 | 
			
		||||
 | 
			
		||||
        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_actor(this._viewSelector.actor);
 | 
			
		||||
 | 
			
		||||
        this._thumbnailsBox = new WorkspaceThumbnail.ThumbnailsBox();
 | 
			
		||||
        this._group.add_actor(this._thumbnailsBox.actor);
 | 
			
		||||
 | 
			
		||||
        Main.ctrlAltTabManager.addGroup(this._thumbnailsBox.actor, _("Workspaces"), 'view-list-symbolic');
 | 
			
		||||
 | 
			
		||||
        // Add our same-line elements after the search entry
 | 
			
		||||
        this._overview.add_actor(this._group);
 | 
			
		||||
 | 
			
		||||
        // Then account for message tray
 | 
			
		||||
        this._messageTrayGhost = new St.Bin({ child: new Clutter.Clone({ source: Main.messageTray.actor }),
 | 
			
		||||
                                              reactive: false,
 | 
			
		||||
                                              opacity: 0,
 | 
			
		||||
                                              x_fill: true,
 | 
			
		||||
                                              y_fill: true });
 | 
			
		||||
        this._overview.add_actor(this._messageTrayGhost);
 | 
			
		||||
 | 
			
		||||
        this._viewSelector.connect('after-page-change', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                this._setSideControlsVisibility(ControlsChange.AFTER_PAGE);
 | 
			
		||||
            }));
 | 
			
		||||
        this._viewSelector.connect('before-page-change', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                this._setSideControlsVisibility(ControlsChange.BEFORE_PAGE);
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
        this.connect('item-drag-begin', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                this._setSideControlsVisibility(ControlsChange.DND_START);
 | 
			
		||||
            }));
 | 
			
		||||
        this.connect('item-drag-cancelled', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                this._setSideControlsVisibility(ControlsChange.DND_END);
 | 
			
		||||
            }));
 | 
			
		||||
        this.connect('item-drag-end', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                this._setSideControlsVisibility(ControlsChange.DND_END);
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
        Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._relayout));
 | 
			
		||||
        this._relayout();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _setSideControlsVisibility: function(changeType) {
 | 
			
		||||
        // 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
 | 
			
		||||
        // unnecesary noise
 | 
			
		||||
        if (!this.visible || this.animationInProgress)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let appsActive = this._viewSelector.getAppsActive();
 | 
			
		||||
        let searchActive = this._viewSelector.getSearchActive();
 | 
			
		||||
        let dashVisible = !searchActive || (changeType == ControlsChange.DND_START);
 | 
			
		||||
        let thumbnailsVisible = (!searchActive && !appsActive) || (changeType == ControlsChange.DND_START);
 | 
			
		||||
        let trayVisible = !searchActive;
 | 
			
		||||
        let trayGhostVisible = trayVisible || dashVisible;
 | 
			
		||||
 | 
			
		||||
        if ((changeType == ControlsChange.BEFORE_PAGE) ||
 | 
			
		||||
            (changeType == ControlsChange.DND_START)) {
 | 
			
		||||
            if (dashVisible)
 | 
			
		||||
                this._dash.show();
 | 
			
		||||
            if (thumbnailsVisible)
 | 
			
		||||
                this._thumbnailsBox.show();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ((changeType == ControlsChange.BEFORE_PAGE) ||
 | 
			
		||||
            (changeType == ControlsChange.DND_END)) {
 | 
			
		||||
            if (!dashVisible) {
 | 
			
		||||
                this._dash.hide();
 | 
			
		||||
            }
 | 
			
		||||
            if (!thumbnailsVisible)
 | 
			
		||||
                this._thumbnailsBox.hide();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (changeType == ControlsChange.BEFORE_PAGE ||
 | 
			
		||||
            changeType == ControlsChange.DND_START) {
 | 
			
		||||
            if (trayGhostVisible)
 | 
			
		||||
                this._messageTrayGhost.show();
 | 
			
		||||
            if (trayVisible)
 | 
			
		||||
                Main.messageTray.show();
 | 
			
		||||
            else
 | 
			
		||||
                Main.messageTray.hide();
 | 
			
		||||
        } else if (changeType == ControlsChange.AFTER_PAGE ||
 | 
			
		||||
                   changeType == ControlsChange.DND_END) {
 | 
			
		||||
            if (!trayGhostVisible)
 | 
			
		||||
                this._messageTrayGhost.hide();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addSearchProvider: function(provider) {
 | 
			
		||||
        this._viewSelector.addSearchProvider(provider);
 | 
			
		||||
    },
 | 
			
		||||
@@ -333,22 +369,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;
 | 
			
		||||
@@ -396,7 +428,7 @@ const Overview = new Lang.Class({
 | 
			
		||||
                                                this._needsFakePointerEvent = true;
 | 
			
		||||
                                                Main.activateWindow(dragEvent.targetActor._delegate.metaWindow,
 | 
			
		||||
                                                                    this._windowSwitchTimestamp);
 | 
			
		||||
                                                this.hide();
 | 
			
		||||
                                                this.hideTemporarily();
 | 
			
		||||
                                                this._lastHoveredWindow = null;
 | 
			
		||||
                                            }));
 | 
			
		||||
        }
 | 
			
		||||
@@ -404,10 +436,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;
 | 
			
		||||
@@ -437,12 +465,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() {
 | 
			
		||||
@@ -454,7 +486,7 @@ const Overview = new Lang.Class({
 | 
			
		||||
            stackIndices[stack[i].get_meta_window().get_stable_sequence()] = i;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.emit('windows-restacked', stackIndices);
 | 
			
		||||
        this.emit('sync-window-stacking', stackIndices);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    //// Public methods ////
 | 
			
		||||
@@ -492,18 +524,12 @@ const Overview = new Lang.Class({
 | 
			
		||||
        if (this._shown)
 | 
			
		||||
            return;
 | 
			
		||||
        this._shown = true;
 | 
			
		||||
 | 
			
		||||
        if (!this._syncInputMode())
 | 
			
		||||
        this._syncInputMode();
 | 
			
		||||
        if (!this._modal)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._animateVisible();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    focusSearch: function() {
 | 
			
		||||
        this.show();
 | 
			
		||||
        this._searchEntry.grab_key_focus();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    fadeInDesktop: function() {
 | 
			
		||||
            this._desktopFade.opacity = 0;
 | 
			
		||||
            this._desktopFade.show();
 | 
			
		||||
@@ -532,8 +558,6 @@ 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
 | 
			
		||||
@@ -545,25 +569,50 @@ const Overview = new Lang.Class({
 | 
			
		||||
        //
 | 
			
		||||
        // Disable unredirection while in the overview
 | 
			
		||||
        Meta.disable_unredirect_for_screen(global.screen);
 | 
			
		||||
        this._stack.show();
 | 
			
		||||
        this._backgroundGroup.show();
 | 
			
		||||
        global.window_group.hide();
 | 
			
		||||
        this._messageTrayGhost.show();
 | 
			
		||||
        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()
 | 
			
		||||
@@ -574,22 +623,30 @@ 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._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() {
 | 
			
		||||
        if (this.isDummy)
 | 
			
		||||
            return;
 | 
			
		||||
@@ -600,20 +657,6 @@ 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._activationTime == 0 || Date.now() / 1000 - this._activationTime > OVERVIEW_ACTIVATION_TIMEOUT)
 | 
			
		||||
            return true;
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    //// Private methods ////
 | 
			
		||||
 | 
			
		||||
    _syncInputMode: function() {
 | 
			
		||||
@@ -621,23 +664,22 @@ const Overview = new Lang.Class({
 | 
			
		||||
        // 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;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                global.stage_input_mode = Shell.StageInputMode.FULLSCREEN;
 | 
			
		||||
            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);
 | 
			
		||||
@@ -646,7 +688,6 @@ const Overview = new Lang.Class({
 | 
			
		||||
            else if (global.stage_input_mode == Shell.StageInputMode.FULLSCREEN)
 | 
			
		||||
                global.stage_input_mode = Shell.StageInputMode.NORMAL;
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _animateNotVisible: function() {
 | 
			
		||||
@@ -654,19 +695,24 @@ const Overview = new Lang.Class({
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this.animationInProgress = true;
 | 
			
		||||
        this.visibleTarget = false;
 | 
			
		||||
        this._hideInProgress = true;
 | 
			
		||||
 | 
			
		||||
        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();
 | 
			
		||||
@@ -680,7 +726,7 @@ 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._syncInputMode();
 | 
			
		||||
@@ -691,19 +737,22 @@ const Overview = new Lang.Class({
 | 
			
		||||
        // Re-enable unredirection
 | 
			
		||||
        Meta.enable_unredirect_for_screen(global.screen);
 | 
			
		||||
 | 
			
		||||
        global.window_group.show();
 | 
			
		||||
 | 
			
		||||
        this._viewSelector.hide();
 | 
			
		||||
        this._desktopFade.hide();
 | 
			
		||||
        this._backgroundGroup.hide();
 | 
			
		||||
        this._stack.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();
 | 
			
		||||
 | 
			
		||||
        this._syncInputMode();
 | 
			
		||||
 
 | 
			
		||||
@@ -1,554 +0,0 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
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 Main = imports.ui.main;
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
const Tweener = imports.ui.tweener;
 | 
			
		||||
const ViewSelector = imports.ui.viewSelector;
 | 
			
		||||
 | 
			
		||||
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 [, , natWidth, natHeight] = child.get_preferred_size();
 | 
			
		||||
        let availWidth = Math.round(box.x2 - box.x1);
 | 
			
		||||
        let availHeight = Math.round(box.y2 - box.y1);
 | 
			
		||||
 | 
			
		||||
        let realDirection = getRtlSlideDirection(this._direction, child);
 | 
			
		||||
        let translationX = (realDirection == SlideDirection.LEFT) ?
 | 
			
		||||
            (availWidth - natWidth) : (natWidth - availWidth);
 | 
			
		||||
 | 
			
		||||
        let actorBox = new Clutter.ActorBox({ x1: translationX,
 | 
			
		||||
                                              y1: 0,
 | 
			
		||||
                                              x2: child.x_expand ? availWidth : natWidth,
 | 
			
		||||
                                              y2: child.y_expand ? availHeight : natHeight });
 | 
			
		||||
 | 
			
		||||
        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;
 | 
			
		||||
        // 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;
 | 
			
		||||
 | 
			
		||||
        // SlideLayout reads the actor's expand flags to decide
 | 
			
		||||
        // whether to allocate the natural size to its child, or the whole
 | 
			
		||||
        // available allocation
 | 
			
		||||
        this._thumbnailsBox.actor.y_expand = true;
 | 
			
		||||
 | 
			
		||||
        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));
 | 
			
		||||
        this.actor.connect('notify::hover', Lang.bind(this, this.updateSlide));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _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;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    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];
 | 
			
		||||
        let visibleWidth = child.get_theme_node().get_length('visible-width');
 | 
			
		||||
 | 
			
		||||
        return visibleWidth / expandedWidth;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getVisibleWidth: function() {
 | 
			
		||||
        let alwaysZoomOut = this._getAlwaysZoomOut();
 | 
			
		||||
        if (alwaysZoomOut)
 | 
			
		||||
            return this.parent();
 | 
			
		||||
 | 
			
		||||
        let child = this.actor.get_first_child();
 | 
			
		||||
        return child.get_theme_node().get_length('visible-width');
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
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._dash.actor.y_expand = true;
 | 
			
		||||
        this.actor.add_actor(this._dash.actor);
 | 
			
		||||
 | 
			
		||||
        this._dash.connect('icon-size-changed', Lang.bind(this, this.updateSlide));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getSlide: function() {
 | 
			
		||||
        if (this.visible || this.inDrag)
 | 
			
		||||
            return 1;
 | 
			
		||||
        else
 | 
			
		||||
            return 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _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 ControlsManager = new Lang.Class({
 | 
			
		||||
    Name: 'ControlsManager',
 | 
			
		||||
 | 
			
		||||
    _init: function(dash, thumbnails, viewSelector) {
 | 
			
		||||
        this._dashSlider = new DashSlider(dash);
 | 
			
		||||
        this.dashActor = this._dashSlider.actor;
 | 
			
		||||
        this.dashSpacer = new DashSpacer();
 | 
			
		||||
        this.dashSpacer.setDashActor(this.dashActor);
 | 
			
		||||
 | 
			
		||||
        this._thumbnailsSlider = new ThumbnailsSlider(thumbnails);
 | 
			
		||||
        this.thumbnailsActor = this._thumbnailsSlider.actor;
 | 
			
		||||
 | 
			
		||||
        this._indicator = new MessagesIndicator(viewSelector);
 | 
			
		||||
        this.indicatorActor = this._indicator.actor;
 | 
			
		||||
 | 
			
		||||
        this._viewSelector = viewSelector;
 | 
			
		||||
        this._viewSelector.connect('page-changed', Lang.bind(this, this._setVisibility));
 | 
			
		||||
        this._viewSelector.connect('page-empty', Lang.bind(this, this._onPageEmpty));
 | 
			
		||||
 | 
			
		||||
        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();
 | 
			
		||||
            }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _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();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										359
									
								
								js/ui/panel.js
									
									
									
									
									
								
							
							
						
						
									
										359
									
								
								js/ui/panel.js
									
									
									
									
									
								
							@@ -15,9 +15,11 @@ const Signals = imports.signals;
 | 
			
		||||
const Atk = imports.gi.Atk;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const CenterLayout = imports.ui.centerLayout;
 | 
			
		||||
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;
 | 
			
		||||
@@ -144,8 +146,8 @@ const AnimatedIcon = new Lang.Class({
 | 
			
		||||
    Name: 'AnimatedIcon',
 | 
			
		||||
    Extends: Animation,
 | 
			
		||||
 | 
			
		||||
    _init: function(filename, size) {
 | 
			
		||||
        this.parent(filename, size, size, ANIMATED_ICON_UPDATE_TIMEOUT);
 | 
			
		||||
    _init: function(name, size) {
 | 
			
		||||
        this.parent(global.datadir + '/theme/' + name, size, size, ANIMATED_ICON_UPDATE_TIMEOUT);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@@ -202,7 +204,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
 | 
			
		||||
@@ -258,7 +260,6 @@ 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);
 | 
			
		||||
@@ -289,31 +290,33 @@ const AppMenuButton = new Lang.Class({
 | 
			
		||||
        this._visible = !Main.overview.visible;
 | 
			
		||||
        if (!this._visible)
 | 
			
		||||
            this.actor.hide();
 | 
			
		||||
        this._overviewHidingId = Main.overview.connect('hiding', Lang.bind(this, function () {
 | 
			
		||||
        Main.overview.connect('hiding', Lang.bind(this, function () {
 | 
			
		||||
            this.show();
 | 
			
		||||
        }));
 | 
			
		||||
        this._overviewShowingId = Main.overview.connect('showing', Lang.bind(this, function () {
 | 
			
		||||
        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;
 | 
			
		||||
@@ -353,18 +356,6 @@ 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 AnimatedIcon(this._spinnerIcon, PANEL_ICON_SIZE);
 | 
			
		||||
        this._container.add_actor(this._spinner.actor);
 | 
			
		||||
        this._spinner.actor.hide();
 | 
			
		||||
        this._spinner.actor.lower_bottom();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onIconBoxStyleChanged: function() {
 | 
			
		||||
        let node = this._iconBox.get_theme_node();
 | 
			
		||||
        this._iconBottomClip = node.get_length('app-icon-bottom-clip');
 | 
			
		||||
@@ -396,10 +387,6 @@ const AppMenuButton = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._stop = true;
 | 
			
		||||
        this.actor.reactive = true;
 | 
			
		||||
 | 
			
		||||
        if (this._spinner == null)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        Tweener.addTween(this._spinner.actor,
 | 
			
		||||
                         { opacity: 0,
 | 
			
		||||
                           time: SPINNER_ANIMATION_TIME,
 | 
			
		||||
@@ -416,10 +403,6 @@ const AppMenuButton = new Lang.Class({
 | 
			
		||||
    startAnimation: function() {
 | 
			
		||||
        this._stop = false;
 | 
			
		||||
        this.actor.reactive = false;
 | 
			
		||||
 | 
			
		||||
        if (this._spinner == null)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._spinner.play();
 | 
			
		||||
        this._spinner.actor.show();
 | 
			
		||||
    },
 | 
			
		||||
@@ -482,9 +465,6 @@ const AppMenuButton = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
        this._label.actor.allocate(childBox, flags);
 | 
			
		||||
 | 
			
		||||
        if (this._spinner == null)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (direction == Clutter.TextDirection.LTR) {
 | 
			
		||||
            childBox.x1 = Math.floor(iconWidth / 2) + this._label.actor.width;
 | 
			
		||||
            childBox.x2 = childBox.x1 + this._spinner.actor.width;
 | 
			
		||||
@@ -575,8 +555,7 @@ const AppMenuButton = new Lang.Class({
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this._spinner)
 | 
			
		||||
            this._spinner.actor.hide();
 | 
			
		||||
        this._spinner.actor.hide();
 | 
			
		||||
        if (this._iconBox.child != null)
 | 
			
		||||
            this._iconBox.child.destroy();
 | 
			
		||||
        this._iconBox.hide();
 | 
			
		||||
@@ -620,11 +599,6 @@ const AppMenuButton = new Lang.Class({
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            menu = new PopupMenu.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());
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
        } else {
 | 
			
		||||
            if (this.menu.isDummyQuitMenu)
 | 
			
		||||
                return;
 | 
			
		||||
@@ -639,33 +613,6 @@ const AppMenuButton = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this.setMenu(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();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@@ -679,15 +626,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") });
 | 
			
		||||
        this.actor.add_actor(this._label);
 | 
			
		||||
        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));
 | 
			
		||||
@@ -702,6 +657,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) {
 | 
			
		||||
@@ -711,14 +704,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;
 | 
			
		||||
@@ -735,12 +728,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;
 | 
			
		||||
@@ -750,9 +747,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));
 | 
			
		||||
@@ -808,12 +808,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)
 | 
			
		||||
@@ -822,9 +822,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) {
 | 
			
		||||
@@ -851,7 +851,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';
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -864,8 +864,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);
 | 
			
		||||
@@ -889,18 +889,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() {
 | 
			
		||||
@@ -919,10 +918,10 @@ const PANEL_ITEM_IMPLEMENTATIONS = {
 | 
			
		||||
    '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
 | 
			
		||||
@@ -939,19 +938,54 @@ try {
 | 
			
		||||
    log('NMApplet is not supported. It is possible that your NetworkManager version is too old');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const PanelLayout = new Lang.Class({
 | 
			
		||||
    Name: 'PanelLayout',
 | 
			
		||||
    Extends: CenterLayout.CenterLayout,
 | 
			
		||||
 | 
			
		||||
    vfunc_allocate: function(container, box, flags) {
 | 
			
		||||
        this.parent(container, box, flags);
 | 
			
		||||
 | 
			
		||||
        let availWidth = box.x2 - box.x1;
 | 
			
		||||
        let availHeight = box.y2 - box.y1;
 | 
			
		||||
 | 
			
		||||
        let [left, center, right, leftCorner, rightCorner] = container.get_children();
 | 
			
		||||
        let childBox = new Clutter.ActorBox();
 | 
			
		||||
 | 
			
		||||
        let cornerMinWidth, cornerMinHeight;
 | 
			
		||||
        let cornerWidth, cornerHeight;
 | 
			
		||||
 | 
			
		||||
        [cornerMinWidth, cornerWidth] = leftCorner.get_preferred_width(-1);
 | 
			
		||||
        [cornerMinHeight, cornerHeight] = leftCorner.get_preferred_height(-1);
 | 
			
		||||
        childBox.x1 = 0;
 | 
			
		||||
        childBox.x2 = cornerWidth;
 | 
			
		||||
        childBox.y1 = availHeight;
 | 
			
		||||
        childBox.y2 = availHeight + cornerHeight;
 | 
			
		||||
        leftCorner.allocate(childBox, flags);
 | 
			
		||||
 | 
			
		||||
        [cornerMinWidth, cornerWidth] = rightCorner.get_preferred_width(-1);
 | 
			
		||||
        [cornerMinHeight, cornerHeight] = rightCorner.get_preferred_height(-1);
 | 
			
		||||
        childBox.x1 = availWidth - cornerWidth;
 | 
			
		||||
        childBox.x2 = availWidth;
 | 
			
		||||
        childBox.y1 = availHeight;
 | 
			
		||||
        childBox.y2 = availHeight + cornerHeight;
 | 
			
		||||
        rightCorner.allocate(childBox, flags);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const Panel = new Lang.Class({
 | 
			
		||||
    Name: 'Panel',
 | 
			
		||||
 | 
			
		||||
    _init : function() {
 | 
			
		||||
        this.actor = new Shell.GenericContainer({ name: 'panel',
 | 
			
		||||
                                                  reactive: true });
 | 
			
		||||
        this.actor = new St.Widget({ name: 'panel',
 | 
			
		||||
                                     reactive: true,
 | 
			
		||||
                                     layoutManager: new PanelLayout() });
 | 
			
		||||
        this.actor._delegate = this;
 | 
			
		||||
 | 
			
		||||
        this._sessionStyle = null;
 | 
			
		||||
 | 
			
		||||
        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);
 | 
			
		||||
@@ -960,24 +994,20 @@ 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));
 | 
			
		||||
        this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
 | 
			
		||||
        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 });
 | 
			
		||||
@@ -986,87 +1016,7 @@ const Panel = new Lang.Class({
 | 
			
		||||
        this._updatePanel();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getPreferredWidth: function(actor, forHeight, alloc) {
 | 
			
		||||
        alloc.min_size = -1;
 | 
			
		||||
        alloc.natural_size = Main.layoutManager.primaryMonitor.width;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getPreferredHeight: function(actor, forWidth, alloc) {
 | 
			
		||||
        // We don't need to implement this; it's forced by the CSS
 | 
			
		||||
        alloc.min_size = -1;
 | 
			
		||||
        alloc.natural_size = -1;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _allocate: function(actor, box, flags) {
 | 
			
		||||
        let allocWidth = box.x2 - box.x1;
 | 
			
		||||
        let allocHeight = box.y2 - box.y1;
 | 
			
		||||
 | 
			
		||||
        let [leftMinWidth, leftNaturalWidth] = this._leftBox.get_preferred_width(-1);
 | 
			
		||||
        let [centerMinWidth, centerNaturalWidth] = this._centerBox.get_preferred_width(-1);
 | 
			
		||||
        let [rightMinWidth, rightNaturalWidth] = this._rightBox.get_preferred_width(-1);
 | 
			
		||||
 | 
			
		||||
        let sideWidth, centerWidth;
 | 
			
		||||
        centerWidth = centerNaturalWidth;
 | 
			
		||||
        sideWidth = (allocWidth - centerWidth) / 2;
 | 
			
		||||
 | 
			
		||||
        let childBox = new Clutter.ActorBox();
 | 
			
		||||
 | 
			
		||||
        childBox.y1 = 0;
 | 
			
		||||
        childBox.y2 = allocHeight;
 | 
			
		||||
        if (this.actor.get_text_direction() == Clutter.TextDirection.RTL) {
 | 
			
		||||
            childBox.x1 = allocWidth - Math.min(Math.floor(sideWidth),
 | 
			
		||||
                                                leftNaturalWidth);
 | 
			
		||||
            childBox.x2 = allocWidth;
 | 
			
		||||
        } else {
 | 
			
		||||
            childBox.x1 = 0;
 | 
			
		||||
            childBox.x2 = Math.min(Math.floor(sideWidth),
 | 
			
		||||
                                   leftNaturalWidth);
 | 
			
		||||
        }
 | 
			
		||||
        this._leftBox.allocate(childBox, flags);
 | 
			
		||||
 | 
			
		||||
        childBox.x1 = Math.ceil(sideWidth);
 | 
			
		||||
        childBox.y1 = 0;
 | 
			
		||||
        childBox.x2 = childBox.x1 + centerWidth;
 | 
			
		||||
        childBox.y2 = allocHeight;
 | 
			
		||||
        this._centerBox.allocate(childBox, flags);
 | 
			
		||||
 | 
			
		||||
        childBox.y1 = 0;
 | 
			
		||||
        childBox.y2 = allocHeight;
 | 
			
		||||
        if (this.actor.get_text_direction() == Clutter.TextDirection.RTL) {
 | 
			
		||||
            childBox.x1 = 0;
 | 
			
		||||
            childBox.x2 = Math.min(Math.floor(sideWidth),
 | 
			
		||||
                                   rightNaturalWidth);
 | 
			
		||||
        } else {
 | 
			
		||||
            childBox.x1 = allocWidth - Math.min(Math.floor(sideWidth),
 | 
			
		||||
                                                rightNaturalWidth);
 | 
			
		||||
            childBox.x2 = allocWidth;
 | 
			
		||||
        }
 | 
			
		||||
        this._rightBox.allocate(childBox, flags);
 | 
			
		||||
 | 
			
		||||
        let cornerMinWidth, cornerMinHeight;
 | 
			
		||||
        let cornerWidth, cornerHeight;
 | 
			
		||||
 | 
			
		||||
        [cornerMinWidth, cornerWidth] = this._leftCorner.actor.get_preferred_width(-1);
 | 
			
		||||
        [cornerMinHeight, cornerHeight] = this._leftCorner.actor.get_preferred_height(-1);
 | 
			
		||||
        childBox.x1 = 0;
 | 
			
		||||
        childBox.x2 = cornerWidth;
 | 
			
		||||
        childBox.y1 = allocHeight;
 | 
			
		||||
        childBox.y2 = allocHeight + cornerHeight;
 | 
			
		||||
        this._leftCorner.actor.allocate(childBox, flags);
 | 
			
		||||
 | 
			
		||||
        [cornerMinWidth, cornerWidth] = this._rightCorner.actor.get_preferred_width(-1);
 | 
			
		||||
        [cornerMinHeight, cornerHeight] = this._rightCorner.actor.get_preferred_height(-1);
 | 
			
		||||
        childBox.x1 = allocWidth - cornerWidth;
 | 
			
		||||
        childBox.x2 = allocWidth;
 | 
			
		||||
        childBox.y1 = allocHeight;
 | 
			
		||||
        childBox.y2 = allocHeight + cornerHeight;
 | 
			
		||||
        this._rightCorner.actor.allocate(childBox, flags);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onButtonPress: function(actor, event) {
 | 
			
		||||
        if (Main.modalCount > 0)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        if (event.get_source() != actor)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
@@ -1105,18 +1055,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) {
 | 
			
		||||
@@ -1147,13 +1096,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;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -205,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() {
 | 
			
		||||
@@ -242,21 +244,18 @@ const SystemStatusButton = new Lang.Class({
 | 
			
		||||
            this.setIcon(iconName);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get icons() {
 | 
			
		||||
        return this._box.get_children();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addIcon: function(gicon) {
 | 
			
		||||
        let icon = new St.Icon({ gicon: gicon,
 | 
			
		||||
                                 style_class: 'system-status-icon' });
 | 
			
		||||
        this._box.add_actor(icon);
 | 
			
		||||
 | 
			
		||||
        this.emit('icons-changed');
 | 
			
		||||
 | 
			
		||||
        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;
 | 
			
		||||
 
 | 
			
		||||
@@ -41,9 +41,10 @@ const PointerWatcher = new Lang.Class({
 | 
			
		||||
    Name: 'PointerWatcher',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._idleMonitor = new GnomeDesktop.IdleMonitor();
 | 
			
		||||
        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;
 | 
			
		||||
@@ -86,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();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -207,7 +207,6 @@ const PopupBaseMenuItem = new Lang.Class({
 | 
			
		||||
            color.alpha / 255);
 | 
			
		||||
        cr.arc(width / 2, height / 2, width / 3, 0, 2 * Math.PI);
 | 
			
		||||
        cr.fill();
 | 
			
		||||
        cr.$dispose();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // This returns column widths in logical order (i.e. from the dot
 | 
			
		||||
@@ -553,10 +552,6 @@ const PopupSliderMenuItem = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        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 sliderWidth = width - 2 * handleRadius;
 | 
			
		||||
        let sliderHeight = themeNode.get_length('-slider-height');
 | 
			
		||||
 | 
			
		||||
@@ -608,17 +603,7 @@ const PopupSliderMenuItem = new Lang.Class({
 | 
			
		||||
            color.blue / 255,
 | 
			
		||||
            color.alpha / 255);
 | 
			
		||||
        cr.arc(handleX, handleY, handleRadius, 0, 2 * Math.PI);
 | 
			
		||||
        cr.fillPreserve();
 | 
			
		||||
        if (hasHandleColor && handleBorderWidth) {
 | 
			
		||||
          cr.setSourceRGBA(
 | 
			
		||||
              handleBorderColor.red / 255,
 | 
			
		||||
              handleBorderColor.green / 255,
 | 
			
		||||
              handleBorderColor.blue / 255,
 | 
			
		||||
              handleBorderColor.alpha / 255);
 | 
			
		||||
          cr.setLineWidth(handleBorderWidth);
 | 
			
		||||
          cr.stroke();
 | 
			
		||||
        }
 | 
			
		||||
        cr.$dispose();
 | 
			
		||||
        cr.fill();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _startDragging: function(actor, event) {
 | 
			
		||||
@@ -651,34 +636,20 @@ const PopupSliderMenuItem = new Lang.Class({
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    scroll: function(event) {
 | 
			
		||||
    _onScrollEvent: function (actor, 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.max(0, this._value - SLIDER_SCROLL_STEP);
 | 
			
		||||
        }
 | 
			
		||||
        else if (direction == Clutter.ScrollDirection.UP) {
 | 
			
		||||
            this._value = Math.min(1, this._value + SLIDER_SCROLL_STEP);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._value = Math.min(Math.max(0, this._value + delta), 1);
 | 
			
		||||
 | 
			
		||||
        this._slider.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();
 | 
			
		||||
@@ -880,6 +851,7 @@ const PopupMenuBase = new Lang.Class({
 | 
			
		||||
        this.blockSourceEvents = false;
 | 
			
		||||
 | 
			
		||||
        this._activeMenuItem = null;
 | 
			
		||||
        this._childMenus = [];
 | 
			
		||||
        this._settingsActions = { };
 | 
			
		||||
 | 
			
		||||
        this._sessionUpdatedId = Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
 | 
			
		||||
@@ -901,7 +873,7 @@ const PopupMenuBase = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    addSettingsAction: function(title, desktopFile) {
 | 
			
		||||
        let menuItem = this.addAction(title, function() {
 | 
			
		||||
                           let app = Shell.AppSystem.get_default().lookup_app(desktopFile);
 | 
			
		||||
                           let app = Shell.AppSystem.get_default().lookup_setting(desktopFile);
 | 
			
		||||
 | 
			
		||||
                           if (!app) {
 | 
			
		||||
                               log('Settings panel for desktop file ' + desktopFile + ' could not be loaded!');
 | 
			
		||||
@@ -926,15 +898,29 @@ const PopupMenuBase = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    isEmpty: function() {
 | 
			
		||||
        let hasVisibleChildren = this.box.get_children().some(function(child) {
 | 
			
		||||
            return child.visible;
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        return !hasVisibleChildren;
 | 
			
		||||
        return this.box.get_n_children() == 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    isChildMenu: function() {
 | 
			
		||||
        return false;
 | 
			
		||||
    isChildMenu: function(menu) {
 | 
			
		||||
        return this._childMenus.indexOf(menu) != -1;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addChildMenu: function(menu) {
 | 
			
		||||
        if (this.isChildMenu(menu))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._childMenus.push(menu);
 | 
			
		||||
        this.emit('child-menu-added', menu);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    removeChildMenu: function(menu) {
 | 
			
		||||
        let index = this._childMenus.indexOf(menu);
 | 
			
		||||
 | 
			
		||||
        if (index == -1)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._childMenus.splice(index, 1);
 | 
			
		||||
        this.emit('child-menu-removed', menu);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -1170,7 +1156,6 @@ const PopupMenuBase = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroy: function() {
 | 
			
		||||
        this.close();
 | 
			
		||||
        this.removeAll();
 | 
			
		||||
        this.actor.destroy();
 | 
			
		||||
 | 
			
		||||
@@ -1210,8 +1195,6 @@ const PopupMenu = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        global.focus_manager.add_group(this.actor);
 | 
			
		||||
        this.actor.reactive = true;
 | 
			
		||||
 | 
			
		||||
        this._childMenus = [];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _boxGetPreferredWidth: function (actor, forHeight, alloc) {
 | 
			
		||||
@@ -1238,28 +1221,6 @@ const PopupMenu = new Lang.Class({
 | 
			
		||||
        this._boxPointer.setSourceAlignment(alignment);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    isChildMenu: function(menu) {
 | 
			
		||||
        return this._childMenus.indexOf(menu) != -1;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addChildMenu: function(menu) {
 | 
			
		||||
        if (this.isChildMenu(menu))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._childMenus.push(menu);
 | 
			
		||||
        this.emit('child-menu-added', menu);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    removeChildMenu: function(menu) {
 | 
			
		||||
        let index = this._childMenus.indexOf(menu);
 | 
			
		||||
 | 
			
		||||
        if (index == -1)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._childMenus.splice(index, 1);
 | 
			
		||||
        this.emit('child-menu-removed', menu);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    open: function(animate) {
 | 
			
		||||
        if (this.isOpen)
 | 
			
		||||
            return;
 | 
			
		||||
@@ -1281,10 +1242,6 @@ const PopupMenu = new Lang.Class({
 | 
			
		||||
        if (this._activeMenuItem)
 | 
			
		||||
            this._activeMenuItem.setActive(false);
 | 
			
		||||
 | 
			
		||||
        this._childMenus.forEach(function(childMenu) {
 | 
			
		||||
            childMenu.close();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        if (this._boxPointer.actor.visible)
 | 
			
		||||
            this._boxPointer.hide(animate);
 | 
			
		||||
 | 
			
		||||
@@ -1705,7 +1662,9 @@ const PopupComboBoxMenuItem = new Lang.Class({
 | 
			
		||||
    _getTopMenu: function() {
 | 
			
		||||
        let actor = this.actor.get_parent();
 | 
			
		||||
        while (actor) {
 | 
			
		||||
            if (actor._delegate && actor._delegate instanceof PopupMenu)
 | 
			
		||||
            if (actor._delegate &&
 | 
			
		||||
                (actor._delegate instanceof PopupMenu ||
 | 
			
		||||
                 actor._delegate instanceof PopupComboMenu))
 | 
			
		||||
                return actor._delegate;
 | 
			
		||||
 | 
			
		||||
            actor = actor.get_parent();
 | 
			
		||||
@@ -1826,8 +1785,6 @@ const RemoteMenu = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._actionStateChangeId = this.actionGroup.connect('action-state-changed', Lang.bind(this, this._actionStateChanged));
 | 
			
		||||
        this._actionEnableChangeId = this.actionGroup.connect('action-enabled-changed', Lang.bind(this, this._actionEnabledChanged));
 | 
			
		||||
 | 
			
		||||
        this._skipSignalConnection = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroy: function() {
 | 
			
		||||
@@ -1875,7 +1832,7 @@ const RemoteMenu = new Lang.Class({
 | 
			
		||||
        let action_id = model.get_item_attribute_value(index, Gio.MENU_ATTRIBUTE_ACTION, null).deep_unpack();
 | 
			
		||||
        if (!this.actionGroup.has_action(action_id)) {
 | 
			
		||||
            // the action may not be there yet, wait for action-added
 | 
			
		||||
            return [null, false, 'action-added::' + action_id];
 | 
			
		||||
            return [null, false, 'action-added'];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!this._actions[action_id])
 | 
			
		||||
@@ -1912,7 +1869,7 @@ const RemoteMenu = new Lang.Class({
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                log('Action "%s" has state of type %s, which is not supported'.format(action_id, action.state.get_type_string()));
 | 
			
		||||
                return [null, false, 'action-state-changed::' + action_id];
 | 
			
		||||
                return [null, false, 'action-state-changed'];
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            target = model.get_item_attribute_value(index, Gio.MENU_ATTRIBUTE_TARGET, null);
 | 
			
		||||
@@ -1988,16 +1945,13 @@ const RemoteMenu = new Lang.Class({
 | 
			
		||||
                    target.addMenuItem(separator, k+1);
 | 
			
		||||
                    k++;
 | 
			
		||||
                }
 | 
			
		||||
            } else if (changeSignal && !this._skipSignalConnection) {
 | 
			
		||||
            } else if (changeSignal) {
 | 
			
		||||
                let signalId = this.actionGroup.connect(changeSignal, Lang.bind(this, function(actionGroup, actionName) {
 | 
			
		||||
                    actionGroup.disconnect(signalId);
 | 
			
		||||
                    if (this._actions[actionName]) return;
 | 
			
		||||
 | 
			
		||||
                    /* force a full update but do not reconnect signals if other
 | 
			
		||||
                     * actions are missing */
 | 
			
		||||
                    this._skipSignalConnection = true;
 | 
			
		||||
                    // force a full update
 | 
			
		||||
                    this._modelChanged(model, 0, -1, model.get_n_items(), target);
 | 
			
		||||
                    this._skipSignalConnection = false;
 | 
			
		||||
                }));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -2070,9 +2024,9 @@ const RemoteMenu = new Lang.Class({
 | 
			
		||||
const PopupMenuManager = new Lang.Class({
 | 
			
		||||
    Name: 'PopupMenuManager',
 | 
			
		||||
 | 
			
		||||
    _init: function(owner, grabParams) {
 | 
			
		||||
    _init: function(owner) {
 | 
			
		||||
        this._owner = owner;
 | 
			
		||||
        this._grabHelper = new GrabHelper.GrabHelper(owner.actor, grabParams);
 | 
			
		||||
        this._grabHelper = new GrabHelper.GrabHelper(owner.actor);
 | 
			
		||||
        this._menus = [];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -2105,7 +2059,7 @@ const PopupMenuManager = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    removeMenu: function(menu) {
 | 
			
		||||
        if (menu == this.activeMenu)
 | 
			
		||||
        if (menu == this._activeMenu)
 | 
			
		||||
            this._closeMenu(menu);
 | 
			
		||||
 | 
			
		||||
        let position = this._findMenu(menu);
 | 
			
		||||
@@ -2129,9 +2083,9 @@ const PopupMenuManager = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get activeMenu() {
 | 
			
		||||
        let firstGrab = this._grabHelper.grabStack[0];
 | 
			
		||||
        if (firstGrab)
 | 
			
		||||
            return firstGrab.actor._delegate;
 | 
			
		||||
        let actor = this._grabHelper.currentGrab.actor;
 | 
			
		||||
        if (actor)
 | 
			
		||||
            return actor._delegate;
 | 
			
		||||
        else
 | 
			
		||||
            return null;
 | 
			
		||||
    },
 | 
			
		||||
@@ -2142,8 +2096,6 @@ const PopupMenuManager = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _onMenuOpenState: function(menu, open) {
 | 
			
		||||
        if (open) {
 | 
			
		||||
            if (this.activeMenu && !this.activeMenu.isChildMenu(menu))
 | 
			
		||||
                this.activeMenu.close(BoxPointer.PopupAnimation.FADE);
 | 
			
		||||
            this._grabHelper.grab({ actor: menu.actor, modal: true, focus: menu.sourceActor,
 | 
			
		||||
                                    onUngrab: Lang.bind(this, this._closeMenu, menu) });
 | 
			
		||||
        } else {
 | 
			
		||||
@@ -2160,8 +2112,13 @@ const PopupMenuManager = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _changeMenu: function(newMenu) {
 | 
			
		||||
        newMenu.open(this.activeMenu ? BoxPointer.PopupAnimation.FADE
 | 
			
		||||
                                     : BoxPointer.PopupAnimation.FULL);
 | 
			
		||||
        let oldMenu = this.activeMenu;
 | 
			
		||||
        if (oldMenu) {
 | 
			
		||||
            oldMenu.close(BoxPointer.PopupAnimation.FADE);
 | 
			
		||||
            newMenu.open(BoxPointer.PopupAnimation.FADE);
 | 
			
		||||
        } else {
 | 
			
		||||
            newMenu.open(BoxPointer.PopupAnimation.FULL);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onMenuSourceEnter: function(menu) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,9 @@
 | 
			
		||||
// -*- 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;
 | 
			
		||||
@@ -123,9 +121,7 @@ function loadRemoteSearchProvider(file, info, data) {
 | 
			
		||||
function remoteProvidersLoaded(loadState) {
 | 
			
		||||
    let searchSettings = new Gio.Settings({ schema: Search.SEARCH_PROVIDERS_SCHEMA });
 | 
			
		||||
    let sortOrder = searchSettings.get_strv('sort-order');
 | 
			
		||||
 | 
			
		||||
    // Special case gnome-control-center to be always active and always first
 | 
			
		||||
    sortOrder.unshift('gnome-control-center.desktop');
 | 
			
		||||
    let numSorted = sortOrder.length;
 | 
			
		||||
 | 
			
		||||
    loadState.loadedProviders.sort(
 | 
			
		||||
        function(providerA, providerB) {
 | 
			
		||||
@@ -139,11 +135,16 @@ function remoteProvidersLoaded(loadState) {
 | 
			
		||||
            idxB = sortOrder.indexOf(appIdB);
 | 
			
		||||
 | 
			
		||||
            // 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 ((idxA == -1) && (idxB == -1))
 | 
			
		||||
                return GLib.utf8_collate(providerA.title, providerB.title);
 | 
			
		||||
 | 
			
		||||
                return GLib.utf8_collate(nameA, nameB);
 | 
			
		||||
            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;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // if providerA isn't found, it's sorted after providerB
 | 
			
		||||
@@ -186,18 +187,18 @@ const RemoteSearchProvider = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    createIcon: function(size, meta) {
 | 
			
		||||
        let gicon;
 | 
			
		||||
        if (meta['gicon']) {
 | 
			
		||||
            gicon = Gio.icon_new_for_string(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);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return new St.Icon({ gicon: gicon,
 | 
			
		||||
                             icon_size: size });
 | 
			
		||||
        return null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getResultsFinished: function(results, error) {
 | 
			
		||||
@@ -214,7 +215,7 @@ 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()));
 | 
			
		||||
            log('Error calling GetInitialResultSet for provider %s: %s'.format( this.title, e.toString()));
 | 
			
		||||
            this.searchSystem.pushResults(this, []);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
@@ -227,7 +228,7 @@ 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()));
 | 
			
		||||
            log('Error calling GetSubsearchResultSet for provider %s: %s'.format(this.title, e.toString()));
 | 
			
		||||
            this.searchSystem.pushResults(this, []);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
@@ -244,7 +245,6 @@ const RemoteSearchProvider = new Lang.Class({
 | 
			
		||||
                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]) });
 | 
			
		||||
        }
 | 
			
		||||
@@ -259,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);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,140 +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 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._sessionModeChanged));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _ensureRecorderForSender: function(sender) {
 | 
			
		||||
        let recorder = this._recorders.get(sender);
 | 
			
		||||
        if (!recorder) {
 | 
			
		||||
            recorder = new Shell.Recorder({ stage: global.stage });
 | 
			
		||||
            recorder._watchNameId =
 | 
			
		||||
                Gio.bus_watch_name(Gio.BusType.SESSION, sender, 0, null,
 | 
			
		||||
                                   Lang.bind(this, this._onNameVanished));
 | 
			
		||||
            this._recorders.set(sender, recorder);
 | 
			
		||||
        }
 | 
			
		||||
        return recorder;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _sessionModeChanged: function() {
 | 
			
		||||
        if (Main.sessionMode.allowScreencast)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        for (let sender in this._recorders.keys())
 | 
			
		||||
            this._recorders.delete(sender);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _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);
 | 
			
		||||
 | 
			
		||||
        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));
 | 
			
		||||
 | 
			
		||||
        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));
 | 
			
		||||
 | 
			
		||||
        let sender = invocation.get_sender();
 | 
			
		||||
        let recorder = this._ensureRecorderForSender(sender);
 | 
			
		||||
 | 
			
		||||
        if (!recorder.is_recording()) {
 | 
			
		||||
            let [x, y, width, height, fileTemplate, options] = params;
 | 
			
		||||
 | 
			
		||||
            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]));
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
@@ -1,277 +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 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 (height <= 0 || width <= 0) {
 | 
			
		||||
            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.set_cursor(Shell.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.unset_cursor();
 | 
			
		||||
 | 
			
		||||
                                   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();
 | 
			
		||||
                           })
 | 
			
		||||
                         });
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
@@ -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,
 | 
			
		||||
@@ -142,7 +120,6 @@ const GridSearchResult = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.actor.set_child(content);
 | 
			
		||||
        this.actor.label_actor = content.label_actor;
 | 
			
		||||
 | 
			
		||||
        let draggable = DND.makeDraggable(this.actor);
 | 
			
		||||
        draggable.connect('drag-begin',
 | 
			
		||||
@@ -201,7 +178,7 @@ const ListSearchResults = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._content = new St.BoxLayout({ style_class: 'list-search-results',
 | 
			
		||||
                                           vertical: true });
 | 
			
		||||
        this.actor.add(this._content, { expand: true });
 | 
			
		||||
        this.actor.add_actor(this._content);
 | 
			
		||||
 | 
			
		||||
        this._notDisplayedResult = [];
 | 
			
		||||
        this._terms = [];
 | 
			
		||||
@@ -231,14 +208,9 @@ const ListSearchResults = new Lang.Class({
 | 
			
		||||
        this._pendingClear = true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _keyFocusIn: function(icon) {
 | 
			
		||||
        this.emit('key-focus-in', icon);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
@@ -255,7 +227,6 @@ const ListSearchResults = new Lang.Class({
 | 
			
		||||
            return null;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(ListSearchResults.prototype);
 | 
			
		||||
 | 
			
		||||
const GridSearchResults = new Lang.Class({
 | 
			
		||||
    Name: 'GridSearchResults',
 | 
			
		||||
@@ -298,14 +269,9 @@ const GridSearchResults = new Lang.Class({
 | 
			
		||||
        this._pendingClear = true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _keyFocusIn: function(icon) {
 | 
			
		||||
        this.emit('key-focus-in', icon);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    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.actor);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
@@ -322,7 +288,6 @@ const GridSearchResults = new Lang.Class({
 | 
			
		||||
            return null;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(GridSearchResults.prototype);
 | 
			
		||||
 | 
			
		||||
const SearchResults = new Lang.Class({
 | 
			
		||||
    Name: 'SearchResults',
 | 
			
		||||
@@ -336,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);
 | 
			
		||||
@@ -382,10 +339,6 @@ const SearchResults = new Lang.Class({
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _keyFocusIn: function(provider, icon) {
 | 
			
		||||
        Util.ensureActorVisibleInScrollView(this._scrollView, icon);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    createProviderMeta: function(provider) {
 | 
			
		||||
        let providerBox = new St.BoxLayout({ style_class: 'search-section',
 | 
			
		||||
                                             vertical: true });
 | 
			
		||||
@@ -399,8 +352,6 @@ const SearchResults = new Lang.Class({
 | 
			
		||||
            resultDisplay = new GridSearchResults(provider);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        resultDisplay.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
 | 
			
		||||
 | 
			
		||||
        let resultDisplayBin = new St.Bin({ child: resultDisplay.actor,
 | 
			
		||||
                                            x_fill: true,
 | 
			
		||||
                                            y_fill: true });
 | 
			
		||||
@@ -445,15 +396,18 @@ const SearchResults = new Lang.Class({
 | 
			
		||||
        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)];
 | 
			
		||||
    },
 | 
			
		||||
@@ -579,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,8 +15,6 @@ const DEFAULT_MODE = 'restrictive';
 | 
			
		||||
const _modes = {
 | 
			
		||||
    'restrictive': {
 | 
			
		||||
        parentMode: null,
 | 
			
		||||
        stylesheetName: 'gnome-shell.css',
 | 
			
		||||
        overridesSchema: 'org.gnome.shell.overrides',
 | 
			
		||||
        hasOverview: false,
 | 
			
		||||
        showCalendarEvents: false,
 | 
			
		||||
        allowSettings: false,
 | 
			
		||||
@@ -46,9 +44,9 @@ const _modes = {
 | 
			
		||||
        unlockDialog: imports.gdm.loginDialog.LoginDialog,
 | 
			
		||||
        components: ['polkitAgent'],
 | 
			
		||||
        panel: {
 | 
			
		||||
            left: [],
 | 
			
		||||
            left: ['logo'],
 | 
			
		||||
            center: ['dateMenu'],
 | 
			
		||||
            right: ['a11yGreeter', 'display', 'keyboard',
 | 
			
		||||
            right: ['a11y', 'display', 'keyboard',
 | 
			
		||||
                    'volume', 'battery', 'powerMenu']
 | 
			
		||||
        },
 | 
			
		||||
        panelStyle: 'login-screen'
 | 
			
		||||
@@ -80,13 +78,12 @@ const _modes = {
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    'initial-setup': {
 | 
			
		||||
        hasWindows: true,
 | 
			
		||||
        isPrimary: true,
 | 
			
		||||
        components: ['networkAgent', 'keyring'],
 | 
			
		||||
        components: ['keyring'],
 | 
			
		||||
        panel: {
 | 
			
		||||
            left: [],
 | 
			
		||||
            center: ['dateMenu'],
 | 
			
		||||
            right: ['a11yGreeter', 'keyboard', 'volume', 'battery']
 | 
			
		||||
            right: ['a11y', 'keyboard', 'volume']
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -160,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] &&
 | 
			
		||||
@@ -168,8 +170,6 @@ const SessionMode = new Lang.Class({
 | 
			
		||||
            let mode = primary ? global.session_mode : 'user';
 | 
			
		||||
            this._modeStack = [mode];
 | 
			
		||||
            this._sync();
 | 
			
		||||
 | 
			
		||||
            this.emit('sessions-loaded');
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -196,10 +196,6 @@ const SessionMode = new Lang.Class({
 | 
			
		||||
        return this._modeStack[this._modeStack.length - 1];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get allowScreencast() {
 | 
			
		||||
        return this.components.indexOf('recorder') != -1;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _sync: function() {
 | 
			
		||||
        let params = this._modes[this.currentMode];
 | 
			
		||||
        let defaults;
 | 
			
		||||
 
 | 
			
		||||
@@ -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 Screencast = imports.ui.screencast;
 | 
			
		||||
const Screenshot = imports.ui.screenshot;
 | 
			
		||||
 | 
			
		||||
const GnomeShellIface = <interface name="org.gnome.Shell">
 | 
			
		||||
<method name="Eval">
 | 
			
		||||
@@ -21,27 +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="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" />
 | 
			
		||||
</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" />
 | 
			
		||||
@@ -71,17 +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._screencastService = new Screencast.ScreencastService();
 | 
			
		||||
        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) {
 | 
			
		||||
                this._emitAcceleratorActivated(action, deviceid);
 | 
			
		||||
            }));
 | 
			
		||||
        this._extensionsSerivce = new GnomeShellExtensions();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -100,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;
 | 
			
		||||
@@ -117,105 +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();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    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) {
 | 
			
		||||
        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('(uu)', [action, deviceid]));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _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,
 | 
			
		||||
 | 
			
		||||
@@ -380,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);
 | 
			
		||||
@@ -402,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;
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,6 @@
 | 
			
		||||
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Mainloop = imports.mainloop;
 | 
			
		||||
 | 
			
		||||
const PanelMenu = imports.ui.panelMenu;
 | 
			
		||||
const PopupMenu = imports.ui.popupMenu;
 | 
			
		||||
@@ -15,7 +14,12 @@ const KEY_MOUSE_KEYS_ENABLED  = 'mousekeys-enable';
 | 
			
		||||
 | 
			
		||||
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_LARGER  = 1.5;
 | 
			
		||||
const DPI_FACTOR_LARGEST = 2.0;
 | 
			
		||||
 | 
			
		||||
const WM_SCHEMA            = 'org.gnome.desktop.wm.preferences';
 | 
			
		||||
const KEY_VISUAL_BELL      = 'visual-bell';
 | 
			
		||||
@@ -70,25 +74,6 @@ const ATIndicator = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
 | 
			
		||||
        this.menu.addSettingsAction(_("Universal Access Settings"), 'gnome-universal-access-panel.desktop');
 | 
			
		||||
 | 
			
		||||
        this._syncMenuVisibility();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _syncMenuVisibility: function() {
 | 
			
		||||
        this._syncMenuVisibilityIdle = 0;
 | 
			
		||||
 | 
			
		||||
        let items = this.menu._getMenuItems();
 | 
			
		||||
 | 
			
		||||
        this.actor.visible = 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));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _buildItemExtended: function(string, initial_value, writable, on_set) {
 | 
			
		||||
@@ -110,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;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -146,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);
 | 
			
		||||
@@ -154,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)
 | 
			
		||||
@@ -185,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() { }
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,6 @@ const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const MessageTray = imports.ui.messageTray;
 | 
			
		||||
const NotificationDaemon = imports.ui.notificationDaemon;
 | 
			
		||||
const PanelMenu = imports.ui.panelMenu;
 | 
			
		||||
const PopupMenu = imports.ui.popupMenu;
 | 
			
		||||
 | 
			
		||||
@@ -56,8 +55,8 @@ const Indicator = new Lang.Class({
 | 
			
		||||
        this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
 | 
			
		||||
 | 
			
		||||
        this._fullMenuItems = [new PopupMenu.PopupSeparatorMenuItem(),
 | 
			
		||||
                               new PopupMenu.PopupMenuItem(_("Send Files to Device…")),
 | 
			
		||||
                               new PopupMenu.PopupMenuItem(_("Set Up a New Device…")),
 | 
			
		||||
                               new PopupMenu.PopupMenuItem(_("Send Files to Device...")),
 | 
			
		||||
                               new PopupMenu.PopupMenuItem(_("Set up a New Device...")),
 | 
			
		||||
                               new PopupMenu.PopupSeparatorMenuItem()];
 | 
			
		||||
        this._hasDevices = false;
 | 
			
		||||
 | 
			
		||||
@@ -81,7 +80,7 @@ const Indicator = new Lang.Class({
 | 
			
		||||
        this._applet.connect('notify::show-full-menu', Lang.bind(this, this._updateFullMenu));
 | 
			
		||||
        this._updateFullMenu();
 | 
			
		||||
 | 
			
		||||
        this.menu.addSettingsAction(_("Bluetooth Settings"), 'gnome-bluetooth-panel.desktop');
 | 
			
		||||
        this.menu.addSettingsAction(_("Bluetooth Settings"), 'bluetooth-properties.desktop');
 | 
			
		||||
 | 
			
		||||
        this._applet.connect('pincode-request', Lang.bind(this, this._pinRequest));
 | 
			
		||||
        this._applet.connect('confirm-request', Lang.bind(this, this._confirmRequest));
 | 
			
		||||
@@ -236,7 +235,7 @@ const Indicator = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (device.capabilities & GnomeBluetoothApplet.Capabilities.OBEX_PUSH) {
 | 
			
		||||
            item.menu.addAction(_("Send Files…"), Lang.bind(this, function() {
 | 
			
		||||
            item.menu.addAction(_("Send Files..."), Lang.bind(this, function() {
 | 
			
		||||
                this._applet.send_to_address(device.bdaddr, device.alias);
 | 
			
		||||
            }));
 | 
			
		||||
        }
 | 
			
		||||
@@ -287,7 +286,6 @@ const Indicator = new Lang.Class({
 | 
			
		||||
    _ensureSource: function() {
 | 
			
		||||
        if (!this._source) {
 | 
			
		||||
            this._source = new MessageTray.Source(_("Bluetooth"), 'bluetooth-active');
 | 
			
		||||
            this._source.policy = new NotificationDaemon.NotificationApplicationPolicy('gnome-bluetooth-panel');
 | 
			
		||||
            Main.messageTray.add(this._source);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
@@ -448,5 +446,10 @@ const PinNotification = new Lang.Class({
 | 
			
		||||
            return this._entry.clutter_text.text.length == 6;
 | 
			
		||||
        else
 | 
			
		||||
            return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    grabFocus: function(lockTray) {
 | 
			
		||||
        this.parent(lockTray);
 | 
			
		||||
        global.stage.set_key_focus(this._entry);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,9 @@
 | 
			
		||||
// -*- 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 GnomeDesktop = imports.gi.GnomeDesktop;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Meta = imports.gi.Meta;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
@@ -23,7 +21,6 @@ try {
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const PopupMenu = imports.ui.popupMenu;
 | 
			
		||||
const PanelMenu = imports.ui.panelMenu;
 | 
			
		||||
const SwitcherPopup = imports.ui.switcherPopup;
 | 
			
		||||
const Util = imports.misc.util;
 | 
			
		||||
 | 
			
		||||
const DESKTOP_INPUT_SOURCES_SCHEMA = 'org.gnome.desktop.input-sources';
 | 
			
		||||
@@ -33,33 +30,6 @@ const KEY_INPUT_SOURCES = 'sources';
 | 
			
		||||
const INPUT_SOURCE_TYPE_XKB = 'xkb';
 | 
			
		||||
const INPUT_SOURCE_TYPE_IBUS = 'ibus';
 | 
			
		||||
 | 
			
		||||
// This is the longest we'll keep the keyboard frozen until an input
 | 
			
		||||
// source is active.
 | 
			
		||||
const MAX_INPUT_SOURCE_ACTIVATION_TIME = 4000; // ms
 | 
			
		||||
 | 
			
		||||
const BUS_NAME = 'org.gnome.SettingsDaemon.Keyboard';
 | 
			
		||||
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Keyboard';
 | 
			
		||||
 | 
			
		||||
const KeyboardManagerInterface =
 | 
			
		||||
<interface name="org.gnome.SettingsDaemon.Keyboard">
 | 
			
		||||
<method name="SetInputSource">
 | 
			
		||||
    <arg type="u" direction="in" />
 | 
			
		||||
</method>
 | 
			
		||||
</interface>;
 | 
			
		||||
 | 
			
		||||
const KeyboardManagerProxy = Gio.DBusProxy.makeProxyWrapper(KeyboardManagerInterface);
 | 
			
		||||
 | 
			
		||||
function releaseKeyboard() {
 | 
			
		||||
    if (Main.modalCount > 0)
 | 
			
		||||
        global.display.unfreeze_keyboard(global.get_current_time());
 | 
			
		||||
    else
 | 
			
		||||
        global.display.ungrab_keyboard(global.get_current_time());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function holdKeyboard() {
 | 
			
		||||
    global.freeze_keyboard(global.get_current_time());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const IBusManager = new Lang.Class({
 | 
			
		||||
    Name: 'IBusManager',
 | 
			
		||||
 | 
			
		||||
@@ -72,33 +42,35 @@ const IBusManager = new Lang.Class({
 | 
			
		||||
        this._readyCallback = readyCallback;
 | 
			
		||||
        this._candidatePopup = new IBusCandidatePopup.CandidatePopup();
 | 
			
		||||
 | 
			
		||||
        this._ibus = null;
 | 
			
		||||
        this._panelService = null;
 | 
			
		||||
        this._engines = {};
 | 
			
		||||
        this._ready = false;
 | 
			
		||||
        this._registerPropertiesId = 0;
 | 
			
		||||
        this._currentEngineName = null;
 | 
			
		||||
 | 
			
		||||
        this._ibus = IBus.Bus.new_async();
 | 
			
		||||
        this._ibus.connect('connected', Lang.bind(this, this._onConnected));
 | 
			
		||||
        this._ibus.connect('disconnected', Lang.bind(this, this._clear));
 | 
			
		||||
        // Need to set this to get 'global-engine-changed' emitions
 | 
			
		||||
        this._ibus.set_watch_ibus_signal(true);
 | 
			
		||||
        this._ibus.connect('global-engine-changed', Lang.bind(this, this._engineChanged));
 | 
			
		||||
        this._nameWatcherId = Gio.DBus.session.watch_name(IBus.SERVICE_IBUS,
 | 
			
		||||
                                                          Gio.BusNameWatcherFlags.NONE,
 | 
			
		||||
                                                          Lang.bind(this, this._onNameAppeared),
 | 
			
		||||
                                                          Lang.bind(this, this._clear));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _clear: function() {
 | 
			
		||||
        if (this._panelService)
 | 
			
		||||
            this._panelService.destroy();
 | 
			
		||||
        if (this._ibus)
 | 
			
		||||
            this._ibus.destroy();
 | 
			
		||||
 | 
			
		||||
        this._ibus = null;
 | 
			
		||||
        this._panelService = null;
 | 
			
		||||
        this._candidatePopup.setPanelService(null);
 | 
			
		||||
        this._engines = {};
 | 
			
		||||
        this._ready = false;
 | 
			
		||||
        this._registerPropertiesId = 0;
 | 
			
		||||
        this._currentEngineName = null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
        if (this._readyCallback)
 | 
			
		||||
            this._readyCallback(false);
 | 
			
		||||
    _onNameAppeared: function() {
 | 
			
		||||
        this._ibus = IBus.Bus.new_async();
 | 
			
		||||
        this._ibus.connect('connected', Lang.bind(this, this._onConnected));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onConnected: function() {
 | 
			
		||||
@@ -107,6 +79,7 @@ const IBusManager = new Lang.Class({
 | 
			
		||||
                                      IBus.BusNameFlag.REPLACE_EXISTING,
 | 
			
		||||
                                      -1, null,
 | 
			
		||||
                                      Lang.bind(this, this._initPanelService));
 | 
			
		||||
        this._ibus.connect('disconnected', Lang.bind(this, this._clear));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _initEngines: function(ibus, result) {
 | 
			
		||||
@@ -116,10 +89,12 @@ const IBusManager = new Lang.Class({
 | 
			
		||||
                let name = enginesList[i].get_name();
 | 
			
		||||
                this._engines[name] = enginesList[i];
 | 
			
		||||
            }
 | 
			
		||||
            this._updateReadiness();
 | 
			
		||||
        } else {
 | 
			
		||||
            this._clear();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._updateReadiness();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _initPanelService: function(ibus, result) {
 | 
			
		||||
@@ -128,38 +103,29 @@ const IBusManager = new Lang.Class({
 | 
			
		||||
            this._panelService = new IBus.PanelService({ connection: this._ibus.get_connection(),
 | 
			
		||||
                                                         object_path: IBus.PATH_PANEL });
 | 
			
		||||
            this._candidatePopup.setPanelService(this._panelService);
 | 
			
		||||
            // Need to set this to get 'global-engine-changed' emitions
 | 
			
		||||
            this._ibus.set_watch_ibus_signal(true);
 | 
			
		||||
            this._ibus.connect('global-engine-changed', Lang.bind(this, this._resetProperties));
 | 
			
		||||
            this._panelService.connect('update-property', Lang.bind(this, this._updateProperty));
 | 
			
		||||
            // If an engine is already active we need to get its properties
 | 
			
		||||
            this._ibus.get_global_engine_async(-1, null, Lang.bind(this, function(i, result) {
 | 
			
		||||
                let engine;
 | 
			
		||||
                try {
 | 
			
		||||
                    engine = this._ibus.get_global_engine_async_finish(result);
 | 
			
		||||
                    if (!engine)
 | 
			
		||||
                        return;
 | 
			
		||||
                } catch(e) {
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                this._engineChanged(this._ibus, engine.get_name());
 | 
			
		||||
            }));
 | 
			
		||||
            this._updateReadiness();
 | 
			
		||||
            this._resetProperties();
 | 
			
		||||
        } else {
 | 
			
		||||
            this._clear();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._updateReadiness();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateReadiness: function() {
 | 
			
		||||
        this._ready = (Object.keys(this._engines).length > 0 &&
 | 
			
		||||
                       this._panelService != null);
 | 
			
		||||
 | 
			
		||||
        if (this._readyCallback)
 | 
			
		||||
            this._readyCallback(this._ready);
 | 
			
		||||
        if (this._ready && this._readyCallback)
 | 
			
		||||
            this._readyCallback();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _engineChanged: function(bus, engineName) {
 | 
			
		||||
        if (!this._ready)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._currentEngineName = engineName;
 | 
			
		||||
    _resetProperties: function() {
 | 
			
		||||
        this.emit('properties-registered', null);
 | 
			
		||||
 | 
			
		||||
        if (this._registerPropertiesId != 0)
 | 
			
		||||
            return;
 | 
			
		||||
@@ -172,18 +138,25 @@ const IBusManager = new Lang.Class({
 | 
			
		||||
                this._panelService.disconnect(this._registerPropertiesId);
 | 
			
		||||
                this._registerPropertiesId = 0;
 | 
			
		||||
 | 
			
		||||
                this.emit('properties-registered', this._currentEngineName, props);
 | 
			
		||||
                this.emit('properties-registered', props);
 | 
			
		||||
            }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateProperty: function(panel, prop) {
 | 
			
		||||
        this.emit('property-updated', this._currentEngineName, prop);
 | 
			
		||||
        this.emit('property-updated', prop);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    activateProperty: function(key, state) {
 | 
			
		||||
        this._panelService.property_activate(key, state);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    hasProperties: function(id) {
 | 
			
		||||
        if (id == 'anthy')
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getEngineDesc: function(id) {
 | 
			
		||||
        if (!IBus || !this._ready)
 | 
			
		||||
            return null;
 | 
			
		||||
@@ -208,127 +181,16 @@ const LayoutMenuItem = new Lang.Class({
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const InputSource = new Lang.Class({
 | 
			
		||||
    Name: 'InputSource',
 | 
			
		||||
 | 
			
		||||
    _init: function(type, id, displayName, shortName, index) {
 | 
			
		||||
        this.type = type;
 | 
			
		||||
        this.id = id;
 | 
			
		||||
        this.displayName = displayName;
 | 
			
		||||
        this._shortName = shortName;
 | 
			
		||||
        this.index = index;
 | 
			
		||||
 | 
			
		||||
        this._menuItem = new LayoutMenuItem(this.displayName, this._shortName);
 | 
			
		||||
        this._menuItem.connect('activate', Lang.bind(this, this.activate));
 | 
			
		||||
        this._indicatorLabel = new St.Label({ text: this._shortName });
 | 
			
		||||
 | 
			
		||||
        this.properties = null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroy: function() {
 | 
			
		||||
        this._menuItem.destroy();
 | 
			
		||||
        this._indicatorLabel.destroy();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get shortName() {
 | 
			
		||||
        return this._shortName;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    set shortName(v) {
 | 
			
		||||
        this._shortName = v;
 | 
			
		||||
        this._menuItem.indicator.set_text(v);
 | 
			
		||||
        this._indicatorLabel.set_text(v);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get menuItem() {
 | 
			
		||||
        return this._menuItem;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get indicatorLabel() {
 | 
			
		||||
        return this._indicatorLabel;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    activate: function() {
 | 
			
		||||
        this.emit('activate');
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(InputSource.prototype);
 | 
			
		||||
 | 
			
		||||
const InputSourcePopup = new Lang.Class({
 | 
			
		||||
    Name: 'InputSourcePopup',
 | 
			
		||||
    Extends: SwitcherPopup.SwitcherPopup,
 | 
			
		||||
 | 
			
		||||
    _init: function(items, action, actionBackward) {
 | 
			
		||||
        this.parent(items);
 | 
			
		||||
 | 
			
		||||
        this._action = action;
 | 
			
		||||
        this._actionBackward = actionBackward;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createSwitcher: function() {
 | 
			
		||||
        this._switcherList = new InputSourceSwitcher(this._items);
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _initialSelection: function(backward, binding) {
 | 
			
		||||
        if (binding == 'switch-input-source') {
 | 
			
		||||
            if (backward)
 | 
			
		||||
                this._selectedIndex = this._items.length - 1;
 | 
			
		||||
        } else if (binding == 'switch-input-source-backward') {
 | 
			
		||||
            if (!backward)
 | 
			
		||||
                this._selectedIndex = this._items.length - 1;
 | 
			
		||||
        }
 | 
			
		||||
        this._select(this._selectedIndex);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _keyPressHandler: function(keysym, backwards, action) {
 | 
			
		||||
        if (action == this._action)
 | 
			
		||||
            this._select(backwards ? this._previous() : this._next());
 | 
			
		||||
        else if (action == this._actionBackward)
 | 
			
		||||
            this._select(backwards ? this._next() : this._previous());
 | 
			
		||||
        else if (keysym == Clutter.Left)
 | 
			
		||||
            this._select(this._previous());
 | 
			
		||||
        else if (keysym == Clutter.Right)
 | 
			
		||||
            this._select(this._next());
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _finish : function() {
 | 
			
		||||
        this.parent();
 | 
			
		||||
 | 
			
		||||
        this._items[this._selectedIndex].activate();
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const InputSourceSwitcher = new Lang.Class({
 | 
			
		||||
    Name: 'InputSourceSwitcher',
 | 
			
		||||
    Extends: SwitcherPopup.SwitcherList,
 | 
			
		||||
 | 
			
		||||
    _init: function(items) {
 | 
			
		||||
        this.parent(true);
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < items.length; i++)
 | 
			
		||||
            this._addIcon(items[i]);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _addIcon: function(item) {
 | 
			
		||||
        let box = new St.BoxLayout({ vertical: true });
 | 
			
		||||
 | 
			
		||||
        let bin = new St.Bin({ style_class: 'input-source-switcher-symbol' });
 | 
			
		||||
        let symbol = new St.Label({ text: item.shortName });
 | 
			
		||||
        bin.set_child(symbol);
 | 
			
		||||
        box.add(bin, { x_fill: false, y_fill: false } );
 | 
			
		||||
 | 
			
		||||
        let text = new St.Label({ text: item.displayName });
 | 
			
		||||
        box.add(text, { x_fill: false });
 | 
			
		||||
 | 
			
		||||
        this.addItem(box, text);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const InputSourceIndicator = new Lang.Class({
 | 
			
		||||
    Name: 'InputSourceIndicator',
 | 
			
		||||
    Extends: PanelMenu.Button,
 | 
			
		||||
 | 
			
		||||
    _propertiesWhitelist: [
 | 
			
		||||
        'InputMode',
 | 
			
		||||
        'TypingMode',
 | 
			
		||||
        'DictMode'
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.parent(0.0, _("Keyboard"));
 | 
			
		||||
 | 
			
		||||
@@ -339,36 +201,14 @@ const InputSourceIndicator = new Lang.Class({
 | 
			
		||||
        this.actor.add_actor(this._container);
 | 
			
		||||
        this.actor.add_style_class_name('panel-status-button');
 | 
			
		||||
 | 
			
		||||
        // All valid input sources currently in the gsettings
 | 
			
		||||
        // KEY_INPUT_SOURCES list indexed by their index there
 | 
			
		||||
        this._inputSources = {};
 | 
			
		||||
        // All valid input sources currently in the gsettings
 | 
			
		||||
        // KEY_INPUT_SOURCES list of type INPUT_SOURCE_TYPE_IBUS
 | 
			
		||||
        // indexed by the IBus ID
 | 
			
		||||
        this._ibusSources = {};
 | 
			
		||||
        this._labelActors = {};
 | 
			
		||||
        this._layoutItems = {};
 | 
			
		||||
 | 
			
		||||
        this._currentSource = null;
 | 
			
		||||
 | 
			
		||||
        // All valid input sources currently in the gsettings
 | 
			
		||||
        // KEY_INPUT_SOURCES list ordered by most recently used
 | 
			
		||||
        this._mruSources = [];
 | 
			
		||||
        this._keybindingAction =
 | 
			
		||||
            Main.wm.addKeybinding('switch-input-source',
 | 
			
		||||
                                  new Gio.Settings({ schema: "org.gnome.desktop.wm.keybindings" }),
 | 
			
		||||
                                  Meta.KeyBindingFlags.REVERSES,
 | 
			
		||||
                                  Shell.KeyBindingMode.ALL,
 | 
			
		||||
                                  Lang.bind(this, this._switchInputSource));
 | 
			
		||||
        this._keybindingActionBackward =
 | 
			
		||||
            Main.wm.addKeybinding('switch-input-source-backward',
 | 
			
		||||
                                  new Gio.Settings({ schema: "org.gnome.desktop.wm.keybindings" }),
 | 
			
		||||
                                  Meta.KeyBindingFlags.REVERSES |
 | 
			
		||||
                                  Meta.KeyBindingFlags.REVERSED,
 | 
			
		||||
                                  Shell.KeyBindingMode.ALL,
 | 
			
		||||
                                  Lang.bind(this, this._switchInputSource));
 | 
			
		||||
        this._settings = new Gio.Settings({ schema: DESKTOP_INPUT_SOURCES_SCHEMA });
 | 
			
		||||
        this._settings.connect('changed::' + KEY_CURRENT_INPUT_SOURCE, Lang.bind(this, this._currentInputSourceChanged));
 | 
			
		||||
        this._settings.connect('changed::' + KEY_INPUT_SOURCES, Lang.bind(this, this._inputSourcesChanged));
 | 
			
		||||
 | 
			
		||||
        this._currentSourceIndex = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE);
 | 
			
		||||
        this._xkbInfo = new GnomeDesktop.XkbInfo();
 | 
			
		||||
 | 
			
		||||
        this._propSeparator = new PopupMenu.PopupSeparatorMenuItem();
 | 
			
		||||
@@ -377,35 +217,20 @@ const InputSourceIndicator = new Lang.Class({
 | 
			
		||||
        this.menu.addMenuItem(this._propSection);
 | 
			
		||||
        this._propSection.actor.hide();
 | 
			
		||||
 | 
			
		||||
        this._ibusReady = false;
 | 
			
		||||
        this._ibusManager = new IBusManager(Lang.bind(this, this._ibusReadyCallback));
 | 
			
		||||
        this._properties = null;
 | 
			
		||||
 | 
			
		||||
        this._ibusManager = new IBusManager(Lang.bind(this, this._inputSourcesChanged));
 | 
			
		||||
        this._ibusManager.connect('properties-registered', Lang.bind(this, this._ibusPropertiesRegistered));
 | 
			
		||||
        this._ibusManager.connect('property-updated', Lang.bind(this, this._ibusPropertyUpdated));
 | 
			
		||||
        this._inputSourcesChanged();
 | 
			
		||||
 | 
			
		||||
        this._keyboardManager = new KeyboardManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH,
 | 
			
		||||
                                                         function(proxy, error) {
 | 
			
		||||
                                                             if (error)
 | 
			
		||||
                                                                 log(error.message);
 | 
			
		||||
                                                         });
 | 
			
		||||
        this._keyboardManager.g_default_timeout = MAX_INPUT_SOURCE_ACTIVATION_TIME;
 | 
			
		||||
 | 
			
		||||
        global.display.connect('modifiers-accelerator-activated', Lang.bind(this, this._modifiersSwitcher));
 | 
			
		||||
 | 
			
		||||
        this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
 | 
			
		||||
        this._showLayoutItem = this.menu.addAction(_("Show Keyboard Layout"), Lang.bind(this, this._showLayout));
 | 
			
		||||
 | 
			
		||||
        Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
 | 
			
		||||
        this._sessionUpdated();
 | 
			
		||||
 | 
			
		||||
        this.menu.addSettingsAction(_("Region & Language Settings"), 'gnome-region-panel.desktop');
 | 
			
		||||
 | 
			
		||||
        this._sourcesPerWindow = false;
 | 
			
		||||
        this._focusWindowNotifyId = 0;
 | 
			
		||||
        this._overviewShowingId = 0;
 | 
			
		||||
        this._overviewHiddenId = 0;
 | 
			
		||||
        this._settings.connect('changed::per-window', Lang.bind(this, this._sourcesPerWindowChanged));
 | 
			
		||||
        this._sourcesPerWindowChanged();
 | 
			
		||||
        this.menu.addSettingsAction(_("Region and Language Settings"), 'gnome-region-panel.desktop');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _sessionUpdated: function() {
 | 
			
		||||
@@ -416,73 +241,18 @@ const InputSourceIndicator = new Lang.Class({
 | 
			
		||||
        this._showLayoutItem.actor.visible = Main.sessionMode.allowSettings;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _ibusReadyCallback: function(ready) {
 | 
			
		||||
        if (this._ibusReady == ready)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._ibusReady = ready;
 | 
			
		||||
        this._mruSources = [];
 | 
			
		||||
        this._inputSourcesChanged();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _modifiersSwitcher: function() {
 | 
			
		||||
        let sourceIndexes = Object.keys(this._inputSources);
 | 
			
		||||
        if (sourceIndexes.length == 0) {
 | 
			
		||||
            releaseKeyboard();
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let is = this._currentSource;
 | 
			
		||||
        if (!is)
 | 
			
		||||
            is = this._inputSources[sourceIndexes[0]];
 | 
			
		||||
 | 
			
		||||
        let nextIndex = is.index + 1;
 | 
			
		||||
        if (nextIndex > sourceIndexes[sourceIndexes.length - 1])
 | 
			
		||||
            nextIndex = 0;
 | 
			
		||||
 | 
			
		||||
        while (!(is = this._inputSources[nextIndex]))
 | 
			
		||||
            nextIndex += 1;
 | 
			
		||||
 | 
			
		||||
        is.activate();
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _switchInputSource: function(display, screen, window, binding) {
 | 
			
		||||
        if (this._mruSources.length < 2)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        // HACK: Fall back on simple input source switching since we
 | 
			
		||||
        // can't show a popup switcher while a GrabHelper grab is in
 | 
			
		||||
        // effect without considerable work to consolidate the usage
 | 
			
		||||
        // of pushModal/popModal and grabHelper. See
 | 
			
		||||
        // https://bugzilla.gnome.org/show_bug.cgi?id=695143 .
 | 
			
		||||
        if (Main.keybindingMode == Shell.KeyBindingMode.MESSAGE_TRAY ||
 | 
			
		||||
            Main.keybindingMode == Shell.KeyBindingMode.TOPBAR_POPUP) {
 | 
			
		||||
            this._modifiersSwitcher();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let popup = new InputSourcePopup(this._mruSources, this._keybindingAction, this._keybindingActionBackward);
 | 
			
		||||
        let modifiers = binding.get_modifiers();
 | 
			
		||||
        let backwards = modifiers & Meta.VirtualModifier.SHIFT_MASK;
 | 
			
		||||
        if (!popup.show(backwards, binding.get_name(), binding.get_mask()))
 | 
			
		||||
            popup.destroy();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _currentInputSourceChanged: function() {
 | 
			
		||||
        let nVisibleSources = Object.keys(this._inputSources).length;
 | 
			
		||||
        let newSourceIndex = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE);
 | 
			
		||||
        let newSource = this._inputSources[newSourceIndex];
 | 
			
		||||
        let nVisibleSources = Object.keys(this._layoutItems).length;
 | 
			
		||||
        let newCurrentSourceIndex = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE);
 | 
			
		||||
        let newLayoutItem = this._layoutItems[newCurrentSourceIndex];
 | 
			
		||||
        let hasProperties;
 | 
			
		||||
 | 
			
		||||
        let oldSource;
 | 
			
		||||
        [oldSource, this._currentSource] = [this._currentSource, newSource];
 | 
			
		||||
        if (newLayoutItem)
 | 
			
		||||
            hasProperties = this._ibusManager.hasProperties(newLayoutItem.ibusEngineId);
 | 
			
		||||
        else
 | 
			
		||||
            hasProperties = false;
 | 
			
		||||
 | 
			
		||||
        if (oldSource) {
 | 
			
		||||
            oldSource.menuItem.setShowDot(false);
 | 
			
		||||
            oldSource.indicatorLabel.hide();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!newSource || (nVisibleSources < 2 && !newSource.properties)) {
 | 
			
		||||
        if (!newLayoutItem || (nVisibleSources < 2 && !hasProperties)) {
 | 
			
		||||
            // This source index might be invalid if we weren't able
 | 
			
		||||
            // to build a menu item for it, so we hide ourselves since
 | 
			
		||||
            // we can't fix it here. *shrug*
 | 
			
		||||
@@ -496,116 +266,106 @@ const InputSourceIndicator = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this.actor.show();
 | 
			
		||||
 | 
			
		||||
        newSource.menuItem.setShowDot(true);
 | 
			
		||||
        newSource.indicatorLabel.show();
 | 
			
		||||
        if (this._layoutItems[this._currentSourceIndex]) {
 | 
			
		||||
            this._layoutItems[this._currentSourceIndex].setShowDot(false);
 | 
			
		||||
            this._container.set_skip_paint(this._labelActors[this._currentSourceIndex], true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._buildPropSection(newSource.properties);
 | 
			
		||||
        newLayoutItem.setShowDot(true);
 | 
			
		||||
 | 
			
		||||
        for (let i = 1; i < this._mruSources.length; ++i)
 | 
			
		||||
            if (this._mruSources[i] == newSource) {
 | 
			
		||||
                let currentSource = this._mruSources.splice(i, 1);
 | 
			
		||||
                this._mruSources = currentSource.concat(this._mruSources);
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        let newLabelActor = this._labelActors[newCurrentSourceIndex];
 | 
			
		||||
        this._container.set_skip_paint(newLabelActor, false);
 | 
			
		||||
 | 
			
		||||
        this._changePerWindowSource();
 | 
			
		||||
        if (hasProperties)
 | 
			
		||||
            newLabelActor.set_text(newLayoutItem.indicator.get_text());
 | 
			
		||||
 | 
			
		||||
        this._currentSourceIndex = newCurrentSourceIndex;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _inputSourcesChanged: function() {
 | 
			
		||||
        let sources = this._settings.get_value(KEY_INPUT_SOURCES);
 | 
			
		||||
        let nSources = sources.n_children();
 | 
			
		||||
 | 
			
		||||
        for (let i in this._inputSources)
 | 
			
		||||
            this._inputSources[i].destroy();
 | 
			
		||||
        for (let i in this._layoutItems)
 | 
			
		||||
            this._layoutItems[i].destroy();
 | 
			
		||||
 | 
			
		||||
        this._inputSources = {};
 | 
			
		||||
        this._ibusSources = {};
 | 
			
		||||
        this._currentSource = null;
 | 
			
		||||
        for (let i in this._labelActors)
 | 
			
		||||
            this._labelActors[i].destroy();
 | 
			
		||||
 | 
			
		||||
        let inputSourcesByShortName = {};
 | 
			
		||||
        this._layoutItems = {};
 | 
			
		||||
        this._labelActors = {};
 | 
			
		||||
 | 
			
		||||
        let infos = [];
 | 
			
		||||
        let infosByShortName = {};
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < nSources; i++) {
 | 
			
		||||
            let displayName;
 | 
			
		||||
            let shortName;
 | 
			
		||||
            let info = { exists: false };
 | 
			
		||||
            let [type, id] = sources.get_child_value(i).deep_unpack();
 | 
			
		||||
            let exists = false;
 | 
			
		||||
 | 
			
		||||
            if (type == INPUT_SOURCE_TYPE_XKB) {
 | 
			
		||||
                [exists, displayName, shortName, , ] =
 | 
			
		||||
                [info.exists, info.displayName, info.shortName, , ] =
 | 
			
		||||
                    this._xkbInfo.get_layout_info(id);
 | 
			
		||||
            } else if (type == INPUT_SOURCE_TYPE_IBUS) {
 | 
			
		||||
                let engineDesc = this._ibusManager.getEngineDesc(id);
 | 
			
		||||
                if (engineDesc) {
 | 
			
		||||
                    let language = IBus.get_language_name(engineDesc.get_language());
 | 
			
		||||
                    exists = true;
 | 
			
		||||
                    displayName = language + ' (' + engineDesc.get_longname() + ')';
 | 
			
		||||
                    shortName = this._makeEngineShortName(engineDesc);
 | 
			
		||||
 | 
			
		||||
                    info.exists = true;
 | 
			
		||||
                    info.displayName = language + ' (' + engineDesc.get_longname() + ')';
 | 
			
		||||
                    info.shortName = this._makeEngineShortName(engineDesc);
 | 
			
		||||
                    info.ibusEngineId = id;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!exists)
 | 
			
		||||
            if (!info.exists)
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            let is = new InputSource(type, id, displayName, shortName, i);
 | 
			
		||||
            info.sourceIndex = i;
 | 
			
		||||
 | 
			
		||||
            is.connect('activate', Lang.bind(this, function() {
 | 
			
		||||
                holdKeyboard();
 | 
			
		||||
                this._keyboardManager.SetInputSourceRemote(is.index, releaseKeyboard);
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
            if (!(is.shortName in inputSourcesByShortName))
 | 
			
		||||
                inputSourcesByShortName[is.shortName] = [];
 | 
			
		||||
            inputSourcesByShortName[is.shortName].push(is);
 | 
			
		||||
 | 
			
		||||
            this._inputSources[is.index] = is;
 | 
			
		||||
 | 
			
		||||
            if (is.type == INPUT_SOURCE_TYPE_IBUS)
 | 
			
		||||
                this._ibusSources[is.id] = is;
 | 
			
		||||
            if (!(info.shortName in infosByShortName))
 | 
			
		||||
                infosByShortName[info.shortName] = [];
 | 
			
		||||
            infosByShortName[info.shortName].push(info);
 | 
			
		||||
            infos.push(info);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let menuIndex = 0;
 | 
			
		||||
        for (let i in this._inputSources) {
 | 
			
		||||
            let is = this._inputSources[i];
 | 
			
		||||
            if (inputSourcesByShortName[is.shortName].length > 1) {
 | 
			
		||||
                let sub = inputSourcesByShortName[is.shortName].indexOf(is) + 1;
 | 
			
		||||
                is.shortName += String.fromCharCode(0x2080 + sub);
 | 
			
		||||
        for (let i = 0; i < infos.length; i++) {
 | 
			
		||||
            let info = infos[i];
 | 
			
		||||
            if (infosByShortName[info.shortName].length > 1) {
 | 
			
		||||
                let sub = infosByShortName[info.shortName].indexOf(info) + 1;
 | 
			
		||||
                info.shortName += String.fromCharCode(0x2080 + sub);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.menu.addMenuItem(is.menuItem, menuIndex++);
 | 
			
		||||
            let item = new LayoutMenuItem(info.displayName, info.shortName);
 | 
			
		||||
            item.ibusEngineId = info.ibusEngineId;
 | 
			
		||||
            this._layoutItems[info.sourceIndex] = item;
 | 
			
		||||
            this.menu.addMenuItem(item, i);
 | 
			
		||||
            item.connect('activate', Lang.bind(this, function() {
 | 
			
		||||
                this._settings.set_value(KEY_CURRENT_INPUT_SOURCE,
 | 
			
		||||
                                         GLib.Variant.new_uint32(info.sourceIndex));
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
            is.indicatorLabel.hide();
 | 
			
		||||
            this._container.add_actor(is.indicatorLabel);
 | 
			
		||||
            let shortLabel = new St.Label({ text: info.shortName });
 | 
			
		||||
            this._labelActors[info.sourceIndex] = shortLabel;
 | 
			
		||||
            this._container.add_actor(shortLabel);
 | 
			
		||||
            this._container.set_skip_paint(shortLabel, true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let sourcesList = [];
 | 
			
		||||
        for (let i in this._inputSources)
 | 
			
		||||
            sourcesList.push(this._inputSources[i]);
 | 
			
		||||
 | 
			
		||||
        let mruSources = [];
 | 
			
		||||
        for (let i = 0; i < this._mruSources.length; i++) {
 | 
			
		||||
            for (let j = 0; j < sourcesList.length; j++)
 | 
			
		||||
                if (this._mruSources[i].type == sourcesList[j].type &&
 | 
			
		||||
                    this._mruSources[i].id == sourcesList[j].id) {
 | 
			
		||||
                    mruSources = mruSources.concat(sourcesList.splice(j, 1));
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
        }
 | 
			
		||||
        this._mruSources = mruSources.concat(sourcesList);
 | 
			
		||||
 | 
			
		||||
        this._currentInputSourceChanged();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _showLayout: function() {
 | 
			
		||||
        Main.overview.hide();
 | 
			
		||||
 | 
			
		||||
        let source = this._currentSource;
 | 
			
		||||
        let sources = this._settings.get_value(KEY_INPUT_SOURCES);
 | 
			
		||||
        let current = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE);
 | 
			
		||||
        let [type, id] = sources.get_child_value(current).deep_unpack();
 | 
			
		||||
        let xkbLayout = '';
 | 
			
		||||
        let xkbVariant = '';
 | 
			
		||||
 | 
			
		||||
        if (source.type == INPUT_SOURCE_TYPE_XKB) {
 | 
			
		||||
            [, , , xkbLayout, xkbVariant] = this._xkbInfo.get_layout_info(source.id);
 | 
			
		||||
        } else if (source.type == INPUT_SOURCE_TYPE_IBUS) {
 | 
			
		||||
            let engineDesc = this._ibusManager.getEngineDesc(source.id);
 | 
			
		||||
        if (type == INPUT_SOURCE_TYPE_XKB) {
 | 
			
		||||
            [, , , xkbLayout, xkbVariant] = this._xkbInfo.get_layout_info(id);
 | 
			
		||||
        } else if (type == INPUT_SOURCE_TYPE_IBUS) {
 | 
			
		||||
            let engineDesc = this._ibusManager.getEngineDesc(id);
 | 
			
		||||
            if (engineDesc) {
 | 
			
		||||
                xkbLayout = engineDesc.get_layout();
 | 
			
		||||
                xkbVariant = '';
 | 
			
		||||
@@ -634,25 +394,26 @@ const InputSourceIndicator = new Lang.Class({
 | 
			
		||||
        return String.fromCharCode(0x2328); // keyboard glyph
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _ibusPropertiesRegistered: function(im, engineName, props) {
 | 
			
		||||
        let source = this._ibusSources[engineName];
 | 
			
		||||
        if (!source)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        source.properties = props;
 | 
			
		||||
 | 
			
		||||
        if (source == this._currentSource)
 | 
			
		||||
            this._currentInputSourceChanged();
 | 
			
		||||
    _propertyWhitelisted: function(prop) {
 | 
			
		||||
        for (let i = 0; i < this._propertiesWhitelist.length; ++i) {
 | 
			
		||||
            let key = prop.get_key();
 | 
			
		||||
            if (key.substr(0, this._propertiesWhitelist[i].length) == this._propertiesWhitelist[i])
 | 
			
		||||
                return true;
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _ibusPropertyUpdated: function(im, engineName, prop) {
 | 
			
		||||
        let source = this._ibusSources[engineName];
 | 
			
		||||
        if (!source)
 | 
			
		||||
    _ibusPropertiesRegistered: function(im, props) {
 | 
			
		||||
        this._properties = props;
 | 
			
		||||
        this._buildPropSection();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _ibusPropertyUpdated: function(im, prop) {
 | 
			
		||||
        if (!this._propertyWhitelisted(prop))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (this._updateSubProperty(source.properties, prop) &&
 | 
			
		||||
            source == this._currentSource)
 | 
			
		||||
            this._currentInputSourceChanged();
 | 
			
		||||
        if (this._updateSubProperty(this._properties, prop))
 | 
			
		||||
            this._buildPropSection();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateSubProperty: function(props, prop) {
 | 
			
		||||
@@ -672,12 +433,25 @@ const InputSourceIndicator = new Lang.Class({
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _buildPropSection: function(properties) {
 | 
			
		||||
    _updateIndicatorLabel: function(text) {
 | 
			
		||||
        let layoutItem = this._layoutItems[this._currentSourceIndex];
 | 
			
		||||
        let hasProperties;
 | 
			
		||||
 | 
			
		||||
        if (layoutItem)
 | 
			
		||||
            hasProperties = this._ibusManager.hasProperties(layoutItem.ibusEngineId);
 | 
			
		||||
        else
 | 
			
		||||
            hasProperties = false;
 | 
			
		||||
 | 
			
		||||
        if (hasProperties)
 | 
			
		||||
            this._labelActors[this._currentSourceIndex].set_text(text);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _buildPropSection: function() {
 | 
			
		||||
        this._propSeparator.actor.hide();
 | 
			
		||||
        this._propSection.actor.hide();
 | 
			
		||||
        this._propSection.removeAll();
 | 
			
		||||
 | 
			
		||||
        this._buildPropSubMenu(this._propSection, properties);
 | 
			
		||||
        this._buildPropSubMenu(this._propSection, this._properties);
 | 
			
		||||
 | 
			
		||||
        if (!this._propSection.isEmpty()) {
 | 
			
		||||
            this._propSection.actor.show();
 | 
			
		||||
@@ -694,7 +468,8 @@ const InputSourceIndicator = new Lang.Class({
 | 
			
		||||
        for (let i = 0; (p = props.get(i)) != null; ++i) {
 | 
			
		||||
            let prop = p;
 | 
			
		||||
 | 
			
		||||
            if (!prop.get_visible())
 | 
			
		||||
            if (!this._propertyWhitelisted(prop) ||
 | 
			
		||||
                !prop.get_visible())
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            if (prop.get_key() == 'InputMode') {
 | 
			
		||||
@@ -705,17 +480,15 @@ const InputSourceIndicator = new Lang.Class({
 | 
			
		||||
                    text = prop.get_label().get_text();
 | 
			
		||||
 | 
			
		||||
                if (text && text.length > 0 && text.length < 3)
 | 
			
		||||
                    this._currentSource.indicatorLabel.set_text(text);
 | 
			
		||||
                    this._updateIndicatorLabel(text);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let item;
 | 
			
		||||
            switch (prop.get_prop_type()) {
 | 
			
		||||
            case IBus.PropType.MENU:
 | 
			
		||||
            let type = prop.get_prop_type();
 | 
			
		||||
            if (type == IBus.PropType.MENU) {
 | 
			
		||||
                item = new PopupMenu.PopupSubMenuMenuItem(prop.get_label().get_text());
 | 
			
		||||
                this._buildPropSubMenu(item.menu, prop.get_sub_props());
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case IBus.PropType.RADIO:
 | 
			
		||||
            } else if (type == IBus.PropType.RADIO) {
 | 
			
		||||
                item = new PopupMenu.PopupMenuItem(prop.get_label().get_text());
 | 
			
		||||
                item.prop = prop;
 | 
			
		||||
                radioGroup.push(item);
 | 
			
		||||
@@ -740,39 +513,7 @@ const InputSourceIndicator = new Lang.Class({
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }));
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case IBus.PropType.TOGGLE:
 | 
			
		||||
                item = new PopupMenu.PopupSwitchMenuItem(prop.get_label().get_text(), prop.get_state() == IBus.PropState.CHECKED);
 | 
			
		||||
                item.prop = prop;
 | 
			
		||||
                item.connect('toggled', Lang.bind(this, function() {
 | 
			
		||||
                    if (item.state) {
 | 
			
		||||
                        item.prop.set_state(IBus.PropState.CHECKED);
 | 
			
		||||
                        this._ibusManager.activateProperty(item.prop.get_key(),
 | 
			
		||||
                                                           IBus.PropState.CHECKED);
 | 
			
		||||
                    } else {
 | 
			
		||||
                        item.prop.set_state(IBus.PropState.UNCHECKED);
 | 
			
		||||
                        this._ibusManager.activateProperty(item.prop.get_key(),
 | 
			
		||||
                                                           IBus.PropState.UNCHECKED);
 | 
			
		||||
                    }
 | 
			
		||||
                }));
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case IBus.PropType.NORMAL:
 | 
			
		||||
                item = new PopupMenu.PopupMenuItem(prop.get_label().get_text());
 | 
			
		||||
                item.prop = prop;
 | 
			
		||||
                item.connect('activate', Lang.bind(this, function() {
 | 
			
		||||
                    this._ibusManager.activateProperty(item.prop.get_key(),
 | 
			
		||||
                                                       item.prop.get_state());
 | 
			
		||||
                }));
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            case IBus.PropType.SEPARATOR:
 | 
			
		||||
                item = new PopupMenu.PopupSeparatorMenuItem();
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            default:
 | 
			
		||||
                log ('IBus property %s has invalid type %d'.format(prop.get_key(), type));
 | 
			
		||||
            } else {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@@ -781,91 +522,14 @@ const InputSourceIndicator = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getNewInputSource: function(current) {
 | 
			
		||||
        for (let i in this._inputSources) {
 | 
			
		||||
            let is = this._inputSources[i];
 | 
			
		||||
            if (is.type == current.type &&
 | 
			
		||||
                is.id == current.id)
 | 
			
		||||
                return is;
 | 
			
		||||
        }
 | 
			
		||||
        return this._currentSource;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getCurrentWindow: function() {
 | 
			
		||||
        if (Main.overview.visible)
 | 
			
		||||
            return Main.overview;
 | 
			
		||||
        else
 | 
			
		||||
            return global.display.focus_window;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _setPerWindowInputSource: function() {
 | 
			
		||||
        let window = this._getCurrentWindow();
 | 
			
		||||
        if (!window)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (!window._inputSources) {
 | 
			
		||||
            window._inputSources = this._inputSources;
 | 
			
		||||
            window._currentSource = this._currentSource;
 | 
			
		||||
        } else if (window._inputSources == this._inputSources) {
 | 
			
		||||
            window._currentSource.activate();
 | 
			
		||||
        } else {
 | 
			
		||||
            window._inputSources = this._inputSources;
 | 
			
		||||
            window._currentSource = this._getNewInputSource(window._currentSource);
 | 
			
		||||
            window._currentSource.activate();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _sourcesPerWindowChanged: function() {
 | 
			
		||||
        this._sourcesPerWindow = this._settings.get_boolean('per-window');
 | 
			
		||||
 | 
			
		||||
        if (this._sourcesPerWindow && this._focusWindowNotifyId == 0) {
 | 
			
		||||
            this._focusWindowNotifyId = global.display.connect('notify::focus-window',
 | 
			
		||||
                                                               Lang.bind(this, this._setPerWindowInputSource));
 | 
			
		||||
            this._overviewShowingId = Main.overview.connect('showing',
 | 
			
		||||
                                                            Lang.bind(this, this._setPerWindowInputSource));
 | 
			
		||||
            this._overviewHiddenId = Main.overview.connect('hidden',
 | 
			
		||||
                                                           Lang.bind(this, this._setPerWindowInputSource));
 | 
			
		||||
        } else if (!this._sourcesPerWindow && this._focusWindowNotifyId != 0) {
 | 
			
		||||
            global.display.disconnect(this._focusWindowNotifyId);
 | 
			
		||||
            this._focusWindowNotifyId = 0;
 | 
			
		||||
            Main.overview.disconnect(this._overviewShowingId);
 | 
			
		||||
            this._overviewShowingId = 0;
 | 
			
		||||
            Main.overview.disconnect(this._overviewHiddenId);
 | 
			
		||||
            this._overviewHiddenId = 0;
 | 
			
		||||
 | 
			
		||||
            let windows = global.get_window_actors().map(function(w) {
 | 
			
		||||
                return w.meta_window;
 | 
			
		||||
            });
 | 
			
		||||
            for (let i = 0; i < windows.length; ++i) {
 | 
			
		||||
                delete windows[i]._inputSources;
 | 
			
		||||
                delete windows[i]._currentSource;
 | 
			
		||||
            }
 | 
			
		||||
            delete Main.overview._inputSources;
 | 
			
		||||
            delete Main.overview._currentSource;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _changePerWindowSource: function() {
 | 
			
		||||
        if (!this._sourcesPerWindow)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let window = this._getCurrentWindow();
 | 
			
		||||
        if (!window)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        window._inputSources = this._inputSources;
 | 
			
		||||
        window._currentSource = this._currentSource;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _containerGetPreferredWidth: function(container, for_height, alloc) {
 | 
			
		||||
        // Here, and in _containerGetPreferredHeight, we need to query
 | 
			
		||||
        // for the height of all children, but we ignore the results
 | 
			
		||||
        // for those we don't actually display.
 | 
			
		||||
        let max_min_width = 0, max_natural_width = 0;
 | 
			
		||||
 | 
			
		||||
        for (let i in this._inputSources) {
 | 
			
		||||
            let is = this._inputSources[i];
 | 
			
		||||
            let [min_width, natural_width] = is.indicatorLabel.get_preferred_width(for_height);
 | 
			
		||||
        for (let i in this._labelActors) {
 | 
			
		||||
            let [min_width, natural_width] = this._labelActors[i].get_preferred_width(for_height);
 | 
			
		||||
            max_min_width = Math.max(max_min_width, min_width);
 | 
			
		||||
            max_natural_width = Math.max(max_natural_width, natural_width);
 | 
			
		||||
        }
 | 
			
		||||
@@ -877,9 +541,8 @@ const InputSourceIndicator = new Lang.Class({
 | 
			
		||||
    _containerGetPreferredHeight: function(container, for_width, alloc) {
 | 
			
		||||
        let max_min_height = 0, max_natural_height = 0;
 | 
			
		||||
 | 
			
		||||
        for (let i in this._inputSources) {
 | 
			
		||||
            let is = this._inputSources[i];
 | 
			
		||||
            let [min_height, natural_height] = is.indicatorLabel.get_preferred_height(for_width);
 | 
			
		||||
        for (let i in this._labelActors) {
 | 
			
		||||
            let [min_height, natural_height] = this._labelActors[i].get_preferred_height(for_width);
 | 
			
		||||
            max_min_height = Math.max(max_min_height, min_height);
 | 
			
		||||
            max_natural_height = Math.max(max_natural_height, natural_height);
 | 
			
		||||
        }
 | 
			
		||||
@@ -895,9 +558,7 @@ const InputSourceIndicator = new Lang.Class({
 | 
			
		||||
        box.y2 -= box.y1;
 | 
			
		||||
        box.y1 = 0;
 | 
			
		||||
 | 
			
		||||
        for (let i in this._inputSources) {
 | 
			
		||||
            let is = this._inputSources[i];
 | 
			
		||||
            is.indicatorLabel.allocate_align_fill(box, 0.5, 0, false, false, flags);
 | 
			
		||||
        }
 | 
			
		||||
        for (let i in this._labelActors)
 | 
			
		||||
            this._labelActors[i].allocate_align_fill(box, 0.5, 0, false, false, flags);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -10,53 +10,48 @@ const PanelMenu = imports.ui.panelMenu;
 | 
			
		||||
const PopupMenu = imports.ui.popupMenu;
 | 
			
		||||
const VolumeMenu = imports.ui.status.volume;
 | 
			
		||||
 | 
			
		||||
const FakeStatusIcon = new Lang.Class({
 | 
			
		||||
    Name: 'FakeStatusIcon',
 | 
			
		||||
 | 
			
		||||
    _init: function(button) {
 | 
			
		||||
        this.actor = new St.BoxLayout({ style_class: 'panel-status-button-box' });
 | 
			
		||||
        this._button = button;
 | 
			
		||||
        this._button.connect('icons-updated', Lang.bind(this, this._reconstructIcons));
 | 
			
		||||
        this._button.actor.bind_property('visible', this.actor, 'visible',
 | 
			
		||||
                                         GObject.BindingFlags.SYNC_CREATE);
 | 
			
		||||
        this._reconstructIcons();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _reconstructIcons: function() {
 | 
			
		||||
        this.actor.destroy_all_children();
 | 
			
		||||
        this._button.icons.forEach(Lang.bind(this, function(icon) {
 | 
			
		||||
            let newIcon = new St.Icon({ style_class: 'system-status-icon' });
 | 
			
		||||
            icon.bind_property('gicon', newIcon, 'gicon',
 | 
			
		||||
                               GObject.BindingFlags.SYNC_CREATE);
 | 
			
		||||
            icon.bind_property('visible', newIcon, 'visible',
 | 
			
		||||
                               GObject.BindingFlags.SYNC_CREATE);
 | 
			
		||||
            this.actor.add_actor(newIcon);
 | 
			
		||||
        }));
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const Indicator = new Lang.Class({
 | 
			
		||||
    Name: 'LockScreenMenuIndicator',
 | 
			
		||||
    Extends: PanelMenu.SystemStatusButton,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.parent(null, _("Volume, network, battery"));
 | 
			
		||||
        this._box.style_class = 'lock-screen-status-button-box';
 | 
			
		||||
 | 
			
		||||
        this._volumeControl = VolumeMenu.getMixerControl();
 | 
			
		||||
        this._volumeMenu = new VolumeMenu.VolumeMenu(this._volumeControl);
 | 
			
		||||
        this.menu.addMenuItem(this._volumeMenu);
 | 
			
		||||
        this._volume = Main.panel.statusArea.volume;
 | 
			
		||||
        if (this._volume) {
 | 
			
		||||
            this._volumeIcon = this.addIcon(null);
 | 
			
		||||
            this._volume.mainIcon.bind_property('gicon', this._volumeIcon, 'gicon',
 | 
			
		||||
                                                GObject.BindingFlags.SYNC_CREATE);
 | 
			
		||||
            this._volume.mainIcon.bind_property('visible', this._volumeIcon, 'visible',
 | 
			
		||||
                                                GObject.BindingFlags.SYNC_CREATE);
 | 
			
		||||
 | 
			
		||||
        this._volume = new FakeStatusIcon(Main.panel.statusArea.volume);
 | 
			
		||||
        this._box.add_child(this._volume.actor);
 | 
			
		||||
 | 
			
		||||
        // Network may not exist if the user doesn't have NetworkManager
 | 
			
		||||
        if (Main.panel.statusArea.network) {
 | 
			
		||||
            this._network = new FakeStatusIcon(Main.panel.statusArea.network);
 | 
			
		||||
            this._box.add_child(this._network.actor);
 | 
			
		||||
            this._volumeControl = VolumeMenu.getMixerControl();
 | 
			
		||||
            this._volumeMenu = new VolumeMenu.VolumeMenu(this._volumeControl);
 | 
			
		||||
            this.menu.addMenuItem(this._volumeMenu);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._battery = new FakeStatusIcon(Main.panel.statusArea.battery);
 | 
			
		||||
        this._box.add_child(this._battery.actor);
 | 
			
		||||
        this._network = Main.panel.statusArea.network;
 | 
			
		||||
        if (this._network) {
 | 
			
		||||
            this._networkIcon = this.addIcon(null);
 | 
			
		||||
            this._network.mainIcon.bind_property('gicon', this._networkIcon, 'gicon',
 | 
			
		||||
                                                 GObject.BindingFlags.SYNC_CREATE);
 | 
			
		||||
            this._network.mainIcon.bind_property('visible', this._networkIcon, 'visible',
 | 
			
		||||
                                                 GObject.BindingFlags.SYNC_CREATE);
 | 
			
		||||
 | 
			
		||||
            this._networkSecondaryIcon = this.addIcon(null);
 | 
			
		||||
            this._network.secondaryIcon.bind_property('gicon', this._networkSecondaryIcon, 'gicon',
 | 
			
		||||
                                                      GObject.BindingFlags.SYNC_CREATE);
 | 
			
		||||
            this._network.secondaryIcon.bind_property('visible', this._networkSecondaryIcon, 'visible',
 | 
			
		||||
                                                      GObject.BindingFlags.SYNC_CREATE);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._battery = Main.panel.statusArea.battery;
 | 
			
		||||
        if (this._battery) {
 | 
			
		||||
            this._batteryIcon = this.addIcon(null);
 | 
			
		||||
            this._battery.mainIcon.bind_property('gicon', this._batteryIcon, 'gicon',
 | 
			
		||||
                                                 GObject.BindingFlags.SYNC_CREATE);
 | 
			
		||||
            this._battery.mainIcon.bind_property('visible', this._batteryIcon, 'visible',
 | 
			
		||||
                                                 GObject.BindingFlags.SYNC_CREATE);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -5,29 +5,20 @@ const Gio = imports.gi.Gio;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const NetworkManager = imports.gi.NetworkManager;
 | 
			
		||||
const NMClient = imports.gi.NMClient;
 | 
			
		||||
const NMGtk = imports.gi.NMGtk;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
// Some of the new code depends on as-yet-unreleased NM
 | 
			
		||||
var NMGtk;
 | 
			
		||||
try {
 | 
			
		||||
    NMGtk = imports.gi.NMGtk;
 | 
			
		||||
} catch(e) {
 | 
			
		||||
    NMGtk = null;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const PanelMenu = imports.ui.panelMenu;
 | 
			
		||||
const PopupMenu = imports.ui.popupMenu;
 | 
			
		||||
const MessageTray = imports.ui.messageTray;
 | 
			
		||||
const NotificationDaemon = imports.ui.notificationDaemon;
 | 
			
		||||
const ModemManager = imports.misc.modemManager;
 | 
			
		||||
const Util = imports.misc.util;
 | 
			
		||||
 | 
			
		||||
const NMConnectionCategory = {
 | 
			
		||||
    INVALID: 'invalid',
 | 
			
		||||
    WIRED: 'wired',
 | 
			
		||||
    VIRTUAL: 'virtual',
 | 
			
		||||
    WIRELESS: 'wireless',
 | 
			
		||||
    WWAN: 'wwan',
 | 
			
		||||
    VPN: 'vpn'
 | 
			
		||||
@@ -49,7 +40,7 @@ const NM80211ApFlags = NetworkManager['80211ApFlags'];
 | 
			
		||||
const NM80211ApSecurityFlags = NetworkManager['80211ApSecurityFlags'];
 | 
			
		||||
 | 
			
		||||
// number of wireless networks that should be visible
 | 
			
		||||
// (the remaining are placed into More…)
 | 
			
		||||
// (the remaining are placed into More...)
 | 
			
		||||
const NUM_VISIBLE_NETWORKS = 5;
 | 
			
		||||
 | 
			
		||||
function macToArray(string) {
 | 
			
		||||
@@ -309,10 +300,13 @@ const NMDevice = new Lang.Class({
 | 
			
		||||
    Extends: NMConnectionBased,
 | 
			
		||||
 | 
			
		||||
    _init: function(client, device, connections) {
 | 
			
		||||
        this._client = client;
 | 
			
		||||
        this._setDevice(device);
 | 
			
		||||
        this.parent(connections);
 | 
			
		||||
        this.device = device;
 | 
			
		||||
        this.device._delegate = this;
 | 
			
		||||
        this._stateChangedId = this.device.connect('state-changed', Lang.bind(this, this._deviceStateChanged));
 | 
			
		||||
 | 
			
		||||
        // protected
 | 
			
		||||
        this._client = client;
 | 
			
		||||
        this.parent(connections);
 | 
			
		||||
        this._activeConnection = null;
 | 
			
		||||
        this._activeConnectionItem = null;
 | 
			
		||||
        this._autoConnectionItem = null;
 | 
			
		||||
@@ -337,12 +331,23 @@ const NMDevice = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroy: function() {
 | 
			
		||||
        this._setDevice(null);
 | 
			
		||||
        if (this.device)
 | 
			
		||||
            this.device._delegate = null;
 | 
			
		||||
 | 
			
		||||
        if (this._deferredWorkId) {
 | 
			
		||||
            // Just clear out, the actual removal is handled when the
 | 
			
		||||
            // actor is destroyed
 | 
			
		||||
            this._deferredWorkId = 0;
 | 
			
		||||
        if (this._stateChangedId) {
 | 
			
		||||
            // Need to go through GObject.Object.prototype because
 | 
			
		||||
            // nm_device_disconnect conflicts with g_signal_disconnect
 | 
			
		||||
            GObject.Object.prototype.disconnect.call(this.device, this._stateChangedId);
 | 
			
		||||
            this._stateChangedId = 0;
 | 
			
		||||
        }
 | 
			
		||||
        if (this._carrierChangedId) {
 | 
			
		||||
            // see above for why this is needed
 | 
			
		||||
            GObject.Object.prototype.disconnect.call(this.device, this._carrierChangedId);
 | 
			
		||||
            this._carrierChangedId = 0;
 | 
			
		||||
        }
 | 
			
		||||
        if (this._firmwareChangedId) {
 | 
			
		||||
            GObject.Object.prototype.disconnect.call(this.device, this._firmwareChangedId);
 | 
			
		||||
            this._firmwareChangedId = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._clearSection();
 | 
			
		||||
@@ -351,33 +356,6 @@ const NMDevice = new Lang.Class({
 | 
			
		||||
        this.section.destroy();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _setDevice: function(device) {
 | 
			
		||||
        if (device) {
 | 
			
		||||
            this.device = device;
 | 
			
		||||
            this.device._delegate = this;
 | 
			
		||||
            this._stateChangedId = this.device.connect('state-changed', Lang.bind(this, this._deviceStateChanged));
 | 
			
		||||
        } else if (this.device) {
 | 
			
		||||
            this.device._delegate = null;
 | 
			
		||||
 | 
			
		||||
            if (this._stateChangedId) {
 | 
			
		||||
                // Need to go through GObject.Object.prototype because
 | 
			
		||||
                // nm_device_disconnect conflicts with g_signal_disconnect
 | 
			
		||||
                GObject.Object.prototype.disconnect.call(this.device, this._stateChangedId);
 | 
			
		||||
                this._stateChangedId = 0;
 | 
			
		||||
            }
 | 
			
		||||
            if (this._carrierChangedId) {
 | 
			
		||||
                GObject.Object.prototype.disconnect.call(this.device, this._carrierChangedId);
 | 
			
		||||
                this._carrierChangedId = 0;
 | 
			
		||||
            }
 | 
			
		||||
            if (this._firmwareChangedId) {
 | 
			
		||||
                GObject.Object.prototype.disconnect.call(this.device, this._firmwareChangedId);
 | 
			
		||||
                this._firmwareChangedId = 0;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.device = null;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    deactivate: function() {
 | 
			
		||||
        this.device.disconnect(null);
 | 
			
		||||
        return true;
 | 
			
		||||
@@ -392,7 +370,7 @@ const NMDevice = new Lang.Class({
 | 
			
		||||
        // Otherwise, if no connection is currently configured,
 | 
			
		||||
        // try automatic configuration (or summon the config dialog)
 | 
			
		||||
        if (this._connections.length == 1) {
 | 
			
		||||
            this._client.activate_connection(this._connections[0].connection, this.device || null, null, null);
 | 
			
		||||
            this._client.activate_connection(this._connections[0].connection, this.device, null, null);
 | 
			
		||||
            return true;
 | 
			
		||||
        } else if (this._connections.length == 0) {
 | 
			
		||||
            return this._activateAutomaticConnection();
 | 
			
		||||
@@ -412,7 +390,7 @@ const NMDevice = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get connected() {
 | 
			
		||||
        return this.device && this.device.state == NetworkManager.DeviceState.ACTIVATED;
 | 
			
		||||
        return this.device.state == NetworkManager.DeviceState.ACTIVATED;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    clearActiveConnection: function(activeConnection) {
 | 
			
		||||
@@ -432,6 +410,7 @@ const NMDevice = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._activeConnection = activeConnection;
 | 
			
		||||
 | 
			
		||||
        this._clearSection();
 | 
			
		||||
        this._queueCreateSection();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -445,9 +424,6 @@ const NMDevice = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getStatusLabel: function() {
 | 
			
		||||
        if (!this.device)
 | 
			
		||||
            return null;
 | 
			
		||||
 | 
			
		||||
        switch(this.device.state) {
 | 
			
		||||
        case NetworkManager.DeviceState.DISCONNECTED:
 | 
			
		||||
        case NetworkManager.DeviceState.ACTIVATED:
 | 
			
		||||
@@ -498,8 +474,7 @@ const NMDevice = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    syncDescription: function() {
 | 
			
		||||
        if (this.device && this.device._description)
 | 
			
		||||
            this.statusItem.label.text = this.device._description;
 | 
			
		||||
        this.statusItem.label.text = this.device._description;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // protected
 | 
			
		||||
@@ -508,10 +483,8 @@ const NMDevice = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _queueCreateSection: function() {
 | 
			
		||||
        if (this._deferredWorkId) {
 | 
			
		||||
            this._clearSection();
 | 
			
		||||
            Main.queueDeferredWork(this._deferredWorkId);
 | 
			
		||||
        }
 | 
			
		||||
        this._clearSection();
 | 
			
		||||
        Main.queueDeferredWork(this._deferredWorkId);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _clearSection: function() {
 | 
			
		||||
@@ -549,7 +522,7 @@ const NMDevice = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
                if (j + activeOffset >= NUM_VISIBLE_NETWORKS) {
 | 
			
		||||
                    if (!this._overflowItem) {
 | 
			
		||||
                        this._overflowItem = new PopupMenu.PopupSubMenuMenuItem(_("More…"));
 | 
			
		||||
                        this._overflowItem = new PopupMenu.PopupSubMenuMenuItem(_("More..."));
 | 
			
		||||
                        this.section.addMenuItem(this._overflowItem);
 | 
			
		||||
                    }
 | 
			
		||||
                    this._overflowItem.menu.addMenuItem(obj.item);
 | 
			
		||||
@@ -611,6 +584,7 @@ const NMDevice = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._updateStatusItem();
 | 
			
		||||
 | 
			
		||||
        this._clearSection();
 | 
			
		||||
        this._queueCreateSection();
 | 
			
		||||
        this.emit('state-changed');
 | 
			
		||||
    },
 | 
			
		||||
@@ -637,11 +611,12 @@ const NMDevice = new Lang.Class({
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const NMDeviceSimple = new Lang.Class({
 | 
			
		||||
    Name: 'NMDeviceSimple',
 | 
			
		||||
const NMDeviceWired = new Lang.Class({
 | 
			
		||||
    Name: 'NMDeviceWired',
 | 
			
		||||
    Extends: NMDevice,
 | 
			
		||||
 | 
			
		||||
    _init: function(client, device, connections) {
 | 
			
		||||
        this._autoConnectionName = _("Auto Ethernet");
 | 
			
		||||
        this.category = NMConnectionCategory.WIRED;
 | 
			
		||||
 | 
			
		||||
        this.parent(client, device, connections);
 | 
			
		||||
@@ -656,19 +631,6 @@ const NMDeviceSimple = new Lang.Class({
 | 
			
		||||
        // we can do it here because addConnection and removeConnection
 | 
			
		||||
        // both call _createSection at some point
 | 
			
		||||
        this.section.actor.visible = this._connections.length > 1;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const NMDeviceWired = new Lang.Class({
 | 
			
		||||
    Name: 'NMDeviceWired',
 | 
			
		||||
    Extends: NMDeviceSimple,
 | 
			
		||||
 | 
			
		||||
    _init: function(client, device, connections) {
 | 
			
		||||
        device._description = _("Wired");
 | 
			
		||||
        this._autoConnectionName = _("Auto Ethernet");
 | 
			
		||||
        this.category = NMConnectionCategory.WIRED;
 | 
			
		||||
 | 
			
		||||
        this.parent(client, device, connections);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createAutomaticConnection: function() {
 | 
			
		||||
@@ -692,24 +654,12 @@ const NMDeviceModem = new Lang.Class({
 | 
			
		||||
    _init: function(client, device, connections) {
 | 
			
		||||
        let is_wwan = false;
 | 
			
		||||
 | 
			
		||||
        device._description = _("Mobile broadband");
 | 
			
		||||
        this._enabled = true;
 | 
			
		||||
        this.mobileDevice = null;
 | 
			
		||||
        this._connectionType = 'ppp';
 | 
			
		||||
 | 
			
		||||
        this._capabilities = device.current_capabilities;
 | 
			
		||||
        // Support new ModemManager1 devices
 | 
			
		||||
        if (device.udi.indexOf('/org/freedesktop/ModemManager1/Modem') == 0) {
 | 
			
		||||
            is_wwan = true;
 | 
			
		||||
            this.mobileDevice = new ModemManager.BroadbandModem(device.udi, device.current_capabilities);
 | 
			
		||||
            if (this._capabilities & NetworkManager.DeviceModemCapabilities.GSM_UMTS) {
 | 
			
		||||
                this._connectionType = NetworkManager.SETTING_GSM_SETTING_NAME;
 | 
			
		||||
            } else if (this._capabilities & NetworkManager.DeviceModemCapabilities.LTE) {
 | 
			
		||||
                this._connectionType = NetworkManager.SETTING_GSM_SETTING_NAME;
 | 
			
		||||
            } else if (this._capabilities & NetworkManager.DeviceModemCapabilities.CDMA_EVDO) {
 | 
			
		||||
                this._connectionType = NetworkManager.SETTING_CDMA_SETTING_NAME;
 | 
			
		||||
            }
 | 
			
		||||
        } else if (this._capabilities & NetworkManager.DeviceModemCapabilities.GSM_UMTS) {
 | 
			
		||||
        if (this._capabilities & NetworkManager.DeviceModemCapabilities.GSM_UMTS) {
 | 
			
		||||
            is_wwan = true;
 | 
			
		||||
            this.mobileDevice = new ModemManager.ModemGsm(device.udi);
 | 
			
		||||
            this._connectionType = NetworkManager.SETTING_GSM_SETTING_NAME;
 | 
			
		||||
@@ -719,8 +669,7 @@ const NMDeviceModem = new Lang.Class({
 | 
			
		||||
            this._connectionType = NetworkManager.SETTING_CDMA_SETTING_NAME;
 | 
			
		||||
        } else if (this._capabilities & NetworkManager.DeviceModemCapabilities.LTE) {
 | 
			
		||||
            is_wwan = true;
 | 
			
		||||
            this.mobileDevice = new ModemManager.ModemGsm(device.udi);
 | 
			
		||||
            this._connectionType = NetworkManager.SETTING_GSM_SETTING_NAME;
 | 
			
		||||
            // FIXME: support signal quality
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (is_wwan) {
 | 
			
		||||
@@ -823,7 +772,6 @@ const NMDeviceBluetooth = new Lang.Class({
 | 
			
		||||
    Extends: NMDevice,
 | 
			
		||||
 | 
			
		||||
    _init: function(client, device, connections) {
 | 
			
		||||
        device._description = _("Bluetooth");
 | 
			
		||||
        this._autoConnectionName = this._makeConnectionName(device);
 | 
			
		||||
        device.connect('notify::name', Lang.bind(this, this._updateAutoConnectionName));
 | 
			
		||||
 | 
			
		||||
@@ -866,6 +814,7 @@ const NMDeviceBluetooth = new Lang.Class({
 | 
			
		||||
    _updateAutoConnectionName: function() {
 | 
			
		||||
        this._autoConnectionName = this._makeConnectionName(this.device);
 | 
			
		||||
 | 
			
		||||
        this._clearSection();
 | 
			
		||||
        this._queueCreateSection();
 | 
			
		||||
        this._updateStatusItem();
 | 
			
		||||
    }
 | 
			
		||||
@@ -1127,8 +1076,10 @@ const NMDeviceWireless = new Lang.Class({
 | 
			
		||||
        this._networks.splice(res.network, 1);
 | 
			
		||||
        let newPos = Util.insertSorted(this._networks, network, Lang.bind(this, this._networkSortFunction));
 | 
			
		||||
 | 
			
		||||
        if (newPos != res.network)
 | 
			
		||||
        if (newPos != res.network) {
 | 
			
		||||
            this._clearSection();
 | 
			
		||||
            this._queueCreateSection();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _accessPointAdded: function(device, accessPoint) {
 | 
			
		||||
@@ -1181,8 +1132,10 @@ const NMDeviceWireless = new Lang.Class({
 | 
			
		||||
        let newPos = Util.insertSorted(this._networks, apObj, this._networkSortFunction);
 | 
			
		||||
 | 
			
		||||
        // Queue an update of the UI if we changed the order
 | 
			
		||||
        if (newPos != pos)
 | 
			
		||||
        if (newPos != pos) {
 | 
			
		||||
            this._clearSection();
 | 
			
		||||
            this._queueCreateSection();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _accessPointRemoved: function(device, accessPoint) {
 | 
			
		||||
@@ -1242,10 +1195,12 @@ const NMDeviceWireless = new Lang.Class({
 | 
			
		||||
            if (res.network < this._networks.length-1)
 | 
			
		||||
                okNext = this._networkSortFunction(this._networks[res.network + 1], apObj) <= 0;
 | 
			
		||||
 | 
			
		||||
            if (!okPrev || !okNext)
 | 
			
		||||
            if (!okPrev || !okNext) {
 | 
			
		||||
                this._clearSection();
 | 
			
		||||
                this._queueCreateSection();
 | 
			
		||||
            else if (apObj.item)
 | 
			
		||||
            } else if (apObj.item) {
 | 
			
		||||
                apObj.item.updateBestAP(apObj.accessPoints[0]);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -1321,6 +1276,7 @@ const NMDeviceWireless = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        if (forceupdate) {
 | 
			
		||||
            this._networks.sort(this._networkSortFunction);
 | 
			
		||||
            this._clearSection();
 | 
			
		||||
            this._queueCreateSection();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
@@ -1353,6 +1309,7 @@ const NMDeviceWireless = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        if (forceupdate) {
 | 
			
		||||
            this._networks.sort(this._networkSortFunction);
 | 
			
		||||
            this._clearSection();
 | 
			
		||||
            this._queueCreateSection();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
@@ -1432,7 +1389,7 @@ const NMDeviceWireless = new Lang.Class({
 | 
			
		||||
            this.section.addMenuItem(apObj.item, position);
 | 
			
		||||
        } else {
 | 
			
		||||
            if (!this._overflowItem) {
 | 
			
		||||
                this._overflowItem = new PopupMenu.PopupSubMenuMenuItem(_("More…"));
 | 
			
		||||
                this._overflowItem = new PopupMenu.PopupSubMenuMenuItem(_("More..."));
 | 
			
		||||
                this.section.addMenuItem(this._overflowItem);
 | 
			
		||||
            }
 | 
			
		||||
            this._overflowItem.menu.addMenuItem(apObj.item, position - NUM_VISIBLE_NETWORKS);
 | 
			
		||||
@@ -1463,56 +1420,6 @@ const NMDeviceWireless = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const NMDeviceVirtual = new Lang.Class({
 | 
			
		||||
    Name: 'NMDeviceVirtual',
 | 
			
		||||
    Extends: NMDeviceSimple,
 | 
			
		||||
 | 
			
		||||
    _init: function(client, iface, connections) {
 | 
			
		||||
        this.iface = iface;
 | 
			
		||||
        this.parent(client, null, connections);
 | 
			
		||||
        this.category = NMConnectionCategory.VIRTUAL;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _shouldShowConnectionList: function() {
 | 
			
		||||
        return this.hasConnections();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    connectionValid: function(connection) {
 | 
			
		||||
        return connection.get_virtual_iface_name() == this.iface;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addConnection: function(connection) {
 | 
			
		||||
        if (!this.device && !this.hasConnections())
 | 
			
		||||
            this.statusItem.label.text = NMGtk.utils_get_connection_device_name(connection);
 | 
			
		||||
 | 
			
		||||
        this.parent(connection);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    adoptDevice: function(device) {
 | 
			
		||||
        if (device.get_iface() == this.iface) {
 | 
			
		||||
            this._setDevice(device);
 | 
			
		||||
            if (device._description)
 | 
			
		||||
                this.syncDescription();
 | 
			
		||||
            this._updateStatusItem();
 | 
			
		||||
            this.emit('state-changed');
 | 
			
		||||
            return true;
 | 
			
		||||
        } else
 | 
			
		||||
            return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    removeDevice: function(device) {
 | 
			
		||||
        if (device == this.device) {
 | 
			
		||||
            this._setDevice(null);
 | 
			
		||||
            this._updateStatusItem();
 | 
			
		||||
            this.emit('state-changed');
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    hasConnections: function() {
 | 
			
		||||
        return this._connections.length != 0;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const NMVPNSection = new Lang.Class({
 | 
			
		||||
    Name: 'NMVPNSection',
 | 
			
		||||
    Extends: NMConnectionBased,
 | 
			
		||||
@@ -1624,7 +1531,7 @@ const NMVPNSection = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
                if (j >= NUM_VISIBLE_NETWORKS) {
 | 
			
		||||
                    if (!this._overflowItem) {
 | 
			
		||||
                        this._overflowItem = new PopupMenu.PopupSubMenuMenuItem(_("More…"));
 | 
			
		||||
                        this._overflowItem = new PopupMenu.PopupSubMenuMenuItem(_("More..."));
 | 
			
		||||
                        this.section.addMenuItem(this._overflowItem);
 | 
			
		||||
                    }
 | 
			
		||||
                    this._overflowItem.menu.addMenuItem(obj.item);
 | 
			
		||||
@@ -1669,59 +1576,7 @@ const NMApplet = new Lang.Class({
 | 
			
		||||
        this.secondaryIcon = this.addIcon(new Gio.ThemedIcon({ name: 'network-vpn-symbolic' }));
 | 
			
		||||
        this.secondaryIcon.hide();
 | 
			
		||||
 | 
			
		||||
        // Device types
 | 
			
		||||
        this._dtypes = { };
 | 
			
		||||
        this._dtypes[NetworkManager.DeviceType.ETHERNET] = NMDeviceWired;
 | 
			
		||||
        this._dtypes[NetworkManager.DeviceType.WIFI] = NMDeviceWireless;
 | 
			
		||||
        this._dtypes[NetworkManager.DeviceType.MODEM] = NMDeviceModem;
 | 
			
		||||
        this._dtypes[NetworkManager.DeviceType.BT] = NMDeviceBluetooth;
 | 
			
		||||
        this._dtypes[NetworkManager.DeviceType.INFINIBAND] = NMDeviceSimple;
 | 
			
		||||
        // TODO: WiMax support
 | 
			
		||||
 | 
			
		||||
        // Virtual device types
 | 
			
		||||
        this._vtypes = { };
 | 
			
		||||
        if (NMGtk) {
 | 
			
		||||
            this._vtypes[NetworkManager.SETTING_VLAN_SETTING_NAME] = NMDeviceVirtual;
 | 
			
		||||
            this._vtypes[NetworkManager.SETTING_BOND_SETTING_NAME] = NMDeviceVirtual;
 | 
			
		||||
            this._vtypes[NetworkManager.SETTING_BRIDGE_SETTING_NAME] = NMDeviceVirtual;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Connection types
 | 
			
		||||
        this._ctypes = { };
 | 
			
		||||
        this._ctypes[NetworkManager.SETTING_WIRELESS_SETTING_NAME] = NMConnectionCategory.WIRELESS;
 | 
			
		||||
        this._ctypes[NetworkManager.SETTING_WIRED_SETTING_NAME] = NMConnectionCategory.WIRED;
 | 
			
		||||
        this._ctypes[NetworkManager.SETTING_PPPOE_SETTING_NAME] = NMConnectionCategory.WIRED;
 | 
			
		||||
        this._ctypes[NetworkManager.SETTING_PPP_SETTING_NAME] = NMConnectionCategory.WIRED;
 | 
			
		||||
        this._ctypes[NetworkManager.SETTING_BLUETOOTH_SETTING_NAME] = NMConnectionCategory.WWAN;
 | 
			
		||||
        this._ctypes[NetworkManager.SETTING_CDMA_SETTING_NAME] = NMConnectionCategory.WWAN;
 | 
			
		||||
        this._ctypes[NetworkManager.SETTING_GSM_SETTING_NAME] = NMConnectionCategory.WWAN;
 | 
			
		||||
        this._ctypes[NetworkManager.SETTING_INFINIBAND_SETTING_NAME] = NMConnectionCategory.WIRED;
 | 
			
		||||
        if (NMGtk) {
 | 
			
		||||
            this._ctypes[NetworkManager.SETTING_VLAN_SETTING_NAME] = NMConnectionCategory.VIRTUAL;
 | 
			
		||||
            this._ctypes[NetworkManager.SETTING_BOND_SETTING_NAME] = NMConnectionCategory.VIRTUAL;
 | 
			
		||||
            this._ctypes[NetworkManager.SETTING_BRIDGE_SETTING_NAME] = NMConnectionCategory.VIRTUAL;
 | 
			
		||||
        }
 | 
			
		||||
        this._ctypes[NetworkManager.SETTING_VPN_SETTING_NAME] = NMConnectionCategory.VPN;
 | 
			
		||||
 | 
			
		||||
        NMClient.Client.new_async(null, Lang.bind(this, this._clientGot));
 | 
			
		||||
        NMClient.RemoteSettings.new_async(null, null, Lang.bind(this, this._remoteSettingsGot));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _clientGot: function(obj, result) {
 | 
			
		||||
        this._client = NMClient.Client.new_finish(result);
 | 
			
		||||
 | 
			
		||||
        this._tryLateInit();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _remoteSettingsGot: function(obj, result) {
 | 
			
		||||
        this._settings = NMClient.RemoteSettings.new_finish(result);
 | 
			
		||||
 | 
			
		||||
        this._tryLateInit();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _tryLateInit: function() {
 | 
			
		||||
        if (!this._client || !this._settings)
 | 
			
		||||
            return;
 | 
			
		||||
        this._client = NMClient.Client.new();
 | 
			
		||||
 | 
			
		||||
        this._statusSection = new PopupMenu.PopupMenuSection();
 | 
			
		||||
        this._statusItem = new PopupMenu.PopupMenuItem('', { reactive: false });
 | 
			
		||||
@@ -1745,7 +1600,6 @@ const NMApplet = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._nmDevices = [];
 | 
			
		||||
        this._devices = { };
 | 
			
		||||
        this._virtualDevices = [ ];
 | 
			
		||||
 | 
			
		||||
        this._devices.wired = {
 | 
			
		||||
            section: new PopupMenu.PopupMenuSection(),
 | 
			
		||||
@@ -1756,15 +1610,6 @@ const NMApplet = new Lang.Class({
 | 
			
		||||
        this.menu.addMenuItem(this._devices.wired.section);
 | 
			
		||||
        this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
 | 
			
		||||
 | 
			
		||||
        this._devices.virtual = {
 | 
			
		||||
            section: new PopupMenu.PopupMenuSection(),
 | 
			
		||||
            devices: [ ],
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        this._devices.virtual.section.actor.hide();
 | 
			
		||||
        this.menu.addMenuItem(this._devices.virtual.section);
 | 
			
		||||
        this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
 | 
			
		||||
 | 
			
		||||
        this._devices.wireless = {
 | 
			
		||||
            section: new PopupMenu.PopupMenuSection(),
 | 
			
		||||
            devices: [ ],
 | 
			
		||||
@@ -1789,24 +1634,50 @@ const NMApplet = new Lang.Class({
 | 
			
		||||
        this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
 | 
			
		||||
        this.menu.addSettingsAction(_("Network Settings"), 'gnome-network-panel.desktop');
 | 
			
		||||
 | 
			
		||||
        this._readConnections();
 | 
			
		||||
        this._readDevices();
 | 
			
		||||
        this._syncNMState();
 | 
			
		||||
        // Device types
 | 
			
		||||
        this._dtypes = { };
 | 
			
		||||
        this._dtypes[NetworkManager.DeviceType.ETHERNET] = NMDeviceWired;
 | 
			
		||||
        this._dtypes[NetworkManager.DeviceType.WIFI] = NMDeviceWireless;
 | 
			
		||||
        this._dtypes[NetworkManager.DeviceType.MODEM] = NMDeviceModem;
 | 
			
		||||
        this._dtypes[NetworkManager.DeviceType.BT] = NMDeviceBluetooth;
 | 
			
		||||
        // TODO: WiMax support
 | 
			
		||||
 | 
			
		||||
        this._client.connect('notify::manager-running', Lang.bind(this, this._syncNMState));
 | 
			
		||||
        this._client.connect('notify::networking-enabled', Lang.bind(this, this._syncNMState));
 | 
			
		||||
        this._client.connect('notify::state', Lang.bind(this, this._syncNMState));
 | 
			
		||||
        this._client.connect('notify::active-connections', Lang.bind(this, this._updateIcon));
 | 
			
		||||
        this._client.connect('device-added', Lang.bind(this, this._deviceAdded));
 | 
			
		||||
        this._client.connect('device-removed', Lang.bind(this, this._deviceRemoved));
 | 
			
		||||
        this._settings.connect('new-connection', Lang.bind(this, this._newConnection));
 | 
			
		||||
        // Connection types
 | 
			
		||||
        this._ctypes = { };
 | 
			
		||||
        this._ctypes[NetworkManager.SETTING_WIRELESS_SETTING_NAME] = NMConnectionCategory.WIRELESS;
 | 
			
		||||
        this._ctypes[NetworkManager.SETTING_WIRED_SETTING_NAME] = NMConnectionCategory.WIRED;
 | 
			
		||||
        this._ctypes[NetworkManager.SETTING_PPPOE_SETTING_NAME] = NMConnectionCategory.WIRED;
 | 
			
		||||
        this._ctypes[NetworkManager.SETTING_PPP_SETTING_NAME] = NMConnectionCategory.WIRED;
 | 
			
		||||
        this._ctypes[NetworkManager.SETTING_BLUETOOTH_SETTING_NAME] = NMConnectionCategory.WWAN;
 | 
			
		||||
        this._ctypes[NetworkManager.SETTING_CDMA_SETTING_NAME] = NMConnectionCategory.WWAN;
 | 
			
		||||
        this._ctypes[NetworkManager.SETTING_GSM_SETTING_NAME] = NMConnectionCategory.WWAN;
 | 
			
		||||
        this._ctypes[NetworkManager.SETTING_VPN_SETTING_NAME] = NMConnectionCategory.VPN;
 | 
			
		||||
 | 
			
		||||
        this._settings = NMClient.RemoteSettings.new(null);
 | 
			
		||||
        this._connectionsReadId = this._settings.connect('connections-read', Lang.bind(this, function() {
 | 
			
		||||
            this._readConnections();
 | 
			
		||||
            this._readDevices();
 | 
			
		||||
            this._syncNMState();
 | 
			
		||||
 | 
			
		||||
            // Connect to signals late so that early signals don't find in inconsistent state
 | 
			
		||||
            // and connect only once (this signal handler can be called again if NetworkManager goes up and down)
 | 
			
		||||
            if (!this._inited) {
 | 
			
		||||
                this._inited = true;
 | 
			
		||||
                this._client.connect('notify::manager-running', Lang.bind(this, this._syncNMState));
 | 
			
		||||
                this._client.connect('notify::networking-enabled', Lang.bind(this, this._syncNMState));
 | 
			
		||||
                this._client.connect('notify::state', Lang.bind(this, this._syncNMState));
 | 
			
		||||
                this._client.connect('notify::active-connections', Lang.bind(this, this._updateIcon));
 | 
			
		||||
                this._client.connect('device-added', Lang.bind(this, this._deviceAdded));
 | 
			
		||||
                this._client.connect('device-removed', Lang.bind(this, this._deviceRemoved));
 | 
			
		||||
                this._settings.connect('new-connection', Lang.bind(this, this._newConnection));
 | 
			
		||||
            }
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _ensureSource: function() {
 | 
			
		||||
        if (!this._source) {
 | 
			
		||||
            this._source = new MessageTray.Source(_("Network Manager"),
 | 
			
		||||
                                                  'network-transmit-receive');
 | 
			
		||||
            this._source.policy = new NotificationDaemon.NotificationApplicationPolicy('gnome-network-panel');
 | 
			
		||||
 | 
			
		||||
            this._source.connect('destroy', Lang.bind(this, function() {
 | 
			
		||||
                this._source = null;
 | 
			
		||||
@@ -1831,14 +1702,7 @@ const NMApplet = new Lang.Class({
 | 
			
		||||
        let devices = this._devices[category].devices;
 | 
			
		||||
        let item = this._devices[category].item;
 | 
			
		||||
        let section = this._devices[category].section;
 | 
			
		||||
 | 
			
		||||
        let visible;
 | 
			
		||||
        if (category == NMConnectionCategory.VIRTUAL)
 | 
			
		||||
            visible = !section.isEmpty();
 | 
			
		||||
        else
 | 
			
		||||
            visible = devices.length > 0;
 | 
			
		||||
 | 
			
		||||
        if (!visible)
 | 
			
		||||
        if (devices.length == 0)
 | 
			
		||||
            section.actor.hide();
 | 
			
		||||
        else {
 | 
			
		||||
            section.actor.show();
 | 
			
		||||
@@ -1899,50 +1763,9 @@ const NMApplet = new Lang.Class({
 | 
			
		||||
                              MessageTray.Urgency.HIGH);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _syncDeviceNames: function() {
 | 
			
		||||
        if (NMGtk) {
 | 
			
		||||
            let names = NMGtk.utils_disambiguate_device_names(this._nmDevices);
 | 
			
		||||
            for (let i = 0; i < this._nmDevices.length; i++) {
 | 
			
		||||
                let device = this._nmDevices[i];
 | 
			
		||||
                device._description = names[i];
 | 
			
		||||
                if (device._delegate)
 | 
			
		||||
                    device._delegate.syncDescription();
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            for (let i = 0; i < this._nmDevices.length; i++) {
 | 
			
		||||
                let device = this._nmDevices[i];
 | 
			
		||||
                device._delegate.syncDescription();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    _makeWrapperDevice: function(wrapperClass, device) {
 | 
			
		||||
        let wrapper = new wrapperClass(this._client, device, this._connections);
 | 
			
		||||
 | 
			
		||||
    _deviceAdded: function(client, device, skipSyncDeviceNames) {
 | 
			
		||||
        if (device._delegate) {
 | 
			
		||||
            // already seen, not adding again
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < this._virtualDevices.length; i++) {
 | 
			
		||||
            if (this._virtualDevices[i].adoptDevice(device)) {
 | 
			
		||||
                this._nmDevices.push(device);
 | 
			
		||||
                if (!skipSyncDeviceNames)
 | 
			
		||||
                    this._syncDeviceNames();
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let wrapperClass = this._dtypes[device.get_device_type()];
 | 
			
		||||
        if (wrapperClass) {
 | 
			
		||||
            let wrapper = new wrapperClass(this._client, device, this._connections);
 | 
			
		||||
            this._addDeviceWrapper(wrapper);
 | 
			
		||||
 | 
			
		||||
            this._nmDevices.push(device);
 | 
			
		||||
            if (!skipSyncDeviceNames)
 | 
			
		||||
                this._syncDeviceNames();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _addDeviceWrapper: function(wrapper) {
 | 
			
		||||
        wrapper._activationFailedId = wrapper.connect('activation-failed',
 | 
			
		||||
                                                      Lang.bind(this, this._onActivationFailed));
 | 
			
		||||
        wrapper._deviceStateChangedId = wrapper.connect('state-changed', Lang.bind(this, function(dev) {
 | 
			
		||||
@@ -1954,42 +1777,60 @@ const NMApplet = new Lang.Class({
 | 
			
		||||
            wrapper.disconnect(wrapper._destroyId);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        let section = this._devices[wrapper.category].section;
 | 
			
		||||
        section.addMenuItem(wrapper.statusItem);
 | 
			
		||||
        section.addMenuItem(wrapper.section);
 | 
			
		||||
        return wrapper;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
        let devices = this._devices[wrapper.category].devices;
 | 
			
		||||
        devices.push(wrapper);
 | 
			
		||||
    _syncDeviceNames: function() {
 | 
			
		||||
        let names = NMGtk.utils_disambiguate_device_names(this._nmDevices);
 | 
			
		||||
        for (let i = 0; i < this._nmDevices.length; i++) {
 | 
			
		||||
            let device = this._nmDevices[i];
 | 
			
		||||
            if (device._description != names[i]) {
 | 
			
		||||
                device._description = names[i];
 | 
			
		||||
                device._delegate.syncDescription();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
        this._syncSectionTitle(wrapper.category);
 | 
			
		||||
    _deviceAdded: function(client, device, skipSyncDeviceNames) {
 | 
			
		||||
        if (device._delegate) {
 | 
			
		||||
            // already seen, not adding again
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        let wrapperClass = this._dtypes[device.get_device_type()];
 | 
			
		||||
        if (wrapperClass) {
 | 
			
		||||
            let wrapper = this._makeWrapperDevice(wrapperClass, device);
 | 
			
		||||
            let section = this._devices[wrapper.category].section;
 | 
			
		||||
            let devices = this._devices[wrapper.category].devices;
 | 
			
		||||
 | 
			
		||||
            section.addMenuItem(wrapper.statusItem);
 | 
			
		||||
            section.addMenuItem(wrapper.section);
 | 
			
		||||
            devices.push(wrapper);
 | 
			
		||||
 | 
			
		||||
            this._nmDevices.push(device);
 | 
			
		||||
            if (!skipSyncDeviceNames)
 | 
			
		||||
                this._syncDeviceNames();
 | 
			
		||||
 | 
			
		||||
            this._syncSectionTitle(wrapper.category);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _deviceRemoved: function(client, device) {
 | 
			
		||||
        let pos = this._nmDevices.indexOf(device);
 | 
			
		||||
        if (pos != -1) {
 | 
			
		||||
            this._nmDevices.splice(pos, 1);
 | 
			
		||||
            this._syncDeviceNames();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let wrapper = device._delegate;
 | 
			
		||||
        if (!wrapper) {
 | 
			
		||||
        if (!device._delegate) {
 | 
			
		||||
            log('Removing a network device that was not added');
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (wrapper instanceof NMDeviceVirtual)
 | 
			
		||||
            wrapper.removeDevice(device);
 | 
			
		||||
        else
 | 
			
		||||
            this._removeDeviceWrapper(wrapper);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _removeDeviceWrapper: function(wrapper) {
 | 
			
		||||
        let wrapper = device._delegate;
 | 
			
		||||
        wrapper.destroy();
 | 
			
		||||
 | 
			
		||||
        let devices = this._devices[wrapper.category].devices;
 | 
			
		||||
        let pos = devices.indexOf(wrapper);
 | 
			
		||||
        devices.splice(pos, 1);
 | 
			
		||||
 | 
			
		||||
        pos = this._nmDevices.indexOf(device);
 | 
			
		||||
        this._nmDevices.splice(pos, 1);
 | 
			
		||||
        this._syncDeviceNames();
 | 
			
		||||
 | 
			
		||||
        this._syncSectionTitle(wrapper.category)
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -2047,7 +1888,6 @@ const NMApplet = new Lang.Class({
 | 
			
		||||
        let default_ip4 = null;
 | 
			
		||||
        let default_ip6 = null;
 | 
			
		||||
        let active_vpn = null;
 | 
			
		||||
        let active_any = null;
 | 
			
		||||
        for (let i = 0; i < this._activeConnections.length; i++) {
 | 
			
		||||
            let a = this._activeConnections[i];
 | 
			
		||||
 | 
			
		||||
@@ -2078,15 +1918,10 @@ const NMApplet = new Lang.Class({
 | 
			
		||||
            if (a.default6)
 | 
			
		||||
                default_ip6 = a;
 | 
			
		||||
 | 
			
		||||
            if (a.state == NetworkManager.ActiveConnectionState.ACTIVATING)
 | 
			
		||||
                activating = a;
 | 
			
		||||
            else if (a.state == NetworkManager.ActiveConnectionState.ACTIVATED)
 | 
			
		||||
                active_any = a;
 | 
			
		||||
 | 
			
		||||
            if (a._type == 'vpn' &&
 | 
			
		||||
                (a.state == NetworkManager.ActiveConnectionState.ACTIVATING ||
 | 
			
		||||
                 a.state == NetworkManager.ActiveConnectionState.ACTIVATED))
 | 
			
		||||
            if (a._type == 'vpn')
 | 
			
		||||
                active_vpn = a;
 | 
			
		||||
            else if (a.state == NetworkManager.ActiveConnectionState.ACTIVATING)
 | 
			
		||||
                activating = a;
 | 
			
		||||
 | 
			
		||||
            if (!a._primaryDevice) {
 | 
			
		||||
                if (a._type != NetworkManager.SETTING_VPN_SETTING_NAME) {
 | 
			
		||||
@@ -2114,7 +1949,7 @@ const NMApplet = new Lang.Class({
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._mainConnection = activating || default_ip4 || default_ip6 || active_any || null;
 | 
			
		||||
        this._mainConnection = activating || default_ip4 || default_ip6 || this._activeConnections[0] || null;
 | 
			
		||||
        this._vpnConnection = active_vpn;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -2190,13 +2025,6 @@ const NMApplet = new Lang.Class({
 | 
			
		||||
                devices[i].removeConnection(connection);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (section == NMConnectionCategory.VIRTUAL) {
 | 
			
		||||
            let iface = connection.get_virtual_iface_name();
 | 
			
		||||
            let wrapper = this._findVirtualDevice(iface);
 | 
			
		||||
            if (wrapper && !wrapper.hasConnections())
 | 
			
		||||
                this._removeDeviceWrapper(wrapper);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        connection.disconnect(connection._removedId);
 | 
			
		||||
        connection.disconnect(connection._updatedId);
 | 
			
		||||
        connection._removedId = connection._updatedId = 0;
 | 
			
		||||
@@ -2210,27 +2038,6 @@ const NMApplet = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        let section = connection._section;
 | 
			
		||||
 | 
			
		||||
        if (section == NMConnectionCategory.VIRTUAL) {
 | 
			
		||||
            let wrapperClass = this._vtypes[connection._type];
 | 
			
		||||
            if (!wrapperClass)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            let iface = connection.get_virtual_iface_name();
 | 
			
		||||
            let wrapper = this._findVirtualDevice(iface);
 | 
			
		||||
            if (!wrapper) {
 | 
			
		||||
                wrapper = new wrapperClass(this._client, iface, this._connections);
 | 
			
		||||
                this._addDeviceWrapper(wrapper);
 | 
			
		||||
                this._virtualDevices.push(wrapper);
 | 
			
		||||
 | 
			
		||||
                // We might already have a device for this connection
 | 
			
		||||
                for (let i = 0; i < this._nmDevices.length; i++) {
 | 
			
		||||
                    let device = this._nmDevices[i];
 | 
			
		||||
                    if (wrapper.adoptDevice(device))
 | 
			
		||||
                        break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (section == NMConnectionCategory.INVALID)
 | 
			
		||||
            return;
 | 
			
		||||
        if (section == NMConnectionCategory.VPN) {
 | 
			
		||||
@@ -2243,15 +2050,6 @@ const NMApplet = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _findVirtualDevice: function(iface) {
 | 
			
		||||
        for (let i = 0; i < this._virtualDevices.length; i++) {
 | 
			
		||||
            if (this._virtualDevices[i].iface == iface)
 | 
			
		||||
                return this._virtualDevices[i];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _hideDevices: function() {
 | 
			
		||||
        this._devicesHidden = true;
 | 
			
		||||
 | 
			
		||||
@@ -2267,7 +2065,6 @@ const NMApplet = new Lang.Class({
 | 
			
		||||
        this._statusSection.actor.hide();
 | 
			
		||||
 | 
			
		||||
        this._syncSectionTitle(NMConnectionCategory.WIRED);
 | 
			
		||||
        this._syncSectionTitle(NMConnectionCategory.VIRTUAL);
 | 
			
		||||
        this._syncSectionTitle(NMConnectionCategory.WIRELESS);
 | 
			
		||||
        this._syncSectionTitle(NMConnectionCategory.WWAN);
 | 
			
		||||
    },
 | 
			
		||||
@@ -2305,7 +2102,6 @@ const NMApplet = new Lang.Class({
 | 
			
		||||
                this.setIcon('network-wireless-acquiring-symbolic');
 | 
			
		||||
                break;
 | 
			
		||||
            case NMConnectionCategory.WIRED:
 | 
			
		||||
            case NMConnectionCategory.VIRTUAL:
 | 
			
		||||
                this.setIcon('network-wired-acquiring-symbolic');
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
@@ -2345,7 +2141,6 @@ const NMApplet = new Lang.Class({
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            case NMConnectionCategory.WIRED:
 | 
			
		||||
            case NMConnectionCategory.VIRTUAL:
 | 
			
		||||
                this.setIcon('network-wired-symbolic');
 | 
			
		||||
                break;
 | 
			
		||||
            case NMConnectionCategory.WWAN:
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@ const St = imports.gi.St;
 | 
			
		||||
const PanelMenu = imports.ui.panelMenu;
 | 
			
		||||
const PopupMenu = imports.ui.popupMenu;
 | 
			
		||||
 | 
			
		||||
const BUS_NAME = 'org.gnome.SettingsDaemon.Power';
 | 
			
		||||
const BUS_NAME = 'org.gnome.SettingsDaemon';
 | 
			
		||||
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Power';
 | 
			
		||||
 | 
			
		||||
const UPDeviceType = {
 | 
			
		||||
@@ -54,23 +54,14 @@ const Indicator = new Lang.Class({
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.parent('battery-missing-symbolic', _("Battery"));
 | 
			
		||||
 | 
			
		||||
        this._proxy = new PowerManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH,
 | 
			
		||||
                                           Lang.bind(this, function(proxy, error) {
 | 
			
		||||
                                               if (error) {
 | 
			
		||||
                                                   log(error.message);
 | 
			
		||||
                                                   return;
 | 
			
		||||
                                               }
 | 
			
		||||
                                               this._proxy.connect('g-properties-changed',
 | 
			
		||||
                                                                   Lang.bind(this, this._devicesChanged));
 | 
			
		||||
                                               this._devicesChanged();
 | 
			
		||||
                                           }));
 | 
			
		||||
        this._proxy = new PowerManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH);
 | 
			
		||||
 | 
			
		||||
        this._deviceItems = [ ];
 | 
			
		||||
        this._hasPrimary = false;
 | 
			
		||||
        this._primaryDeviceId = null;
 | 
			
		||||
 | 
			
		||||
        this._batteryItem = new PopupMenu.PopupMenuItem('', { reactive: false });
 | 
			
		||||
        this._primaryPercentage = new St.Label({ style_class: 'popup-battery-percentage' });
 | 
			
		||||
        this._primaryPercentage = new St.Label();
 | 
			
		||||
        this._batteryItem.addActor(this._primaryPercentage, { align: St.Align.END });
 | 
			
		||||
        this.menu.addMenuItem(this._batteryItem);
 | 
			
		||||
 | 
			
		||||
@@ -79,6 +70,10 @@ const Indicator = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
 | 
			
		||||
        this.menu.addSettingsAction(_("Power Settings"), 'gnome-power-panel.desktop');
 | 
			
		||||
 | 
			
		||||
        this._proxy.connect('g-properties-changed',
 | 
			
		||||
                            Lang.bind(this, this._devicesChanged));
 | 
			
		||||
        this._devicesChanged();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _readPrimaryDevice: function() {
 | 
			
		||||
@@ -96,7 +91,7 @@ const Indicator = new Lang.Class({
 | 
			
		||||
                if (time == 0) {
 | 
			
		||||
                    // 0 is reported when UPower does not have enough data
 | 
			
		||||
                    // to estimate battery life
 | 
			
		||||
                    this._batteryItem.label.text = _("Estimating…");
 | 
			
		||||
                    this._batteryItem.label.text = _("Estimating...");
 | 
			
		||||
                } else {
 | 
			
		||||
                    let minutes = time % 60;
 | 
			
		||||
                    let hours = Math.floor(time / 60);
 | 
			
		||||
@@ -188,8 +183,7 @@ const DeviceItem = new Lang.Class({
 | 
			
		||||
        this._box.add_actor(this._label);
 | 
			
		||||
        this.addActor(this._box);
 | 
			
		||||
 | 
			
		||||
        let percentLabel = new St.Label({ text: C_("percent of battery remaining", "%d%%").format(Math.round(percentage)),
 | 
			
		||||
                                          style_class: 'popup-battery-percentage' });
 | 
			
		||||
        let percentLabel = new St.Label({ text: C_("percent of battery remaining", "%d%%").format(Math.round(percentage)) });
 | 
			
		||||
        this.addActor(percentLabel, { align: St.Align.END });
 | 
			
		||||
        //FIXME: ideally we would like to expose this._label and percentLabel
 | 
			
		||||
        this.actor.label_actor = percentLabel;
 | 
			
		||||
@@ -198,9 +192,9 @@ const DeviceItem = new Lang.Class({
 | 
			
		||||
    _deviceTypeToString: function(type) {
 | 
			
		||||
	switch (type) {
 | 
			
		||||
	case UPDeviceType.AC_POWER:
 | 
			
		||||
            return _("AC Adapter");
 | 
			
		||||
            return _("AC adapter");
 | 
			
		||||
        case UPDeviceType.BATTERY:
 | 
			
		||||
            return _("Laptop Battery");
 | 
			
		||||
            return _("Laptop battery");
 | 
			
		||||
        case UPDeviceType.UPS:
 | 
			
		||||
            return _("UPS");
 | 
			
		||||
        case UPDeviceType.MONITOR:
 | 
			
		||||
@@ -212,9 +206,9 @@ const DeviceItem = new Lang.Class({
 | 
			
		||||
        case UPDeviceType.PDA:
 | 
			
		||||
            return _("PDA");
 | 
			
		||||
        case UPDeviceType.PHONE:
 | 
			
		||||
            return _("Cell Phone");
 | 
			
		||||
            return _("Cell phone");
 | 
			
		||||
        case UPDeviceType.MEDIA_PLAYER:
 | 
			
		||||
            return _("Media Player");
 | 
			
		||||
            return _("Media player");
 | 
			
		||||
        case UPDeviceType.TABLET:
 | 
			
		||||
            return _("Tablet");
 | 
			
		||||
        case UPDeviceType.COMPUTER:
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,6 @@ const Lang = imports.lang;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const Gvc = imports.gi.Gvc;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
 | 
			
		||||
const PanelMenu = imports.ui.panelMenu;
 | 
			
		||||
const PopupMenu = imports.ui.popupMenu;
 | 
			
		||||
@@ -27,210 +26,6 @@ function getMixerControl() {
 | 
			
		||||
    return _mixerControl;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const StreamSlider = new Lang.Class({
 | 
			
		||||
    Name: 'StreamSlider',
 | 
			
		||||
 | 
			
		||||
    _init: function(control, title) {
 | 
			
		||||
        this._control = control;
 | 
			
		||||
 | 
			
		||||
        this.item = new PopupMenu.PopupMenuSection();
 | 
			
		||||
 | 
			
		||||
        this._title = new PopupMenu.PopupMenuItem(title, { reactive: false });
 | 
			
		||||
        this._slider = new PopupMenu.PopupSliderMenuItem(0);
 | 
			
		||||
        this._slider.connect('value-changed', Lang.bind(this, this._sliderChanged));
 | 
			
		||||
        this._slider.connect('drag-end', Lang.bind(this, this._notifyVolumeChange));
 | 
			
		||||
 | 
			
		||||
        this.item.addMenuItem(this._title);
 | 
			
		||||
        this.item.addMenuItem(this._slider);
 | 
			
		||||
 | 
			
		||||
        this._stream = null;
 | 
			
		||||
        this._shouldShow = true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get stream() {
 | 
			
		||||
        return this._stream;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    set stream(stream) {
 | 
			
		||||
        if (this._stream) {
 | 
			
		||||
            this._disconnectStream(this._stream);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._stream = stream;
 | 
			
		||||
 | 
			
		||||
        if (this._stream) {
 | 
			
		||||
            this._connectStream(this._stream);
 | 
			
		||||
            this._updateVolume();
 | 
			
		||||
        } else {
 | 
			
		||||
            this.emit('stream-updated');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._updateVisibility();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _disconnectStream: function(stream) {
 | 
			
		||||
        stream.disconnect(this._mutedChangedId);
 | 
			
		||||
        this._mutedChangedId = 0;
 | 
			
		||||
        stream.disconnect(this._volumeChangedId);
 | 
			
		||||
        this._volumeChangedId = 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _connectStream: function(stream) {
 | 
			
		||||
        this._mutedChangedId = stream.connect('notify::is-muted', Lang.bind(this, this._updateVolume));
 | 
			
		||||
        this._volumeChangedId = stream.connect('notify::volume', Lang.bind(this, this._updateVolume));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _shouldBeVisible: function() {
 | 
			
		||||
        return this._stream != null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateVisibility: function() {
 | 
			
		||||
        let visible = this._shouldBeVisible();
 | 
			
		||||
        this._title.actor.visible = visible;
 | 
			
		||||
        this._slider.actor.visible = visible;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    scroll: function(event) {
 | 
			
		||||
        this._slider.scroll(event);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setValue: function(value) {
 | 
			
		||||
        // piggy-back off of sliderChanged
 | 
			
		||||
        this._slider.setValue(value);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _sliderChanged: function(slider, value, property) {
 | 
			
		||||
        if (!this._stream)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let volume = value * this._control.get_vol_max_norm();
 | 
			
		||||
        let prevMuted = this._stream.is_muted;
 | 
			
		||||
        if (volume < 1) {
 | 
			
		||||
            this._stream.volume = 0;
 | 
			
		||||
            if (!prevMuted)
 | 
			
		||||
                this._stream.change_is_muted(true);
 | 
			
		||||
        } else {
 | 
			
		||||
            this._stream.volume = volume;
 | 
			
		||||
            if (prevMuted)
 | 
			
		||||
                this._stream.change_is_muted(false);
 | 
			
		||||
        }
 | 
			
		||||
        this._stream.push_volume();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _notifyVolumeChange: function() {
 | 
			
		||||
        global.cancel_theme_sound(VOLUME_NOTIFY_ID);
 | 
			
		||||
        global.play_theme_sound(VOLUME_NOTIFY_ID,
 | 
			
		||||
                                'audio-volume-change',
 | 
			
		||||
                                _("Volume changed"),
 | 
			
		||||
                                Clutter.get_current_event ());
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateVolume: function() {
 | 
			
		||||
        let muted = this._stream.is_muted;
 | 
			
		||||
        this._slider.setValue(muted ? 0 : (this._stream.volume / this._control.get_vol_max_norm()));
 | 
			
		||||
        this.emit('stream-updated');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getIcon: function() {
 | 
			
		||||
        if (!this._stream)
 | 
			
		||||
            return null;
 | 
			
		||||
 | 
			
		||||
        let volume = this._stream.volume;
 | 
			
		||||
        if (this._stream.is_muted || volume <= 0) {
 | 
			
		||||
            return 'audio-volume-muted-symbolic';
 | 
			
		||||
        } else {
 | 
			
		||||
            let n = Math.floor(3 * volume / this._control.get_vol_max_norm()) + 1;
 | 
			
		||||
            if (n < 2)
 | 
			
		||||
                return 'audio-volume-low-symbolic';
 | 
			
		||||
            if (n >= 3)
 | 
			
		||||
                return 'audio-volume-high-symbolic';
 | 
			
		||||
            return 'audio-volume-medium-symbolic';
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(StreamSlider.prototype);
 | 
			
		||||
 | 
			
		||||
const OutputStreamSlider = new Lang.Class({
 | 
			
		||||
    Name: 'OutputStreamSlider',
 | 
			
		||||
    Extends: StreamSlider,
 | 
			
		||||
 | 
			
		||||
    _connectStream: function(stream) {
 | 
			
		||||
        this.parent(stream);
 | 
			
		||||
        this._portChangedId = stream.connect('notify::port', Lang.bind(this, this._portChanged));
 | 
			
		||||
        this._portChanged();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _findHeadphones: function(sink) {
 | 
			
		||||
        // This only works for external headphones (e.g. bluetooth)
 | 
			
		||||
        if (sink.get_form_factor() == 'headset' ||
 | 
			
		||||
            sink.get_form_factor() == 'headphone')
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        // a bit hackish, but ALSA/PulseAudio have a number
 | 
			
		||||
        // of different identifiers for headphones, and I could
 | 
			
		||||
        // not find the complete list
 | 
			
		||||
        if (sink.get_ports().length > 0)
 | 
			
		||||
            return sink.get_port().port.indexOf('headphone') >= 0;
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _disconnectStream: function(stream) {
 | 
			
		||||
        this.parent(stream);
 | 
			
		||||
        stream.disconnect(this._portChangedId);
 | 
			
		||||
        this._portChangedId = 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _portChanged: function() {
 | 
			
		||||
        let hasHeadphones = this._findHeadphones(this._stream);
 | 
			
		||||
        if (hasHeadphones != this._hasHeadphones) {
 | 
			
		||||
            this._hasHeadphones = hasHeadphones;
 | 
			
		||||
            this.emit('headphones-changed', this._hasHeadphones);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const InputStreamSlider = new Lang.Class({
 | 
			
		||||
    Name: 'InputStreamSlider',
 | 
			
		||||
    Extends: StreamSlider,
 | 
			
		||||
 | 
			
		||||
    _init: function(control, title) {
 | 
			
		||||
        this.parent(control, title);
 | 
			
		||||
        this._control.connect('stream-added', Lang.bind(this, this._maybeShowInput));
 | 
			
		||||
        this._control.connect('stream-removed', Lang.bind(this, this._maybeShowInput));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _connectStream: function(stream) {
 | 
			
		||||
        this.parent(stream);
 | 
			
		||||
        this._maybeShowInput();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _maybeShowInput: function() {
 | 
			
		||||
        // only show input widgets if any application is recording audio
 | 
			
		||||
        let showInput = false;
 | 
			
		||||
        let recordingApps = this._control.get_source_outputs();
 | 
			
		||||
        if (this._stream && recordingApps) {
 | 
			
		||||
            for (let i = 0; i < recordingApps.length; i++) {
 | 
			
		||||
                let outputStream = recordingApps[i];
 | 
			
		||||
                let id = outputStream.get_application_id();
 | 
			
		||||
                // but skip gnome-volume-control and pavucontrol
 | 
			
		||||
                // (that appear as recording because they show the input level)
 | 
			
		||||
                if (!id || (id != 'org.gnome.VolumeControl' && id != 'org.PulseAudio.pavucontrol')) {
 | 
			
		||||
                    showInput = true;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._showInput = showInput;
 | 
			
		||||
        this._updateVisibility();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _shouldBeVisible: function() {
 | 
			
		||||
        return this.parent() && this._showInput;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const VolumeMenu = new Lang.Class({
 | 
			
		||||
    Name: 'VolumeMenu',
 | 
			
		||||
    Extends: PopupMenu.PopupMenuSection,
 | 
			
		||||
@@ -244,48 +39,207 @@ const VolumeMenu = new Lang.Class({
 | 
			
		||||
        this._control.connect('state-changed', Lang.bind(this, this._onControlStateChanged));
 | 
			
		||||
        this._control.connect('default-sink-changed', Lang.bind(this, this._readOutput));
 | 
			
		||||
        this._control.connect('default-source-changed', Lang.bind(this, this._readInput));
 | 
			
		||||
        this._control.connect('stream-added', Lang.bind(this, this._maybeShowInput));
 | 
			
		||||
        this._control.connect('stream-removed', Lang.bind(this, this._maybeShowInput));
 | 
			
		||||
        this._volumeMax = this._control.get_vol_max_norm();
 | 
			
		||||
 | 
			
		||||
        this._output = null;
 | 
			
		||||
        this._outputVolumeId = 0;
 | 
			
		||||
        this._outputMutedId = 0;
 | 
			
		||||
        /* Translators: This is the label for audio volume */
 | 
			
		||||
        this._output = new OutputStreamSlider(this._control, _("Volume"));
 | 
			
		||||
        this._output.connect('stream-updated', Lang.bind(this, function() {
 | 
			
		||||
            this.emit('icon-changed');
 | 
			
		||||
        }));
 | 
			
		||||
        this._output.connect('headphones-changed', Lang.bind(this, function(stream, value) {
 | 
			
		||||
            this.emit('headphones-changed', value);
 | 
			
		||||
        }));
 | 
			
		||||
        this.addMenuItem(this._output.item);
 | 
			
		||||
 | 
			
		||||
        this._input = new InputStreamSlider(this._control, _("Microphone"));
 | 
			
		||||
        this.addMenuItem(this._input.item);
 | 
			
		||||
        this._outputTitle = new PopupMenu.PopupMenuItem(_("Volume"), { reactive: false });
 | 
			
		||||
        this._outputSlider = new PopupMenu.PopupSliderMenuItem(0);
 | 
			
		||||
        this._outputSlider.connect('value-changed', Lang.bind(this, this._sliderChanged, '_output'));
 | 
			
		||||
        this._outputSlider.connect('drag-end', Lang.bind(this, this._notifyVolumeChange));
 | 
			
		||||
        this.addMenuItem(this._outputTitle);
 | 
			
		||||
        this.addMenuItem(this._outputSlider);
 | 
			
		||||
 | 
			
		||||
        this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
 | 
			
		||||
 | 
			
		||||
        this._input = null;
 | 
			
		||||
        this._inputVolumeId = 0;
 | 
			
		||||
        this._inputMutedId = 0;
 | 
			
		||||
        this._inputTitle = new PopupMenu.PopupMenuItem(_("Microphone"), { reactive: false });
 | 
			
		||||
        this._inputSlider = new PopupMenu.PopupSliderMenuItem(0);
 | 
			
		||||
        this._inputSlider.connect('value-changed', Lang.bind(this, this._sliderChanged, '_input'));
 | 
			
		||||
        this._inputSlider.connect('drag-end', Lang.bind(this, this._notifyVolumeChange));
 | 
			
		||||
        this.addMenuItem(this._inputTitle);
 | 
			
		||||
        this.addMenuItem(this._inputSlider);
 | 
			
		||||
 | 
			
		||||
        this._onControlStateChanged();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    scroll: function(event) {
 | 
			
		||||
        this._output.scroll(event);
 | 
			
		||||
    scroll: function(direction) {
 | 
			
		||||
        let currentVolume = this._output.volume;
 | 
			
		||||
 | 
			
		||||
        if (direction == Clutter.ScrollDirection.DOWN) {
 | 
			
		||||
            let prev_muted = this._output.is_muted;
 | 
			
		||||
            this._output.volume = Math.max(0, currentVolume - this._volumeMax * VOLUME_ADJUSTMENT_STEP);
 | 
			
		||||
            if (this._output.volume < 1) {
 | 
			
		||||
                this._output.volume = 0;
 | 
			
		||||
                if (!prev_muted)
 | 
			
		||||
                    this._output.change_is_muted(true);
 | 
			
		||||
            }
 | 
			
		||||
            this._output.push_volume();
 | 
			
		||||
        }
 | 
			
		||||
        else if (direction == Clutter.ScrollDirection.UP) {
 | 
			
		||||
            this._output.volume = Math.min(this._volumeMax, currentVolume + this._volumeMax * VOLUME_ADJUSTMENT_STEP);
 | 
			
		||||
            this._output.change_is_muted(false);
 | 
			
		||||
            this._output.push_volume();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._notifyVolumeChange();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onControlStateChanged: function() {
 | 
			
		||||
        if (this._control.get_state() == Gvc.MixerControlState.READY) {
 | 
			
		||||
            this._readInput();
 | 
			
		||||
            this._readOutput();
 | 
			
		||||
            this._readInput();
 | 
			
		||||
            this._maybeShowInput();
 | 
			
		||||
        } else {
 | 
			
		||||
            this.emit('icon-changed');
 | 
			
		||||
            this.emit('icon-changed', null);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _findHeadphones: function(sink) {
 | 
			
		||||
        // This only works for external headphones (e.g. bluetooth)
 | 
			
		||||
        if (sink.get_form_factor() == 'headset' ||
 | 
			
		||||
            sink.get_form_factor() == 'headphone')
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        // a bit hackish, but ALSA/PulseAudio have a number
 | 
			
		||||
        // of different identifiers for headphones, and I could
 | 
			
		||||
        // not find the complete list
 | 
			
		||||
        let port = sink.get_port();
 | 
			
		||||
        if (port)
 | 
			
		||||
            return port.port.indexOf('headphone') >= 0;
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _portChanged: function() {
 | 
			
		||||
        this.hasHeadphones = this._findHeadphones(this._output);
 | 
			
		||||
        this.emit('headphones-changed');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _readOutput: function() {
 | 
			
		||||
        this._output.stream = this._control.get_default_sink();
 | 
			
		||||
        if (this._outputVolumeId) {
 | 
			
		||||
            this._output.disconnect(this._outputVolumeId);
 | 
			
		||||
            this._output.disconnect(this._outputMutedId);
 | 
			
		||||
            this._output.disconnect(this._outputPortId);
 | 
			
		||||
            this._outputVolumeId = 0;
 | 
			
		||||
            this._outputMutedId = 0;
 | 
			
		||||
            this._outputPortId = 0;
 | 
			
		||||
        }
 | 
			
		||||
        this._output = this._control.get_default_sink();
 | 
			
		||||
        if (this._output) {
 | 
			
		||||
            this._outputMutedId = this._output.connect('notify::is-muted', Lang.bind(this, this._mutedChanged, '_output'));
 | 
			
		||||
            this._outputVolumeId = this._output.connect('notify::volume', Lang.bind(this, this._volumeChanged, '_output'));
 | 
			
		||||
            this._outputPortId = this._output.connect('notify::port', Lang.bind(this, this._portChanged));
 | 
			
		||||
 | 
			
		||||
            this._mutedChanged(null, null, '_output');
 | 
			
		||||
            this._volumeChanged(null, null, '_output');
 | 
			
		||||
            this._portChanged();
 | 
			
		||||
        } else {
 | 
			
		||||
            this.hasHeadphones = false;
 | 
			
		||||
            this._outputSlider.setValue(0);
 | 
			
		||||
            this.emit('icon-changed', 'audio-volume-muted-symbolic');
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _readInput: function() {
 | 
			
		||||
        this._input.stream = this._control.get_default_source();
 | 
			
		||||
        if (this._inputVolumeId) {
 | 
			
		||||
            this._input.disconnect(this._inputVolumeId);
 | 
			
		||||
            this._input.disconnect(this._inputMutedId);
 | 
			
		||||
            this._inputVolumeId = 0;
 | 
			
		||||
            this._inputMutedId = 0;
 | 
			
		||||
        }
 | 
			
		||||
        this._input = this._control.get_default_source();
 | 
			
		||||
        if (this._input) {
 | 
			
		||||
            this._inputMutedId = this._input.connect('notify::is-muted', Lang.bind(this, this._mutedChanged, '_input'));
 | 
			
		||||
            this._inputVolumeId = this._input.connect('notify::volume', Lang.bind(this, this._volumeChanged, '_input'));
 | 
			
		||||
            this._mutedChanged (null, null, '_input');
 | 
			
		||||
            this._volumeChanged (null, null, '_input');
 | 
			
		||||
        } else {
 | 
			
		||||
            this._inputTitle.actor.hide();
 | 
			
		||||
            this._inputSlider.actor.hide();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getIcon: function() {
 | 
			
		||||
        return this._output.getIcon();
 | 
			
		||||
    _maybeShowInput: function() {
 | 
			
		||||
        // only show input widgets if any application is recording audio
 | 
			
		||||
        let showInput = false;
 | 
			
		||||
        let recordingApps = this._control.get_source_outputs();
 | 
			
		||||
        if (this._input && recordingApps) {
 | 
			
		||||
            for (let i = 0; i < recordingApps.length; i++) {
 | 
			
		||||
                let outputStream = recordingApps[i];
 | 
			
		||||
                let id = outputStream.get_application_id();
 | 
			
		||||
                // but skip gnome-volume-control and pavucontrol
 | 
			
		||||
                // (that appear as recording because they show the input level)
 | 
			
		||||
                if (!id || (id != 'org.gnome.VolumeControl' && id != 'org.PulseAudio.pavucontrol')) {
 | 
			
		||||
                    showInput = true;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._inputTitle.actor.visible = showInput;
 | 
			
		||||
        this._inputSlider.actor.visible = showInput;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _volumeToIcon: function(volume) {
 | 
			
		||||
        if (volume <= 0) {
 | 
			
		||||
            return 'audio-volume-muted-symbolic';
 | 
			
		||||
        } else {
 | 
			
		||||
            let n = Math.floor(3 * volume / this._volumeMax) + 1;
 | 
			
		||||
            if (n < 2)
 | 
			
		||||
                return 'audio-volume-low-symbolic';
 | 
			
		||||
            if (n >= 3)
 | 
			
		||||
                return 'audio-volume-high-symbolic';
 | 
			
		||||
            return 'audio-volume-medium-symbolic';
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _sliderChanged: function(slider, value, property) {
 | 
			
		||||
        if (this[property] == null) {
 | 
			
		||||
            log ('Volume slider changed for %s, but %s does not exist'.format(property, property));
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        let volume = value * this._volumeMax;
 | 
			
		||||
        let prev_muted = this[property].is_muted;
 | 
			
		||||
        if (volume < 1) {
 | 
			
		||||
            this[property].volume = 0;
 | 
			
		||||
            if (!prev_muted)
 | 
			
		||||
                this[property].change_is_muted(true);
 | 
			
		||||
        } else {
 | 
			
		||||
            this[property].volume = volume;
 | 
			
		||||
            if (prev_muted)
 | 
			
		||||
                this[property].change_is_muted(false);
 | 
			
		||||
        }
 | 
			
		||||
        this[property].push_volume();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _notifyVolumeChange: function() {
 | 
			
		||||
        global.cancel_theme_sound(VOLUME_NOTIFY_ID);
 | 
			
		||||
        global.play_theme_sound(VOLUME_NOTIFY_ID, 'audio-volume-change');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _mutedChanged: function(object, param_spec, property) {
 | 
			
		||||
        let muted = this[property].is_muted;
 | 
			
		||||
        let slider = this[property+'Slider'];
 | 
			
		||||
        slider.setValue(muted ? 0 : (this[property].volume / this._volumeMax));
 | 
			
		||||
        if (property == '_output') {
 | 
			
		||||
            if (muted)
 | 
			
		||||
                this.emit('icon-changed', 'audio-volume-muted-symbolic');
 | 
			
		||||
            else
 | 
			
		||||
                this.emit('icon-changed', this._volumeToIcon(this._output.volume));
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _volumeChanged: function(object, param_spec, property) {
 | 
			
		||||
        this[property+'Slider'].setValue(this[property].volume / this._volumeMax);
 | 
			
		||||
        if (property == '_output' && !this._output.is_muted)
 | 
			
		||||
            this.emit('icon-changed', this._volumeToIcon(this._output.volume));
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@@ -298,16 +252,16 @@ const Indicator = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._control = getMixerControl();
 | 
			
		||||
        this._volumeMenu = new VolumeMenu(this._control);
 | 
			
		||||
        this._volumeMenu.connect('icon-changed', Lang.bind(this, function(menu) {
 | 
			
		||||
            let icon = this._volumeMenu.getIcon();
 | 
			
		||||
            this.actor.visible = (icon != null);
 | 
			
		||||
        this._volumeMenu.connect('icon-changed', Lang.bind(this, function(menu, icon) {
 | 
			
		||||
            this._hasPulseAudio = (icon != null);
 | 
			
		||||
            this.setIcon(icon);
 | 
			
		||||
            this._syncVisibility();
 | 
			
		||||
        }));
 | 
			
		||||
        this._volumeMenu.connect('headphones-changed', Lang.bind(this, function(menu, value) {
 | 
			
		||||
            this._headphoneIcon.visible = value;
 | 
			
		||||
        this._volumeMenu.connect('headphones-changed', Lang.bind(this, function() {
 | 
			
		||||
            this._syncVisibility();
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        this._headphoneIcon = this.addIcon(new Gio.ThemedIcon({ name: 'audio-headphones-symbolic' }));
 | 
			
		||||
        this._headphoneIcon = this.addIcon(new Gio.ThemedIcon({ name: 'headphones-symbolic' }));
 | 
			
		||||
        this._headphoneIcon.visible = false;
 | 
			
		||||
 | 
			
		||||
        this.menu.addMenuItem(this._volumeMenu);
 | 
			
		||||
@@ -318,7 +272,13 @@ const Indicator = new Lang.Class({
 | 
			
		||||
        this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _syncVisibility: function() {
 | 
			
		||||
        this.actor.visible = this._hasPulseAudio;
 | 
			
		||||
        this.mainIcon.visible = this._hasPulseAudio;
 | 
			
		||||
        this._headphoneIcon.visible = this._hasPulseAudio && this._volumeMenu.hasHeadphones;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onScrollEvent: function(actor, event) {
 | 
			
		||||
        this._volumeMenu.scroll(event);
 | 
			
		||||
        this._volumeMenu.scroll(event.get_scroll_direction());
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -46,8 +46,7 @@ const SwitcherPopup = new Lang.Class({
 | 
			
		||||
        this._selectedIndex = 0;
 | 
			
		||||
 | 
			
		||||
        this.actor = new Shell.GenericContainer({ style_class: 'switcher-popup',
 | 
			
		||||
                                                  reactive: true,
 | 
			
		||||
                                                  visible: false });
 | 
			
		||||
                                                  reactive: true });
 | 
			
		||||
        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));
 | 
			
		||||
@@ -160,7 +159,6 @@ const SwitcherPopup = new Lang.Class({
 | 
			
		||||
        // disturbed by the popup briefly flashing.
 | 
			
		||||
        this._initialDelayTimeoutId = Mainloop.timeout_add(POPUP_DELAY_TIMEOUT,
 | 
			
		||||
                                                           Lang.bind(this, function () {
 | 
			
		||||
                                                               Main.osdWindow.cancel();
 | 
			
		||||
                                                               this.actor.opacity = 255;
 | 
			
		||||
                                                               this._initialDelayTimeoutId = 0;
 | 
			
		||||
                                                           }));
 | 
			
		||||
@@ -305,7 +303,7 @@ const SwitcherList = new Lang.Class({
 | 
			
		||||
        this.actor.connect('allocate', Lang.bind(this, this._allocateTop));
 | 
			
		||||
 | 
			
		||||
        // Here we use a GenericContainer so that we can force all the
 | 
			
		||||
        // children to have the same width.
 | 
			
		||||
        // children except the separator to have the same width.
 | 
			
		||||
        this._list = new Shell.GenericContainer({ style_class: 'switcher-list-item-container' });
 | 
			
		||||
        this._list.spacing = 0;
 | 
			
		||||
        this._list.connect('style-changed', Lang.bind(this, function() {
 | 
			
		||||
@@ -340,6 +338,7 @@ const SwitcherList = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._items = [];
 | 
			
		||||
        this._highlighted = -1;
 | 
			
		||||
        this._separator = null;
 | 
			
		||||
        this._squareItems = squareItems;
 | 
			
		||||
        this._minSize = 0;
 | 
			
		||||
        this._scrollableRight = true;
 | 
			
		||||
@@ -402,6 +401,12 @@ const SwitcherList = new Lang.Class({
 | 
			
		||||
        this._itemEntered(index);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addSeparator: function () {
 | 
			
		||||
        let box = new St.Bin({ style_class: 'separator' });
 | 
			
		||||
        this._separator = box;
 | 
			
		||||
        this._list.add_actor(box);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    highlight: function(index, justOutline) {
 | 
			
		||||
        if (this._highlighted != -1) {
 | 
			
		||||
            this._items[this._highlighted].remove_style_pseudo_class('outlined');
 | 
			
		||||
@@ -509,8 +514,14 @@ const SwitcherList = new Lang.Class({
 | 
			
		||||
    _getPreferredWidth: function (actor, forHeight, alloc) {
 | 
			
		||||
        let [maxChildMin, maxChildNat] = this._maxChildWidth(forHeight);
 | 
			
		||||
 | 
			
		||||
        let separatorWidth = 0;
 | 
			
		||||
        if (this._separator) {
 | 
			
		||||
            let [sepMin, sepNat] = this._separator.get_preferred_width(forHeight);
 | 
			
		||||
            separatorWidth = sepNat + this._list.spacing;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let totalSpacing = this._list.spacing * (this._items.length - 1);
 | 
			
		||||
        alloc.min_size = this._items.length * maxChildMin + totalSpacing;
 | 
			
		||||
        alloc.min_size = this._items.length * maxChildMin + separatorWidth + totalSpacing;
 | 
			
		||||
        alloc.natural_size = alloc.min_size;
 | 
			
		||||
        this._minSize = alloc.min_size;
 | 
			
		||||
    },
 | 
			
		||||
@@ -541,7 +552,14 @@ const SwitcherList = new Lang.Class({
 | 
			
		||||
        let [maxChildMin, maxChildNat] = this._maxChildWidth(childHeight);
 | 
			
		||||
        let totalSpacing = this._list.spacing * (this._items.length - 1);
 | 
			
		||||
 | 
			
		||||
        let childWidth = Math.floor(Math.max(0, box.x2 - box.x1 - totalSpacing) / this._items.length);
 | 
			
		||||
        let separatorWidth = 0;
 | 
			
		||||
        if (this._separator) {
 | 
			
		||||
            let [sepMin, sepNat] = this._separator.get_preferred_width(childHeight);
 | 
			
		||||
            separatorWidth = sepNat;
 | 
			
		||||
            totalSpacing += this._list.spacing;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let childWidth = Math.floor(Math.max(0, box.x2 - box.x1 - totalSpacing - separatorWidth) / this._items.length);
 | 
			
		||||
 | 
			
		||||
        let x = 0;
 | 
			
		||||
        let children = this._list.get_children();
 | 
			
		||||
@@ -561,6 +579,14 @@ const SwitcherList = new Lang.Class({
 | 
			
		||||
                children[i].allocate(childBox, flags);
 | 
			
		||||
 | 
			
		||||
                x += this._list.spacing + childWidth;
 | 
			
		||||
            } else if (children[i] == this._separator) {
 | 
			
		||||
                // We want the separator to be more compact than the rest.
 | 
			
		||||
                childBox.x1 = x;
 | 
			
		||||
                childBox.y1 = 0;
 | 
			
		||||
                childBox.x2 = x + separatorWidth;
 | 
			
		||||
                childBox.y2 = childHeight;
 | 
			
		||||
                children[i].allocate(childBox, flags);
 | 
			
		||||
                x += this._list.spacing + separatorWidth;
 | 
			
		||||
            } else {
 | 
			
		||||
                // Something else, eg, AppSwitcher's arrows;
 | 
			
		||||
                // we don't allocate it.
 | 
			
		||||
@@ -611,6 +637,5 @@ function drawArrow(area, side) {
 | 
			
		||||
 | 
			
		||||
    Clutter.cairo_set_source_color(cr, bodyColor);
 | 
			
		||||
    cr.fill();
 | 
			
		||||
    cr.$dispose();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,6 @@
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Gtk = imports.gi.Gtk;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Mainloop = imports.mainloop;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
@@ -74,9 +73,6 @@ function _wrapTweening(target, tweeningParameters) {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!Gtk.Settings.get_default().gtk_enable_animations)
 | 
			
		||||
        tweeningParameters['time'] = 0.000001;
 | 
			
		||||
 | 
			
		||||
    _addHandler(target, tweeningParameters, 'onStart', _tweenStarted);
 | 
			
		||||
    _addHandler(target, tweeningParameters, 'onComplete', _tweenCompleted);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -18,7 +18,6 @@ const Panel = imports.ui.panel;
 | 
			
		||||
const ShellEntry = imports.ui.shellEntry;
 | 
			
		||||
const Tweener = imports.ui.tweener;
 | 
			
		||||
const UserMenu = imports.ui.userMenu;
 | 
			
		||||
const UserWidget = imports.ui.userWidget;
 | 
			
		||||
 | 
			
		||||
const Batch = imports.gdm.batch;
 | 
			
		||||
const GdmUtil = imports.gdm.util;
 | 
			
		||||
@@ -27,6 +26,88 @@ const LoginDialog = imports.gdm.loginDialog;
 | 
			
		||||
// The timeout before going back automatically to the lock screen (in seconds)
 | 
			
		||||
const IDLE_TIMEOUT = 2 * 60;
 | 
			
		||||
 | 
			
		||||
function versionCompare(required, reference) {
 | 
			
		||||
    required = required.split('.');
 | 
			
		||||
    reference = reference.split('.');
 | 
			
		||||
 | 
			
		||||
    for (let i = 0; i < required.length; i++) {
 | 
			
		||||
        if (required[i] != reference[i])
 | 
			
		||||
            return required[i] < reference[i];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function isSupported() {
 | 
			
		||||
    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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// A widget showing the user avatar and name
 | 
			
		||||
const UserWidget = new Lang.Class({
 | 
			
		||||
    Name: 'UserWidget',
 | 
			
		||||
 | 
			
		||||
    _init: function(user) {
 | 
			
		||||
        this._user = user;
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.BoxLayout({ style_class: 'unlock-dialog-user-name-container',
 | 
			
		||||
                                        vertical: false });
 | 
			
		||||
 | 
			
		||||
        this._avatar = new UserMenu.UserAvatarWidget(user);
 | 
			
		||||
        this.actor.add(this._avatar.actor,
 | 
			
		||||
                       { x_fill: true, y_fill: true });
 | 
			
		||||
 | 
			
		||||
        this._label = new St.Label({ style_class: 'login-dialog-username' });
 | 
			
		||||
        this.actor.add(this._label,
 | 
			
		||||
                       { expand: true,
 | 
			
		||||
                         x_fill: true,
 | 
			
		||||
                         y_fill: false,
 | 
			
		||||
                         y_align: St.Align.MIDDLE });
 | 
			
		||||
 | 
			
		||||
        this._userLoadedId = this._user.connect('notify::is-loaded',
 | 
			
		||||
                                                Lang.bind(this, this._updateUser));
 | 
			
		||||
        this._userChangedId = this._user.connect('changed',
 | 
			
		||||
                                                 Lang.bind(this, this._updateUser));
 | 
			
		||||
        if (this._user.is_loaded)
 | 
			
		||||
            this._updateUser();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroy: function() {
 | 
			
		||||
        if (this._userLoadedId != 0) {
 | 
			
		||||
            this._user.disconnect(this._userLoadedId);
 | 
			
		||||
            this._userLoadedId = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this._userChangedId != 0) {
 | 
			
		||||
            this._user.disconnect(this._userChangedId);
 | 
			
		||||
            this._userChangedId = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.actor.destroy();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateUser: function() {
 | 
			
		||||
        if (this._user.is_loaded)
 | 
			
		||||
            this._label.text = this._user.get_real_name();
 | 
			
		||||
        else
 | 
			
		||||
            this._label.text = '';
 | 
			
		||||
 | 
			
		||||
        this._avatar.update();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const UnlockDialog = new Lang.Class({
 | 
			
		||||
    Name: 'UnlockDialog',
 | 
			
		||||
    Extends: ModalDialog.ModalDialog,
 | 
			
		||||
@@ -34,7 +115,7 @@ const UnlockDialog = new Lang.Class({
 | 
			
		||||
    _init: function(parentActor) {
 | 
			
		||||
        this.parent({ shellReactive: true,
 | 
			
		||||
                      styleClass: 'login-dialog',
 | 
			
		||||
                      keybindingMode: Shell.KeyBindingMode.UNLOCK_SCREEN,
 | 
			
		||||
                      keybindingMode: Main.KeybindingMode.UNLOCK_SCREEN,
 | 
			
		||||
                      parentActor: parentActor
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
@@ -47,7 +128,6 @@ const UnlockDialog = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._greeterClient = new Gdm.Client();
 | 
			
		||||
        this._userVerifier = new GdmUtil.ShellUserVerifier(this._greeterClient, { reauthenticationOnly: true });
 | 
			
		||||
        this._userVerified = false;
 | 
			
		||||
 | 
			
		||||
        this._userVerifier.connect('ask-question', Lang.bind(this, this._onAskQuestion));
 | 
			
		||||
        this._userVerifier.connect('show-message', Lang.bind(this, this._showMessage));
 | 
			
		||||
@@ -58,7 +138,7 @@ const UnlockDialog = new Lang.Class({
 | 
			
		||||
        this._userVerifier.connect('show-login-hint', Lang.bind(this, this._showLoginHint));
 | 
			
		||||
        this._userVerifier.connect('hide-login-hint', Lang.bind(this, this._hideLoginHint));
 | 
			
		||||
 | 
			
		||||
        this._userWidget = new UserWidget.UserWidget(this._user);
 | 
			
		||||
        this._userWidget = new UserWidget(this._user);
 | 
			
		||||
        this.contentLayout.add_actor(this._userWidget.actor);
 | 
			
		||||
 | 
			
		||||
        this._promptLayout = new St.BoxLayout({ style_class: 'login-dialog-prompt-layout',
 | 
			
		||||
@@ -70,7 +150,6 @@ const UnlockDialog = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._promptEntry = new St.Entry({ style_class: 'login-dialog-prompt-entry',
 | 
			
		||||
                                           can_focus: true });
 | 
			
		||||
        this._promptEntry.clutter_text.connect('activate', Lang.bind(this, this._doUnlock));
 | 
			
		||||
        this._promptEntry.clutter_text.set_password_char('\u25cf');
 | 
			
		||||
        ShellEntry.addContextMenu(this._promptEntry, { isPassword: true });
 | 
			
		||||
        this.setInitialKeyFocus(this._promptEntry);
 | 
			
		||||
@@ -91,6 +170,9 @@ const UnlockDialog = new Lang.Class({
 | 
			
		||||
        this._promptLoginHint.hide();
 | 
			
		||||
        this.contentLayout.add_actor(this._promptLoginHint);
 | 
			
		||||
 | 
			
		||||
        this._workSpinner = new Panel.AnimatedIcon('process-working.svg', LoginDialog.WORK_SPINNER_ICON_SIZE);
 | 
			
		||||
        this._workSpinner.actor.opacity = 0;
 | 
			
		||||
 | 
			
		||||
        this.allowCancel = false;
 | 
			
		||||
        this.buttonLayout.visible = true;
 | 
			
		||||
        this.addButton({ label: _("Cancel"),
 | 
			
		||||
@@ -101,11 +183,12 @@ const UnlockDialog = new Lang.Class({
 | 
			
		||||
                         y_fill: false,
 | 
			
		||||
                         x_align: St.Align.START,
 | 
			
		||||
                         y_align: St.Align.MIDDLE });
 | 
			
		||||
        this.placeSpinner({ expand: false,
 | 
			
		||||
                            x_fill: false,
 | 
			
		||||
                            y_fill: false,
 | 
			
		||||
                            x_align: St.Align.END,
 | 
			
		||||
                            y_align: St.Align.MIDDLE });
 | 
			
		||||
        this.buttonLayout.add(this._workSpinner.actor,
 | 
			
		||||
                              { expand: false,
 | 
			
		||||
                                x_fill: false,
 | 
			
		||||
                                y_fill: false,
 | 
			
		||||
                                x_align: St.Align.END,
 | 
			
		||||
                                y_align: St.Align.MIDDLE });
 | 
			
		||||
        this._okButton = this.addButton({ label: _("Unlock"),
 | 
			
		||||
                                          action: Lang.bind(this, this._doUnlock),
 | 
			
		||||
                                          default: true },
 | 
			
		||||
@@ -115,43 +198,41 @@ const UnlockDialog = new Lang.Class({
 | 
			
		||||
                                          x_align: St.Align.END,
 | 
			
		||||
                                          y_align: St.Align.MIDDLE });
 | 
			
		||||
 | 
			
		||||
        let screenSaverSettings = new Gio.Settings({ schema: 'org.gnome.desktop.screensaver' });
 | 
			
		||||
        if (screenSaverSettings.get_boolean('user-switch-enabled')) {
 | 
			
		||||
            let otherUserLabel = new St.Label({ text: _("Log in as another user"),
 | 
			
		||||
                                                style_class: 'login-dialog-not-listed-label' });
 | 
			
		||||
            this._otherUserButton = new St.Button({ style_class: 'login-dialog-not-listed-button',
 | 
			
		||||
                                                    can_focus: true,
 | 
			
		||||
                                                    child: otherUserLabel,
 | 
			
		||||
                                                    reactive: true,
 | 
			
		||||
                                                    x_align: St.Align.START,
 | 
			
		||||
                                                    x_fill: true });
 | 
			
		||||
            this._otherUserButton.connect('clicked', Lang.bind(this, this._otherUserClicked));
 | 
			
		||||
            this.dialogLayout.add(this._otherUserButton,
 | 
			
		||||
                                  { x_align: St.Align.START,
 | 
			
		||||
                                    x_fill: false });
 | 
			
		||||
        } else {
 | 
			
		||||
            this._otherUserButton = null;
 | 
			
		||||
        }
 | 
			
		||||
        let otherUserLabel = new St.Label({ text: _("Log in as another user"),
 | 
			
		||||
                                            style_class: 'login-dialog-not-listed-label' });
 | 
			
		||||
        this._otherUserButton = new St.Button({ style_class: 'login-dialog-not-listed-button',
 | 
			
		||||
                                                can_focus: true,
 | 
			
		||||
                                                child: otherUserLabel,
 | 
			
		||||
                                                reactive: true,
 | 
			
		||||
                                                x_align: St.Align.START,
 | 
			
		||||
                                                x_fill: true });
 | 
			
		||||
        this._otherUserButton.connect('clicked', Lang.bind(this, this._otherUserClicked));
 | 
			
		||||
        this.dialogLayout.add(this._otherUserButton,
 | 
			
		||||
                              { x_align: St.Align.START,
 | 
			
		||||
                                x_fill: false });
 | 
			
		||||
 | 
			
		||||
        this._updateSensitivity(true);
 | 
			
		||||
 | 
			
		||||
        let batch = new Batch.Hold();
 | 
			
		||||
        this._userVerifier.begin(this._userName, batch);
 | 
			
		||||
 | 
			
		||||
        GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
 | 
			
		||||
            this.emit('loaded');
 | 
			
		||||
            return false;
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        Main.ctrlAltTabManager.addGroup(this.dialogLayout, _("Unlock Window"), 'dialog-password-symbolic');
 | 
			
		||||
 | 
			
		||||
        this._idleMonitor = new GnomeDesktop.IdleMonitor();
 | 
			
		||||
        this._idleWatchId = this._idleMonitor.add_idle_watch(IDLE_TIMEOUT * 1000, Lang.bind(this, this._escape));
 | 
			
		||||
        this._idleWatchId = this._idleMonitor.add_watch(IDLE_TIMEOUT * 1000, Lang.bind(this, this._escape));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateSensitivity: function(sensitive) {
 | 
			
		||||
        this._promptEntry.reactive = sensitive;
 | 
			
		||||
        this._promptEntry.clutter_text.editable = sensitive;
 | 
			
		||||
        this._updateOkButtonSensitivity(sensitive && this._promptEntry.text.length > 0);
 | 
			
		||||
        if (this._otherUserButton) {
 | 
			
		||||
            this._otherUserButton.reactive = sensitive;
 | 
			
		||||
            this._otherUserButton.can_focus = sensitive;
 | 
			
		||||
        }
 | 
			
		||||
        this._otherUserButton.reactive = sensitive;
 | 
			
		||||
        this._otherUserButton.can_focus = sensitive;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateOkButtonSensitivity: function(sensitive) {
 | 
			
		||||
@@ -159,6 +240,28 @@ const UnlockDialog = new Lang.Class({
 | 
			
		||||
        this._okButton.can_focus = sensitive;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _setWorking: function(working) {
 | 
			
		||||
        if (working) {
 | 
			
		||||
            this._workSpinner.play();
 | 
			
		||||
            Tweener.addTween(this._workSpinner.actor,
 | 
			
		||||
                             { opacity: 255,
 | 
			
		||||
                               delay: LoginDialog.WORK_SPINNER_ANIMATION_DELAY,
 | 
			
		||||
                               time: LoginDialog.WORK_SPINNER_ANIMATION_TIME,
 | 
			
		||||
                               transition: 'linear'
 | 
			
		||||
                             });
 | 
			
		||||
        } else {
 | 
			
		||||
            Tweener.addTween(this._workSpinner.actor,
 | 
			
		||||
                             { opacity: 0,
 | 
			
		||||
                               time: LoginDialog.WORK_SPINNER_ANIMATION_TIME,
 | 
			
		||||
                               transition: 'linear',
 | 
			
		||||
                               onCompleteScope: this,
 | 
			
		||||
                               onComplete: function() {
 | 
			
		||||
                                   this._workSpinner.stop();
 | 
			
		||||
                               }
 | 
			
		||||
                             });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _showMessage: function(userVerifier, message, styleClass) {
 | 
			
		||||
        if (message) {
 | 
			
		||||
            this._promptMessage.text = message;
 | 
			
		||||
@@ -189,7 +292,7 @@ const UnlockDialog = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._currentQuery = serviceName;
 | 
			
		||||
        this._updateSensitivity(true);
 | 
			
		||||
        this.setWorking(false);
 | 
			
		||||
        this._setWorking(false);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _showLoginHint: function(verifier, message) {
 | 
			
		||||
@@ -208,7 +311,7 @@ const UnlockDialog = new Lang.Class({
 | 
			
		||||
            // the actual reply to GDM will be sent as soon as asked
 | 
			
		||||
            this._firstQuestionAnswer = this._promptEntry.text;
 | 
			
		||||
            this._updateSensitivity(false);
 | 
			
		||||
            this.setWorking(true);
 | 
			
		||||
            this._setWorking(true);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -219,47 +322,30 @@ const UnlockDialog = new Lang.Class({
 | 
			
		||||
        this._currentQuery = null;
 | 
			
		||||
 | 
			
		||||
        this._updateSensitivity(false);
 | 
			
		||||
        this.setWorking(true);
 | 
			
		||||
        this._setWorking(true);
 | 
			
		||||
 | 
			
		||||
        this._userVerifier.answerQuery(query, this._promptEntry.text);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _finishUnlock: function() {
 | 
			
		||||
    _onVerificationComplete: function() {
 | 
			
		||||
        this._userVerifier.clear();
 | 
			
		||||
        this.emit('unlocked');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onVerificationComplete: function() {
 | 
			
		||||
        this._userVerified = true;
 | 
			
		||||
        if (!this._userVerifier.hasPendingMessages) {
 | 
			
		||||
            this._finishUnlock();
 | 
			
		||||
        } else {
 | 
			
		||||
            let signalId = this._userVerifier.connect('no-more-messages',
 | 
			
		||||
                                                      Lang.bind(this, function() {
 | 
			
		||||
                                                          this._userVerifier.disconnect(signalId);
 | 
			
		||||
                                                          this._finishUnlock();
 | 
			
		||||
                                                      }));
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onReset: function() {
 | 
			
		||||
        if (!this._userVerified) {
 | 
			
		||||
            this._userVerifier.clear();
 | 
			
		||||
            this.emit('failed');
 | 
			
		||||
        }
 | 
			
		||||
        this.emit('failed');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onVerificationFailed: function() {
 | 
			
		||||
        this._currentQuery = null;
 | 
			
		||||
        this._firstQuestion = true;
 | 
			
		||||
        this._userVerified = false;
 | 
			
		||||
 | 
			
		||||
        this._promptEntry.text = '';
 | 
			
		||||
        this._promptEntry.clutter_text.set_password_char('\u25cf');
 | 
			
		||||
        this._promptEntry.menu.isPassword = true;
 | 
			
		||||
 | 
			
		||||
        this._updateSensitivity(false);
 | 
			
		||||
        this.setWorking(false);
 | 
			
		||||
        this._setWorking(false);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _escape: function() {
 | 
			
		||||
@@ -292,8 +378,4 @@ const UnlockDialog = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this.destroy();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addCharacter: function(unichar) {
 | 
			
		||||
        this._promptEntry.clutter_text.insert_unichar(unichar);
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -4,20 +4,17 @@ const AccountsService = imports.gi.AccountsService;
 | 
			
		||||
const Gdm = imports.gi.Gdm;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Gtk = imports.gi.Gtk;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Pango = imports.gi.Pango;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Tp = imports.gi.TelepathyGLib;
 | 
			
		||||
const Atk = imports.gi.Atk;
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
 | 
			
		||||
const BoxPointer = imports.ui.boxpointer;
 | 
			
		||||
const GnomeSession = imports.misc.gnomeSession;
 | 
			
		||||
const LoginManager = imports.misc.loginManager;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const ModalDialog = imports.ui.modalDialog;
 | 
			
		||||
const PanelMenu = imports.ui.panelMenu;
 | 
			
		||||
const PopupMenu = imports.ui.popupMenu;
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
@@ -25,17 +22,15 @@ const Util = imports.misc.util;
 | 
			
		||||
 | 
			
		||||
const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown';
 | 
			
		||||
const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver';
 | 
			
		||||
const PRIVACY_SCHEMA = 'org.gnome.desktop.privacy'
 | 
			
		||||
const DISABLE_USER_SWITCH_KEY = 'disable-user-switching';
 | 
			
		||||
const DISABLE_LOCK_SCREEN_KEY = 'disable-lock-screen';
 | 
			
		||||
const DISABLE_LOG_OUT_KEY = 'disable-log-out';
 | 
			
		||||
const LOCK_ENABLED_KEY = 'lock-enabled';
 | 
			
		||||
const ALWAYS_SHOW_LOG_OUT_KEY = 'always-show-log-out';
 | 
			
		||||
const SHOW_FULL_NAME_IN_TOP_BAR_KEY = 'show-full-name-in-top-bar';
 | 
			
		||||
const SHOW_FULL_NAME_KEY = 'show-full-name';
 | 
			
		||||
 | 
			
		||||
const DIALOG_ICON_SIZE = 64;
 | 
			
		||||
 | 
			
		||||
const MAX_USERS_IN_SESSION_DIALOG = 5;
 | 
			
		||||
 | 
			
		||||
const IMStatus = {
 | 
			
		||||
    AVAILABLE: 0,
 | 
			
		||||
    BUSY: 1,
 | 
			
		||||
@@ -46,17 +41,6 @@ const IMStatus = {
 | 
			
		||||
    LAST: 6
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const SystemdLoginSessionIface = <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 SystemdLoginSession = Gio.DBusProxy.makeProxyWrapper(SystemdLoginSessionIface);
 | 
			
		||||
 | 
			
		||||
// Adapted from gdm/gui/user-switch-applet/applet.c
 | 
			
		||||
//
 | 
			
		||||
// Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
 | 
			
		||||
@@ -84,7 +68,7 @@ const UserAvatarWidget = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    update: function() {
 | 
			
		||||
        let iconFile = this._user.get_icon_file();
 | 
			
		||||
        if (iconFile && !GLib.file_test(iconFile, GLib.FileTest.EXISTS))
 | 
			
		||||
        if (!GLib.file_test(iconFile, GLib.FileTest.EXISTS))
 | 
			
		||||
            iconFile = null;
 | 
			
		||||
 | 
			
		||||
        if (iconFile) {
 | 
			
		||||
@@ -493,7 +477,6 @@ const UserMenuButton = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._screenSaverSettings = new Gio.Settings({ schema: SCREENSAVER_SCHEMA });
 | 
			
		||||
        this._lockdownSettings = new Gio.Settings({ schema: LOCKDOWN_SCHEMA });
 | 
			
		||||
        this._privacySettings = new Gio.Settings({ schema: PRIVACY_SCHEMA });
 | 
			
		||||
 | 
			
		||||
        this._userManager = AccountsService.UserManager.get_default();
 | 
			
		||||
 | 
			
		||||
@@ -570,12 +553,10 @@ const UserMenuButton = new Lang.Class({
 | 
			
		||||
                                       Lang.bind(this, this._updateLogout));
 | 
			
		||||
        this._lockdownSettings.connect('changed::' + DISABLE_LOCK_SCREEN_KEY,
 | 
			
		||||
                                       Lang.bind(this, this._updateLockScreen));
 | 
			
		||||
        global.settings.connect('changed::' + ALWAYS_SHOW_LOG_OUT_KEY,
 | 
			
		||||
                                Lang.bind(this, this._updateLogout));
 | 
			
		||||
        this._screenSaverSettings.connect('changed::' + SHOW_FULL_NAME_IN_TOP_BAR_KEY,
 | 
			
		||||
        this._screenSaverSettings.connect('changed::' + SHOW_FULL_NAME_KEY,
 | 
			
		||||
                                           Lang.bind(this, this._updateUserName));
 | 
			
		||||
        this._privacySettings.connect('changed::' + SHOW_FULL_NAME_IN_TOP_BAR_KEY,
 | 
			
		||||
                                      Lang.bind(this, this._updateUserName));
 | 
			
		||||
        global.settings.connect('changed::' + SHOW_FULL_NAME_KEY,
 | 
			
		||||
                                Lang.bind(this, this._updateUserName));
 | 
			
		||||
        this._updateSwitchUser();
 | 
			
		||||
        this._updateLogout();
 | 
			
		||||
        this._updateLockScreen();
 | 
			
		||||
@@ -600,8 +581,6 @@ const UserMenuButton = new Lang.Class({
 | 
			
		||||
                                       Lang.bind(this, this._updateHaveShutdown));
 | 
			
		||||
 | 
			
		||||
        Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
 | 
			
		||||
        if (Main.screenShield)
 | 
			
		||||
            Main.screenShield.connect('locked-changed', Lang.bind(this, this._updatePresenceIcon));
 | 
			
		||||
        this._sessionUpdated();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -614,7 +593,6 @@ const UserMenuButton = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this.setSensitive(!Main.sessionMode.isLocked);
 | 
			
		||||
        this._updatePresenceIcon();
 | 
			
		||||
        this._updateUserName();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDestroy: function() {
 | 
			
		||||
@@ -623,10 +601,10 @@ const UserMenuButton = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateUserName: function() {
 | 
			
		||||
        let settings = this._privacySettings;
 | 
			
		||||
        let settings = global.settings;
 | 
			
		||||
        if (Main.sessionMode.isLocked)
 | 
			
		||||
            settings = this._screenSaverSettings;
 | 
			
		||||
        if (this._user.is_loaded && settings.get_boolean(SHOW_FULL_NAME_IN_TOP_BAR_KEY))
 | 
			
		||||
        if (this._user.is_loaded && settings.get_boolean(SHOW_FULL_NAME_KEY))
 | 
			
		||||
            this._name.set_text(this._user.get_real_name());
 | 
			
		||||
        else
 | 
			
		||||
            this._name.set_text("");
 | 
			
		||||
@@ -657,7 +635,7 @@ const UserMenuButton = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _updateLockScreen: function() {
 | 
			
		||||
        let allowLockScreen = !this._lockdownSettings.get_boolean(DISABLE_LOCK_SCREEN_KEY);
 | 
			
		||||
        this._lockScreenItem.actor.visible = allowLockScreen && LoginManager.canLock();
 | 
			
		||||
        this._lockScreenItem.actor.visible = allowLockScreen;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateInstallUpdates: function() {
 | 
			
		||||
@@ -721,11 +699,6 @@ const UserMenuButton = new Lang.Class({
 | 
			
		||||
            this._iconBox.child = this._idleIcon;
 | 
			
		||||
        else
 | 
			
		||||
            this._iconBox.child = this._offlineIcon;
 | 
			
		||||
 | 
			
		||||
        if (Main.sessionMode.isLocked)
 | 
			
		||||
            this._iconBox.visible = Main.screenShield.locked;
 | 
			
		||||
        else
 | 
			
		||||
            this._iconBox.visible = true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _setupAccounts: function() {
 | 
			
		||||
@@ -846,7 +819,7 @@ const UserMenuButton = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _onMyAccountActivate: function() {
 | 
			
		||||
        Main.overview.hide();
 | 
			
		||||
        let app = Shell.AppSystem.get_default().lookup_app('gnome-user-accounts-panel.desktop');
 | 
			
		||||
        let app = Shell.AppSystem.get_default().lookup_setting('gnome-user-accounts-panel.desktop');
 | 
			
		||||
        app.activate();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -865,8 +838,7 @@ const UserMenuButton = new Lang.Class({
 | 
			
		||||
    _onLoginScreenActivate: function() {
 | 
			
		||||
        this.menu.close(BoxPointer.PopupAnimation.NONE);
 | 
			
		||||
        Main.overview.hide();
 | 
			
		||||
        if (Main.screenShield)
 | 
			
		||||
            Main.screenShield.lock(false);
 | 
			
		||||
        Main.screenShield.lock(false);
 | 
			
		||||
        Gdm.goto_login_session_sync(null);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -882,117 +854,25 @@ const UserMenuButton = new Lang.Class({
 | 
			
		||||
        this._session.RebootRemote();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _openSessionWarnDialog: function(sessions) {
 | 
			
		||||
        let dialog = new ModalDialog.ModalDialog();
 | 
			
		||||
        let subjectLabel = new St.Label({ style_class: 'end-session-dialog-subject',
 | 
			
		||||
                                          text: _("Other users are logged in.") });
 | 
			
		||||
        dialog.contentLayout.add(subjectLabel, { y_fill: true,
 | 
			
		||||
                                                 y_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        let descriptionLabel = new St.Label({ style_class: 'end-session-dialog-description'});
 | 
			
		||||
        descriptionLabel.set_text(_("Shutting down might cause them to lose unsaved work."));
 | 
			
		||||
        descriptionLabel.clutter_text.line_wrap = true;
 | 
			
		||||
        dialog.contentLayout.add(descriptionLabel, { x_fill: true,
 | 
			
		||||
                                                     y_fill: true,
 | 
			
		||||
                                                     y_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        let scrollView = new St.ScrollView({ style_class: 'end-session-dialog-app-list' });
 | 
			
		||||
        scrollView.add_style_class_name('vfade');
 | 
			
		||||
        scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
 | 
			
		||||
        dialog.contentLayout.add(scrollView, { x_fill: true, y_fill: true });
 | 
			
		||||
 | 
			
		||||
        let userList = new St.BoxLayout({ vertical: true });
 | 
			
		||||
        scrollView.add_actor(userList);
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < sessions.length; i++) {
 | 
			
		||||
            let session = sessions[i];
 | 
			
		||||
            let userEntry = new St.BoxLayout({ style_class: 'login-dialog-user-list-item',
 | 
			
		||||
                                               vertical: false });
 | 
			
		||||
            let avatar = new UserAvatarWidget(session.user);
 | 
			
		||||
            avatar.update();
 | 
			
		||||
            userEntry.add(avatar.actor);
 | 
			
		||||
 | 
			
		||||
            let userLabelText = "";;
 | 
			
		||||
            let userName = session.user.get_real_name() ?
 | 
			
		||||
                           session.user.get_real_name() : session.username;
 | 
			
		||||
 | 
			
		||||
            if (session.info.remote)
 | 
			
		||||
                /* Translators: Remote here refers to a remote session, like a ssh login */
 | 
			
		||||
                userLabelText = _("%s (remote)").format(userName);
 | 
			
		||||
            else if (session.info.type == "tty")
 | 
			
		||||
                /* Translators: Console here refers to a tty like a VT console */
 | 
			
		||||
                userLabelText = _("%s (console)").format(userName);
 | 
			
		||||
            else
 | 
			
		||||
                userLabelText = userName;
 | 
			
		||||
 | 
			
		||||
            let textLayout = new St.BoxLayout({ style_class: 'login-dialog-user-list-item-text-box',
 | 
			
		||||
                                                vertical: true });
 | 
			
		||||
            textLayout.add(new St.Label({ text: userLabelText }),
 | 
			
		||||
                           { y_fill: false,
 | 
			
		||||
                             y_align: St.Align.MIDDLE,
 | 
			
		||||
                             expand: true });
 | 
			
		||||
            userEntry.add(textLayout, { expand: true });
 | 
			
		||||
            userList.add(userEntry, { x_fill: true });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let cancelButton = { label: _("Cancel"),
 | 
			
		||||
                             action: function() { dialog.close(); },
 | 
			
		||||
                             key: Clutter.Escape };
 | 
			
		||||
 | 
			
		||||
        let powerOffButton = { label: _("Power Off"),  action: Lang.bind(this, function() {
 | 
			
		||||
            dialog.close();
 | 
			
		||||
            this._session.ShutdownRemote();
 | 
			
		||||
        }), default: true };
 | 
			
		||||
 | 
			
		||||
        dialog.setButtons([cancelButton, powerOffButton]);
 | 
			
		||||
 | 
			
		||||
        dialog.open();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onSuspendOrPowerOffActivate: function() {
 | 
			
		||||
        Main.overview.hide();
 | 
			
		||||
 | 
			
		||||
        if (this._haveShutdown &&
 | 
			
		||||
            this._suspendOrPowerOffItem.state == PopupMenu.PopupAlternatingMenuItemState.DEFAULT) {
 | 
			
		||||
            this._loginManager.listSessions(Lang.bind(this,
 | 
			
		||||
                function(result) {
 | 
			
		||||
                    let sessions = [];
 | 
			
		||||
                    let n = 0;
 | 
			
		||||
                    for (let i = 0; i < result.length; i++) {
 | 
			
		||||
                        let[id, uid, userName, seat, sessionPath] = result[i];
 | 
			
		||||
                        let proxy = new SystemdLoginSession(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;
 | 
			
		||||
 | 
			
		||||
                        sessions.push({ user: this._userManager.get_user(userName),
 | 
			
		||||
                                        username: userName,
 | 
			
		||||
                                        info: { type: proxy.Type,
 | 
			
		||||
                                                remote: proxy.Remote }
 | 
			
		||||
                        });
 | 
			
		||||
 | 
			
		||||
                        // limit the number of entries
 | 
			
		||||
                        n++;
 | 
			
		||||
                        if (n == MAX_USERS_IN_SESSION_DIALOG)
 | 
			
		||||
                            break;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if (n != 0)
 | 
			
		||||
                        this._openSessionWarnDialog(sessions);
 | 
			
		||||
                    else
 | 
			
		||||
                        this._session.ShutdownRemote();
 | 
			
		||||
            }));
 | 
			
		||||
            this._session.ShutdownRemote();
 | 
			
		||||
        } else {
 | 
			
		||||
            this.menu.close(BoxPointer.PopupAnimation.NONE);
 | 
			
		||||
            this._loginManager.suspend();
 | 
			
		||||
            if (this._screenSaverSettings.get_boolean(LOCK_ENABLED_KEY)) {
 | 
			
		||||
                let tmpId = Main.screenShield.connect('lock-screen-shown', Lang.bind(this, function() {
 | 
			
		||||
                    Main.screenShield.disconnect(tmpId);
 | 
			
		||||
 | 
			
		||||
                    this._loginManager.suspend();
 | 
			
		||||
                }));
 | 
			
		||||
 | 
			
		||||
                this.menu.close(BoxPointer.PopupAnimation.NONE);
 | 
			
		||||
                Main.screenShield.lock(true);
 | 
			
		||||
            } else {
 | 
			
		||||
                this._loginManager.suspend();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -1,60 +0,0 @@
 | 
			
		||||
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
//
 | 
			
		||||
// A widget showing the user avatar and name
 | 
			
		||||
const AccountsService = imports.gi.AccountsService;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
const UserMenu = imports.ui.userMenu;
 | 
			
		||||
 | 
			
		||||
const UserWidget = new Lang.Class({
 | 
			
		||||
    Name: 'UserWidget',
 | 
			
		||||
 | 
			
		||||
    _init: function(user) {
 | 
			
		||||
        this._user = user;
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.BoxLayout({ style_class: 'user-widget',
 | 
			
		||||
                                        vertical: false });
 | 
			
		||||
        this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
 | 
			
		||||
 | 
			
		||||
        this._avatar = new UserMenu.UserAvatarWidget(user);
 | 
			
		||||
        this.actor.add(this._avatar.actor,
 | 
			
		||||
                       { x_fill: true, y_fill: true });
 | 
			
		||||
 | 
			
		||||
        this._label = new St.Label({ style_class: 'user-widget-label' });
 | 
			
		||||
        this.actor.add(this._label,
 | 
			
		||||
                       { expand: true,
 | 
			
		||||
                         x_fill: true,
 | 
			
		||||
                         y_fill: false,
 | 
			
		||||
                         y_align: St.Align.MIDDLE });
 | 
			
		||||
 | 
			
		||||
        this._userLoadedId = this._user.connect('notify::is-loaded',
 | 
			
		||||
                                                Lang.bind(this, this._updateUser));
 | 
			
		||||
        this._userChangedId = this._user.connect('changed',
 | 
			
		||||
                                                 Lang.bind(this, this._updateUser));
 | 
			
		||||
        if (this._user.is_loaded)
 | 
			
		||||
            this._updateUser();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDestroy: function() {
 | 
			
		||||
        if (this._userLoadedId != 0) {
 | 
			
		||||
            this._user.disconnect(this._userLoadedId);
 | 
			
		||||
            this._userLoadedId = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this._userChangedId != 0) {
 | 
			
		||||
            this._user.disconnect(this._userChangedId);
 | 
			
		||||
            this._userChangedId = 0;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateUser: function() {
 | 
			
		||||
        if (this._user.is_loaded)
 | 
			
		||||
            this._label.text = this._user.get_real_name();
 | 
			
		||||
        else
 | 
			
		||||
            this._label.text = '';
 | 
			
		||||
 | 
			
		||||
        this._avatar.update();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
@@ -12,8 +12,6 @@ const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
const AppDisplay = imports.ui.appDisplay;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const OverviewControls = imports.ui.overviewControls;
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
const RemoteSearch = imports.ui.remoteSearch;
 | 
			
		||||
const Search = imports.ui.search;
 | 
			
		||||
const SearchDisplay = imports.ui.searchDisplay;
 | 
			
		||||
@@ -24,12 +22,6 @@ const WorkspacesView = imports.ui.workspacesView;
 | 
			
		||||
 | 
			
		||||
const SHELL_KEYBINDINGS_SCHEMA = 'org.gnome.shell.keybindings';
 | 
			
		||||
 | 
			
		||||
const ViewPage = {
 | 
			
		||||
    WINDOWS: 1,
 | 
			
		||||
    APPS: 2,
 | 
			
		||||
    SEARCH: 3
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const FocusTrap = new Lang.Class({
 | 
			
		||||
    Name: 'FocusTrap',
 | 
			
		||||
    Extends: St.Widget,
 | 
			
		||||
@@ -42,25 +34,22 @@ const FocusTrap = new Lang.Class({
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
function getTermsForSearchString(searchString) {
 | 
			
		||||
    searchString = searchString.replace(/^\s+/g, '').replace(/\s+$/g, '');
 | 
			
		||||
    if (searchString == '')
 | 
			
		||||
        return [];
 | 
			
		||||
 | 
			
		||||
    let terms = searchString.split(/\s+/);
 | 
			
		||||
    return terms;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const ViewSelector = new Lang.Class({
 | 
			
		||||
    Name: 'ViewSelector',
 | 
			
		||||
 | 
			
		||||
    _init : function(searchEntry, showAppsButton) {
 | 
			
		||||
        this.actor = new Shell.Stack({ name: 'viewSelector' });
 | 
			
		||||
        this.actor = new St.BoxLayout({ name: 'viewSelector',
 | 
			
		||||
                                        vertical: true });
 | 
			
		||||
 | 
			
		||||
        this._showAppsBlocked = false;
 | 
			
		||||
        this._showAppsButton = showAppsButton;
 | 
			
		||||
        this._showAppsButton.connect('notify::checked', Lang.bind(this, this._onShowAppsButtonToggled));
 | 
			
		||||
 | 
			
		||||
        this._pageArea = new Shell.Stack();
 | 
			
		||||
        this.actor.add(this._pageArea, { x_fill: true,
 | 
			
		||||
                                         y_fill: true,
 | 
			
		||||
                                         expand: true });
 | 
			
		||||
 | 
			
		||||
        this._activePage = null;
 | 
			
		||||
 | 
			
		||||
        this._searchActive = false;
 | 
			
		||||
@@ -83,26 +72,26 @@ const ViewSelector = new Lang.Class({
 | 
			
		||||
        this._entry.connect('notify::mapped', Lang.bind(this, this._onMapped));
 | 
			
		||||
        global.stage.connect('notify::key-focus', Lang.bind(this, this._onStageKeyFocusChanged));
 | 
			
		||||
 | 
			
		||||
        this._entry.set_primary_icon(new St.Icon({ style_class: 'search-entry-icon',
 | 
			
		||||
                                                   icon_name: 'edit-find-symbolic' }));
 | 
			
		||||
        this._clearIcon = new St.Icon({ style_class: 'search-entry-icon',
 | 
			
		||||
                                        icon_name: 'edit-clear-symbolic' });
 | 
			
		||||
        this._inactiveIcon = new St.Icon({ style_class: 'search-entry-icon',
 | 
			
		||||
                                           icon_name: 'edit-find-symbolic' });
 | 
			
		||||
        this._activeIcon = new St.Icon({ style_class: 'search-entry-icon',
 | 
			
		||||
                                         icon_name: 'edit-clear-symbolic' });
 | 
			
		||||
        this._entry.set_secondary_icon(this._inactiveIcon);
 | 
			
		||||
 | 
			
		||||
        this._iconClickedId = 0;
 | 
			
		||||
        this._capturedEventId = 0;
 | 
			
		||||
 | 
			
		||||
        this._workspacesDisplay = new WorkspacesView.WorkspacesDisplay();
 | 
			
		||||
        this._workspacesPage = this._addPage(this._workspacesDisplay.actor,
 | 
			
		||||
        this._workspacesPage = this._addPage(this._workspacesDisplay.actor, null,
 | 
			
		||||
                                             _("Windows"), 'emblem-documents-symbolic');
 | 
			
		||||
 | 
			
		||||
        this._appDisplay = new AppDisplay.AppDisplay();
 | 
			
		||||
        this._appsPage = this._addPage(this._appDisplay.actor,
 | 
			
		||||
        this._appDisplay = new AppDisplay.AllAppDisplay();
 | 
			
		||||
        this._appsPage = this._addPage(this._appDisplay.actor, null,
 | 
			
		||||
                                       _("Applications"), 'view-grid-symbolic');
 | 
			
		||||
 | 
			
		||||
        this._searchResults = new SearchDisplay.SearchResults(this._searchSystem);
 | 
			
		||||
        this._searchPage = this._addPage(this._searchResults.actor,
 | 
			
		||||
                                         _("Search"), 'edit-find-symbolic',
 | 
			
		||||
                                         { a11yFocus: this._entry });
 | 
			
		||||
        this._searchPage = this._addPage(this._searchResults.actor, this._entry,
 | 
			
		||||
                                         _("Search"), 'edit-find-symbolic');
 | 
			
		||||
 | 
			
		||||
        this._searchSettings = new Gio.Settings({ schema: Search.SEARCH_PROVIDERS_SCHEMA });
 | 
			
		||||
        this._searchSettings.connect('changed::disabled', Lang.bind(this, this._reloadRemoteProviders));
 | 
			
		||||
@@ -113,6 +102,7 @@ const ViewSelector = new Lang.Class({
 | 
			
		||||
        // Wanda comes obviously first
 | 
			
		||||
        this.addSearchProvider(new Wanda.WandaSearchProvider());
 | 
			
		||||
        this.addSearchProvider(new AppDisplay.AppSearchProvider());
 | 
			
		||||
        this.addSearchProvider(new AppDisplay.SettingsSearchProvider());
 | 
			
		||||
 | 
			
		||||
        // Load remote search providers provided by applications
 | 
			
		||||
        RemoteSearch.loadRemoteSearchProviders(Lang.bind(this, this.addSearchProvider));
 | 
			
		||||
@@ -147,8 +137,8 @@ const ViewSelector = new Lang.Class({
 | 
			
		||||
        Main.wm.addKeybinding('toggle-application-view',
 | 
			
		||||
                              new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }),
 | 
			
		||||
                              Meta.KeyBindingFlags.NONE,
 | 
			
		||||
                              Shell.KeyBindingMode.NORMAL |
 | 
			
		||||
                              Shell.KeyBindingMode.OVERVIEW,
 | 
			
		||||
                              Main.KeybindingMode.NORMAL |
 | 
			
		||||
                              Main.KeybindingMode.OVERVIEW,
 | 
			
		||||
                              Lang.bind(this, this._toggleAppsPage));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -160,7 +150,6 @@ const ViewSelector = new Lang.Class({
 | 
			
		||||
    show: function() {
 | 
			
		||||
        this._activePage = this._workspacesPage;
 | 
			
		||||
 | 
			
		||||
        this.reset();
 | 
			
		||||
        this._appsPage.hide();
 | 
			
		||||
        this._searchPage.hide();
 | 
			
		||||
        this._workspacesDisplay.show();
 | 
			
		||||
@@ -168,7 +157,7 @@ const ViewSelector = new Lang.Class({
 | 
			
		||||
        if (!this._workspacesDisplay.activeWorkspaceHasMaximizedWindows())
 | 
			
		||||
            Main.overview.fadeOutDesktop();
 | 
			
		||||
 | 
			
		||||
        this._showPage(this._workspacesPage, true);
 | 
			
		||||
        this._showPage(this._workspacesPage);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    zoomFromOverview: function() {
 | 
			
		||||
@@ -182,16 +171,14 @@ const ViewSelector = new Lang.Class({
 | 
			
		||||
        this._workspacesDisplay.hide();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _addPage: function(actor, name, a11yIcon, params) {
 | 
			
		||||
        params = Params.parse(params, { a11yFocus: null });
 | 
			
		||||
 | 
			
		||||
    _addPage: function(actor, a11yFocus, name, a11yIcon) {
 | 
			
		||||
        let page = new St.Bin({ child: actor,
 | 
			
		||||
                                x_align: St.Align.START,
 | 
			
		||||
                                y_align: St.Align.START,
 | 
			
		||||
                                x_fill: true,
 | 
			
		||||
                                y_fill: true });
 | 
			
		||||
        if (params.a11yFocus)
 | 
			
		||||
            Main.ctrlAltTabManager.addGroup(params.a11yFocus, name, a11yIcon);
 | 
			
		||||
        if (a11yFocus)
 | 
			
		||||
            Main.ctrlAltTabManager.addGroup(a11yFocus, name, a11yIcon);
 | 
			
		||||
        else
 | 
			
		||||
            Main.ctrlAltTabManager.addGroup(actor, name, a11yIcon,
 | 
			
		||||
                                            { proxy: this.actor,
 | 
			
		||||
@@ -200,44 +187,38 @@ const ViewSelector = new Lang.Class({
 | 
			
		||||
                                                      this._a11yFocusPage(page);
 | 
			
		||||
                                                  })
 | 
			
		||||
                                            });;
 | 
			
		||||
        this.actor.add_actor(page);
 | 
			
		||||
        this._pageArea.add_actor(page);
 | 
			
		||||
        return page;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _fadePageIn: function(oldPage) {
 | 
			
		||||
        if (oldPage)
 | 
			
		||||
            oldPage.hide();
 | 
			
		||||
 | 
			
		||||
        this.emit('page-empty');
 | 
			
		||||
 | 
			
		||||
        this._activePage.show();
 | 
			
		||||
        Tweener.addTween(this._activePage,
 | 
			
		||||
            { opacity: 255,
 | 
			
		||||
              time: OverviewControls.SIDE_CONTROLS_ANIMATION_TIME,
 | 
			
		||||
              transition: 'easeOutQuad'
 | 
			
		||||
            });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _showPage: function(page, noFade) {
 | 
			
		||||
        if (page == this._activePage)
 | 
			
		||||
    _showPage: function(page) {
 | 
			
		||||
        if(page == this._activePage)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let oldPage = this._activePage;
 | 
			
		||||
        this._activePage = page;
 | 
			
		||||
        this.emit('page-changed');
 | 
			
		||||
 | 
			
		||||
        if (oldPage && !noFade)
 | 
			
		||||
            Tweener.addTween(oldPage,
 | 
			
		||||
        if(this._activePage) {
 | 
			
		||||
            Tweener.addTween(this._activePage,
 | 
			
		||||
                             { opacity: 0,
 | 
			
		||||
                               time: OverviewControls.SIDE_CONTROLS_ANIMATION_TIME,
 | 
			
		||||
                               time: 0.1,
 | 
			
		||||
                               transition: 'easeOutQuad',
 | 
			
		||||
                               onComplete: Lang.bind(this,
 | 
			
		||||
                                   function() {
 | 
			
		||||
                                       this._fadePageIn(oldPage);
 | 
			
		||||
                                       this._activePage.hide();
 | 
			
		||||
                                       this._activePage = page;
 | 
			
		||||
                                   })
 | 
			
		||||
                             });
 | 
			
		||||
        else
 | 
			
		||||
            this._fadePageIn(oldPage);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.emit('before-page-change');
 | 
			
		||||
        page.show();
 | 
			
		||||
        Tweener.addTween(page,
 | 
			
		||||
                         { opacity: 255,
 | 
			
		||||
                           time: 0.1,
 | 
			
		||||
                           transition: 'easeOutQuad',
 | 
			
		||||
                           onComplete: Lang.bind(this,
 | 
			
		||||
                               function() {
 | 
			
		||||
                                   this.emit('after-page-change');
 | 
			
		||||
                               })
 | 
			
		||||
                         });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _a11yFocusPage: function(page) {
 | 
			
		||||
@@ -246,19 +227,15 @@ const ViewSelector = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onShowAppsButtonToggled: function() {
 | 
			
		||||
        if (this._showAppsBlocked)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._showPage(this._showAppsButton.checked ?
 | 
			
		||||
                       this._appsPage : this._workspacesPage);
 | 
			
		||||
        if (this._searchActive)
 | 
			
		||||
            this.reset();
 | 
			
		||||
        else
 | 
			
		||||
            this._showPage(this._showAppsButton.checked ? this._appsPage
 | 
			
		||||
                                                        : this._workspacesPage);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _resetShowAppsButton: function() {
 | 
			
		||||
        this._showAppsBlocked = true;
 | 
			
		||||
        this._showAppsButton.checked = false;
 | 
			
		||||
        this._showAppsBlocked = false;
 | 
			
		||||
 | 
			
		||||
        this._showPage(this._workspacesPage, true);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onStageKeyPress: function(actor, event) {
 | 
			
		||||
@@ -274,11 +251,12 @@ const ViewSelector = new Lang.Class({
 | 
			
		||||
            if (this._searchActive)
 | 
			
		||||
                this.reset();
 | 
			
		||||
            else if (this._showAppsButton.checked)
 | 
			
		||||
                this._showAppsButton.checked = false;
 | 
			
		||||
                this._resetShowAppsButton();
 | 
			
		||||
            else
 | 
			
		||||
                Main.overview.hide();
 | 
			
		||||
            return true;
 | 
			
		||||
        } else if (this._shouldTriggerSearch(symbol)) {
 | 
			
		||||
        } else if (Clutter.keysym_to_unicode(symbol) ||
 | 
			
		||||
                   (symbol == Clutter.BackSpace && this._searchActive)) {
 | 
			
		||||
            this.startSearch(event);
 | 
			
		||||
        } else if (!this._searchActive) {
 | 
			
		||||
            if (symbol == Clutter.Tab || symbol == Clutter.Down) {
 | 
			
		||||
@@ -343,17 +321,6 @@ const ViewSelector = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _shouldTriggerSearch: function(symbol) {
 | 
			
		||||
        let unicode = Clutter.keysym_to_unicode(symbol);
 | 
			
		||||
        if (unicode == 0)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        if (getTermsForSearchString(String.fromCharCode(unicode)).length > 0)
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        return symbol == Clutter.BackSpace && this._searchActive;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    startSearch: function(event) {
 | 
			
		||||
        global.stage.set_key_focus(this._text);
 | 
			
		||||
        this._text.event(event, true);
 | 
			
		||||
@@ -365,39 +332,41 @@ const ViewSelector = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onTextChanged: function (se, prop) {
 | 
			
		||||
        let terms = getTermsForSearchString(this._entry.get_text());
 | 
			
		||||
 | 
			
		||||
        let searchPreviouslyActive = this._searchActive;
 | 
			
		||||
        this._searchActive = (terms.length > 0);
 | 
			
		||||
        this._searchActive = this._entry.get_text() != '';
 | 
			
		||||
 | 
			
		||||
        let startSearch = this._searchActive && !searchPreviouslyActive;
 | 
			
		||||
        if (startSearch)
 | 
			
		||||
        if (startSearch) {
 | 
			
		||||
            this._searchResults.startingSearch();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this._searchActive) {
 | 
			
		||||
            this._entry.set_secondary_icon(this._clearIcon);
 | 
			
		||||
            this._entry.set_secondary_icon(this._activeIcon);
 | 
			
		||||
 | 
			
		||||
            if (this._iconClickedId == 0)
 | 
			
		||||
            if (this._iconClickedId == 0) {
 | 
			
		||||
                this._iconClickedId = this._entry.connect('secondary-icon-clicked',
 | 
			
		||||
                    Lang.bind(this, this.reset));
 | 
			
		||||
 | 
			
		||||
            if (this._searchTimeoutId == 0)
 | 
			
		||||
                this._searchTimeoutId = Mainloop.timeout_add(150,
 | 
			
		||||
                    Lang.bind(this, this._doSearch));
 | 
			
		||||
        } else {
 | 
			
		||||
            if (this._iconClickedId > 0) {
 | 
			
		||||
                this._entry.disconnect(this._iconClickedId);
 | 
			
		||||
                this._iconClickedId = 0;
 | 
			
		||||
                    Lang.bind(this, function() {
 | 
			
		||||
                        this.reset();
 | 
			
		||||
                    }));
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            if (this._iconClickedId > 0)
 | 
			
		||||
                this._entry.disconnect(this._iconClickedId);
 | 
			
		||||
            this._iconClickedId = 0;
 | 
			
		||||
 | 
			
		||||
            this._entry.set_secondary_icon(this._inactiveIcon);
 | 
			
		||||
            this._searchCancelled();
 | 
			
		||||
 | 
			
		||||
            if (this._searchTimeoutId > 0) {
 | 
			
		||||
                Mainloop.source_remove(this._searchTimeoutId);
 | 
			
		||||
                this._searchTimeoutId = 0;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this._entry.set_secondary_icon(null);
 | 
			
		||||
            this._searchCancelled();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this._searchTimeoutId > 0)
 | 
			
		||||
            return;
 | 
			
		||||
        this._searchTimeoutId = Mainloop.timeout_add(150, Lang.bind(this, this._doSearch));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onKeyPress: function(entry, event) {
 | 
			
		||||
@@ -407,6 +376,15 @@ const ViewSelector = new Lang.Class({
 | 
			
		||||
                this.reset();
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
        } else if (symbol == Clutter.Return || symbol == Clutter.KP_Enter) {
 | 
			
		||||
            // We can't connect to 'activate' here because search providers
 | 
			
		||||
            // might want to do something with the modifiers in activateDefault.
 | 
			
		||||
            if (this._searchTimeoutId > 0) {
 | 
			
		||||
                Mainloop.source_remove(this._searchTimeoutId);
 | 
			
		||||
                this._doSearch();
 | 
			
		||||
            }
 | 
			
		||||
            this._searchResults.activateDefault();
 | 
			
		||||
            return true;
 | 
			
		||||
        } else if (this._searchActive) {
 | 
			
		||||
            let arrowNext, nextDirection;
 | 
			
		||||
            if (entry.get_text_direction() == Clutter.TextDirection.RTL) {
 | 
			
		||||
@@ -431,15 +409,6 @@ const ViewSelector = new Lang.Class({
 | 
			
		||||
            } else if (symbol == arrowNext && this._text.position == -1) {
 | 
			
		||||
                this._searchResults.navigateFocus(nextDirection);
 | 
			
		||||
                return true;
 | 
			
		||||
            } else if (symbol == Clutter.Return || symbol == Clutter.KP_Enter) {
 | 
			
		||||
                // We can't connect to 'activate' here because search providers
 | 
			
		||||
                // might want to do something with the modifiers in activateDefault.
 | 
			
		||||
                if (this._searchTimeoutId > 0) {
 | 
			
		||||
                    Mainloop.source_remove(this._searchTimeoutId);
 | 
			
		||||
                    this._doSearch();
 | 
			
		||||
                }
 | 
			
		||||
                this._searchResults.activateDefault();
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
@@ -462,10 +431,9 @@ const ViewSelector = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _doSearch: function () {
 | 
			
		||||
        this._searchTimeoutId = 0;
 | 
			
		||||
        let text = this._text.get_text().replace(/^\s+/g, '').replace(/\s+$/g, '');
 | 
			
		||||
        this._searchResults.doSearch(text);
 | 
			
		||||
 | 
			
		||||
        let terms = getTermsForSearchString(this._entry.get_text());
 | 
			
		||||
 | 
			
		||||
        this._searchSystem.updateSearchResults(terms);
 | 
			
		||||
        this._showPage(this._searchPage);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -491,6 +459,10 @@ const ViewSelector = new Lang.Class({
 | 
			
		||||
        RemoteSearch.loadRemoteSearchProviders(Lang.bind(this, this.addSearchProvider));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getAppsActive: function() {
 | 
			
		||||
        return this._showAppsButton.checked;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addSearchProvider: function(provider) {
 | 
			
		||||
        if (!this._shouldUseSearchProvider(provider))
 | 
			
		||||
            return;
 | 
			
		||||
@@ -504,29 +476,8 @@ const ViewSelector = new Lang.Class({
 | 
			
		||||
        this._searchResults.destroyProviderMeta(provider);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getActivePage: function() {
 | 
			
		||||
        if (this._activePage == this._workspacesPage)
 | 
			
		||||
            return ViewPage.WINDOWS;
 | 
			
		||||
        else if (this._activePage == this._appsPage)
 | 
			
		||||
            return ViewPage.APPS;
 | 
			
		||||
        else
 | 
			
		||||
            return ViewPage.SEARCH;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    fadeIn: function() {
 | 
			
		||||
        let actor = this._activePage;
 | 
			
		||||
        Tweener.addTween(actor, { opacity: 255,
 | 
			
		||||
                                  time: OverviewControls.SIDE_CONTROLS_ANIMATION_TIME / 2,
 | 
			
		||||
                                  transition: 'easeInQuad'
 | 
			
		||||
                                });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    fadeHalf: function() {
 | 
			
		||||
        let actor = this._activePage;
 | 
			
		||||
        Tweener.addTween(actor, { opacity: 128,
 | 
			
		||||
                                  time: OverviewControls.SIDE_CONTROLS_ANIMATION_TIME / 2,
 | 
			
		||||
                                  transition: 'easeOutQuad'
 | 
			
		||||
                                });
 | 
			
		||||
    getSearchActive: function() {
 | 
			
		||||
        return this._searchActive;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(ViewSelector.prototype);
 | 
			
		||||
 
 | 
			
		||||
@@ -11,14 +11,12 @@ const Layout = imports.ui.layout;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const Panel = imports.ui.panel;
 | 
			
		||||
 | 
			
		||||
// we could make these gsettings
 | 
			
		||||
const FISH_NAME = 'wanda';
 | 
			
		||||
const FISH_FILENAME = 'wanda.png';
 | 
			
		||||
const FISH_SPEED = 300;
 | 
			
		||||
const FISH_COMMAND = 'fortune';
 | 
			
		||||
// The size of an individual frame in the animation
 | 
			
		||||
const FISH_HEIGHT = 22;
 | 
			
		||||
const FISH_WIDTH = 36;
 | 
			
		||||
 | 
			
		||||
const GNOME_PANEL_PIXMAPDIR = '../gnome-panel/fish';
 | 
			
		||||
const FISH_GROUP = 'Fish Animation';
 | 
			
		||||
 | 
			
		||||
const MAGIC_FISH_KEY = 'free the fish';
 | 
			
		||||
@@ -28,16 +26,33 @@ const WandaIcon = new Lang.Class({
 | 
			
		||||
    Extends: IconGrid.BaseIcon,
 | 
			
		||||
 | 
			
		||||
    _init : function(fish, label, params) {
 | 
			
		||||
        this.parent(label, params);
 | 
			
		||||
 | 
			
		||||
        this._fish = fish;
 | 
			
		||||
        this._imageFile = GLib.build_filenamev([global.datadir, fish + '.png']);
 | 
			
		||||
        let file = GLib.build_filenamev([global.datadir, GNOME_PANEL_PIXMAPDIR, fish + '.fish']);
 | 
			
		||||
 | 
			
		||||
        this._imgHeight = FISH_HEIGHT;
 | 
			
		||||
        this._imgWidth = FISH_WIDTH;
 | 
			
		||||
        if (GLib.file_test(file, GLib.FileTest.EXISTS)) {
 | 
			
		||||
            this._keyfile = new GLib.KeyFile();
 | 
			
		||||
            this._keyfile.load_from_file(file, GLib.KeyFileFlags.NONE);
 | 
			
		||||
 | 
			
		||||
            this._imageFile = GLib.build_filenamev([global.datadir, GNOME_PANEL_PIXMAPDIR,
 | 
			
		||||
                                                    this._keyfile.get_string(FISH_GROUP, 'image')]);
 | 
			
		||||
 | 
			
		||||
            let tmpPixbuf = GdkPixbuf.Pixbuf.new_from_file(this._imageFile);
 | 
			
		||||
 | 
			
		||||
            this._imgHeight = tmpPixbuf.height;
 | 
			
		||||
            this._imgWidth = tmpPixbuf.width / this._keyfile.get_integer(FISH_GROUP, 'frames');
 | 
			
		||||
        } else {
 | 
			
		||||
            this._imageFile = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.parent(label, params);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    createIcon: function(iconSize) {
 | 
			
		||||
        if (!this._imageFile) {
 | 
			
		||||
            return new St.Icon({ icon_name: 'face-smile',
 | 
			
		||||
                                 icon_size: iconSize });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._animations = new Panel.Animation(this._imageFile, this._imgWidth, this._imgHeight, FISH_SPEED);
 | 
			
		||||
        this._animations.play();
 | 
			
		||||
        return this._animations.actor;
 | 
			
		||||
 
 | 
			
		||||
@@ -105,112 +105,75 @@ const WindowManager = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._workspaceSwitcherPopup = null;
 | 
			
		||||
        this.setCustomKeybindingHandler('switch-to-workspace-left',
 | 
			
		||||
                                        Shell.KeyBindingMode.NORMAL |
 | 
			
		||||
                                        Shell.KeyBindingMode.OVERVIEW,
 | 
			
		||||
                                        Main.KeybindingMode.NORMAL |
 | 
			
		||||
                                        Main.KeybindingMode.OVERVIEW,
 | 
			
		||||
                                        Lang.bind(this, this._showWorkspaceSwitcher));
 | 
			
		||||
        this.setCustomKeybindingHandler('switch-to-workspace-right',
 | 
			
		||||
                                        Shell.KeyBindingMode.NORMAL |
 | 
			
		||||
                                        Shell.KeyBindingMode.OVERVIEW,
 | 
			
		||||
                                        Main.KeybindingMode.NORMAL |
 | 
			
		||||
                                        Main.KeybindingMode.OVERVIEW,
 | 
			
		||||
                                        Lang.bind(this, this._showWorkspaceSwitcher));
 | 
			
		||||
        this.setCustomKeybindingHandler('switch-to-workspace-up',
 | 
			
		||||
                                        Shell.KeyBindingMode.NORMAL |
 | 
			
		||||
                                        Shell.KeyBindingMode.OVERVIEW,
 | 
			
		||||
                                        Main.KeybindingMode.NORMAL |
 | 
			
		||||
                                        Main.KeybindingMode.OVERVIEW,
 | 
			
		||||
                                        Lang.bind(this, this._showWorkspaceSwitcher));
 | 
			
		||||
        this.setCustomKeybindingHandler('switch-to-workspace-down',
 | 
			
		||||
                                        Shell.KeyBindingMode.NORMAL |
 | 
			
		||||
                                        Shell.KeyBindingMode.OVERVIEW,
 | 
			
		||||
                                        Main.KeybindingMode.NORMAL |
 | 
			
		||||
                                        Main.KeybindingMode.OVERVIEW,
 | 
			
		||||
                                        Lang.bind(this, this._showWorkspaceSwitcher));
 | 
			
		||||
        this.setCustomKeybindingHandler('move-to-workspace-left',
 | 
			
		||||
                                        Shell.KeyBindingMode.NORMAL |
 | 
			
		||||
                                        Shell.KeyBindingMode.OVERVIEW,
 | 
			
		||||
                                        Main.KeybindingMode.NORMAL |
 | 
			
		||||
                                        Main.KeybindingMode.OVERVIEW,
 | 
			
		||||
                                        Lang.bind(this, this._showWorkspaceSwitcher));
 | 
			
		||||
        this.setCustomKeybindingHandler('move-to-workspace-right',
 | 
			
		||||
                                        Shell.KeyBindingMode.NORMAL |
 | 
			
		||||
                                        Shell.KeyBindingMode.OVERVIEW,
 | 
			
		||||
                                        Main.KeybindingMode.NORMAL |
 | 
			
		||||
                                        Main.KeybindingMode.OVERVIEW,
 | 
			
		||||
                                        Lang.bind(this, this._showWorkspaceSwitcher));
 | 
			
		||||
        this.setCustomKeybindingHandler('move-to-workspace-up',
 | 
			
		||||
                                        Shell.KeyBindingMode.NORMAL |
 | 
			
		||||
                                        Shell.KeyBindingMode.OVERVIEW,
 | 
			
		||||
                                        Main.KeybindingMode.NORMAL |
 | 
			
		||||
                                        Main.KeybindingMode.OVERVIEW,
 | 
			
		||||
                                        Lang.bind(this, this._showWorkspaceSwitcher));
 | 
			
		||||
        this.setCustomKeybindingHandler('move-to-workspace-down',
 | 
			
		||||
                                        Shell.KeyBindingMode.NORMAL |
 | 
			
		||||
                                        Shell.KeyBindingMode.OVERVIEW,
 | 
			
		||||
                                        Main.KeybindingMode.NORMAL |
 | 
			
		||||
                                        Main.KeybindingMode.OVERVIEW,
 | 
			
		||||
                                        Lang.bind(this, this._showWorkspaceSwitcher));
 | 
			
		||||
        this.allowKeybinding('switch-to-workspace-1',
 | 
			
		||||
                             Shell.KeyBindingMode.NORMAL |
 | 
			
		||||
                             Shell.KeyBindingMode.OVERVIEW);
 | 
			
		||||
        this.allowKeybinding('switch-to-workspace-2',
 | 
			
		||||
                             Shell.KeyBindingMode.NORMAL |
 | 
			
		||||
                             Shell.KeyBindingMode.OVERVIEW);
 | 
			
		||||
        this.allowKeybinding('switch-to-workspace-3',
 | 
			
		||||
                             Shell.KeyBindingMode.NORMAL |
 | 
			
		||||
                             Shell.KeyBindingMode.OVERVIEW);
 | 
			
		||||
        this.allowKeybinding('switch-to-workspace-4',
 | 
			
		||||
                             Shell.KeyBindingMode.NORMAL |
 | 
			
		||||
                             Shell.KeyBindingMode.OVERVIEW);
 | 
			
		||||
        this.allowKeybinding('switch-to-workspace-5',
 | 
			
		||||
                             Shell.KeyBindingMode.NORMAL |
 | 
			
		||||
                             Shell.KeyBindingMode.OVERVIEW);
 | 
			
		||||
        this.allowKeybinding('switch-to-workspace-6',
 | 
			
		||||
                             Shell.KeyBindingMode.NORMAL |
 | 
			
		||||
                             Shell.KeyBindingMode.OVERVIEW);
 | 
			
		||||
        this.allowKeybinding('switch-to-workspace-7',
 | 
			
		||||
                             Shell.KeyBindingMode.NORMAL |
 | 
			
		||||
                             Shell.KeyBindingMode.OVERVIEW);
 | 
			
		||||
        this.allowKeybinding('switch-to-workspace-8',
 | 
			
		||||
                             Shell.KeyBindingMode.NORMAL |
 | 
			
		||||
                             Shell.KeyBindingMode.OVERVIEW);
 | 
			
		||||
        this.allowKeybinding('switch-to-workspace-9',
 | 
			
		||||
                             Shell.KeyBindingMode.NORMAL |
 | 
			
		||||
                             Shell.KeyBindingMode.OVERVIEW);
 | 
			
		||||
        this.allowKeybinding('switch-to-workspace-10',
 | 
			
		||||
                             Shell.KeyBindingMode.NORMAL |
 | 
			
		||||
                             Shell.KeyBindingMode.OVERVIEW);
 | 
			
		||||
        this.allowKeybinding('switch-to-workspace-11',
 | 
			
		||||
                             Shell.KeyBindingMode.NORMAL |
 | 
			
		||||
                             Shell.KeyBindingMode.OVERVIEW);
 | 
			
		||||
        this.allowKeybinding('switch-to-workspace-12',
 | 
			
		||||
                             Shell.KeyBindingMode.NORMAL |
 | 
			
		||||
                             Shell.KeyBindingMode.OVERVIEW);
 | 
			
		||||
        this.setCustomKeybindingHandler('switch-applications',
 | 
			
		||||
                                        Shell.KeyBindingMode.NORMAL,
 | 
			
		||||
                                        Main.KeybindingMode.NORMAL,
 | 
			
		||||
                                        Lang.bind(this, this._startAppSwitcher));
 | 
			
		||||
        this.setCustomKeybindingHandler('switch-group',
 | 
			
		||||
                                        Shell.KeyBindingMode.NORMAL,
 | 
			
		||||
                                        Main.KeybindingMode.NORMAL,
 | 
			
		||||
                                        Lang.bind(this, this._startAppSwitcher));
 | 
			
		||||
        this.setCustomKeybindingHandler('switch-applications-backward',
 | 
			
		||||
                                        Shell.KeyBindingMode.NORMAL,
 | 
			
		||||
                                        Main.KeybindingMode.NORMAL,
 | 
			
		||||
                                        Lang.bind(this, this._startAppSwitcher));
 | 
			
		||||
        this.setCustomKeybindingHandler('switch-group-backward',
 | 
			
		||||
                                        Shell.KeyBindingMode.NORMAL,
 | 
			
		||||
                                        Main.KeybindingMode.NORMAL,
 | 
			
		||||
                                        Lang.bind(this, this._startAppSwitcher));
 | 
			
		||||
        this.setCustomKeybindingHandler('switch-windows',
 | 
			
		||||
                                        Shell.KeyBindingMode.NORMAL,
 | 
			
		||||
                                        Main.KeybindingMode.NORMAL,
 | 
			
		||||
                                        Lang.bind(this, this._startWindowSwitcher));
 | 
			
		||||
        this.setCustomKeybindingHandler('switch-windows-backward',
 | 
			
		||||
                                        Shell.KeyBindingMode.NORMAL,
 | 
			
		||||
                                        Main.KeybindingMode.NORMAL,
 | 
			
		||||
                                        Lang.bind(this, this._startWindowSwitcher));
 | 
			
		||||
        this.setCustomKeybindingHandler('switch-panels',
 | 
			
		||||
                                        Shell.KeyBindingMode.NORMAL |
 | 
			
		||||
                                        Shell.KeyBindingMode.OVERVIEW |
 | 
			
		||||
                                        Shell.KeyBindingMode.LOCK_SCREEN |
 | 
			
		||||
                                        Shell.KeyBindingMode.UNLOCK_SCREEN |
 | 
			
		||||
                                        Shell.KeyBindingMode.LOGIN_SCREEN,
 | 
			
		||||
                                        Main.KeybindingMode.NORMAL |
 | 
			
		||||
                                        Main.KeybindingMode.OVERVIEW |
 | 
			
		||||
                                        Main.KeybindingMode.LOCK_SCREEN |
 | 
			
		||||
                                        Main.KeybindingMode.UNLOCK_SCREEN |
 | 
			
		||||
                                        Main.KeybindingMode.LOGIN_SCREEN,
 | 
			
		||||
                                        Lang.bind(this, this._startA11ySwitcher));
 | 
			
		||||
        this.setCustomKeybindingHandler('switch-panels-backward',
 | 
			
		||||
                                        Shell.KeyBindingMode.NORMAL |
 | 
			
		||||
                                        Shell.KeyBindingMode.OVERVIEW |
 | 
			
		||||
                                        Shell.KeyBindingMode.LOCK_SCREEN |
 | 
			
		||||
                                        Shell.KeyBindingMode.UNLOCK_SCREEN |
 | 
			
		||||
                                        Shell.KeyBindingMode.LOGIN_SCREEN,
 | 
			
		||||
                                        Main.KeybindingMode.NORMAL |
 | 
			
		||||
                                        Main.KeybindingMode.OVERVIEW |
 | 
			
		||||
                                        Main.KeybindingMode.LOCK_SCREEN |
 | 
			
		||||
                                        Main.KeybindingMode.UNLOCK_SCREEN |
 | 
			
		||||
                                        Main.KeybindingMode.LOGIN_SCREEN,
 | 
			
		||||
                                        Lang.bind(this, this._startA11ySwitcher));
 | 
			
		||||
 | 
			
		||||
        this.addKeybinding('open-application-menu',
 | 
			
		||||
                           new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }),
 | 
			
		||||
                           Meta.KeyBindingFlags.NONE,
 | 
			
		||||
                           Shell.KeyBindingMode.NORMAL |
 | 
			
		||||
                           Shell.KeyBindingMode.TOPBAR_POPUP,
 | 
			
		||||
                           Lang.bind(this, this._toggleAppMenu));
 | 
			
		||||
                           Main.KeybindingMode.NORMAL,
 | 
			
		||||
                           Lang.bind(this, this._openAppMenu));
 | 
			
		||||
 | 
			
		||||
        Main.overview.connect('showing', Lang.bind(this, function() {
 | 
			
		||||
            for (let i = 0; i < this._dimmedWindows.length; i++)
 | 
			
		||||
@@ -228,15 +191,13 @@ const WindowManager = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addKeybinding: function(name, settings, flags, modes, handler) {
 | 
			
		||||
        let action = global.display.add_keybinding(name, settings, flags, handler);
 | 
			
		||||
        if (action != Meta.KeyBindingAction.NONE)
 | 
			
		||||
        if (global.display.add_keybinding(name, settings, flags, handler))
 | 
			
		||||
            this.allowKeybinding(name, modes);
 | 
			
		||||
        return action;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    removeKeybinding: function(name) {
 | 
			
		||||
        if (global.display.remove_keybinding(name))
 | 
			
		||||
            this.allowKeybinding(name, Shell.KeyBindingMode.NONE);
 | 
			
		||||
            this.allowKeybinding(name, Main.KeybindingMode.NONE);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    allowKeybinding: function(name, modes) {
 | 
			
		||||
@@ -258,9 +219,7 @@ const WindowManager = new Lang.Class({
 | 
			
		||||
    _shouldAnimateActor: function(actor) {
 | 
			
		||||
        if (!this._shouldAnimate())
 | 
			
		||||
            return false;
 | 
			
		||||
        let windowType = actor.meta_window.get_window_type();
 | 
			
		||||
        return windowType == Meta.WindowType.NORMAL ||
 | 
			
		||||
            windowType == Meta.WindowType.MODAL_DIALOG;
 | 
			
		||||
        return actor.meta_window.get_window_type() == Meta.WindowType.NORMAL;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _removeEffect : function(list, actor) {
 | 
			
		||||
@@ -279,12 +238,23 @@ const WindowManager = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        actor.set_scale(1.0, 1.0);
 | 
			
		||||
        actor.move_anchor_point_from_gravity(Clutter.Gravity.CENTER);
 | 
			
		||||
 | 
			
		||||
        /* scale window down to 0x0.
 | 
			
		||||
         * maybe TODO: get icon geometry passed through and move the window towards it?
 | 
			
		||||
         */
 | 
			
		||||
        this._minimizing.push(actor);
 | 
			
		||||
 | 
			
		||||
        if (actor.meta_window.is_monitor_sized()) {
 | 
			
		||||
            Tweener.addTween(actor,
 | 
			
		||||
                         { opacity: 0,
 | 
			
		||||
        let primary = Main.layoutManager.primaryMonitor;
 | 
			
		||||
        let xDest = primary.x;
 | 
			
		||||
        if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
 | 
			
		||||
            xDest += primary.width;
 | 
			
		||||
 | 
			
		||||
        Tweener.addTween(actor,
 | 
			
		||||
                         { scale_x: 0.0,
 | 
			
		||||
                           scale_y: 0.0,
 | 
			
		||||
                           x: xDest,
 | 
			
		||||
                           y: 0,
 | 
			
		||||
                           time: WINDOW_ANIMATION_TIME,
 | 
			
		||||
                           transition: 'easeOutQuad',
 | 
			
		||||
                           onComplete: this._minimizeWindowDone,
 | 
			
		||||
@@ -294,46 +264,12 @@ const WindowManager = new Lang.Class({
 | 
			
		||||
                           onOverwriteScope: this,
 | 
			
		||||
                           onOverwriteParams: [shellwm, actor]
 | 
			
		||||
                         });
 | 
			
		||||
        } else {
 | 
			
		||||
            let xDest, yDest, xScale, yScale;
 | 
			
		||||
            let [success, geom] = actor.meta_window.get_icon_geometry();
 | 
			
		||||
            if (success) {
 | 
			
		||||
                xDest = geom.x;
 | 
			
		||||
                yDest = geom.y;
 | 
			
		||||
                xScale = geom.width / actor.width;
 | 
			
		||||
                yScale = geom.height / actor.height;
 | 
			
		||||
            } else {
 | 
			
		||||
                let monitor = Main.layoutManager.monitors[actor.meta_window.get_monitor()];
 | 
			
		||||
                xDest = monitor.x;
 | 
			
		||||
                yDest = monitor.y;
 | 
			
		||||
                if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
 | 
			
		||||
                    xDest += monitor.width;
 | 
			
		||||
                xScale = 0;
 | 
			
		||||
                yScale = 0;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            Tweener.addTween(actor,
 | 
			
		||||
                             { scale_x: xScale,
 | 
			
		||||
                               scale_y: yScale,
 | 
			
		||||
                               x: xDest,
 | 
			
		||||
                               y: yDest,
 | 
			
		||||
                               time: WINDOW_ANIMATION_TIME,
 | 
			
		||||
                               transition: 'easeOutQuad',
 | 
			
		||||
                               onComplete: this._minimizeWindowDone,
 | 
			
		||||
                               onCompleteScope: this,
 | 
			
		||||
                               onCompleteParams: [shellwm, actor],
 | 
			
		||||
                               onOverwrite: this._minimizeWindowOverwritten,
 | 
			
		||||
                               onOverwriteScope: this,
 | 
			
		||||
                               onOverwriteParams: [shellwm, actor]
 | 
			
		||||
                             });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _minimizeWindowDone : function(shellwm, actor) {
 | 
			
		||||
        if (this._removeEffect(this._minimizing, actor)) {
 | 
			
		||||
            Tweener.removeTweens(actor);
 | 
			
		||||
            actor.set_scale(1.0, 1.0);
 | 
			
		||||
            actor.set_opacity(255);
 | 
			
		||||
            actor.move_anchor_point_from_gravity(Clutter.Gravity.NORTH_WEST);
 | 
			
		||||
 | 
			
		||||
            shellwm.completed_minimize(actor);
 | 
			
		||||
@@ -433,56 +369,57 @@ const WindowManager = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
            actor._windowType = type;
 | 
			
		||||
        }));
 | 
			
		||||
        if (actor.meta_window.is_attached_dialog()) {
 | 
			
		||||
            this._checkDimming(actor.get_meta_window().get_transient_for());
 | 
			
		||||
            if (this._shouldAnimate()) {
 | 
			
		||||
                actor.set_scale(1.0, 0.0);
 | 
			
		||||
                actor.scale_gravity = Clutter.Gravity.CENTER;
 | 
			
		||||
                actor.show();
 | 
			
		||||
                this._mapping.push(actor);
 | 
			
		||||
 | 
			
		||||
                Tweener.addTween(actor,
 | 
			
		||||
                                 { scale_y: 1,
 | 
			
		||||
                                   time: WINDOW_ANIMATION_TIME,
 | 
			
		||||
                                   transition: "easeOutQuad",
 | 
			
		||||
                                   onComplete: this._mapWindowDone,
 | 
			
		||||
                                   onCompleteScope: this,
 | 
			
		||||
                                   onCompleteParams: [shellwm, actor],
 | 
			
		||||
                                   onOverwrite: this._mapWindowOverwrite,
 | 
			
		||||
                                   onOverwriteScope: this,
 | 
			
		||||
                                   onOverwriteParams: [shellwm, actor]
 | 
			
		||||
                                 });
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            shellwm.completed_map(actor);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        if (!this._shouldAnimateActor(actor)) {
 | 
			
		||||
            shellwm.completed_map(actor);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (actor.meta_window.is_attached_dialog()) {
 | 
			
		||||
            /* Scale the window from the center of the parent */
 | 
			
		||||
            this._checkDimming(actor.get_meta_window().get_transient_for());
 | 
			
		||||
            actor.set_scale(1.0, 0.0);
 | 
			
		||||
            actor.scale_gravity = Clutter.Gravity.CENTER;
 | 
			
		||||
            actor.show();
 | 
			
		||||
            this._mapping.push(actor);
 | 
			
		||||
        actor.opacity = 0;
 | 
			
		||||
        actor.show();
 | 
			
		||||
 | 
			
		||||
            Tweener.addTween(actor,
 | 
			
		||||
                             { scale_y: 1,
 | 
			
		||||
                               time: WINDOW_ANIMATION_TIME,
 | 
			
		||||
                               transition: "easeOutQuad",
 | 
			
		||||
                               onComplete: this._mapWindowDone,
 | 
			
		||||
                               onCompleteScope: this,
 | 
			
		||||
                               onCompleteParams: [shellwm, actor],
 | 
			
		||||
                               onOverwrite: this._mapWindowOverwrite,
 | 
			
		||||
                               onOverwriteScope: this,
 | 
			
		||||
                               onOverwriteParams: [shellwm, actor]
 | 
			
		||||
                             });
 | 
			
		||||
        } else {
 | 
			
		||||
            /* Fade window in */
 | 
			
		||||
            actor.opacity = 0;
 | 
			
		||||
            actor.show();
 | 
			
		||||
            this._mapping.push(actor);
 | 
			
		||||
 | 
			
		||||
            Tweener.addTween(actor,
 | 
			
		||||
                             { opacity: 255,
 | 
			
		||||
                               time: WINDOW_ANIMATION_TIME,
 | 
			
		||||
                               transition: 'easeOutQuad',
 | 
			
		||||
                               onComplete: this._mapWindowDone,
 | 
			
		||||
                               onCompleteScope: this,
 | 
			
		||||
                               onCompleteParams: [shellwm, actor],
 | 
			
		||||
                               onOverwrite: this._mapWindowOverwrite,
 | 
			
		||||
                               onOverwriteScope: this,
 | 
			
		||||
                               onOverwriteParams: [shellwm, actor]
 | 
			
		||||
                             });
 | 
			
		||||
        }
 | 
			
		||||
        /* Fade window in */
 | 
			
		||||
        this._mapping.push(actor);
 | 
			
		||||
        Tweener.addTween(actor,
 | 
			
		||||
                         { opacity: 255,
 | 
			
		||||
                           time: WINDOW_ANIMATION_TIME,
 | 
			
		||||
                           transition: 'easeOutQuad',
 | 
			
		||||
                           onComplete: this._mapWindowDone,
 | 
			
		||||
                           onCompleteScope: this,
 | 
			
		||||
                           onCompleteParams: [shellwm, actor],
 | 
			
		||||
                           onOverwrite: this._mapWindowOverwrite,
 | 
			
		||||
                           onOverwriteScope: this,
 | 
			
		||||
                           onOverwriteParams: [shellwm, actor]
 | 
			
		||||
                         });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _mapWindowDone : function(shellwm, actor) {
 | 
			
		||||
        if (this._removeEffect(this._mapping, actor)) {
 | 
			
		||||
            Tweener.removeTweens(actor);
 | 
			
		||||
            actor.opacity = 255;
 | 
			
		||||
            actor.scale_y = 1;
 | 
			
		||||
            shellwm.completed_map(actor);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
@@ -504,21 +441,18 @@ const WindowManager = new Lang.Class({
 | 
			
		||||
                                                                 return win != window;
 | 
			
		||||
                                                             });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!this._shouldAnimateActor(actor)) {
 | 
			
		||||
            shellwm.completed_destroy(actor);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._destroying.push(actor);
 | 
			
		||||
 | 
			
		||||
        if (window.is_attached_dialog()) {
 | 
			
		||||
            let parent = window.get_transient_for();
 | 
			
		||||
            this._checkDimming(parent, window);
 | 
			
		||||
            if (!this._shouldAnimate()) {
 | 
			
		||||
                shellwm.completed_destroy(actor);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            actor.set_scale(1.0, 1.0);
 | 
			
		||||
            actor.scale_gravity = Clutter.Gravity.CENTER;
 | 
			
		||||
            actor.show();
 | 
			
		||||
            this._destroying.push(actor);
 | 
			
		||||
 | 
			
		||||
            actor._parentDestroyId = parent.connect('unmanaged', Lang.bind(this, function () {
 | 
			
		||||
                Tweener.removeTweens(actor);
 | 
			
		||||
@@ -553,14 +487,14 @@ const WindowManager = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _filterKeybinding: function(shellwm, binding) {
 | 
			
		||||
        if (Main.keybindingMode == Shell.KeyBindingMode.NONE)
 | 
			
		||||
        if (Main.keybindingMode == Main.KeybindingMode.NONE)
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        // There's little sense in implementing a keybinding in mutter and
 | 
			
		||||
        // not having it work in NORMAL mode; handle this case generically
 | 
			
		||||
        // so we don't have to explicitly allow all builtin keybindings in
 | 
			
		||||
        // NORMAL mode.
 | 
			
		||||
        if (Main.keybindingMode == Shell.KeyBindingMode.NORMAL &&
 | 
			
		||||
        if (Main.keybindingMode == Main.KeybindingMode.NORMAL &&
 | 
			
		||||
            binding.is_builtin())
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
@@ -584,11 +518,11 @@ const WindowManager = new Lang.Class({
 | 
			
		||||
        if (direction == Meta.MotionDirection.UP ||
 | 
			
		||||
            direction == Meta.MotionDirection.UP_LEFT ||
 | 
			
		||||
            direction == Meta.MotionDirection.UP_RIGHT)
 | 
			
		||||
                yDest = global.screen_height - Main.panel.actor.height;
 | 
			
		||||
                yDest = global.screen_height;
 | 
			
		||||
        else if (direction == Meta.MotionDirection.DOWN ||
 | 
			
		||||
            direction == Meta.MotionDirection.DOWN_LEFT ||
 | 
			
		||||
            direction == Meta.MotionDirection.DOWN_RIGHT)
 | 
			
		||||
                yDest = -global.screen_height + Main.panel.actor.height;
 | 
			
		||||
                yDest = -global.screen_height;
 | 
			
		||||
 | 
			
		||||
        if (direction == Meta.MotionDirection.LEFT ||
 | 
			
		||||
            direction == Meta.MotionDirection.UP_LEFT ||
 | 
			
		||||
@@ -601,9 +535,9 @@ const WindowManager = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        let switchData = {};
 | 
			
		||||
        this._switchData = switchData;
 | 
			
		||||
        switchData.inGroup = new Clutter.Actor();
 | 
			
		||||
        switchData.outGroup = new Clutter.Actor();
 | 
			
		||||
        switchData.movingWindowBin = new Clutter.Actor();
 | 
			
		||||
        switchData.inGroup = new Clutter.Group();
 | 
			
		||||
        switchData.outGroup = new Clutter.Group();
 | 
			
		||||
        switchData.movingWindowBin = new Clutter.Group();
 | 
			
		||||
        switchData.windows = [];
 | 
			
		||||
 | 
			
		||||
        let wgroup = global.window_group;
 | 
			
		||||
@@ -630,7 +564,7 @@ const WindowManager = new Lang.Class({
 | 
			
		||||
                switchData.windows.push({ window: window,
 | 
			
		||||
                                          parent: window.get_parent() });
 | 
			
		||||
                window.reparent(switchData.inGroup);
 | 
			
		||||
                window.show();
 | 
			
		||||
                window.show_all();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -716,8 +650,8 @@ const WindowManager = new Lang.Class({
 | 
			
		||||
        Main.ctrlAltTabManager.popup(backwards, binding.get_name(), binding.get_mask());
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _toggleAppMenu : function(display, screen, window, event, binding) {
 | 
			
		||||
        Main.panel.toggleAppMenu();
 | 
			
		||||
    _openAppMenu : function(display, screen, window, event, binding) {
 | 
			
		||||
        Main.panel.openAppMenu();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _showWorkspaceSwitcher : function(display, screen, window, binding) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
// -*- 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 Meta = imports.gi.Meta;
 | 
			
		||||
@@ -29,6 +30,9 @@ const CLOSE_BUTTON_FADE_TIME = 0.1;
 | 
			
		||||
 | 
			
		||||
const DRAGGING_WINDOW_OPACITY = 100;
 | 
			
		||||
 | 
			
		||||
const BUTTON_LAYOUT_SCHEMA = 'org.gnome.shell.overrides';
 | 
			
		||||
const BUTTON_LAYOUT_KEY = 'button-layout';
 | 
			
		||||
 | 
			
		||||
// When calculating a layout, we calculate the scale of windows and the percent
 | 
			
		||||
// of the available area the new layout uses. If the values for the new layout,
 | 
			
		||||
// when weighted with the values as below, are worse than the previous layout's,
 | 
			
		||||
@@ -41,6 +45,46 @@ function _interpolate(start, end, step) {
 | 
			
		||||
    return start + (end - start) * step;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _clamp(value, min, max) {
 | 
			
		||||
    return Math.max(min, Math.min(max, value));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const ScaledPoint = new Lang.Class({
 | 
			
		||||
    Name: 'ScaledPoint',
 | 
			
		||||
 | 
			
		||||
    _init: function(x, y, scaleX, scaleY) {
 | 
			
		||||
        [this.x, this.y, this.scaleX, this.scaleY] = arguments;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getPosition : function() {
 | 
			
		||||
        return [this.x, this.y];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getScale : function() {
 | 
			
		||||
        return [this.scaleX, this.scaleY];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setPosition : function(x, y) {
 | 
			
		||||
        [this.x, this.y] = arguments;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setScale : function(scaleX, scaleY) {
 | 
			
		||||
        [this.scaleX, this.scaleY] = arguments;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    interpPosition : function(other, step) {
 | 
			
		||||
        return [_interpolate(this.x, other.x, step),
 | 
			
		||||
                _interpolate(this.y, other.y, step)];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    interpScale : function(other, step) {
 | 
			
		||||
        return [_interpolate(this.scaleX, other.scaleX, step),
 | 
			
		||||
                _interpolate(this.scaleY, other.scaleY, step)];
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const WindowClone = new Lang.Class({
 | 
			
		||||
    Name: 'WindowClone',
 | 
			
		||||
 | 
			
		||||
@@ -68,20 +112,18 @@ const WindowClone = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        // The MetaShapedTexture that we clone has a size that includes
 | 
			
		||||
        // the invisible border; this is inconvenient; rather than trying
 | 
			
		||||
        // to compensate all over the place we insert a ClutterActor into
 | 
			
		||||
        // to compensate all over the place we insert a ClutterGroup into
 | 
			
		||||
        // the hierarchy that is sized to only the visible portion.
 | 
			
		||||
        this.actor = new Clutter.Actor({ reactive: true,
 | 
			
		||||
        this.actor = new Clutter.Group({ reactive: true,
 | 
			
		||||
                                         x: this.origX,
 | 
			
		||||
                                         y: this.origY,
 | 
			
		||||
                                         width: outerRect.width,
 | 
			
		||||
                                         height: outerRect.height });
 | 
			
		||||
 | 
			
		||||
        this.actor.add_child(this._windowClone);
 | 
			
		||||
        this.actor.add_actor(this._windowClone);
 | 
			
		||||
 | 
			
		||||
        this.actor._delegate = this;
 | 
			
		||||
 | 
			
		||||
        this._slot = [0, 0, 0, 0];
 | 
			
		||||
        this._dragSlot = [0, 0, 0, 0];
 | 
			
		||||
        this._stackAbove = null;
 | 
			
		||||
 | 
			
		||||
        this._sizeChangedId = this.realWindow.connect('size-changed',
 | 
			
		||||
@@ -95,7 +137,12 @@ const WindowClone = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this.actor.add_action(clickAction);
 | 
			
		||||
 | 
			
		||||
        this.actor.connect('scroll-event',
 | 
			
		||||
                           Lang.bind(this, this._onScroll));
 | 
			
		||||
 | 
			
		||||
        this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
 | 
			
		||||
        this.actor.connect('leave-event',
 | 
			
		||||
                           Lang.bind(this, this._onLeave));
 | 
			
		||||
 | 
			
		||||
        this._draggable = DND.makeDraggable(this.actor,
 | 
			
		||||
                                            { restoreOnSuccess: true,
 | 
			
		||||
@@ -107,53 +154,58 @@ const WindowClone = new Lang.Class({
 | 
			
		||||
        this._draggable.connect('drag-end', Lang.bind(this, this._onDragEnd));
 | 
			
		||||
        this.inDrag = false;
 | 
			
		||||
 | 
			
		||||
        this._windowIsZooming = false;
 | 
			
		||||
        this._zooming = false;
 | 
			
		||||
        this._selected = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    set slot(slot) {
 | 
			
		||||
        this._slot = slot;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get slot() {
 | 
			
		||||
        if (this.inDrag)
 | 
			
		||||
            return this._dragSlot;
 | 
			
		||||
        else
 | 
			
		||||
            return this._slot;
 | 
			
		||||
    },
 | 
			
		||||
        let x, y, w, h;
 | 
			
		||||
 | 
			
		||||
    // Find the actor just below us, respecting reparenting done
 | 
			
		||||
    // by DND code
 | 
			
		||||
    getActualStackAbove: function() {
 | 
			
		||||
        if (this._stackAbove == null)
 | 
			
		||||
            return null;
 | 
			
		||||
 | 
			
		||||
        if (this.inDrag || this._zooming) {
 | 
			
		||||
            if (this._stackAbove._delegate)
 | 
			
		||||
                return this._stackAbove._delegate.getActualStackAbove();
 | 
			
		||||
            else
 | 
			
		||||
                return null;
 | 
			
		||||
        if (this.inDrag) {
 | 
			
		||||
            x = this.dragOrigX;
 | 
			
		||||
            y = this.dragOrigY;
 | 
			
		||||
            w = this.actor.width * this.dragOrigScale;
 | 
			
		||||
            h = this.actor.height * this.dragOrigScale;
 | 
			
		||||
        } else {
 | 
			
		||||
            return this._stackAbove;
 | 
			
		||||
            x = this.actor.x;
 | 
			
		||||
            y = this.actor.y;
 | 
			
		||||
            w = this.actor.width * this.actor.scale_x;
 | 
			
		||||
            h = this.actor.height * this.actor.scale_y;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return [x, y, w, h];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setStackAbove: function (actor) {
 | 
			
		||||
        this._stackAbove = actor;
 | 
			
		||||
        if (this.inDrag)
 | 
			
		||||
            // We'll fix up the stack after the drag
 | 
			
		||||
        if (this.inDrag || this._zooming)
 | 
			
		||||
            // We'll fix up the stack after the drag/zooming
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let actualAbove = this.getActualStackAbove();
 | 
			
		||||
        if (actualAbove == null)
 | 
			
		||||
        if (this._stackAbove == null)
 | 
			
		||||
            this.actor.lower_bottom();
 | 
			
		||||
        else
 | 
			
		||||
            this.actor.raise(actualAbove);
 | 
			
		||||
            this.actor.raise(this._stackAbove);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroy: function () {
 | 
			
		||||
        this.actor.destroy();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    zoomFromOverview: function() {
 | 
			
		||||
        if (this._zooming) {
 | 
			
		||||
            // If the user clicked on the zoomed window, or we are
 | 
			
		||||
            // returning there anyways, then we can zoom right to the
 | 
			
		||||
            // window, but if we are going to some other window, then
 | 
			
		||||
            // we need to cancel the zoom before animating, or it
 | 
			
		||||
            // will look funny.
 | 
			
		||||
 | 
			
		||||
            if (!this._selected &&
 | 
			
		||||
                this.metaWindow != global.display.focus_window)
 | 
			
		||||
                this._zoomEnd();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _disconnectRealWindowSignals: function() {
 | 
			
		||||
        if (this._sizeChangedId > 0)
 | 
			
		||||
            this.realWindow.disconnect(this._sizeChangedId);
 | 
			
		||||
@@ -195,6 +247,8 @@ const WindowClone = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this.metaWindow._delegate = null;
 | 
			
		||||
        this.actor._delegate = null;
 | 
			
		||||
        if (this._zoomLightbox)
 | 
			
		||||
            this._zoomLightbox.destroy();
 | 
			
		||||
 | 
			
		||||
        if (this.inDrag) {
 | 
			
		||||
            this.emit('drag-end');
 | 
			
		||||
@@ -204,6 +258,112 @@ const WindowClone = new Lang.Class({
 | 
			
		||||
        this.disconnectAll();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onLeave: function (actor, event) {
 | 
			
		||||
        if (this._zoomStep)
 | 
			
		||||
            this._zoomEnd();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onScroll : function (actor, event) {
 | 
			
		||||
        let direction = event.get_scroll_direction();
 | 
			
		||||
        if (direction == Clutter.ScrollDirection.UP) {
 | 
			
		||||
            if (this._zoomStep == undefined)
 | 
			
		||||
                this._zoomStart();
 | 
			
		||||
            if (this._zoomStep < 100) {
 | 
			
		||||
                this._zoomStep += SCROLL_SCALE_AMOUNT;
 | 
			
		||||
                this._zoomUpdate();
 | 
			
		||||
            }
 | 
			
		||||
        } else if (direction == Clutter.ScrollDirection.DOWN) {
 | 
			
		||||
            if (this._zoomStep > 0) {
 | 
			
		||||
                this._zoomStep -= SCROLL_SCALE_AMOUNT;
 | 
			
		||||
                this._zoomStep = Math.max(0, this._zoomStep);
 | 
			
		||||
                this._zoomUpdate();
 | 
			
		||||
            }
 | 
			
		||||
            if (this._zoomStep <= 0.0)
 | 
			
		||||
                this._zoomEnd();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _zoomUpdate : function () {
 | 
			
		||||
        [this.actor.x, this.actor.y] = this._zoomGlobalOrig.interpPosition(this._zoomTarget, this._zoomStep / 100);
 | 
			
		||||
        [this.actor.scale_x, this.actor.scale_y] = this._zoomGlobalOrig.interpScale(this._zoomTarget, this._zoomStep / 100);
 | 
			
		||||
 | 
			
		||||
        let [width, height] = this.actor.get_transformed_size();
 | 
			
		||||
 | 
			
		||||
        let monitorIndex = this.metaWindow.get_monitor();
 | 
			
		||||
        let monitor = Main.layoutManager.monitors[monitorIndex];
 | 
			
		||||
        let availArea = new Meta.Rectangle({ x: monitor.x,
 | 
			
		||||
                                             y: monitor.y,
 | 
			
		||||
                                             width: monitor.width,
 | 
			
		||||
                                             height: monitor.height });
 | 
			
		||||
        if (monitorIndex == Main.layoutManager.primaryIndex) {
 | 
			
		||||
            availArea.y += Main.panel.actor.height;
 | 
			
		||||
            availArea.height -= Main.panel.actor.height;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.actor.x = _clamp(this.actor.x, availArea.x, availArea.x + availArea.width - width);
 | 
			
		||||
        this.actor.y = _clamp(this.actor.y, availArea.y, availArea.y + availArea.height - height);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _zoomStart : function () {
 | 
			
		||||
        this._zooming = true;
 | 
			
		||||
        this.emit('zoom-start');
 | 
			
		||||
 | 
			
		||||
        if (!this._zoomLightbox)
 | 
			
		||||
            this._zoomLightbox = new Lightbox.Lightbox(Main.uiGroup,
 | 
			
		||||
                                                       { fadeInTime: LIGHTBOX_FADE_TIME,
 | 
			
		||||
                                                         fadeOutTime: LIGHTBOX_FADE_TIME });
 | 
			
		||||
        this._zoomLightbox.show();
 | 
			
		||||
 | 
			
		||||
        this._zoomLocalOrig  = new ScaledPoint(this.actor.x, this.actor.y, this.actor.scale_x, this.actor.scale_y);
 | 
			
		||||
        this._zoomGlobalOrig = new ScaledPoint();
 | 
			
		||||
        let parent = this._origParent = this.actor.get_parent();
 | 
			
		||||
        let [width, height] = this.actor.get_transformed_size();
 | 
			
		||||
        this._zoomGlobalOrig.setPosition.apply(this._zoomGlobalOrig, this.actor.get_transformed_position());
 | 
			
		||||
        this._zoomGlobalOrig.setScale(width / this.actor.width, height / this.actor.height);
 | 
			
		||||
 | 
			
		||||
        this.actor.reparent(Main.uiGroup);
 | 
			
		||||
        this._zoomLightbox.highlight(this.actor);
 | 
			
		||||
 | 
			
		||||
        [this.actor.x, this.actor.y]             = this._zoomGlobalOrig.getPosition();
 | 
			
		||||
        [this.actor.scale_x, this.actor.scale_y] = this._zoomGlobalOrig.getScale();
 | 
			
		||||
 | 
			
		||||
        this.actor.raise_top();
 | 
			
		||||
 | 
			
		||||
        this._zoomTarget = new ScaledPoint(0, 0, 1.0, 1.0);
 | 
			
		||||
        this._zoomTarget.setPosition(this.actor.x - (this.actor.width - width) / 2, this.actor.y - (this.actor.height - height) / 2);
 | 
			
		||||
        this._zoomStep = 0;
 | 
			
		||||
 | 
			
		||||
        this._zoomUpdate();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _zoomEnd : function () {
 | 
			
		||||
        this._zooming = false;
 | 
			
		||||
        this.emit('zoom-end');
 | 
			
		||||
 | 
			
		||||
        this.actor.reparent(this._origParent);
 | 
			
		||||
        if (this._stackAbove == null)
 | 
			
		||||
            this.actor.lower_bottom();
 | 
			
		||||
        // If the workspace has been destroyed while we were reparented to
 | 
			
		||||
        // the stage, _stackAbove will be unparented and we can't raise our
 | 
			
		||||
        // actor above it - as we are bound to be destroyed anyway in that
 | 
			
		||||
        // case, we can skip that step
 | 
			
		||||
        else if (this._stackAbove.get_parent())
 | 
			
		||||
            this.actor.raise(this._stackAbove);
 | 
			
		||||
 | 
			
		||||
        [this.actor.x, this.actor.y]             = this._zoomLocalOrig.getPosition();
 | 
			
		||||
        [this.actor.scale_x, this.actor.scale_y] = this._zoomLocalOrig.getScale();
 | 
			
		||||
 | 
			
		||||
        this._zoomLightbox.hide();
 | 
			
		||||
 | 
			
		||||
        this._zoomLocalPosition  = undefined;
 | 
			
		||||
        this._zoomLocalScale     = undefined;
 | 
			
		||||
        this._zoomGlobalPosition = undefined;
 | 
			
		||||
        this._zoomGlobalScale    = undefined;
 | 
			
		||||
        this._zoomTargetPosition = undefined;
 | 
			
		||||
        this._zoomStep           = undefined;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onClicked: function(action, actor) {
 | 
			
		||||
        this._selected = true;
 | 
			
		||||
        this.emit('selected', global.get_current_time());
 | 
			
		||||
@@ -229,7 +389,9 @@ const WindowClone = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDragBegin : function (draggable, time) {
 | 
			
		||||
        this._dragSlot = this._slot;
 | 
			
		||||
        if (this._zooming)
 | 
			
		||||
            this._zoomEnd();
 | 
			
		||||
 | 
			
		||||
        [this.dragOrigX, this.dragOrigY] = this.actor.get_position();
 | 
			
		||||
        this.dragOrigScale = this.actor.scale_x;
 | 
			
		||||
        this.inDrag = true;
 | 
			
		||||
@@ -294,7 +456,11 @@ const WindowOverlay = new Lang.Class({
 | 
			
		||||
        this._updateCaptionId = metaWindow.connect('notify::title',
 | 
			
		||||
            Lang.bind(this, function(w) {
 | 
			
		||||
                this.title.text = w.title;
 | 
			
		||||
                this.relayout(false);
 | 
			
		||||
                // we need this for the next call to get_preferred_width
 | 
			
		||||
                // to return useful results
 | 
			
		||||
                this.title.set_size(-1, -1);
 | 
			
		||||
 | 
			
		||||
                this._repositionSelf();
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
        let button = new St.Button({ style_class: 'window-close' });
 | 
			
		||||
@@ -310,6 +476,8 @@ const WindowOverlay = new Lang.Class({
 | 
			
		||||
                                  Lang.bind(this, this._onLeave));
 | 
			
		||||
 | 
			
		||||
        this._windowAddedId = 0;
 | 
			
		||||
        windowClone.connect('zoom-start', Lang.bind(this, this.hide));
 | 
			
		||||
        windowClone.connect('zoom-end', Lang.bind(this, this.show));
 | 
			
		||||
 | 
			
		||||
        button.hide();
 | 
			
		||||
 | 
			
		||||
@@ -364,23 +532,38 @@ const WindowOverlay = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    chromeWidths: function () {
 | 
			
		||||
        return [this.borderSize,
 | 
			
		||||
                Math.max(this.borderSize, this.closeButton.width - this.closeButton._overlap)];
 | 
			
		||||
        return [this.borderSize, this.borderSize];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    relayout: function(animate) {
 | 
			
		||||
    _repositionSelf: function() {
 | 
			
		||||
        let [cloneX, cloneY, cloneWidth, cloneHeight] = this._windowClone.slot;
 | 
			
		||||
        this.updatePositions(cloneX, cloneY, cloneWidth, cloneHeight, false);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @cloneX: x position of windowClone
 | 
			
		||||
     * @cloneY: y position of windowClone
 | 
			
		||||
     * @cloneWidth: width of windowClone
 | 
			
		||||
     * @cloneHeight height of windowClone
 | 
			
		||||
     */
 | 
			
		||||
    // These parameters are not the values retrieved with
 | 
			
		||||
    // get_transformed_position() and get_transformed_size(),
 | 
			
		||||
    // as windowClone might be moving.
 | 
			
		||||
    // See Workspace._showWindowOverlay
 | 
			
		||||
    updatePositions: function(cloneX, cloneY, cloneWidth, cloneHeight, animate) {
 | 
			
		||||
        let button = this.closeButton;
 | 
			
		||||
        let title = this.title;
 | 
			
		||||
        let border = this.border;
 | 
			
		||||
 | 
			
		||||
        Tweener.removeTweens(button);
 | 
			
		||||
        Tweener.removeTweens(title);
 | 
			
		||||
        Tweener.removeTweens(border);
 | 
			
		||||
        let settings = new Gio.Settings({ schema: BUTTON_LAYOUT_SCHEMA });
 | 
			
		||||
        let layout = settings.get_string(BUTTON_LAYOUT_KEY);
 | 
			
		||||
        let rtl = Clutter.get_default_text_direction() == Clutter.TextDirection.RTL;
 | 
			
		||||
 | 
			
		||||
        let [cloneX, cloneY, cloneWidth, cloneHeight] = this._windowClone.slot;
 | 
			
		||||
 | 
			
		||||
        let layout = Meta.prefs_get_button_layout();
 | 
			
		||||
        let side = layout.left_buttons.indexOf(Meta.ButtonFunction.CLOSE) > -1 ? St.Side.LEFT : St.Side.RIGHT;
 | 
			
		||||
        let split = layout.split(":");
 | 
			
		||||
        let side;
 | 
			
		||||
        if (split[0].indexOf("close") > -1)
 | 
			
		||||
            side = rtl ? St.Side.RIGHT : St.Side.LEFT;
 | 
			
		||||
        else
 | 
			
		||||
            side = rtl ? St.Side.LEFT : St.Side.RIGHT;
 | 
			
		||||
 | 
			
		||||
        let buttonX;
 | 
			
		||||
        let buttonY = cloneY - (button.height - button._overlap);
 | 
			
		||||
@@ -394,16 +577,8 @@ const WindowOverlay = new Lang.Class({
 | 
			
		||||
        else
 | 
			
		||||
            button.set_position(Math.floor(buttonX), Math.floor(buttonY));
 | 
			
		||||
 | 
			
		||||
        // Clutter.Actor.get_preferred_width() will return the fixed width if one
 | 
			
		||||
        // is set, so we need to reset the width by calling set_width(-1), to forward
 | 
			
		||||
        // the call down to StLabel.
 | 
			
		||||
        // We also need to save and restore the current width, otherwise the animation
 | 
			
		||||
        // starts from the wrong point.
 | 
			
		||||
        let prevTitleWidth = title.width;
 | 
			
		||||
        title.set_width(-1);
 | 
			
		||||
        let [titleMinWidth, titleNatWidth] = title.get_preferred_width(-1);
 | 
			
		||||
        let titleWidth = Math.max(titleMinWidth, Math.min(titleNatWidth, cloneWidth));
 | 
			
		||||
        title.width = prevTitleWidth;
 | 
			
		||||
 | 
			
		||||
        let titleX = cloneX + (cloneWidth - titleWidth) / 2;
 | 
			
		||||
        let titleY = cloneY + cloneHeight + title._spacing;
 | 
			
		||||
@@ -574,93 +749,19 @@ const WindowOverlay = new Lang.Class({
 | 
			
		||||
Signals.addSignalMethods(WindowOverlay.prototype);
 | 
			
		||||
 | 
			
		||||
const WindowPositionFlags = {
 | 
			
		||||
    NONE: 0,
 | 
			
		||||
    INITIAL: 1 << 0,
 | 
			
		||||
    ANIMATE: 1 << 1
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Window Thumbnail Layout Algorithm
 | 
			
		||||
// =================================
 | 
			
		||||
//
 | 
			
		||||
// General overview
 | 
			
		||||
// ----------------
 | 
			
		||||
//
 | 
			
		||||
// The window thumbnail layout algorithm calculates some optimal layout
 | 
			
		||||
// by computing layouts with some number of rows, calculating how good
 | 
			
		||||
// each layout is, and stopping iterating when it finds one that is worse
 | 
			
		||||
// than the previous layout. A layout consists of which windows are in
 | 
			
		||||
// which rows, row sizes and other general state tracking that would make
 | 
			
		||||
// calculating window positions from this information fairly easy.
 | 
			
		||||
//
 | 
			
		||||
// We don't compute some global order of windows right now for optimal
 | 
			
		||||
// travel when animating into the overview; windows are assumed to be
 | 
			
		||||
// in some stable order.
 | 
			
		||||
//
 | 
			
		||||
// After a layout is computed that's considered the best layout, we
 | 
			
		||||
// compute the layout scale to fit it in the area, and then compute
 | 
			
		||||
// slots (sizes and positions) for each thumbnail.
 | 
			
		||||
//
 | 
			
		||||
// Layout generation
 | 
			
		||||
// -----------------
 | 
			
		||||
//
 | 
			
		||||
// Layout generation is naive and simple: we simply add windows to a row
 | 
			
		||||
// until we've added too many windows to a row, and then make a new row,
 | 
			
		||||
// until we have our required N rows. The potential issue with this strategy
 | 
			
		||||
// is that we may have too many windows at the bottom in some pathological
 | 
			
		||||
// cases, which tends to make the thumbnails have the shape of a pile of
 | 
			
		||||
// sand with a peak, with one window at the top.
 | 
			
		||||
//
 | 
			
		||||
// Scaling factors
 | 
			
		||||
// ---------------
 | 
			
		||||
//
 | 
			
		||||
// Thumbnail position is mostly straightforward -- the main issue is
 | 
			
		||||
// computing an optimal scale for each window that fits the constraints,
 | 
			
		||||
// and doesn't make the thumbnail too small to see. There are two factors
 | 
			
		||||
// involved in thumbnail scale to make sure that these two goals are met:
 | 
			
		||||
// the window scale (calculated by _computeWindowScale) and the layout
 | 
			
		||||
// scale (calculated by computeSizeAndScale).
 | 
			
		||||
//
 | 
			
		||||
// The calculation logic becomes slightly more complicated because row
 | 
			
		||||
// and column spacing are not scaled, they're constant, so we can't
 | 
			
		||||
// simply generate a bunch of window positions and then scale it. In
 | 
			
		||||
// practice, it's not too bad -- we can simply try to fit the layout
 | 
			
		||||
// in the input area minus whatever spacing we have, and then add
 | 
			
		||||
// it back afterwards.
 | 
			
		||||
//
 | 
			
		||||
// The window scale is constant for the window's size regardless of the
 | 
			
		||||
// input area or the layout scale or rows or anything else, and right
 | 
			
		||||
// now just enlarges the window if it's too small. The fact that this
 | 
			
		||||
// factor is stable makes it easy to calculate, so there's no sense
 | 
			
		||||
// in not applying it in most calculations.
 | 
			
		||||
//
 | 
			
		||||
// The layout scale depends on the input area, the rows, etc, but is the
 | 
			
		||||
// same for the entire layout, rather than being per-window. After
 | 
			
		||||
// generating the rows of windows, we basically do some basic math to
 | 
			
		||||
// fit the full, unscaled layout to the input area, as described above.
 | 
			
		||||
//
 | 
			
		||||
// With these two factors combined, the final scale of each thumbnail is
 | 
			
		||||
// simply windowScale * layoutScale... almost.
 | 
			
		||||
//
 | 
			
		||||
// There's one additional constraint: the thumbnail scale must never be
 | 
			
		||||
// larger than WINDOW_CLONE_MAXIMUM_SCALE, which means that the inequality:
 | 
			
		||||
//
 | 
			
		||||
//   windowScale * layoutScale <= WINDOW_CLONE_MAXIMUM_SCALE
 | 
			
		||||
//
 | 
			
		||||
// must always be true. This is for each individual window -- while we
 | 
			
		||||
// could adjust layoutScale to make the largest thumbnail smaller than
 | 
			
		||||
// WINDOW_CLONE_MAXIMUM_SCALE, it would shrink windows which are already
 | 
			
		||||
// under the inequality. To solve this, we simply cheat: we simply keep
 | 
			
		||||
// each window's "cell" area to be the same, but we shrink the thumbnail
 | 
			
		||||
// and center it horizontally, and align it to the bottom vertically.
 | 
			
		||||
 | 
			
		||||
const LayoutStrategy = new Lang.Class({
 | 
			
		||||
    Name: 'LayoutStrategy',
 | 
			
		||||
    Abstract: true,
 | 
			
		||||
 | 
			
		||||
    _init: function(monitor, rowSpacing, columnSpacing) {
 | 
			
		||||
    _init: function(monitor, rowSpacing, columnSpacing, bottomPadding) {
 | 
			
		||||
        this._monitor = monitor;
 | 
			
		||||
        this._rowSpacing = rowSpacing;
 | 
			
		||||
        this._columnSpacing = columnSpacing;
 | 
			
		||||
        this._bottomPadding = bottomPadding;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _newRow: function() {
 | 
			
		||||
@@ -675,34 +776,40 @@ const LayoutStrategy = new Lang.Class({
 | 
			
		||||
        //   meant to be scaled
 | 
			
		||||
        //
 | 
			
		||||
        // * neither height/fullHeight have any sort of spacing or padding
 | 
			
		||||
        //
 | 
			
		||||
        // * if cellWidth is present, all windows in the row will occupy
 | 
			
		||||
        //   the space of cellWidth, centered.
 | 
			
		||||
        return { x: 0, y: 0,
 | 
			
		||||
                 width: 0, height: 0,
 | 
			
		||||
                 fullWidth: 0, fullHeight: 0,
 | 
			
		||||
                 cellWidth: 0,
 | 
			
		||||
                 windows: [] };
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Computes and returns an individual scaling factor for @window,
 | 
			
		||||
    // to be applied in addition to the overal layout scale.
 | 
			
		||||
    _computeWindowScale: function(window) {
 | 
			
		||||
        // Since we align windows next to each other, the height of the
 | 
			
		||||
        // thumbnails is much more important to preserve than the width of
 | 
			
		||||
        // them, so two windows with equal height, but maybe differering
 | 
			
		||||
        // widths line up.
 | 
			
		||||
        let ratio = window.actor.height / this._monitor.height;
 | 
			
		||||
    // Compute the size and fancy scale for @window using the
 | 
			
		||||
    // base scale, @scale.
 | 
			
		||||
    //
 | 
			
		||||
    // Returns a list structure: [ scaledWidth, scaledHeight, fancyScale ]
 | 
			
		||||
    // where scaledWidth and scaledHeight are the window's
 | 
			
		||||
    // width and height, scaled by fancyScale for convenience.
 | 
			
		||||
    _computeWindowSizeAndScale: function(window, scale) {
 | 
			
		||||
        let width = window.actor.width;
 | 
			
		||||
        let height = window.actor.height;
 | 
			
		||||
        let ratio;
 | 
			
		||||
 | 
			
		||||
        // The purpose of this manipulation here is to prevent windows
 | 
			
		||||
        // from getting too small. For something like a calculator window,
 | 
			
		||||
        // we need to bump up the size just a bit to make sure it looks
 | 
			
		||||
        // good. We'll use a multiplier of 1.5 for this.
 | 
			
		||||
        if (width > height)
 | 
			
		||||
            ratio = width / this._monitor.width;
 | 
			
		||||
        else
 | 
			
		||||
            ratio = height / this._monitor.height;
 | 
			
		||||
 | 
			
		||||
        // Map from [0, 1] to [1.5, 1]
 | 
			
		||||
        return _interpolate(1.5, 1, ratio);
 | 
			
		||||
        let fancyScale = (2 / (1 + ratio)) * scale;
 | 
			
		||||
        return [width * fancyScale, height * fancyScale, fancyScale];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Compute the size of each row, by assigning to the properties
 | 
			
		||||
    // row.width, row.height, row.fullWidth, row.fullHeight, and
 | 
			
		||||
    // (optionally) for each row in @layout.rows. This method is
 | 
			
		||||
    // intended to be called by subclasses.
 | 
			
		||||
    // (optionally) row.cellWidth, for each row in @layout.rows.
 | 
			
		||||
    // This method is intended to be called by subclasses.
 | 
			
		||||
    _computeRowSizes: function(layout) {
 | 
			
		||||
        throw new Error('_computeRowSizes not implemented');
 | 
			
		||||
    },
 | 
			
		||||
@@ -734,7 +841,7 @@ const LayoutStrategy = new Lang.Class({
 | 
			
		||||
        let area = layout.area;
 | 
			
		||||
 | 
			
		||||
        let hspacing = (layout.maxColumns - 1) * this._columnSpacing;
 | 
			
		||||
        let vspacing = (layout.numRows - 1) * this._rowSpacing;
 | 
			
		||||
        let vspacing = (layout.numRows - 1) * this._rowSpacing + this._bottomPadding;
 | 
			
		||||
 | 
			
		||||
        let spacedWidth = area.width - hspacing;
 | 
			
		||||
        let spacedHeight = area.height - vspacing;
 | 
			
		||||
@@ -753,17 +860,10 @@ const LayoutStrategy = new Lang.Class({
 | 
			
		||||
        layout.space = space;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getDistance: function (row, actor) {
 | 
			
		||||
        let dist_x = actor.x - row.x;
 | 
			
		||||
        let dist_y = actor.y - row.y;
 | 
			
		||||
 | 
			
		||||
        return Math.sqrt(Math.pow(dist_x, 2) + Math.pow(dist_y, 2));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    computeWindowSlots: function(layout, area) {
 | 
			
		||||
        this._computeRowSizes(layout);
 | 
			
		||||
 | 
			
		||||
        let { rows: rows, scale: scale } = layout;
 | 
			
		||||
        let { rows: rows, scale: scale, state: state } = layout;
 | 
			
		||||
 | 
			
		||||
        let slots = [];
 | 
			
		||||
 | 
			
		||||
@@ -773,33 +873,29 @@ const LayoutStrategy = new Lang.Class({
 | 
			
		||||
            row.x = area.x + (area.width - row.width) / 2;
 | 
			
		||||
            row.y = area.y + y;
 | 
			
		||||
            y += row.height + this._rowSpacing;
 | 
			
		||||
            row.windows.sort(Lang.bind(this, function(a, b) {
 | 
			
		||||
                return this._getDistance(row, a.realWindow) - this._getDistance(row, b.realWindow);
 | 
			
		||||
            }));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let height = y - this._rowSpacing;
 | 
			
		||||
        let height = y - this._rowSpacing + this._bottomPadding;
 | 
			
		||||
        let baseY = (area.height - height) / 2;
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < rows.length; i++) {
 | 
			
		||||
            let row = rows[i];
 | 
			
		||||
            row.y += baseY;
 | 
			
		||||
            let x = row.x;
 | 
			
		||||
            let baseX = row.x;
 | 
			
		||||
            for (let j = 0; j < row.windows.length; j++) {
 | 
			
		||||
                let window = row.windows[j];
 | 
			
		||||
 | 
			
		||||
                let s = scale * this._computeWindowScale(window);
 | 
			
		||||
                let cellWidth = window.actor.width * s;
 | 
			
		||||
                let cellHeight = window.actor.height * s;
 | 
			
		||||
                let [width, height, s] = this._computeWindowSizeAndScale(window, scale);
 | 
			
		||||
                let y = row.y + row.height - height;
 | 
			
		||||
 | 
			
		||||
                s = Math.min(s, WINDOW_CLONE_MAXIMUM_SCALE);
 | 
			
		||||
                let cloneWidth = window.actor.width * s;
 | 
			
		||||
                let x = baseX;
 | 
			
		||||
                if (row.cellWidth) {
 | 
			
		||||
                    x += (row.cellWidth - width) / 2;
 | 
			
		||||
                    width = row.cellWidth;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                let cloneX = x + (cellWidth - cloneWidth) / 2;
 | 
			
		||||
                let cloneY = row.y + row.height - cellHeight;
 | 
			
		||||
 | 
			
		||||
                slots.push([cloneX, cloneY, s, window]);
 | 
			
		||||
                x += cellWidth + this._columnSpacing;
 | 
			
		||||
                slots.push([x, y, s]);
 | 
			
		||||
                baseX += width + this._columnSpacing;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return slots;
 | 
			
		||||
@@ -838,9 +934,7 @@ const UnalignedLayoutStrategy = new Lang.Class({
 | 
			
		||||
        let rows = [];
 | 
			
		||||
        let totalWidth = 0;
 | 
			
		||||
        for (let i = 0; i < windows.length; i++) {
 | 
			
		||||
            let window = windows[i];
 | 
			
		||||
            let s = this._computeWindowScale(window);
 | 
			
		||||
            totalWidth += window.actor.width * s;
 | 
			
		||||
            totalWidth += windows[i].actor.width;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let idealRowWidth = totalWidth / numRows;
 | 
			
		||||
@@ -852,9 +946,7 @@ const UnalignedLayoutStrategy = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
            for (; windowIdx < windows.length; windowIdx++) {
 | 
			
		||||
                let window = windows[windowIdx];
 | 
			
		||||
                let s = this._computeWindowScale(window);
 | 
			
		||||
                let width = window.actor.width * s;
 | 
			
		||||
                let height = window.actor.height * s;
 | 
			
		||||
                let [width, height] = this._computeWindowSizeAndScale(window, 1);
 | 
			
		||||
                row.fullHeight = Math.max(row.fullHeight, height);
 | 
			
		||||
 | 
			
		||||
                // either new width is < idealWidth or new width is nearer from idealWidth then oldWidth
 | 
			
		||||
@@ -883,6 +975,57 @@ const UnalignedLayoutStrategy = new Lang.Class({
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const GridLayoutStrategy = new Lang.Class({
 | 
			
		||||
    Name: 'GridLayoutStrategy',
 | 
			
		||||
    Extends: LayoutStrategy,
 | 
			
		||||
 | 
			
		||||
    _computeRowSizes: function(layout) {
 | 
			
		||||
        let { rows: rows, scale: scale } = layout;
 | 
			
		||||
 | 
			
		||||
        let gridWidth = layout.numColumns * layout.maxWindowWidth;
 | 
			
		||||
        let hspacing = (layout.numColumns - 1) * this._columnSpacing;
 | 
			
		||||
        for (let i = 0; i < rows.length; i++) {
 | 
			
		||||
            let row = rows[i];
 | 
			
		||||
            row.fullWidth = layout.gridWidth;
 | 
			
		||||
            row.fullHeight = layout.maxWindowHeight;
 | 
			
		||||
 | 
			
		||||
            row.width = row.fullWidth * scale + hspacing;
 | 
			
		||||
            row.height = row.fullHeight * scale;
 | 
			
		||||
            row.cellWidth = layout.maxWindowWidth * scale;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    computeLayout: function(windows, layout) {
 | 
			
		||||
        let { numRows: numRows, numColumns: numColumns } = layout;
 | 
			
		||||
        let rows = [];
 | 
			
		||||
        let windowIdx = 0;
 | 
			
		||||
 | 
			
		||||
        let maxWindowWidth = 0;
 | 
			
		||||
        let maxWindowHeight = 0;
 | 
			
		||||
        for (let i = 0; i < numRows; i++) {
 | 
			
		||||
            let row = this._newRow();
 | 
			
		||||
            rows.push(row);
 | 
			
		||||
            for (; windowIdx < windows.length; windowIdx++) {
 | 
			
		||||
                if (row.windows.length >= numColumns)
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                let window = windows[windowIdx];
 | 
			
		||||
                row.windows.push(window);
 | 
			
		||||
 | 
			
		||||
                let [width, height] = this._computeWindowSizeAndScale(window, 1);
 | 
			
		||||
                maxWindowWidth = Math.max(maxWindowWidth, width);
 | 
			
		||||
                maxWindowHeight = Math.max(maxWindowHeight, height);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        layout.rows = rows;
 | 
			
		||||
        layout.maxColumns = numColumns;
 | 
			
		||||
        layout.gridWidth = numColumns * maxWindowWidth;
 | 
			
		||||
        layout.gridHeight = numRows * maxWindowHeight;
 | 
			
		||||
        layout.maxWindowWidth = maxWindowWidth;
 | 
			
		||||
        layout.maxWindowHeight = maxWindowHeight;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @metaWorkspace: a #Meta.Workspace, or null
 | 
			
		||||
@@ -901,13 +1044,11 @@ const Workspace = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this.monitorIndex = monitorIndex;
 | 
			
		||||
        this._monitor = Main.layoutManager.monitors[this.monitorIndex];
 | 
			
		||||
        this._windowOverlaysGroup = new Clutter.Actor();
 | 
			
		||||
        this._windowOverlaysGroup = new Clutter.Group();
 | 
			
		||||
        // Without this the drop area will be overlapped.
 | 
			
		||||
        this._windowOverlaysGroup.set_size(0, 0);
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.Widget({ style_class: 'window-picker' });
 | 
			
		||||
        if (monitorIndex != Main.layoutManager.primaryIndex)
 | 
			
		||||
            this.actor.add_style_class_name('external-monitor');
 | 
			
		||||
        this.actor.set_size(0, 0);
 | 
			
		||||
 | 
			
		||||
        this._dropRect = new Clutter.Rectangle({ opacity: 0 });
 | 
			
		||||
@@ -926,7 +1067,7 @@ const Workspace = new Lang.Class({
 | 
			
		||||
        this._windowOverlays = [];
 | 
			
		||||
        for (let i = 0; i < windows.length; i++) {
 | 
			
		||||
            if (this._isOverviewWindow(windows[i])) {
 | 
			
		||||
                this._addWindowClone(windows[i], true);
 | 
			
		||||
                this._addWindowClone(windows[i]);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -963,7 +1104,7 @@ const Workspace = new Lang.Class({
 | 
			
		||||
            return false;
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        this.positionWindows(WindowPositionFlags.NONE);
 | 
			
		||||
        this.positionWindows(WindowPositionFlags.ANIMATE);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _lookupIndex: function (metaWindow) {
 | 
			
		||||
@@ -1039,35 +1180,23 @@ const Workspace = new Lang.Class({
 | 
			
		||||
        let currentWorkspace = global.screen.get_active_workspace();
 | 
			
		||||
        let isOnCurrentWorkspace = this.metaWorkspace == null || this.metaWorkspace == currentWorkspace;
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < slots.length; i++) {
 | 
			
		||||
        for (let i = 0; i < clones.length; i++) {
 | 
			
		||||
            let slot = slots[i];
 | 
			
		||||
            let [x, y, scale, clone] = slot;
 | 
			
		||||
            let clone = clones[i];
 | 
			
		||||
            let metaWindow = clone.metaWindow;
 | 
			
		||||
            let overlay = clone.overlay;
 | 
			
		||||
            clone.slotId = i;
 | 
			
		||||
            let mainIndex = this._lookupIndex(metaWindow);
 | 
			
		||||
            let overlay = this._windowOverlays[mainIndex];
 | 
			
		||||
 | 
			
		||||
            // Positioning a window currently being dragged must be avoided;
 | 
			
		||||
            // we'll just leave a blank spot in the layout for it.
 | 
			
		||||
            if (clone.inDrag)
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            let cloneWidth = clone.actor.width * scale;
 | 
			
		||||
            let cloneHeight = clone.actor.height * scale;
 | 
			
		||||
            clone.slot = [x, y, cloneWidth, cloneHeight];
 | 
			
		||||
            let [x, y, scale] = slot;
 | 
			
		||||
 | 
			
		||||
            if (overlay && (initialPositioning || !clone.positioned))
 | 
			
		||||
            if (overlay && initialPositioning)
 | 
			
		||||
                overlay.hide();
 | 
			
		||||
 | 
			
		||||
            if (!clone.positioned) {
 | 
			
		||||
                // This window appeared after the overview was already up
 | 
			
		||||
                // Grow the clone from the center of the slot
 | 
			
		||||
                clone.actor.x = x + cloneWidth / 2;
 | 
			
		||||
                clone.actor.y = y + cloneHeight / 2;
 | 
			
		||||
                clone.actor.scale_x = 0;
 | 
			
		||||
                clone.actor.scale_y = 0;
 | 
			
		||||
                clone.positioned = true;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (animate && isOnCurrentWorkspace) {
 | 
			
		||||
                if (!metaWindow.showing_on_its_workspace()) {
 | 
			
		||||
                    /* Hidden windows should fade in and grow
 | 
			
		||||
@@ -1081,6 +1210,7 @@ const Workspace = new Lang.Class({
 | 
			
		||||
                        clone.actor.y = y;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    // Make the window slightly transparent to indicate it's hidden
 | 
			
		||||
                    Tweener.addTween(clone.actor,
 | 
			
		||||
                                     { opacity: 255,
 | 
			
		||||
                                       time: Overview.ANIMATION_TIME,
 | 
			
		||||
@@ -1094,7 +1224,7 @@ const Workspace = new Lang.Class({
 | 
			
		||||
                Tweener.removeTweens(clone.actor);
 | 
			
		||||
                clone.actor.set_position(x, y);
 | 
			
		||||
                clone.actor.set_scale(scale, scale);
 | 
			
		||||
                clone.overlay.relayout(false);
 | 
			
		||||
                this._updateWindowOverlayPositions(clone, overlay, x, y, scale, false);
 | 
			
		||||
                this._showWindowOverlay(clone, overlay, isOnCurrentWorkspace);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -1129,7 +1259,15 @@ const Workspace = new Lang.Class({
 | 
			
		||||
                           })
 | 
			
		||||
                         });
 | 
			
		||||
 | 
			
		||||
        clone.overlay.relayout(true);
 | 
			
		||||
        this._updateWindowOverlayPositions(clone, overlay, x, y, scale, true);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateWindowOverlayPositions: function(clone, overlay, x, y, scale, animate) {
 | 
			
		||||
        if (!overlay)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let [cloneWidth, cloneHeight] = clone.actor.get_size();
 | 
			
		||||
        overlay.updatePositions(x, y, cloneWidth * scale, cloneHeight * scale, animate);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _showWindowOverlay: function(clone, overlay, fade) {
 | 
			
		||||
@@ -1145,6 +1283,9 @@ const Workspace = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _delayedWindowRepositioning: function() {
 | 
			
		||||
        if (this._windowIsZooming)
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        let [x, y, mask] = global.get_pointer();
 | 
			
		||||
 | 
			
		||||
        let pointerHasMoved = (this._cursorX != x && this._cursorY != y);
 | 
			
		||||
@@ -1253,7 +1394,7 @@ const Workspace = new Lang.Class({
 | 
			
		||||
        if (!this._isMyWindow(win) || !this._isOverviewWindow(win))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let [clone, overlay] = this._addWindowClone(win, false);
 | 
			
		||||
        let [clone, overlay] = this._addWindowClone(win);
 | 
			
		||||
 | 
			
		||||
        if (win._overviewHint) {
 | 
			
		||||
            let x = win._overviewHint.x - this.actor.x;
 | 
			
		||||
@@ -1261,11 +1402,16 @@ const Workspace = new Lang.Class({
 | 
			
		||||
            let scale = win._overviewHint.scale;
 | 
			
		||||
            delete win._overviewHint;
 | 
			
		||||
 | 
			
		||||
            clone.slot = [x, y, clone.actor.width * scale, clone.actor.height * scale];
 | 
			
		||||
            clone.positioned = true;
 | 
			
		||||
            clone.actor.set_position (x, y);
 | 
			
		||||
            clone.actor.set_scale (scale, scale);
 | 
			
		||||
            clone.overlay.relayout(false);
 | 
			
		||||
            this._updateWindowOverlayPositions(clone, overlay, x, y, scale, false);
 | 
			
		||||
        } else {
 | 
			
		||||
            // Position new windows at the top corner of the workspace rather
 | 
			
		||||
            // than where they were placed for real to avoid the window
 | 
			
		||||
            // being clipped to the workspaceView. Its not really more
 | 
			
		||||
            // natural for the window to suddenly appear in the overview
 | 
			
		||||
            // on some seemingly random location anyway.
 | 
			
		||||
            clone.actor.set_position (this._x, this._y);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._currentLayout = null;
 | 
			
		||||
@@ -1344,6 +1490,8 @@ const Workspace = new Lang.Class({
 | 
			
		||||
            if (overlay)
 | 
			
		||||
                overlay.hide();
 | 
			
		||||
 | 
			
		||||
            clone.zoomFromOverview();
 | 
			
		||||
 | 
			
		||||
            if (clone.metaWindow.showing_on_its_workspace()) {
 | 
			
		||||
                Tweener.addTween(clone.actor,
 | 
			
		||||
                                 { x: clone.origX,
 | 
			
		||||
@@ -1390,6 +1538,13 @@ const Workspace = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        if (this._positionWindowsId > 0)
 | 
			
		||||
            Meta.later_remove(this._positionWindowsId);
 | 
			
		||||
 | 
			
		||||
        // Usually, the windows will be destroyed automatically with
 | 
			
		||||
        // their parent (this.actor), but we might have a zoomed window
 | 
			
		||||
        // which has been reparented to the stage - _windows[0] holds
 | 
			
		||||
        // the desktop window, which is never reparented
 | 
			
		||||
        for (let w = 0; w < this._windows.length; w++)
 | 
			
		||||
            this._windows[w].destroy();
 | 
			
		||||
        this._windows = [];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -1398,11 +1553,10 @@ const Workspace = new Lang.Class({
 | 
			
		||||
        this.leavingOverview = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Tests if @actor belongs to this workspaces and monitor
 | 
			
		||||
    _isMyWindow : function (actor) {
 | 
			
		||||
        let win = actor.meta_window;
 | 
			
		||||
        return (this.metaWorkspace == null || win.located_on_workspace(this.metaWorkspace)) &&
 | 
			
		||||
            (win.get_monitor() == this.monitorIndex);
 | 
			
		||||
    // Tests if @win belongs to this workspaces and monitor
 | 
			
		||||
    _isMyWindow : function (win) {
 | 
			
		||||
        return (this.metaWorkspace == null || Main.isWindowActorDisplayedOnWorkspace(win, this.metaWorkspace.index())) &&
 | 
			
		||||
            (!win.get_meta_window() || win.get_meta_window().get_monitor() == this.monitorIndex);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Tests if @win should be shown in the Overview
 | 
			
		||||
@@ -1412,11 +1566,9 @@ const Workspace = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Create a clone of a (non-desktop) window and add it to the window list
 | 
			
		||||
    _addWindowClone : function(win, positioned) {
 | 
			
		||||
    _addWindowClone : function(win) {
 | 
			
		||||
        let clone = new WindowClone(win, this);
 | 
			
		||||
        let overlay = new WindowOverlay(clone, this._windowOverlaysGroup);
 | 
			
		||||
        clone.overlay = overlay;
 | 
			
		||||
        clone.positioned = positioned;
 | 
			
		||||
 | 
			
		||||
        clone.connect('selected',
 | 
			
		||||
                      Lang.bind(this, this._onCloneSelected));
 | 
			
		||||
@@ -1434,6 +1586,14 @@ const Workspace = new Lang.Class({
 | 
			
		||||
                          Main.overview.endWindowDrag();
 | 
			
		||||
                          overlay.show();
 | 
			
		||||
                      }));
 | 
			
		||||
        clone.connect('zoom-start',
 | 
			
		||||
                      Lang.bind(this, function() {
 | 
			
		||||
                          this._windowIsZooming = true;
 | 
			
		||||
                      }));
 | 
			
		||||
        clone.connect('zoom-end',
 | 
			
		||||
                      Lang.bind(this, function() {
 | 
			
		||||
                          this._windowIsZooming = false;
 | 
			
		||||
                      }));
 | 
			
		||||
        clone.connect('size-changed',
 | 
			
		||||
                      Lang.bind(this, function() {
 | 
			
		||||
                          this.positionWindows(0);
 | 
			
		||||
@@ -1443,11 +1603,6 @@ const Workspace = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        overlay.connect('show-close-button', Lang.bind(this, this._onShowOverlayClose));
 | 
			
		||||
 | 
			
		||||
        if (this._windows.length == 0)
 | 
			
		||||
            clone.setStackAbove(null);
 | 
			
		||||
        else
 | 
			
		||||
            clone.setStackAbove(this._windows[this._windows.length - 1].actor);
 | 
			
		||||
 | 
			
		||||
        this._windows.push(clone);
 | 
			
		||||
        this._windowOverlays.push(overlay);
 | 
			
		||||
 | 
			
		||||
@@ -1485,7 +1640,7 @@ const Workspace = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _computeLayout: function(windows, area, rowSpacing, columnSpacing) {
 | 
			
		||||
    _computeLayout: function(windows, area, rowSpacing, columnSpacing, bottomPadding) {
 | 
			
		||||
        // We look for the largest scale that allows us to fit the
 | 
			
		||||
        // largest row/tallest column on the workspace.
 | 
			
		||||
 | 
			
		||||
@@ -1500,7 +1655,8 @@ const Workspace = new Lang.Class({
 | 
			
		||||
            if (numColumns == lastLayout.numColumns)
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            let strategy = new UnalignedLayoutStrategy(this._monitor, rowSpacing, columnSpacing);
 | 
			
		||||
            let strategyClass = numRows > 2 ? GridLayoutStrategy : UnalignedLayoutStrategy;
 | 
			
		||||
            let strategy = new strategyClass(this._monitor, rowSpacing, columnSpacing, bottomPadding);
 | 
			
		||||
 | 
			
		||||
            let layout = { area: area, strategy: strategy, numRows: numRows, numColumns: numColumns };
 | 
			
		||||
            strategy.computeLayout(windows, layout);
 | 
			
		||||
@@ -1532,12 +1688,6 @@ const Workspace = new Lang.Class({
 | 
			
		||||
        // Window grid spacing
 | 
			
		||||
        let columnSpacing = node.get_length('-horizontal-spacing');
 | 
			
		||||
        let rowSpacing = node.get_length('-vertical-spacing');
 | 
			
		||||
        let padding = {
 | 
			
		||||
            left: node.get_padding(St.Side.LEFT),
 | 
			
		||||
            top: node.get_padding(St.Side.TOP),
 | 
			
		||||
            bottom: node.get_padding(St.Side.BOTTOM),
 | 
			
		||||
            right: node.get_padding(St.Side.RIGHT),
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        if (!totalWindows)
 | 
			
		||||
            return [];
 | 
			
		||||
@@ -1552,25 +1702,19 @@ const Workspace = new Lang.Class({
 | 
			
		||||
            [leftBorder, rightBorder] = overlay.chromeWidths();
 | 
			
		||||
        } else {
 | 
			
		||||
            [closeButtonHeight, captionHeight] = [0, 0];
 | 
			
		||||
            [leftBorder, rightBorder] = [0, 0];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        rowSpacing += captionHeight;
 | 
			
		||||
        columnSpacing += (rightBorder + leftBorder) / 2;
 | 
			
		||||
        padding.top += closeButtonHeight;
 | 
			
		||||
        padding.bottom += captionHeight;
 | 
			
		||||
        padding.left += leftBorder;
 | 
			
		||||
        padding.right += rightBorder;
 | 
			
		||||
        columnSpacing += rightBorder;
 | 
			
		||||
 | 
			
		||||
        let area = {
 | 
			
		||||
            x: this._x + padding.left,
 | 
			
		||||
            y: this._y + padding.top,
 | 
			
		||||
            width: this._width - padding.left - padding.right,
 | 
			
		||||
            height: this._height - padding.top - padding.bottom,
 | 
			
		||||
        };
 | 
			
		||||
        let area = { x: this._x, y: this._y, width: this._width, height: this._height };
 | 
			
		||||
        area.y += closeButtonHeight;
 | 
			
		||||
        area.height -= closeButtonHeight;
 | 
			
		||||
        area.x += leftBorder;
 | 
			
		||||
        area.width -= leftBorder;
 | 
			
		||||
 | 
			
		||||
        if (!this._currentLayout)
 | 
			
		||||
            this._currentLayout = this._computeLayout(windows, area, rowSpacing, columnSpacing);
 | 
			
		||||
            this._currentLayout = this._computeLayout(windows, area, rowSpacing, columnSpacing, captionHeight);
 | 
			
		||||
 | 
			
		||||
        let layout = this._currentLayout;
 | 
			
		||||
        let strategy = layout.strategy;
 | 
			
		||||
 
 | 
			
		||||
@@ -55,9 +55,10 @@ const WorkspaceSwitcherPopup = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _getPreferredHeight : function (actor, forWidth, alloc) {
 | 
			
		||||
        let children = this._list.get_children();
 | 
			
		||||
        let workArea = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex);
 | 
			
		||||
        let primary = Main.layoutManager.primaryMonitor;
 | 
			
		||||
 | 
			
		||||
        let availHeight = workArea.height;
 | 
			
		||||
        let availHeight = primary.height;
 | 
			
		||||
        availHeight -= Main.panel.actor.height;
 | 
			
		||||
        availHeight -= this.actor.get_theme_node().get_vertical_padding();
 | 
			
		||||
        availHeight -= this._container.get_theme_node().get_vertical_padding();
 | 
			
		||||
        availHeight -= this._list.get_theme_node().get_vertical_padding();
 | 
			
		||||
@@ -66,7 +67,7 @@ const WorkspaceSwitcherPopup = new Lang.Class({
 | 
			
		||||
        for (let i = 0; i < children.length; i++) {
 | 
			
		||||
            let [childMinHeight, childNaturalHeight] = children[i].get_preferred_height(-1);
 | 
			
		||||
            let [childMinWidth, childNaturalWidth] = children[i].get_preferred_width(childNaturalHeight);
 | 
			
		||||
            height += childNaturalHeight * workArea.width / workArea.height;
 | 
			
		||||
            height += childNaturalHeight * primary.width / primary.height;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let spacing = this._itemSpacing * (global.screen.n_workspaces - 1);
 | 
			
		||||
@@ -80,8 +81,8 @@ const WorkspaceSwitcherPopup = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getPreferredWidth : function (actor, forHeight, alloc) {
 | 
			
		||||
        let workArea = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex);
 | 
			
		||||
        this._childWidth = Math.round(this._childHeight * workArea.width / workArea.height);
 | 
			
		||||
        let primary = Main.layoutManager.primaryMonitor;
 | 
			
		||||
        this._childWidth = Math.round(this._childHeight * primary.width / primary.height);
 | 
			
		||||
 | 
			
		||||
        alloc.min_size = this._childWidth;
 | 
			
		||||
        alloc.natural_size = this._childWidth;
 | 
			
		||||
@@ -121,11 +122,12 @@ const WorkspaceSwitcherPopup = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let workArea = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex);
 | 
			
		||||
        let primary = Main.layoutManager.primaryMonitor;
 | 
			
		||||
        let [containerMinHeight, containerNatHeight] = this._container.get_preferred_height(global.screen_width);
 | 
			
		||||
        let [containerMinWidth, containerNatWidth] = this._container.get_preferred_width(containerNatHeight);
 | 
			
		||||
        this._container.x = workArea.x + Math.floor((workArea.width - containerNatWidth) / 2);
 | 
			
		||||
        this._container.y = workArea.y + Math.floor((workArea.height - containerNatHeight) / 2);
 | 
			
		||||
        this._container.x = primary.x + Math.floor((primary.width - containerNatWidth) / 2);
 | 
			
		||||
        this._container.y = primary.y + Main.panel.actor.height +
 | 
			
		||||
                            Math.floor(((primary.height - Main.panel.actor.height) - containerNatHeight) / 2);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _show : function() {
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,6 @@ const Shell = imports.gi.Shell;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
const Background = imports.ui.background;
 | 
			
		||||
const DND = imports.ui.dnd;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const Tweener = imports.ui.tweener;
 | 
			
		||||
@@ -62,35 +61,12 @@ const WindowClone = new Lang.Class({
 | 
			
		||||
        this.inDrag = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Find the actor just below us, respecting reparenting done
 | 
			
		||||
    // by DND code
 | 
			
		||||
    getActualStackAbove: function() {
 | 
			
		||||
        if (this._stackAbove == null)
 | 
			
		||||
            return null;
 | 
			
		||||
 | 
			
		||||
        if (this.inDrag) {
 | 
			
		||||
            if (this._stackAbove._delegate)
 | 
			
		||||
                return this._stackAbove._delegate.getActualStackAbove();
 | 
			
		||||
            else
 | 
			
		||||
                return null;
 | 
			
		||||
        } else {
 | 
			
		||||
            return this._stackAbove;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setStackAbove: function (actor) {
 | 
			
		||||
        this._stackAbove = actor;
 | 
			
		||||
 | 
			
		||||
        // Don't apply the new stacking now, it will be applied
 | 
			
		||||
        // when dragging ends and window are stacked again
 | 
			
		||||
        if (actor.inDrag)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let actualAbove = this.getActualStackAbove();
 | 
			
		||||
        if (actualAbove == null)
 | 
			
		||||
        if (this._stackAbove == null)
 | 
			
		||||
            this.actor.lower_bottom();
 | 
			
		||||
        else
 | 
			
		||||
            this.actor.raise(actualAbove);
 | 
			
		||||
            this.actor.raise(this._stackAbove);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroy: function () {
 | 
			
		||||
@@ -189,20 +165,18 @@ const WorkspaceThumbnail = new Lang.Class({
 | 
			
		||||
                                     style_class: 'workspace-thumbnail' });
 | 
			
		||||
        this.actor._delegate = this;
 | 
			
		||||
 | 
			
		||||
        this._contents = new Clutter.Actor();
 | 
			
		||||
        this.actor.add_child(this._contents);
 | 
			
		||||
        this._contents = new Clutter.Group();
 | 
			
		||||
        this.actor.add_actor(this._contents);
 | 
			
		||||
 | 
			
		||||
        this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
 | 
			
		||||
 | 
			
		||||
        this._createBackground();
 | 
			
		||||
        this._background = Meta.BackgroundActor.new_for_screen(global.screen);
 | 
			
		||||
        this._contents.add_actor(this._background);
 | 
			
		||||
 | 
			
		||||
        let monitor = Main.layoutManager.primaryMonitor;
 | 
			
		||||
        this.setPorthole(monitor.x, monitor.y, monitor.width, monitor.height);
 | 
			
		||||
 | 
			
		||||
        let windows = global.get_window_actors().filter(Lang.bind(this, function(actor) {
 | 
			
		||||
            let win = actor.meta_window;
 | 
			
		||||
            return win.located_on_workspace(metaWorkspace);
 | 
			
		||||
        }));
 | 
			
		||||
        let windows = global.get_window_actors().filter(this._isWorkspaceWindow, this);
 | 
			
		||||
 | 
			
		||||
        // Create clones for windows that should be visible in the Overview
 | 
			
		||||
        this._windows = [];
 | 
			
		||||
@@ -236,12 +210,6 @@ const WorkspaceThumbnail = new Lang.Class({
 | 
			
		||||
        this._collapseFraction = 0; // Not collapsed
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createBackground: function() {
 | 
			
		||||
        this._bgManager = new Background.BackgroundManager({ monitorIndex: Main.layoutManager.primaryIndex,
 | 
			
		||||
                                                             container: this._contents,
 | 
			
		||||
                                                             effects: Meta.BackgroundEffects.NONE });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setPorthole: function(x, y, width, height) {
 | 
			
		||||
        this._portholeX = x;
 | 
			
		||||
        this._portholeY = y;
 | 
			
		||||
@@ -265,7 +233,7 @@ const WorkspaceThumbnail = new Lang.Class({
 | 
			
		||||
            let clone = this._windows[i];
 | 
			
		||||
            let metaWindow = clone.metaWindow;
 | 
			
		||||
            if (i == 0) {
 | 
			
		||||
                clone.setStackAbove(this._bgManager.background.actor);
 | 
			
		||||
                clone.setStackAbove(this._background);
 | 
			
		||||
            } else {
 | 
			
		||||
                let previousClone = this._windows[i - 1];
 | 
			
		||||
                clone.setStackAbove(previousClone.actor);
 | 
			
		||||
@@ -384,8 +352,7 @@ const WorkspaceThumbnail = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroy : function() {
 | 
			
		||||
        if (this.actor)
 | 
			
		||||
          this.actor.destroy();
 | 
			
		||||
        this.actor.destroy();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    workspaceRemoved : function() {
 | 
			
		||||
@@ -406,20 +373,19 @@ const WorkspaceThumbnail = new Lang.Class({
 | 
			
		||||
    _onDestroy: function(actor) {
 | 
			
		||||
        this.workspaceRemoved();
 | 
			
		||||
 | 
			
		||||
        if (this._bgManager) {
 | 
			
		||||
          this._bgManager.destroy();
 | 
			
		||||
          this._bgManager = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._windows = [];
 | 
			
		||||
        this.actor = null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Tests if @actor belongs to this workspace and monitor
 | 
			
		||||
    _isMyWindow : function (actor) {
 | 
			
		||||
        let win = actor.meta_window;
 | 
			
		||||
        return win.located_on_workspace(this.metaWorkspace) &&
 | 
			
		||||
            (win.get_monitor() == this.monitorIndex);
 | 
			
		||||
    // Tests if @win belongs to this workspace
 | 
			
		||||
    _isWorkspaceWindow : function (win) {
 | 
			
		||||
        return Main.isWindowActorDisplayedOnWorkspace(win, this.metaWorkspace.index());
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Tests if @win belongs to this workspace and monitor
 | 
			
		||||
    _isMyWindow : function (win) {
 | 
			
		||||
        return this._isWorkspaceWindow(win) &&
 | 
			
		||||
            (!win.get_meta_window() || win.get_meta_window().get_monitor() == this.monitorIndex);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Tests if @win should be shown in the Overview
 | 
			
		||||
@@ -452,7 +418,7 @@ const WorkspaceThumbnail = new Lang.Class({
 | 
			
		||||
        this._contents.add_actor(clone.actor);
 | 
			
		||||
 | 
			
		||||
        if (this._windows.length == 0)
 | 
			
		||||
            clone.setStackAbove(this._bgManager.background.actor);
 | 
			
		||||
            clone.setStackAbove(this._background);
 | 
			
		||||
        else
 | 
			
		||||
            clone.setStackAbove(this._windows[this._windows.length - 1].actor);
 | 
			
		||||
 | 
			
		||||
@@ -530,6 +496,8 @@ const ThumbnailsBox = new Lang.Class({
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.actor = new Shell.GenericContainer({ reactive: true,
 | 
			
		||||
                                                  style_class: 'workspace-thumbnails',
 | 
			
		||||
                                                  can_focus: true,
 | 
			
		||||
                                                  track_hover: true,
 | 
			
		||||
                                                  request_mode: Clutter.RequestMode.WIDTH_FOR_HEIGHT });
 | 
			
		||||
        this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
 | 
			
		||||
        this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
 | 
			
		||||
@@ -558,11 +526,11 @@ const ThumbnailsBox = new Lang.Class({
 | 
			
		||||
        this._indicator = indicator;
 | 
			
		||||
        this.actor.add_actor(indicator);
 | 
			
		||||
 | 
			
		||||
        this._inDrag = false;
 | 
			
		||||
        this._dropWorkspace = -1;
 | 
			
		||||
        this._dropPlaceholderPos = -1;
 | 
			
		||||
        this._dropPlaceholder = new St.Bin({ style_class: 'placeholder' });
 | 
			
		||||
        this.actor.add_actor(this._dropPlaceholder);
 | 
			
		||||
        this._spliceIndex = -1;
 | 
			
		||||
 | 
			
		||||
        this._targetScale = 0;
 | 
			
		||||
        this._scale = 0;
 | 
			
		||||
@@ -579,6 +547,13 @@ const ThumbnailsBox = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this.actor.connect('button-press-event', function() { return true; });
 | 
			
		||||
        this.actor.connect('button-release-event', Lang.bind(this, this._onButtonRelease));
 | 
			
		||||
        this.actor.connect('scroll-event',
 | 
			
		||||
                           Lang.bind(this, this._onScrollEvent));
 | 
			
		||||
        this.actor.connect('key-release-event',
 | 
			
		||||
                           Lang.bind(this, this._onKeyRelease));
 | 
			
		||||
 | 
			
		||||
        Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._updateTranslation));
 | 
			
		||||
        this.actor.connect('notify::hover', Lang.bind(this, this._updateTranslation));
 | 
			
		||||
 | 
			
		||||
        Main.overview.connect('showing',
 | 
			
		||||
                              Lang.bind(this, this._createThumbnails));
 | 
			
		||||
@@ -603,6 +578,40 @@ const ThumbnailsBox = new Lang.Class({
 | 
			
		||||
            Lang.bind(this, this._updateSwitcherVisibility));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getInitialTranslation: 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 || 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;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (alwaysZoomOut)
 | 
			
		||||
            return 0;
 | 
			
		||||
 | 
			
		||||
        let visibleWidth = this.actor.get_theme_node().get_length('visible-width');
 | 
			
		||||
        let rtl = (this.actor.get_text_direction() == Clutter.TextDirection.RTL);
 | 
			
		||||
        return rtl ? (visibleWidth - this.actor.width) : (this.actor.width - visibleWidth);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateTranslation: function() {
 | 
			
		||||
        Tweener.addTween(this, { slideX: this._getInitialTranslation(),
 | 
			
		||||
                                 time: SLIDE_ANIMATION_TIME,
 | 
			
		||||
                                 transition: 'easeOutQuad' });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateSwitcherVisibility: function() {
 | 
			
		||||
        this.actor.visible =
 | 
			
		||||
            this._settings.get_boolean('dynamic-workspaces') ||
 | 
			
		||||
@@ -617,7 +626,7 @@ const ThumbnailsBox = new Lang.Class({
 | 
			
		||||
            let thumbnail = this._thumbnails[i]
 | 
			
		||||
            let [w, h] = thumbnail.actor.get_transformed_size();
 | 
			
		||||
            if (y >= thumbnail.actor.y && y <= thumbnail.actor.y + h) {
 | 
			
		||||
                thumbnail.activate(event.get_time());
 | 
			
		||||
                thumbnail.activate(event.time);
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -626,6 +635,9 @@ const ThumbnailsBox = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDragBegin: function() {
 | 
			
		||||
        this._inDrag = true;
 | 
			
		||||
        this._updateTranslation();
 | 
			
		||||
 | 
			
		||||
        this._dragCancelled = false;
 | 
			
		||||
        this._dragMonitor = {
 | 
			
		||||
            dragMotion: Lang.bind(this, this._onDragMotion)
 | 
			
		||||
@@ -648,6 +660,8 @@ const ThumbnailsBox = new Lang.Class({
 | 
			
		||||
    _endDrag: function() {
 | 
			
		||||
        this._clearDragPlaceholder();
 | 
			
		||||
        DND.removeDragMonitor(this._dragMonitor);
 | 
			
		||||
        this._inDrag = false;
 | 
			
		||||
        this._updateTranslation();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDragMotion: function(dragEvent) {
 | 
			
		||||
@@ -745,8 +759,6 @@ const ThumbnailsBox = new Lang.Class({
 | 
			
		||||
                    return win.get_workspace() >= newWorkspaceIndex;
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            this._spliceIndex = newWorkspaceIndex;
 | 
			
		||||
 | 
			
		||||
            // ... move them down one.
 | 
			
		||||
            windows.forEach(function(win) {
 | 
			
		||||
                win.meta_window.change_workspace_by_index(win.get_workspace() + 1,
 | 
			
		||||
@@ -768,14 +780,6 @@ const ThumbnailsBox = new Lang.Class({
 | 
			
		||||
                                        WORKSPACE_KEEP_ALIVE_TIME);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Start the animation on the workspace (which is actually
 | 
			
		||||
            // an old one which just became empty)
 | 
			
		||||
            let thumbnail = this._thumbnails[newWorkspaceIndex];
 | 
			
		||||
            this._setThumbnailState(thumbnail, ThumbnailState.NEW);
 | 
			
		||||
            thumbnail.slidePosition = 1;
 | 
			
		||||
 | 
			
		||||
            this._queueUpdateStates();
 | 
			
		||||
 | 
			
		||||
            return true;
 | 
			
		||||
        } else {
 | 
			
		||||
            return false;
 | 
			
		||||
@@ -790,7 +794,7 @@ const ThumbnailsBox = new Lang.Class({
 | 
			
		||||
            global.screen.connect('notify::n-workspaces',
 | 
			
		||||
                                  Lang.bind(this, this._workspacesChanged));
 | 
			
		||||
        this._syncStackingId =
 | 
			
		||||
            Main.overview.connect('windows-restacked',
 | 
			
		||||
            Main.overview.connect('sync-window-stacking',
 | 
			
		||||
                                  Lang.bind(this, this._syncStacking));
 | 
			
		||||
 | 
			
		||||
        this._targetScale = 0;
 | 
			
		||||
@@ -803,10 +807,22 @@ const ThumbnailsBox = new Lang.Class({
 | 
			
		||||
            this._stateCounts[ThumbnailState[key]] = 0;
 | 
			
		||||
 | 
			
		||||
        // The "porthole" is the portion of the screen that we show in the workspaces
 | 
			
		||||
        this._porthole = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex);
 | 
			
		||||
        let panelHeight = Main.panel.actor.height;
 | 
			
		||||
        let monitor = Main.layoutManager.primaryMonitor;
 | 
			
		||||
        this._porthole = {
 | 
			
		||||
            x: monitor.x,
 | 
			
		||||
            y: monitor.y + panelHeight,
 | 
			
		||||
            width: monitor.width,
 | 
			
		||||
            height: monitor.height - panelHeight
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        this.addThumbnails(0, global.screen.n_workspaces);
 | 
			
		||||
 | 
			
		||||
        // reset any translation and make sure the actor is visible when
 | 
			
		||||
        // entering the overview
 | 
			
		||||
        this.slideX = this._getInitialTranslation();
 | 
			
		||||
        this.actor.show();
 | 
			
		||||
 | 
			
		||||
        this._updateSwitcherVisibility();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -819,7 +835,6 @@ const ThumbnailsBox = new Lang.Class({
 | 
			
		||||
            global.screen.disconnect(this._nWorkspacesNotifyId);
 | 
			
		||||
            this._nWorkspacesNotifyId = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this._syncStackingId > 0) {
 | 
			
		||||
            Main.overview.disconnect(this._syncStackingId);
 | 
			
		||||
            this._syncStackingId = 0;
 | 
			
		||||
@@ -830,6 +845,49 @@ const ThumbnailsBox = new Lang.Class({
 | 
			
		||||
        this._thumbnails = [];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _computeTranslation: function() {
 | 
			
		||||
        let rtl = (this.actor.get_text_direction() == Clutter.TextDirection.RTL);
 | 
			
		||||
 | 
			
		||||
        if (rtl)
 | 
			
		||||
            return - this.actor.width;
 | 
			
		||||
        else
 | 
			
		||||
            return this.actor.width;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get slideX() {
 | 
			
		||||
        return this._slideX;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    set slideX(value) {
 | 
			
		||||
        this._slideX = value;
 | 
			
		||||
        this.actor.translation_x = this._slideX;
 | 
			
		||||
 | 
			
		||||
        if (this._slideX > 0) {
 | 
			
		||||
            let rect = new Clutter.Rect();
 | 
			
		||||
            rect.size.width = this._background.width - this._slideX;
 | 
			
		||||
            rect.size.height = this._background.height;
 | 
			
		||||
            this.actor.clip_rect = rect;
 | 
			
		||||
        } else {
 | 
			
		||||
            this.actor.clip_rect = null;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    show: function() {
 | 
			
		||||
        this.actor.show();
 | 
			
		||||
        this._updateTranslation();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    hide: function() {
 | 
			
		||||
        let hiddenX = this._computeTranslation();
 | 
			
		||||
        Tweener.addTween(this, { slideX: hiddenX,
 | 
			
		||||
                                 transition: 'easeOutQuad',
 | 
			
		||||
                                 time: SLIDE_ANIMATION_TIME,
 | 
			
		||||
                                 onComplete: Lang.bind(this, function () {
 | 
			
		||||
                                     this.actor.hide();
 | 
			
		||||
                                 })
 | 
			
		||||
                               });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _workspacesChanged: function() {
 | 
			
		||||
        let oldNumWorkspaces = this._thumbnails.length;
 | 
			
		||||
        let newNumWorkspaces = global.screen.n_workspaces;
 | 
			
		||||
@@ -863,8 +921,7 @@ const ThumbnailsBox = new Lang.Class({
 | 
			
		||||
            this._thumbnails.push(thumbnail);
 | 
			
		||||
            this.actor.add_actor(thumbnail.actor);
 | 
			
		||||
 | 
			
		||||
            if (start > 0 && this._spliceIndex == -1) {
 | 
			
		||||
                // not the initial fill, and not splicing via DND
 | 
			
		||||
            if (start > 0) { // not the initial fill
 | 
			
		||||
                thumbnail.state = ThumbnailState.NEW;
 | 
			
		||||
                thumbnail.slidePosition = 1; // start slid out
 | 
			
		||||
                this._haveNewThumbnails = true;
 | 
			
		||||
@@ -879,9 +936,6 @@ const ThumbnailsBox = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        // The thumbnails indicator actually needs to be on top of the thumbnails
 | 
			
		||||
        this._indicator.raise_top();
 | 
			
		||||
 | 
			
		||||
        // Clear the splice index, we got the message
 | 
			
		||||
        this._spliceIndex = -1;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    removeThumbnails: function(start, count) {
 | 
			
		||||
@@ -903,7 +957,7 @@ const ThumbnailsBox = new Lang.Class({
 | 
			
		||||
        this._queueUpdateStates();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _syncStacking: function(overview, stackIndices) {
 | 
			
		||||
    _syncStacking: function(actor, stackIndices) {
 | 
			
		||||
        for (let i = 0; i < this._thumbnails.length; i++)
 | 
			
		||||
            this._thumbnails[i].syncStacking(stackIndices);
 | 
			
		||||
    },
 | 
			
		||||
@@ -1041,6 +1095,8 @@ const ThumbnailsBox = new Lang.Class({
 | 
			
		||||
        // See comment about this._background in _init()
 | 
			
		||||
        let themeNode = this._background.get_theme_node();
 | 
			
		||||
 | 
			
		||||
        forWidth = themeNode.adjust_for_width(forWidth);
 | 
			
		||||
 | 
			
		||||
        // Note that for getPreferredWidth/Height we cheat a bit and skip propagating
 | 
			
		||||
        // the size request to our children because we know how big they are and know
 | 
			
		||||
        // that the actors aren't depending on the virtual functions being called.
 | 
			
		||||
@@ -1256,5 +1312,30 @@ const ThumbnailsBox = new Lang.Class({
 | 
			
		||||
                           },
 | 
			
		||||
                           onCompleteScope: this
 | 
			
		||||
                         });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onScrollEvent: function (actor, event) {
 | 
			
		||||
        switch (event.get_scroll_direction()) {
 | 
			
		||||
        case Clutter.ScrollDirection.UP:
 | 
			
		||||
            Main.wm.actionMoveWorkspace(Meta.MotionDirection.UP);
 | 
			
		||||
            break;
 | 
			
		||||
        case Clutter.ScrollDirection.DOWN:
 | 
			
		||||
            Main.wm.actionMoveWorkspace(Meta.MotionDirection.DOWN);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onKeyRelease: function (actor, event) {
 | 
			
		||||
        switch (event.get_key_symbol()) {
 | 
			
		||||
        case Clutter.KEY_Up:
 | 
			
		||||
            Main.wm.actionMoveWorkspace(Meta.MotionDirection.UP);
 | 
			
		||||
            break;
 | 
			
		||||
        case Clutter.KEY_Down:
 | 
			
		||||
            Main.wm.actionMoveWorkspace(Meta.MotionDirection.DOWN);
 | 
			
		||||
            break;
 | 
			
		||||
        case Clutter.KEY_Return:
 | 
			
		||||
            Main.overview.toggle();
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -28,8 +28,7 @@ const WorkspacesView = new Lang.Class({
 | 
			
		||||
    Name: 'WorkspacesView',
 | 
			
		||||
 | 
			
		||||
    _init: function(workspaces) {
 | 
			
		||||
        this.actor = new St.Widget({ style_class: 'workspaces-view',
 | 
			
		||||
                                     reactive: true });
 | 
			
		||||
        this.actor = new St.Widget({ style_class: 'workspaces-view' });
 | 
			
		||||
 | 
			
		||||
        // The actor itself isn't a drop target, so we don't want to pick on its area
 | 
			
		||||
        this.actor.set_size(0, 0);
 | 
			
		||||
@@ -47,6 +46,10 @@ const WorkspacesView = new Lang.Class({
 | 
			
		||||
        this._height = 0;
 | 
			
		||||
        this._x = 0;
 | 
			
		||||
        this._y = 0;
 | 
			
		||||
        this._clipX = 0;
 | 
			
		||||
        this._clipY = 0;
 | 
			
		||||
        this._clipWidth = 0;
 | 
			
		||||
        this._clipHeight = 0;
 | 
			
		||||
        this._spacing = 0;
 | 
			
		||||
        this._animating = false; // tweening
 | 
			
		||||
        this._scrolling = false; // swipe-scrolling
 | 
			
		||||
@@ -64,7 +67,7 @@ const WorkspacesView = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        // Add workspace actors
 | 
			
		||||
        for (let w = 0; w < global.screen.n_workspaces; w++)
 | 
			
		||||
            this.actor.add_actor(this._workspaces[w].actor);
 | 
			
		||||
            this._workspaces[w].actor.reparent(this.actor);
 | 
			
		||||
        this._workspaces[activeWorkspaceIndex].actor.raise_top();
 | 
			
		||||
 | 
			
		||||
        this._extraWorkspaces = [];
 | 
			
		||||
@@ -85,8 +88,8 @@ const WorkspacesView = new Lang.Class({
 | 
			
		||||
        this._overviewShownId =
 | 
			
		||||
            Main.overview.connect('shown',
 | 
			
		||||
                                 Lang.bind(this, function() {
 | 
			
		||||
                this.actor.set_clip(this._x, this._y,
 | 
			
		||||
                                    this._width, this._height);
 | 
			
		||||
                this.actor.set_clip(this._clipX, this._clipY,
 | 
			
		||||
                                    this._clipWidth, this._clipHeight);
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        this.scrollAdjustment = new St.Adjustment({ value: activeWorkspaceIndex,
 | 
			
		||||
@@ -124,10 +127,11 @@ const WorkspacesView = new Lang.Class({
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            let ws = new Workspace.Workspace(null, i);
 | 
			
		||||
            ws.setGeometry(monitors[i].x,
 | 
			
		||||
                           monitors[i].y,
 | 
			
		||||
                           monitors[i].width,
 | 
			
		||||
                           monitors[i].height);
 | 
			
		||||
            let overviewSpacing = Main.overview._spacing;
 | 
			
		||||
            ws.setGeometry(monitors[i].x + overviewSpacing/2,
 | 
			
		||||
                           monitors[i].y + overviewSpacing/2,
 | 
			
		||||
                           monitors[i].width - overviewSpacing,
 | 
			
		||||
                           monitors[i].height - overviewSpacing);
 | 
			
		||||
            global.overlay_group.add_actor(ws.actor);
 | 
			
		||||
            this._extraWorkspaces.push(ws);
 | 
			
		||||
        }
 | 
			
		||||
@@ -153,6 +157,13 @@ const WorkspacesView = new Lang.Class({
 | 
			
		||||
            this._workspaces[i].setGeometry(x, y, width, height);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setClipRect: function(x, y, width, height) {
 | 
			
		||||
        this._clipX = x;
 | 
			
		||||
        this._clipY = y;
 | 
			
		||||
        this._clipWidth = width;
 | 
			
		||||
        this._clipHeight = height;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _lookupWorkspaceForMetaWindow: function (metaWindow) {
 | 
			
		||||
        for (let i = 0; i < this._workspaces.length; i++) {
 | 
			
		||||
            if (this._workspaces[i].containsMetaWindow(metaWindow))
 | 
			
		||||
@@ -191,6 +202,11 @@ const WorkspacesView = new Lang.Class({
 | 
			
		||||
            this._extraWorkspaces[i].syncStacking(stackIndices);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    updateWindowPositions: function() {
 | 
			
		||||
        for (let w = 0; w < this._workspaces.length; w++)
 | 
			
		||||
            this._workspaces[w].positionWindows(Workspace.WindowPositionFlags.ANIMATE);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _scrollToActive: function() {
 | 
			
		||||
        let active = global.screen.get_active_workspace_index();
 | 
			
		||||
 | 
			
		||||
@@ -386,15 +402,6 @@ const WorkspacesView = new Lang.Class({
 | 
			
		||||
        let current = Math.round(adj.value);
 | 
			
		||||
 | 
			
		||||
        if (active != current) {
 | 
			
		||||
            if (!this._workspaces[current]) {
 | 
			
		||||
                // The current workspace was destroyed. This could happen
 | 
			
		||||
                // when you are on the last empty workspace, and consolidate
 | 
			
		||||
                // windows using the thumbnail bar.
 | 
			
		||||
                // In that case, the intended behavior is to stay on the empty
 | 
			
		||||
                // workspace, which is the last one, so pick it.
 | 
			
		||||
                current = this._workspaces.length - 1;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let metaWorkspace = this._workspaces[current].metaWorkspace;
 | 
			
		||||
            metaWorkspace.activate(global.get_current_time());
 | 
			
		||||
        }
 | 
			
		||||
@@ -429,9 +436,10 @@ const WorkspacesDisplay = new Lang.Class({
 | 
			
		||||
    Name: 'WorkspacesDisplay',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.actor = new St.Widget({ clip_to_allocation: true });
 | 
			
		||||
        this.actor.connect('notify::allocation', Lang.bind(this, this._updateWorkspacesGeometry));
 | 
			
		||||
        this.actor = new Shell.GenericContainer();
 | 
			
		||||
        this.actor.connect('allocate', Lang.bind(this, this._updateWorkspacesGeometry));
 | 
			
		||||
        this.actor.connect('parent-set', Lang.bind(this, this._parentSet));
 | 
			
		||||
        this.actor.set_clip_to_allocation(true);
 | 
			
		||||
 | 
			
		||||
        let clickAction = new Clutter.ClickAction()
 | 
			
		||||
        clickAction.connect('clicked', Lang.bind(this, function(action) {
 | 
			
		||||
@@ -452,11 +460,6 @@ const WorkspacesDisplay = new Lang.Class({
 | 
			
		||||
                this._workspacesViews[i].startSwipeScroll();
 | 
			
		||||
            return true;
 | 
			
		||||
        }));
 | 
			
		||||
        panAction.connect('gesture-cancel', Lang.bind(this, function() {
 | 
			
		||||
            clickAction.release();
 | 
			
		||||
            for (let i = 0; i < this._workspacesViews.length; i++)
 | 
			
		||||
                this._workspacesViews[i].endSwipeScroll();
 | 
			
		||||
        }));
 | 
			
		||||
        panAction.connect('gesture-end', Lang.bind(this, function() {
 | 
			
		||||
            clickAction.release();
 | 
			
		||||
            for (let i = 0; i < this._workspacesViews.length; i++)
 | 
			
		||||
@@ -467,8 +470,7 @@ const WorkspacesDisplay = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._primaryIndex = Main.layoutManager.primaryIndex;
 | 
			
		||||
 | 
			
		||||
        this._workspacesViews = [];
 | 
			
		||||
        this._workspaces = [];
 | 
			
		||||
        this._workspacesViews = null;
 | 
			
		||||
        this._primaryScrollAdjustment = null;
 | 
			
		||||
 | 
			
		||||
        this._settings = new Gio.Settings({ schema: OVERRIDE_SCHEMA });
 | 
			
		||||
@@ -477,13 +479,23 @@ const WorkspacesDisplay = new Lang.Class({
 | 
			
		||||
                                         this._workspacesOnlyOnPrimaryChanged));
 | 
			
		||||
        this._workspacesOnlyOnPrimaryChanged();
 | 
			
		||||
 | 
			
		||||
        this._inDrag = false;
 | 
			
		||||
        this._cancelledDrag = false;
 | 
			
		||||
 | 
			
		||||
        global.screen.connect('notify::n-workspaces',
 | 
			
		||||
                              Lang.bind(this, this._workspacesChanged));
 | 
			
		||||
 | 
			
		||||
        this._switchWorkspaceNotifyId = 0;
 | 
			
		||||
 | 
			
		||||
        this._itemDragBeginId = 0;
 | 
			
		||||
        this._itemDragCancelledId = 0;
 | 
			
		||||
        this._itemDragEndId = 0;
 | 
			
		||||
        this._windowDragBeginId = 0;
 | 
			
		||||
        this._windowDragCancelledId = 0;
 | 
			
		||||
        this._windowDragEndId = 0;
 | 
			
		||||
        this._notifyOpacityId = 0;
 | 
			
		||||
        this._scrollEventId = 0;
 | 
			
		||||
        this._swipeScrollBeginId = 0;
 | 
			
		||||
        this._swipeScrollEndId = 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onPan: function(action) {
 | 
			
		||||
@@ -497,10 +509,27 @@ const WorkspacesDisplay = new Lang.Class({
 | 
			
		||||
        this._updateWorkspacesViews();
 | 
			
		||||
 | 
			
		||||
        this._restackedNotifyId =
 | 
			
		||||
            Main.overview.connect('windows-restacked',
 | 
			
		||||
            Main.overview.connect('sync-window-stacking',
 | 
			
		||||
                                  Lang.bind(this, this._onRestacked));
 | 
			
		||||
        if (this._scrollEventId == 0)
 | 
			
		||||
            this._scrollEventId = Main.overview.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
 | 
			
		||||
 | 
			
		||||
        if (this._itemDragBeginId == 0)
 | 
			
		||||
            this._itemDragBeginId = Main.overview.connect('item-drag-begin',
 | 
			
		||||
                                                          Lang.bind(this, this._dragBegin));
 | 
			
		||||
        if (this._itemDragCancelledId == 0)
 | 
			
		||||
            this._itemDragCancelledId = Main.overview.connect('item-drag-cancelled',
 | 
			
		||||
                                                              Lang.bind(this, this._dragCancelled));
 | 
			
		||||
        if (this._itemDragEndId == 0)
 | 
			
		||||
            this._itemDragEndId = Main.overview.connect('item-drag-end',
 | 
			
		||||
                                                        Lang.bind(this, this._dragEnd));
 | 
			
		||||
        if (this._windowDragBeginId == 0)
 | 
			
		||||
            this._windowDragBeginId = Main.overview.connect('window-drag-begin',
 | 
			
		||||
                                                            Lang.bind(this, this._dragBegin));
 | 
			
		||||
        if (this._windowDragCancelledId == 0)
 | 
			
		||||
            this._windowDragCancelledId = Main.overview.connect('window-drag-cancelled',
 | 
			
		||||
                                                            Lang.bind(this, this._dragCancelled));
 | 
			
		||||
        if (this._windowDragEndId == 0)
 | 
			
		||||
            this._windowDragEndId = Main.overview.connect('window-drag-end',
 | 
			
		||||
                                                          Lang.bind(this, this._dragEnd));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    zoomFromOverview: function() {
 | 
			
		||||
@@ -514,14 +543,34 @@ const WorkspacesDisplay = new Lang.Class({
 | 
			
		||||
            Main.overview.disconnect(this._restackedNotifyId);
 | 
			
		||||
            this._restackedNotifyId = 0;
 | 
			
		||||
        }
 | 
			
		||||
        if (this._scrollEventId > 0) {
 | 
			
		||||
            Main.overview.disconnect(this._scrollEventId);
 | 
			
		||||
            this._scrollEventId = 0;
 | 
			
		||||
        if (this._itemDragBeginId > 0) {
 | 
			
		||||
            Main.overview.disconnect(this._itemDragBeginId);
 | 
			
		||||
            this._itemDragBeginId = 0;
 | 
			
		||||
        }
 | 
			
		||||
        if (this._itemDragCancelledId > 0) {
 | 
			
		||||
            Main.overview.disconnect(this._itemDragCancelledId);
 | 
			
		||||
            this._itemDragCancelledId = 0;
 | 
			
		||||
        }
 | 
			
		||||
        if (this._itemDragEndId > 0) {
 | 
			
		||||
            Main.overview.disconnect(this._itemDragEndId);
 | 
			
		||||
            this._itemDragEndId = 0;
 | 
			
		||||
        }
 | 
			
		||||
        if (this._windowDragBeginId > 0) {
 | 
			
		||||
            Main.overview.disconnect(this._windowDragBeginId);
 | 
			
		||||
            this._windowDragBeginId = 0;
 | 
			
		||||
        }
 | 
			
		||||
        if (this._windowDragCancelledId > 0) {
 | 
			
		||||
            Main.overview.disconnect(this._windowDragCancelledId);
 | 
			
		||||
            this._windowDragCancelledId = 0;
 | 
			
		||||
        }
 | 
			
		||||
        if (this._windowDragEndId > 0) {
 | 
			
		||||
            Main.overview.disconnect(this._windowDragEndId);
 | 
			
		||||
            this._windowDragEndId = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < this._workspacesViews.length; i++)
 | 
			
		||||
            this._workspacesViews[i].destroy();
 | 
			
		||||
        this._workspacesViews = [];
 | 
			
		||||
        this._workspacesViews = null;
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < this._workspaces.length; i++)
 | 
			
		||||
            for (let w = 0; w < this._workspaces[i].length; w++) {
 | 
			
		||||
@@ -540,12 +589,14 @@ const WorkspacesDisplay = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateWorkspacesViews: function() {
 | 
			
		||||
        for (let i = 0; i < this._workspacesViews.length; i++)
 | 
			
		||||
            this._workspacesViews[i].destroy();
 | 
			
		||||
        if (this._workspacesViews)
 | 
			
		||||
            for (let i = 0; i < this._workspacesViews.length; i++)
 | 
			
		||||
                this._workspacesViews[i].destroy();
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < this._workspaces.length; i++)
 | 
			
		||||
            for (let w = 0; w < this._workspaces[i].length; w++)
 | 
			
		||||
                this._workspaces[i][w].destroy();
 | 
			
		||||
        if (this._workspaces)
 | 
			
		||||
            for (let i = 0; i < this._workspaces.length; i++)
 | 
			
		||||
                for (let w = 0; w < this._workspaces[i].length; w++)
 | 
			
		||||
                    this._workspaces[i][w].destroy();
 | 
			
		||||
 | 
			
		||||
        this._workspacesViews = [];
 | 
			
		||||
        this._workspaces = [];
 | 
			
		||||
@@ -563,7 +614,6 @@ const WorkspacesDisplay = new Lang.Class({
 | 
			
		||||
            this._workspaces.push(monitorWorkspaces);
 | 
			
		||||
 | 
			
		||||
            let view = new WorkspacesView(monitorWorkspaces);
 | 
			
		||||
            view.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
 | 
			
		||||
            if (this._workspacesOnlyOnPrimary || i == this._primaryIndex) {
 | 
			
		||||
                this._scrollAdjustment = view.scrollAdjustment;
 | 
			
		||||
                this._scrollAdjustment.connect('notify::value',
 | 
			
		||||
@@ -594,7 +644,7 @@ const WorkspacesDisplay = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getPrimaryView: function() {
 | 
			
		||||
        if (!this._workspacesViews.length)
 | 
			
		||||
        if (!this._workspacesViews)
 | 
			
		||||
            return null;
 | 
			
		||||
        if (this._workspacesOnlyOnPrimary)
 | 
			
		||||
            return this._workspacesViews[0];
 | 
			
		||||
@@ -633,7 +683,7 @@ const WorkspacesDisplay = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateWorkspacesGeometry: function() {
 | 
			
		||||
        if (!this._workspacesViews.length)
 | 
			
		||||
        if (!this._workspacesViews)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let fullWidth = this.actor.allocation.x2 - this.actor.allocation.x1;
 | 
			
		||||
@@ -646,29 +696,45 @@ const WorkspacesDisplay = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        let rtl = (Clutter.get_default_text_direction () == Clutter.TextDirection.RTL);
 | 
			
		||||
 | 
			
		||||
        let clipWidth = width;
 | 
			
		||||
        let clipHeight = fullHeight;
 | 
			
		||||
        let clipX = x;
 | 
			
		||||
        let clipY = y + (fullHeight - clipHeight) / 2;
 | 
			
		||||
 | 
			
		||||
        let overviewSpacing = Main.overview._spacing;
 | 
			
		||||
        width -= overviewSpacing;
 | 
			
		||||
        if (rtl)
 | 
			
		||||
            x += overviewSpacing;
 | 
			
		||||
 | 
			
		||||
        let monitors = Main.layoutManager.monitors;
 | 
			
		||||
        let m = 0;
 | 
			
		||||
        for (let i = 0; i < monitors.length; i++) {
 | 
			
		||||
            if (i == this._primaryIndex) {
 | 
			
		||||
                this._workspacesViews[m].setClipRect(clipX, clipY,
 | 
			
		||||
                                                     clipWidth, clipHeight);
 | 
			
		||||
                this._workspacesViews[m].setGeometry(x, y, width, height);
 | 
			
		||||
                m++;
 | 
			
		||||
            } else if (!this._workspacesOnlyOnPrimary) {
 | 
			
		||||
                this._workspacesViews[m].setGeometry(monitors[i].x,
 | 
			
		||||
                this._workspacesViews[m].setClipRect(monitors[i].x,
 | 
			
		||||
                                                     monitors[i].y,
 | 
			
		||||
                                                     monitors[i].width,
 | 
			
		||||
                                                     monitors[i].height);
 | 
			
		||||
                this._workspacesViews[m].setGeometry(monitors[i].x + overviewSpacing/2,
 | 
			
		||||
                                                     monitors[i].y + overviewSpacing/2,
 | 
			
		||||
                                                     monitors[i].width - overviewSpacing,
 | 
			
		||||
                                                     monitors[i].height - overviewSpacing);
 | 
			
		||||
                m++;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onRestacked: function(overview, stackIndices) {
 | 
			
		||||
    _onRestacked: function(actor, stackIndices) {
 | 
			
		||||
        for (let i = 0; i < this._workspacesViews.length; i++)
 | 
			
		||||
            this._workspacesViews[i].syncStacking(stackIndices);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _workspacesChanged: function() {
 | 
			
		||||
        if (!this._workspacesViews.length)
 | 
			
		||||
        if (this._workspacesViews == null)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let oldNumWorkspaces = this._workspaces[0].length;
 | 
			
		||||
@@ -721,18 +787,26 @@ const WorkspacesDisplay = new Lang.Class({
 | 
			
		||||
                                                      newNumWorkspaces);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onScrollEvent: function(actor, event) {
 | 
			
		||||
        if (!this.actor.mapped)
 | 
			
		||||
            return false;
 | 
			
		||||
        switch (event.get_scroll_direction()) {
 | 
			
		||||
        case Clutter.ScrollDirection.UP:
 | 
			
		||||
            Main.wm.actionMoveWorkspace(Meta.MotionDirection.UP);
 | 
			
		||||
            return true;
 | 
			
		||||
        case Clutter.ScrollDirection.DOWN:
 | 
			
		||||
            Main.wm.actionMoveWorkspace(Meta.MotionDirection.DOWN);
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    _dragBegin: function() {
 | 
			
		||||
        this._inDrag = true;
 | 
			
		||||
        this._cancelledDrag = false;
 | 
			
		||||
        this._dragMonitor = {
 | 
			
		||||
            dragMotion: Lang.bind(this, this._onDragMotion)
 | 
			
		||||
        };
 | 
			
		||||
        DND.addDragMonitor(this._dragMonitor);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _dragCancelled: function() {
 | 
			
		||||
        this._cancelledDrag = true;
 | 
			
		||||
        DND.removeDragMonitor(this._dragMonitor);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDragMotion: function(dragEvent) {
 | 
			
		||||
        return DND.DragMotionResult.CONTINUE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _dragEnd: function() {
 | 
			
		||||
        this._inDrag = false;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(WorkspacesDisplay.prototype);
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,6 @@
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const DND = imports.ui.dnd;
 | 
			
		||||
@@ -17,11 +16,18 @@ const XdndHandler = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        // Used as a drag actor in case we don't have a cursor window clone
 | 
			
		||||
        this._dummy = new Clutter.Rectangle({ width: 1, height: 1, opacity: 0 });
 | 
			
		||||
        Main.uiGroup.add_actor(this._dummy);
 | 
			
		||||
        global.stage.add_actor(this._dummy);
 | 
			
		||||
        Shell.util_set_hidden_from_pick(this._dummy, true);
 | 
			
		||||
        this._dummy.hide();
 | 
			
		||||
 | 
			
		||||
        global.init_xdnd();
 | 
			
		||||
        // Mutter delays the creation of the output window as long
 | 
			
		||||
        // as possible to avoid flicker. In case a plugin wants to
 | 
			
		||||
        // access it directly it has to connect to the stage's show
 | 
			
		||||
        // signal. (see comment in compositor.c:meta_compositor_manage_screen)
 | 
			
		||||
        global.stage.connect('show', function () {
 | 
			
		||||
                                        global.init_xdnd();
 | 
			
		||||
                                        return false;
 | 
			
		||||
                                      });
 | 
			
		||||
 | 
			
		||||
        global.connect('xdnd-enter', Lang.bind(this, this._onEnter));
 | 
			
		||||
        global.connect('xdnd-position-changed', Lang.bind(this, this._onPositionChanged));
 | 
			
		||||
@@ -68,7 +74,7 @@ const XdndHandler = new Lang.Class({
 | 
			
		||||
                                                                   source: cursorWindow});
 | 
			
		||||
 | 
			
		||||
            this._cursorWindowClone = new Clutter.Clone({ source: cursorWindow });
 | 
			
		||||
            Main.uiGroup.add_actor(this._cursorWindowClone);
 | 
			
		||||
            global.overlay_group.add_actor(this._cursorWindowClone);
 | 
			
		||||
            Shell.util_set_hidden_from_pick(this._cursorWindowClone, true);
 | 
			
		||||
 | 
			
		||||
            // Make sure that the clone has the same position as the source
 | 
			
		||||
@@ -82,7 +88,7 @@ const XdndHandler = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onPositionChanged: function(obj, x, y) {
 | 
			
		||||
        let pickedActor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y);
 | 
			
		||||
        let pickedActor = global.stage.get_actor_at_pos(Clutter.PickMode.ALL, x, y);
 | 
			
		||||
 | 
			
		||||
        // Make sure that the cursor window is on top
 | 
			
		||||
        if (this._cursorWindowClone)
 | 
			
		||||
 
 | 
			
		||||
@@ -125,12 +125,6 @@
 | 
			
		||||
                                <listitem><para>List possible modes and exit</para></listitem>
 | 
			
		||||
                        </varlistentry>
 | 
			
		||||
 | 
			
		||||
                        <varlistentry>
 | 
			
		||||
                                <term><option>--clutter-display=<replaceable>DISPLAY</replaceable></option></term>
 | 
			
		||||
 | 
			
		||||
                                <listitem><para>Clutter the option display (otherwise ignored)</para></listitem>
 | 
			
		||||
                        </varlistentry>
 | 
			
		||||
 | 
			
		||||
                </variablelist>
 | 
			
		||||
 | 
			
		||||
        </refsect1>
 | 
			
		||||
 
 | 
			
		||||
@@ -21,14 +21,12 @@ eu
 | 
			
		||||
fa
 | 
			
		||||
fi
 | 
			
		||||
fr
 | 
			
		||||
fur
 | 
			
		||||
ga
 | 
			
		||||
gl
 | 
			
		||||
gu
 | 
			
		||||
he
 | 
			
		||||
hi
 | 
			
		||||
hu
 | 
			
		||||
ia
 | 
			
		||||
id
 | 
			
		||||
it
 | 
			
		||||
ja
 | 
			
		||||
@@ -61,7 +59,6 @@ sr@latin
 | 
			
		||||
sv
 | 
			
		||||
ta
 | 
			
		||||
te
 | 
			
		||||
tg
 | 
			
		||||
th
 | 
			
		||||
tr
 | 
			
		||||
ug 
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,3 @@
 | 
			
		||||
# List of source files containing translatable strings.
 | 
			
		||||
# Please keep this file sorted alphabetically.
 | 
			
		||||
[encoding: UTF-8]
 | 
			
		||||
data/50-gnome-shell-screenshot.xml.in
 | 
			
		||||
data/50-gnome-shell-system.xml.in
 | 
			
		||||
data/gnome-shell.desktop.in.in
 | 
			
		||||
@@ -13,9 +10,7 @@ js/gdm/util.js
 | 
			
		||||
js/misc/util.js
 | 
			
		||||
js/ui/appDisplay.js
 | 
			
		||||
js/ui/appFavorites.js
 | 
			
		||||
js/ui/backgroundMenu.js
 | 
			
		||||
js/ui/calendar.js
 | 
			
		||||
js/ui/components/automountManager.js
 | 
			
		||||
js/ui/components/autorunManager.js
 | 
			
		||||
js/ui/components/keyring.js
 | 
			
		||||
js/ui/components/networkAgent.js
 | 
			
		||||
@@ -33,7 +28,6 @@ js/ui/lookingGlass.js
 | 
			
		||||
js/ui/main.js
 | 
			
		||||
js/ui/messageTray.js
 | 
			
		||||
js/ui/notificationDaemon.js
 | 
			
		||||
js/ui/overviewControls.js
 | 
			
		||||
js/ui/overview.js
 | 
			
		||||
js/ui/panel.js
 | 
			
		||||
js/ui/popupMenu.js
 | 
			
		||||
@@ -55,12 +49,12 @@ js/ui/viewSelector.js
 | 
			
		||||
js/ui/wanda.js
 | 
			
		||||
js/ui/windowAttentionHandler.js
 | 
			
		||||
src/calendar-server/evolution-calendar.desktop.in.in
 | 
			
		||||
# Please do not remove this file from POTFILES.in. Run "git submodule init && git submodule update" to get it.
 | 
			
		||||
src/gvc/gvc-mixer-control.c
 | 
			
		||||
src/main.c
 | 
			
		||||
src/shell-app.c
 | 
			
		||||
src/shell-app-system.c
 | 
			
		||||
src/shell-global.c
 | 
			
		||||
src/shell-keyring-prompt.c
 | 
			
		||||
src/shell-mobile-providers.c
 | 
			
		||||
src/shell-polkit-authentication-agent.c
 | 
			
		||||
src/shell-util.c
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user