Compare commits
	
		
			24 Commits
		
	
	
		
			3.1.4
			...
			overview-r
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					9efa271888 | ||
| 
						 | 
					9ed3ce9006 | ||
| 
						 | 
					b400fc2837 | ||
| 
						 | 
					a66af6fbac | ||
| 
						 | 
					1bbc138ed2 | ||
| 
						 | 
					868d4756e4 | ||
| 
						 | 
					dfa3be59dc | ||
| 
						 | 
					62677336a5 | ||
| 
						 | 
					264a82cbfc | ||
| 
						 | 
					f8cfd9f903 | ||
| 
						 | 
					13f8cd3bcf | ||
| 
						 | 
					e1f336374b | ||
| 
						 | 
					0ba1387d19 | ||
| 
						 | 
					8832ce3a2b | ||
| 
						 | 
					91d7a022a9 | ||
| 
						 | 
					95d15725fc | ||
| 
						 | 
					bb2b7d83af | ||
| 
						 | 
					0abac6f67d | ||
| 
						 | 
					e00398e2ac | ||
| 
						 | 
					0f19492545 | ||
| 
						 | 
					9d46a6ceea | ||
| 
						 | 
					6432de95cc | ||
| 
						 | 
					738fc375c0 | ||
| 
						 | 
					4e9a530a64 | 
							
								
								
									
										25
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						@@ -3,11 +3,11 @@
 | 
			
		||||
*.o
 | 
			
		||||
.deps
 | 
			
		||||
.libs
 | 
			
		||||
ABOUT-NLS
 | 
			
		||||
ChangeLog
 | 
			
		||||
INSTALL
 | 
			
		||||
Makefile
 | 
			
		||||
Makefile.in
 | 
			
		||||
NEWS
 | 
			
		||||
aclocal.m4
 | 
			
		||||
autom4te.cache
 | 
			
		||||
config.h
 | 
			
		||||
@@ -18,10 +18,13 @@ config
 | 
			
		||||
configure
 | 
			
		||||
data/gnome-shell.desktop
 | 
			
		||||
data/gnome-shell.desktop.in
 | 
			
		||||
data/gnome-shell-clock-preferences.desktop
 | 
			
		||||
data/gnome-shell-clock-preferences.desktop.in
 | 
			
		||||
data/gschemas.compiled
 | 
			
		||||
data/org.gnome.shell.gschema.xml
 | 
			
		||||
data/org.gnome.shell.gschema.valid
 | 
			
		||||
js/misc/config.js
 | 
			
		||||
data/org.gnome.accessibility.magnifier.gschema.xml
 | 
			
		||||
data/org.gnome.accessibility.magnifier.gschema.valid
 | 
			
		||||
intltool-extract.in
 | 
			
		||||
intltool-merge.in
 | 
			
		||||
intltool-update.in
 | 
			
		||||
@@ -30,13 +33,8 @@ m4/
 | 
			
		||||
omf.make
 | 
			
		||||
po/*.gmo
 | 
			
		||||
po/gnome-shell.pot
 | 
			
		||||
po/*.header
 | 
			
		||||
po/*.sed
 | 
			
		||||
po/*.sin
 | 
			
		||||
po/Makefile.in.in
 | 
			
		||||
po/Makevars.template
 | 
			
		||||
po/POTFILES
 | 
			
		||||
po/Rules-quot
 | 
			
		||||
po/stamp-it
 | 
			
		||||
scripts/launcher.pyc
 | 
			
		||||
src/*.gir
 | 
			
		||||
@@ -45,25 +43,16 @@ src/*-enum-types.[ch]
 | 
			
		||||
src/*-marshal.[ch]
 | 
			
		||||
src/Makefile
 | 
			
		||||
src/Makefile.in
 | 
			
		||||
src/calendar-server/org.gnome.Shell.CalendarServer.service
 | 
			
		||||
src/gnomeshell-taskpanel
 | 
			
		||||
src/gnome-shell
 | 
			
		||||
src/gnome-shell-calendar-server
 | 
			
		||||
src/gnome-shell-extension-tool
 | 
			
		||||
src/gnome-shell-hotplug-sniffer
 | 
			
		||||
src/gnome-shell-jhbuild
 | 
			
		||||
src/gnome-shell-perf-helper
 | 
			
		||||
src/gnome-shell-real
 | 
			
		||||
src/hotplug-sniffer/org.gnome.Shell.HotplugSniffer.service
 | 
			
		||||
src/gnome-shell-clock-preferences
 | 
			
		||||
src/run-js-test
 | 
			
		||||
src/test-recorder
 | 
			
		||||
src/test-recorder.ogg
 | 
			
		||||
src/test-theme
 | 
			
		||||
src/st.h
 | 
			
		||||
src/stamp-st.h
 | 
			
		||||
src/stamp-st.h.tmp
 | 
			
		||||
stamp-h1
 | 
			
		||||
tests/run-test.sh
 | 
			
		||||
xmldocs.make
 | 
			
		||||
*~
 | 
			
		||||
*.patch
 | 
			
		||||
*.sw?
 | 
			
		||||
 
 | 
			
		||||
@@ -3,5 +3,5 @@ E-mail: otaylor@redhat.com
 | 
			
		||||
Userid: otaylor
 | 
			
		||||
 | 
			
		||||
Colin Walters
 | 
			
		||||
E-mail: walters@verbum.org
 | 
			
		||||
E-mail: walters@redhat.com
 | 
			
		||||
Userid: walters
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										309
									
								
								NEWS
									
									
									
									
									
								
							
							
						
						@@ -1,309 +0,0 @@
 | 
			
		||||
3.1.4
 | 
			
		||||
=====
 | 
			
		||||
* Take over inserted media handling and autorun from gnome-session [Cosimo]
 | 
			
		||||
* Message Tray
 | 
			
		||||
  - Display a count of unread notifications on icons
 | 
			
		||||
    [Jasper, Guillaume; #649356, #654139]
 | 
			
		||||
  - Only remove icons when the sender quits from D-Bus, not when it
 | 
			
		||||
    closes its last window [Neha, Marina; #645764]
 | 
			
		||||
  - Solve problems switching chats between shell and Empathy
 | 
			
		||||
    [Guillaume; #654237]
 | 
			
		||||
  - Fix handling of bad GMarkup in messages [Dan; #650298]
 | 
			
		||||
  - Never show notifications when the screensaver is active [Dan; #654550]
 | 
			
		||||
* Telepathy integrationpp
 | 
			
		||||
  - Implement Telepathy Debug interface to enable empathy-debugger
 | 
			
		||||
    [Guillaume; #652816]
 | 
			
		||||
  - Allow approving room invitations, and audio/video calls
 | 
			
		||||
    [Guillaume; #653740 #653939]
 | 
			
		||||
  - Send typing notifications [Jonny; #650196]
 | 
			
		||||
* Fix selection highlighting for light-on-dark entries [Jasper; #643768]
 | 
			
		||||
* Make control-Return in the overview open a new window [Maxim]
 | 
			
		||||
* Delay showing the alt-Tab switcher to reduce visual noise when
 | 
			
		||||
  flipping betweeen windows [Dan; #652346]
 | 
			
		||||
* When we have vertically stacked monitors, put the message tray
 | 
			
		||||
  on the bottom one [Dan; #636963]
 | 
			
		||||
* Fix various problems with keynav and the Activities button
 | 
			
		||||
  [Dan; #641253 #645759]
 | 
			
		||||
* Ensure screensaver is locked when switching users [Colin; #654565]
 | 
			
		||||
* Improve extension creation tool [Jasper; #653206]
 | 
			
		||||
* Fix compatibility with latest GJS [Giovanni; #654349]
 | 
			
		||||
* Code cleanups [Adel, Dan, Jasper; #645759 #654577 #654791 #654987]
 | 
			
		||||
* Misc bug fixes [Richard, Dan, Florian, Giovanni, Jasper, Marc-Antoine, Rui;
 | 
			
		||||
  #647175 #649513 #650452 #651082 #653700 #653989 #654105 #654791 #654267
 | 
			
		||||
  #654269 #654527 #655446]
 | 
			
		||||
* Build fixes [Florian, Siegfried; #654300]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
 Giovanni Campagna, Cosimo Cecchi, Guillaume Desmottes, Neha Doijode,
 | 
			
		||||
 Maxim Ermilov, Adel Gadllah, Siegfried-Angel Gevatter Pujals, Richard Hughes,
 | 
			
		||||
 Jonny Lamb, Rui Matos, Florian Müllner, Marc-Antoine Perennou, Colin Walters,
 | 
			
		||||
 Dan Winship, Marina Zhurakhinskaya
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
 Mario Blättermann, Paul Seyfert [de], Jorge González, Daniel Mustieles [es],
 | 
			
		||||
 Fran Dieguez [gl], Yaron Shahrabani [he], Luca Ferretti [it],
 | 
			
		||||
 Rudolfs Mazurs [lv], Kjartan Maraas [nb], A S Alam [pa], Yuri Kozlov [ru],
 | 
			
		||||
 Michal Štrba, Matej Urbančič [sl]
 | 
			
		||||
 | 
			
		||||
3.1.3
 | 
			
		||||
=====
 | 
			
		||||
* Fix problem with "user theme extension" breaking the CSS for other
 | 
			
		||||
  extensions [Giovanni; #650971]
 | 
			
		||||
* Telepathy IM framework integration
 | 
			
		||||
  - Switch to using telepathy-glib rather than talking to
 | 
			
		||||
    Telepathy via D-Bus [Guillaume, Jasper; #645585, #649633, #651138, #651227]
 | 
			
		||||
  - Acknowledge messages when the user clicks on them [Guillaume, #647893]
 | 
			
		||||
  - Fix problem with telepathy icon blinking for incoming messages
 | 
			
		||||
    even though the user has been notified of them [Guillaume; #643594]
 | 
			
		||||
* Networking
 | 
			
		||||
  - keep wirelesss networks in predictable order [Giovanni; #646580, #652313]
 | 
			
		||||
  - Show unmanaged devices in the menu [Giovanni; #646946]
 | 
			
		||||
  - Fix overflow when too many VPN connections [Giovanni; #651602]
 | 
			
		||||
* Bluetooth
 | 
			
		||||
  - Show "hardware disabled" when disabled by rfkill [Giovanni; #648048]
 | 
			
		||||
  - Fix bug updating status of devices [Giovanni; #647565]
 | 
			
		||||
* LookingGlass console:
 | 
			
		||||
  - Add a "Memory" tab [Colin; #650692]
 | 
			
		||||
  - Make escape work from any page [Dan Winship; #647303]
 | 
			
		||||
  - Provide a way to refer to panel items as, e.g.,
 | 
			
		||||
    Main.panel._activities [Dan Winship; #646915]
 | 
			
		||||
* User menu
 | 
			
		||||
  - Fix problem with suspend menu option locking the screen even when the user
 | 
			
		||||
    disabled that. [Florian; #652327]
 | 
			
		||||
  - Hide "power off..." option if shutdown is disabled via PolicyKit
 | 
			
		||||
    [Florian; #652038]
 | 
			
		||||
* Track changes to WM_CLASS (fixes problems with LibreOffice tracking)
 | 
			
		||||
  [Colin; #649315]
 | 
			
		||||
* Remove app tracking workarounds for Firefox and LibreOffice [Colin; #651015]
 | 
			
		||||
* Use upstream gettext autoconfigury rather than glib version [Javier; #631576]
 | 
			
		||||
* Show messages in the message tray when an application is fullscreen
 | 
			
		||||
  [Dan Winship; #608667]
 | 
			
		||||
* Don't autohide the workspace pager if there is more than one workspace
 | 
			
		||||
  [Florian; #652714, #653078, #653142]
 | 
			
		||||
* Don't always slide out the workspace pager at drag begin [Florian; #652730]
 | 
			
		||||
* Only offer to remove a favorite app when dragging it's icon [Owen; #642895]
 | 
			
		||||
* Allow dropping an icon anywhere on a workspace [Adel; #652079]
 | 
			
		||||
* st-scroll-view: Make the fade effect and offset themable [Jasper; #651813]
 | 
			
		||||
* Obey the user's preference when running an application in a terminal
 | 
			
		||||
  from the run dialog [Florian; #648422]
 | 
			
		||||
* Consistently exit overview when launching external applications
 | 
			
		||||
  [Colin; #653095]
 | 
			
		||||
* Adapt to changes in GJS for how GObject APIs are bound
 | 
			
		||||
  [Alex, Colin, Florian, Jasper, Marc-Antoine; #649981, #652597]
 | 
			
		||||
* Fix problems with scrolling in overflow for alt-Tab switcher
 | 
			
		||||
  [Dan Winship, Adel; #647807]
 | 
			
		||||
* Mark relationships between labels and actors for accessibility [Alejandro]
 | 
			
		||||
* Add org.gnome.shell.enabled-extensions complementing disabled-extensions
 | 
			
		||||
  GSetting [Tassilo; #651088]
 | 
			
		||||
* Visual tweaks [Jakub, Jasper; #646261, #652715]
 | 
			
		||||
* Switch to building against clutter-1.7 with independent Cogl [Adel; #653397]
 | 
			
		||||
* Code cleanups [Colin, Dan Winship, Florian; #633620, #645031, #648755, #648758,
 | 
			
		||||
  #648760, #649203, #649517, #650317, #652730]
 | 
			
		||||
* Memory leak fixes [Colin, Maxim; #649508, #650934]
 | 
			
		||||
* Build Fixes [Colin, Dan Winship, Florian, Ionut, Morten, Owen, Sean; #647395,
 | 
			
		||||
  #648006, #650869, #653199, #653275
 | 
			
		||||
* Miscellaneous bug fixes [Adam, Adel, Dan Williams, Dan Winship, Florian,
 | 
			
		||||
  Ionut, Jasper, Maxim, Ray; #620105, #639459, #641570, #642793, #643513,
 | 
			
		||||
  #645848, #646919, #647186, #648305, #648410, #648562, #648894, #649001,
 | 
			
		||||
  #645990, #647893, #647907, #651012, #651086, #651606, #651569, #651866,
 | 
			
		||||
  #652388,  #653511]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
 Ionut Biru, Giovanni Campagna, Guillaume Desmottes, Adam Dingle,
 | 
			
		||||
 Maxim Ermilov, Adel Gadllah, Tassilo Horn, Javier Jardón, Jonny Lamb,
 | 
			
		||||
 Alexander Larsson, Rui Matos, Morten Mjelva, Florian Müllner,
 | 
			
		||||
 Marc-Antoine Perennou, Alejandro Piñeiro, Jasper St. Pierre, Jakub Steiner,
 | 
			
		||||
 Ray Strode, Owen Taylor, Colin Walters, Dan Williams, Sean Wilson, Dan Winship
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
 Daniel Martinez Cucalon [ar], Ihar Hrachyshka [be], Carles Ferrando,
 | 
			
		||||
 Gil Forcada, Sílvia Miranda [ca], Kristjan Schmidt [eo], Jorge González,
 | 
			
		||||
 Daniel Mustieles [es], Seán de Búrca [ga], Fran Diéguez [gl],
 | 
			
		||||
 Yaron Shahrabani [he], Kjartan Maraas [nb], Misha Shnurapet,
 | 
			
		||||
 Yuri Myasoedov [ru], Daniel Nylander [se], Peter Mráz [sk],
 | 
			
		||||
 Matej Urbančič [sl], Krishnababu Krothapalli [te], Daniel Korostil [uk],
 | 
			
		||||
 Aron Xu [zh_CN]
 | 
			
		||||
 | 
			
		||||
3.0.2
 | 
			
		||||
=====
 | 
			
		||||
* Network Menu [Dan Williams]
 | 
			
		||||
  - Fix connecting to WPA2 Enterprise access points
 | 
			
		||||
    Fixes https://bugzilla.gnome.org/show_bug.cgi?id=648171
 | 
			
		||||
  - Show the mobile broadband wizard when selecting 3G network
 | 
			
		||||
    Fixes https://bugzilla.gnome.org/show_bug.cgi?id=649318
 | 
			
		||||
  - Miscellaneous bug fixes
 | 
			
		||||
    648648, 650124
 | 
			
		||||
* Fix duplicate icons in the application browser [Owen]
 | 
			
		||||
  https://bugzilla.gnome.org/show_bug.cgi?id=648739
 | 
			
		||||
* Make clicking anywhere on the volume icon slider work [Giovanni]
 | 
			
		||||
  https://bugzilla.gnome.org/show_bug.cgi?id=646660
 | 
			
		||||
* Fix a case where activating and clicking the hot corner
 | 
			
		||||
  at the same time could result in immediately leaving the
 | 
			
		||||
  overview [Rui]
 | 
			
		||||
  https://bugzilla.gnome.org/show_bug.cgi?id=649427
 | 
			
		||||
* Fix a case where applications became misordered in Alt-Tab [Jasper]
 | 
			
		||||
  https://bugzilla.gnome.org/show_bug.cgi?id=643302
 | 
			
		||||
* Fix a bug where messages you send could show up in
 | 
			
		||||
  notifications as if someone else sent them [Jonny]
 | 
			
		||||
  https://bugzilla.gnome.org/show_bug.cgi?id=650219
 | 
			
		||||
* Memory leak fixes [Colin, Maxim]
 | 
			
		||||
  642652, 649508, 649497
 | 
			
		||||
* Miscellaneous minor bug fixes [Adel, Christopher, Jasper]
 | 
			
		||||
  649596, 648765, 648983, 649632
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
 Christopher Aillon, Giovanni Campagna, Maxim Ermilov,
 | 
			
		||||
 Adel Gadllah, Jonny Lamb, Rui Matos, Jasper St. Pierre,
 | 
			
		||||
 Owen Taylor, Colin Walters, Dan Williams
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
 Arash Mousavi [fa], Seán de Búrca [ga], Timo Jyrinki [fi],
 | 
			
		||||
 Sigurd Gartmann [nb], Daniel Nylander [se], Peter Mráz [sl],
 | 
			
		||||
 Abduxukur Abdurixit [ug], Nguyễn Thái Ngọc Duy [vi]
 | 
			
		||||
 | 
			
		||||
3.0.1
 | 
			
		||||
=====
 | 
			
		||||
 | 
			
		||||
* Network menu
 | 
			
		||||
  - Fix problems updating the menu for mobile broadband devices [Giovanni]
 | 
			
		||||
    https://bugzilla.gnome.org/show_bug.cgi?id=646395
 | 
			
		||||
  - Fix missing device descriptions with multiple devices of the
 | 
			
		||||
    same type [Giovanni]
 | 
			
		||||
    https://bugzilla.gnome.org/show_bug.cgi?id=646074
 | 
			
		||||
  - Label ad-hoc neworks with an appropriate icon [Dan]
 | 
			
		||||
    https://bugzilla.gnome.org/show_bug.cgi?id=646141
 | 
			
		||||
  - Fix displaying some devices states as "invalid" [Dan]
 | 
			
		||||
    https://bugzilla.gnome.org/show_bug.cgi?id=646946
 | 
			
		||||
  - Fix problems with access points that don't report a SSID [Giovanni]
 | 
			
		||||
    https://bugzilla.gnome.org/show_bug.cgi?id=647040
 | 
			
		||||
  - Miscellaneous minor bug fixes [Dan, Giovanni, Owen]
 | 
			
		||||
    645981, 646558, 646443, 646708, 646968
 | 
			
		||||
* Application menu and icon
 | 
			
		||||
  - Fix bug where application menu icon was missing at GNOME Shell
 | 
			
		||||
    startup. [Florian]
 | 
			
		||||
    https://bugzilla.gnome.org/show_bug.cgi?id=644122
 | 
			
		||||
  - Fix missing application menu for dialog windows [Colin]
 | 
			
		||||
    https://bugzilla.gnome.org/show_bug.cgi?id=647082
 | 
			
		||||
 -  When launching an application through an alternate launcher
 | 
			
		||||
    (like for a System Settings pane), association the windows with
 | 
			
		||||
    the application, not the launcher. [Colin]
 | 
			
		||||
    https://bugzilla.gnome.org/show_bug.cgi?id=646689
 | 
			
		||||
* Activities overview
 | 
			
		||||
  - Load the applications view incrementally to avoid potentially freezing
 | 
			
		||||
    for multiple seconds [Colin]
 | 
			
		||||
    https://bugzilla.gnome.org/show_bug.cgi?id=647778
 | 
			
		||||
  - Fix bug where package installation while the overview
 | 
			
		||||
    was up could result in a corrupted application display. [Giovanni]
 | 
			
		||||
    https://bugzilla.gnome.org/show_bug.cgi?id=645801
 | 
			
		||||
  - Fix dragging from the search results to launch apps and docs [Florian]
 | 
			
		||||
    https://bugzilla.gnome.org/show_bug.cgi?id=645990
 | 
			
		||||
  - Fix flickering of selection when searching in the overview [Florian]
 | 
			
		||||
    https://bugzilla.gnome.org/show_bug.cgi?id=646019
 | 
			
		||||
  - Fix bug when typing into the search box when text was already
 | 
			
		||||
    selected [Nohemi]
 | 
			
		||||
    https://bugzilla.gnome.org/show_bug.cgi?id=636341
 | 
			
		||||
* Fix layout of notifications for right-to-left languages [Florian]
 | 
			
		||||
  https://bugzilla.gnome.org/show_bug.cgi?id=646921
 | 
			
		||||
* Remove a confusing special case where Alt-Tab sometimes switched
 | 
			
		||||
  to a different window of the same application rather than to
 | 
			
		||||
  a different application. [Rui]
 | 
			
		||||
  https://bugzilla.gnome.org/show_bug.cgi?id=648132
 | 
			
		||||
* Fix a crash that could happen when a window was opened on a
 | 
			
		||||
  workspace that was immediately removed [Dan]
 | 
			
		||||
  https://bugzilla.gnome.org/show_bug.cgi?id=648132
 | 
			
		||||
* Fix keyboard navigation in logout/reboot dialogs [Dan]
 | 
			
		||||
  https://bugzilla.gnome.org/show_bug.cgi?id=646740
 | 
			
		||||
* Fix missing inspector icon in Looking Glass console [Dan]
 | 
			
		||||
* Miscellaneous minor bug fixes [Adel, Colin, Dan, Florian, Nohemi]
 | 
			
		||||
  645648, 646205, 646257, 646855, 647098, 646730
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
 Giovanni Campagna, Nohemi Fernandez, Adel Gadllah, Rui Matos, Florian Müllner,
 | 
			
		||||
 Owen Taylor, Colin Walters, Dan Winship
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
 Hendrik Richter [de], Jorge González [es], Arash Mousavi [fa],
 | 
			
		||||
 Fran Diéguez [gl], Jiro Matsuzawa [ja], Piotr Drąg [pl], Daniel Nylander [sv],
 | 
			
		||||
 Sira Nokyoongtong [th], Muhammet Kara [tr], Nguyễn Thái Ngọc Duy [vi],
 | 
			
		||||
 Aron Xu [zh_CN], Chao-Hsiung Liao [zh_HK, zh_TW]
 | 
			
		||||
 | 
			
		||||
3.0.0.2
 | 
			
		||||
=======
 | 
			
		||||
 | 
			
		||||
* Fix missing import that was preventing extensions from loading.
 | 
			
		||||
  [Maxim Ermilov]
 | 
			
		||||
  https://bugzilla.gnome.org/show_bug.cgi?id=646333
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
 Timo Jyrinki [fi]
 | 
			
		||||
 | 
			
		||||
3.0.0.1
 | 
			
		||||
=======
 | 
			
		||||
 | 
			
		||||
* Fix problem with stuck event handling if network menu pops down while
 | 
			
		||||
  user is using the scrollbar. [Owen Taylor]
 | 
			
		||||
  https://bugzilla.gnome.org/show_bug.cgi?id=646825
 | 
			
		||||
 | 
			
		||||
Contributors to GNOME Shell 3.0
 | 
			
		||||
===============================
 | 
			
		||||
 | 
			
		||||
Code:
 | 
			
		||||
 | 
			
		||||
 Josh Adams, Kiyoshi Aman, Nuno Araujo, Emmanuele Bassi, Dirk-Jan C. Binnema,
 | 
			
		||||
 Wouter Bolsterlee, Raphael Bosshard, Milan Bouchet-Valat, Christina Boumpouka,
 | 
			
		||||
 Mathieu Bridon, Alban Browaeys, Phil Bull, Micro Cai, Giovanni Campagna,
 | 
			
		||||
 Cosimo Cecchi, Tor-björn Claesson, Matthias Clasen, Jason D. Clinton,
 | 
			
		||||
 Frederic Crozat, Guillaume Desmottes, Sander Dijkhuis, Neha Doijode,
 | 
			
		||||
 Maxim Ermilov, Diego Escalante Urrelo, Luca Ferretti, Steve Frécinaux,
 | 
			
		||||
 Takao Fujiwara, Adel Gadllah, Vadim Girlin, Nick Glynn, Guido Günther,
 | 
			
		||||
 Leon Handreke, Lex Hider, Richard Hughes, Javier Jardón, Abderrahim Kitouni,
 | 
			
		||||
 Andre Klapper, Alexander Larsson, Nickolas Lloyd, Ryan Lortie, Kjartan Maraas,
 | 
			
		||||
 Koop Mast, Rui Matos, Jonathan Matthew, William Jon McCann, Morten Mjelva,
 | 
			
		||||
 Federico Mena Quintero, Florian Müllner, Jon Nettleton, Hellyna Ng,
 | 
			
		||||
 Discardi Nicola, Carlos Martín Nieto, Bastien Nocera, Bill Nottingham,
 | 
			
		||||
 Matt Novenstern, Marc-Antoine Perennou, Neil Perry, Frédéric Péters,
 | 
			
		||||
 Alejandro Piñeiro, Siegfried-Angel Gevatter Pujals, "res", Neil Roberts,
 | 
			
		||||
 "Sardem FF7", Florian Scandella, Joseph Scheuhammer, Christian Schramm,
 | 
			
		||||
 Gustavo Noronha Silva, Jasper St. Pierre, Eric Springer, Jakub Steiner,
 | 
			
		||||
 Jonathan Strander, Ray Strode, Owen Taylor, Rico Tzschichholz,
 | 
			
		||||
 Sergey V. Udaltsov, Daiki Ueno, Vincent Untz, Marcelo Jorge Vieira,
 | 
			
		||||
 Mads Villadsen, Colin Walters, Dan Winship, William Wolf, Thomas Wood,
 | 
			
		||||
 Pierre Yager, David Zeuthen, Marina Zhurakhinskaya
 | 
			
		||||
 | 
			
		||||
Design:
 | 
			
		||||
 | 
			
		||||
 Allan Day, William Jon McCann, Jeremy Perry, Jakub Steiner
 | 
			
		||||
 2008 Boston GNOME design hackfest participants (especially Neil J. Patel
 | 
			
		||||
 for turning the resulting sketches into our first mockups.)
 | 
			
		||||
 Everybody on irc.gnome.org:#gnome-design
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
 | 
			
		||||
 Friedel Wolff (af), Khaled Hosny (ar), Ivaylo Valkov (bg), Jamil Ahmed (bn)
 | 
			
		||||
 Runa Bhattacharjee (bn_IN), Gil Forcada, Siegfried-Angel Gevatter Pujals,
 | 
			
		||||
 Jordi Serratosa (ca), Andre Klapper, Petr Kovar (cs), Kenneth Nielsen,
 | 
			
		||||
 Kris Thomsen (da), Mario Blättermann, Hendrik Brandt, Christian Kirbach,
 | 
			
		||||
 Hendrik Richter, Wolfgang Stöggl (de), Michael Kotsarinis, Kostas Papadimas,
 | 
			
		||||
 Jennie Petoumenou, Sterios Prosiniklis, Fotis Tsamis, Simos Xenitellis (el),
 | 
			
		||||
 Bruce Cowan, Philip Withnall (en_GB), Jorge Gonzalez, Daniel Mustieles (es),
 | 
			
		||||
 Mattias Põldaru, Ivar Smolin (et), Inaki Larranaga Murgoitio (eu),
 | 
			
		||||
 Mahyar Moghimi (fa), Timo Jyrinki (fi), Cyril Arnaud, Bruno Brouard,
 | 
			
		||||
 Pablo Martin-Gomez, Claude Paroz, Frédéric Peters (fr), Seán de Búrca (ga)
 | 
			
		||||
 Francisco Diéguez, Antón Méixome (gl), Sweta Kothari (gu), Liel Fridman,
 | 
			
		||||
 Yaron Shahrabani (he), Rajesh Ranjan (hi), Gabor Kelemen (hu), Milo Casagrande,
 | 
			
		||||
 Luca Ferretti (it), Dirgita, Andika Triwidada (id), Takayuki KUSANO,
 | 
			
		||||
 Takayoshi OKANO, Kiyotaka NISHIBORI, Futoshi NISHIO (ja), Shankar Prasad (kn),
 | 
			
		||||
 Young-Ho Cha, Changwoo Ryu (ko), Žygimantas Beručka, Gintautas Miliauskas (lt),
 | 
			
		||||
 Rudolfs Mazurs (lv), Sandeep Shedmake (mr), Kjartan Maraas (nb),
 | 
			
		||||
 Wouter Bolsterlee, Sander Dijkhuis, Reinout van Schouwen (nl),
 | 
			
		||||
 Torstein Winterseth (nn), A S Alam (pa), Tomasz Dominikowski, Piotr Drąg (pl),
 | 
			
		||||
 Duarte Loreto (pt), Felipe Borges, Rodrigo Padula de Oliveira,
 | 
			
		||||
 Rodrigo L. M. Flores, Amanda Magalhães, Og B. Maciel, Gabriel F. Vilar,
 | 
			
		||||
 Jonh Wendell (pt_BR), Lucian Adrian Grijincu, Daniel Șerbănescu (ro),
 | 
			
		||||
 Sergey V. Kovylov, Andrey Korzinev, Yuri Myasoedov, Marina Zhurakhinskaya (ru),
 | 
			
		||||
 Daniel Nylander (se), Matej Urbančič, Andrej Žnidaršič (sl),
 | 
			
		||||
 Miloš Popović (sr, sr@latin), Miroslav Nikolić (sr), Tirumurti Vasudevan (ta),
 | 
			
		||||
 Sira Nokyoongtong (th), Baris Cicek (tr), Abduxukur Abdurixit,
 | 
			
		||||
 Gheyret T. Kenji (ug), Maxim V. Dziumanenko, Daniel Korostil (uk),
 | 
			
		||||
 Nguyễn Thái Ngọc Duy (vi), Jessica Ban, 'jiero', Wei Li, YunQiang Su, Ray Wang,
 | 
			
		||||
 Aron Xu (zh_CN), Chao-Hsiung Liao (zh_HK, zh_TW)
 | 
			
		||||
							
								
								
									
										136
									
								
								configure.ac
									
									
									
									
									
								
							
							
						
						@@ -1,14 +1,11 @@
 | 
			
		||||
AC_PREREQ(2.63)
 | 
			
		||||
AC_INIT([gnome-shell],[3.1.4],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
 | 
			
		||||
AC_INIT([gnome-shell],[2.91.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])
 | 
			
		||||
AC_CONFIG_MACRO_DIR([m4])
 | 
			
		||||
AC_CONFIG_AUX_DIR([config])
 | 
			
		||||
 | 
			
		||||
AC_SUBST([PACKAGE_NAME], ["$PACKAGE_NAME"])
 | 
			
		||||
AC_SUBST([PACKAGE_VERSION], ["$PACKAGE_VERSION"])
 | 
			
		||||
 | 
			
		||||
AM_INIT_AUTOMAKE([1.10 dist-bzip2 no-dist-gzip foreign])
 | 
			
		||||
AM_MAINTAINER_MODE
 | 
			
		||||
 | 
			
		||||
@@ -23,16 +20,12 @@ AM_PROG_CC_C_O
 | 
			
		||||
LT_PREREQ([2.2.6])
 | 
			
		||||
LT_INIT([disable-static])
 | 
			
		||||
 | 
			
		||||
# i18n
 | 
			
		||||
IT_PROG_INTLTOOL([0.40])
 | 
			
		||||
 | 
			
		||||
AM_GNU_GETTEXT([external])
 | 
			
		||||
AM_GNU_GETTEXT_VERSION([0.17])
 | 
			
		||||
 | 
			
		||||
GETTEXT_PACKAGE=gnome-shell
 | 
			
		||||
AC_SUBST(GETTEXT_PACKAGE)
 | 
			
		||||
AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE",
 | 
			
		||||
                   [The prefix for our gettext translation domains.])
 | 
			
		||||
IT_PROG_INTLTOOL(0.26)
 | 
			
		||||
AM_GLIB_GNU_GETTEXT
 | 
			
		||||
 | 
			
		||||
PKG_PROG_PKG_CONFIG([0.22])
 | 
			
		||||
 | 
			
		||||
@@ -56,100 +49,65 @@ AC_MSG_CHECKING([for GStreamer (needed for recording functionality)])
 | 
			
		||||
if $PKG_CONFIG --exists gstreamer-0.10 '>=' $GSTREAMER_MIN_VERSION ; then
 | 
			
		||||
   AC_MSG_RESULT(yes)
 | 
			
		||||
   build_recorder=true
 | 
			
		||||
   recorder_modules="gstreamer-0.10 gstreamer-base-0.10 x11"
 | 
			
		||||
   PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-1.0 xfixes)
 | 
			
		||||
   recorder_modules="gstreamer-0.10 gstreamer-base-0.10 xfixes"
 | 
			
		||||
   PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-1.0)
 | 
			
		||||
else
 | 
			
		||||
   AC_MSG_RESULT(no)
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
 | 
			
		||||
 | 
			
		||||
CLUTTER_MIN_VERSION=1.7.5
 | 
			
		||||
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
 | 
			
		||||
GJS_MIN_VERSION=1.29.15
 | 
			
		||||
MUTTER_MIN_VERSION=3.0.0
 | 
			
		||||
GTK_MIN_VERSION=3.0.0
 | 
			
		||||
CLUTTER_MIN_VERSION=1.3.14
 | 
			
		||||
GOBJECT_INTROSPECTION_MIN_VERSION=0.6.11
 | 
			
		||||
GJS_MIN_VERSION=0.7
 | 
			
		||||
MUTTER_MIN_VERSION=2.91.0
 | 
			
		||||
GTK_MIN_VERSION=2.91.0
 | 
			
		||||
GIO_MIN_VERSION=2.25.9
 | 
			
		||||
LIBECAL_MIN_VERSION=2.32.0
 | 
			
		||||
LIBEDATASERVER_MIN_VERSION=1.2.0
 | 
			
		||||
LIBEDATASERVERUI_MIN_VERSION=2.91.6
 | 
			
		||||
TELEPATHY_GLIB_MIN_VERSION=0.15.3
 | 
			
		||||
TELEPATHY_LOGGER_MIN_VERSION=0.2.4
 | 
			
		||||
POLKIT_MIN_VERSION=0.100
 | 
			
		||||
STARTUP_NOTIFICATION_MIN_VERSION=0.11
 | 
			
		||||
 | 
			
		||||
# Collect more than 20 libraries for a prize!
 | 
			
		||||
PKG_CHECK_MODULES(GNOME_SHELL, gio-2.0 >= $GIO_MIN_VERSION
 | 
			
		||||
                               gio-unix-2.0 dbus-glib-1 libxml-2.0
 | 
			
		||||
                               gtk+-3.0 >= $GTK_MIN_VERSION
 | 
			
		||||
                               libmutter >= $MUTTER_MIN_VERSION
 | 
			
		||||
                               gjs-internals-1.0 >= $GJS_MIN_VERSION
 | 
			
		||||
			       libgnome-menu $recorder_modules gconf-2.0
 | 
			
		||||
                               gdk-x11-3.0 libsoup-2.4
 | 
			
		||||
			       clutter-x11-1.0 >= $CLUTTER_MIN_VERSION
 | 
			
		||||
			       clutter-glx-1.0 >= $CLUTTER_MIN_VERSION
 | 
			
		||||
                               libstartup-notification-1.0 >= $STARTUP_NOTIFICATION_MIN_VERSION
 | 
			
		||||
                               gobject-introspection-1.0 >= $GOBJECT_INTROSPECTION_MIN_VERSION
 | 
			
		||||
			       libcanberra
 | 
			
		||||
                               telepathy-glib >= $TELEPATHY_GLIB_MIN_VERSION
 | 
			
		||||
                               telepathy-logger-0.2 >= $TELEPATHY_LOGGER_MIN_VERSION
 | 
			
		||||
                               polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes)
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
 | 
			
		||||
GJS_VERSION=`$PKG_CONFIG --modversion gjs-internals-1.0`
 | 
			
		||||
AC_DEFINE_UNQUOTED([GJS_VERSION], ["$GJS_VERSION"], [The version of GJS we're linking to])
 | 
			
		||||
AC_SUBST([GJS_VERSION], ["$GJS_VERSION"])
 | 
			
		||||
 | 
			
		||||
GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION])
 | 
			
		||||
JHBUILD_TYPELIBDIR="$INTROSPECTION_TYPELIBDIR"
 | 
			
		||||
# NM is the only typelib we use that we don't jhbuild
 | 
			
		||||
PKG_CHECK_EXISTS([libnm-glib >= 0.8.999],
 | 
			
		||||
        [NM_TYPELIBDIR=`$PKG_CONFIG --variable=libdir libnm-glib`/girepository-1.0
 | 
			
		||||
	 if test "$INTROSPECTION_TYPELIBDIR" != "$NM_TYPELIBDIR"; then
 | 
			
		||||
	     JHBUILD_TYPELIBDIR="$JHBUILD_TYPELIBDIR:$NM_TYPELIBDIR"
 | 
			
		||||
	 fi])
 | 
			
		||||
AC_SUBST(JHBUILD_TYPELIBDIR)
 | 
			
		||||
PKG_CHECK_MODULES(MUTTER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION
 | 
			
		||||
                                 gio-unix-2.0 dbus-glib-1
 | 
			
		||||
                                 gtk+-3.0 >= $GTK_MIN_VERSION
 | 
			
		||||
                                 mutter-plugins >= $MUTTER_MIN_VERSION
 | 
			
		||||
                                 gjs-internals-1.0 >= $GJS_MIN_VERSION
 | 
			
		||||
				 libgnome-menu $recorder_modules gconf-2.0
 | 
			
		||||
                                 gdk-x11-3.0
 | 
			
		||||
				 clutter-x11-1.0 >= $CLUTTER_MIN_VERSION
 | 
			
		||||
				 clutter-glx-1.0 >= $CLUTTER_MIN_VERSION
 | 
			
		||||
                                 libstartup-notification-1.0
 | 
			
		||||
                                 gobject-introspection-1.0 >= $GOBJECT_INTROSPECTION_MIN_VERSION
 | 
			
		||||
				 libcanberra)
 | 
			
		||||
 | 
			
		||||
saved_CFLAGS=$CFLAGS
 | 
			
		||||
saved_LIBS=$LIBS
 | 
			
		||||
CFLAGS=$GNOME_SHELL_CFLAGS
 | 
			
		||||
LIBS=$GNOME_SHELL_LIBS
 | 
			
		||||
AC_CHECK_FUNCS(JS_NewGlobalObject XFixesCreatePointerBarrier)
 | 
			
		||||
CFLAGS=$MUTTER_PLUGIN_CFLAGS
 | 
			
		||||
LIBS=$MUTTER_PLUGIN_LIBS
 | 
			
		||||
# sn_startup_sequence_get_application_id, we can replace with a version check later
 | 
			
		||||
AC_CHECK_FUNCS(JS_NewGlobalObject sn_startup_sequence_get_application_id)
 | 
			
		||||
CFLAGS=$saved_CFLAGS
 | 
			
		||||
LIBS=$saved_LIBS
 | 
			
		||||
 | 
			
		||||
PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 >= 0.6.2 gnome-desktop-3.0 >= 2.90.0 x11)
 | 
			
		||||
PKG_CHECK_MODULES(TIDY, clutter-1.0)
 | 
			
		||||
PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 gnome-desktop-3.0 >= 2.90.0)
 | 
			
		||||
PKG_CHECK_MODULES(GDMUSER, dbus-glib-1 gtk+-3.0)
 | 
			
		||||
PKG_CHECK_MODULES(TRAY, gtk+-3.0)
 | 
			
		||||
PKG_CHECK_MODULES(GVC, libpulse libpulse-mainloop-glib gobject-2.0)
 | 
			
		||||
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 0.1.7)
 | 
			
		||||
 | 
			
		||||
AC_MSG_CHECKING([for bluetooth support])
 | 
			
		||||
PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.1.0],
 | 
			
		||||
        [BLUETOOTH_DIR=`$PKG_CONFIG --variable=applet_libdir gnome-bluetooth-1.0`
 | 
			
		||||
	 BLUETOOTH_LIBS=`$PKG_CONFIG --variable=applet_libs gnome-bluetooth-1.0`
 | 
			
		||||
	 AC_SUBST([BLUETOOTH_LIBS],["$BLUETOOTH_LIBS"])
 | 
			
		||||
	 AC_DEFINE_UNQUOTED([BLUETOOTH_DIR],["$BLUETOOTH_DIR"],[Path to installed GnomeBluetooth typelib and library])
 | 
			
		||||
	 AC_DEFINE([HAVE_BLUETOOTH],[1],[Define if you have libgnome-bluetooth-applet])
 | 
			
		||||
	 AC_SUBST([HAVE_BLUETOOTH],[1])
 | 
			
		||||
	 AC_MSG_RESULT([yes])],
 | 
			
		||||
	[AC_DEFINE([HAVE_BLUETOOTH],[0])
 | 
			
		||||
	 AC_SUBST([HAVE_BLUETOOTH],[0])
 | 
			
		||||
	 AC_MSG_RESULT([no])])
 | 
			
		||||
PKG_CHECK_MODULES(JS_TEST, clutter-x11-1.0 gjs-1.0 gobject-introspection-1.0 gtk+-3.0)
 | 
			
		||||
 | 
			
		||||
PKG_CHECK_MODULES(CALENDAR_SERVER, libecal-1.2 >= $LIBECAL_MIN_VERSION libedataserver-1.2 >= $LIBEDATASERVER_MIN_VERSION libedataserverui-3.0 >= $LIBEDATASERVERUI_MIN_VERSION gio-2.0)
 | 
			
		||||
AC_SUBST(CALENDAR_SERVER_CFLAGS)
 | 
			
		||||
AC_SUBST(CALENDAR_SERVER_LIBS)
 | 
			
		||||
 | 
			
		||||
MUTTER_GIR_DIR=`$PKG_CONFIG --variable=girdir libmutter`
 | 
			
		||||
MUTTER_TYPELIB_DIR=`$PKG_CONFIG --variable=typelibdir libmutter`
 | 
			
		||||
AC_SUBST(MUTTER_GIR_DIR)
 | 
			
		||||
AC_SUBST(MUTTER_TYPELIB_DIR)
 | 
			
		||||
MUTTER_BIN_DIR=`$PKG_CONFIG --variable=exec_prefix mutter-plugins`/bin
 | 
			
		||||
# FIXME: metacity-plugins.pc should point directly to its .gir file
 | 
			
		||||
MUTTER_LIB_DIR=`$PKG_CONFIG --variable=libdir mutter-plugins`
 | 
			
		||||
MUTTER_PLUGIN_DIR=`$PKG_CONFIG --variable=plugindir mutter-plugins`
 | 
			
		||||
AC_SUBST(MUTTER_BIN_DIR)
 | 
			
		||||
AC_SUBST(MUTTER_LIB_DIR)
 | 
			
		||||
AC_SUBST(MUTTER_PLUGIN_DIR)
 | 
			
		||||
 | 
			
		||||
GJS_JS_DIR=`$PKG_CONFIG --variable=jsdir gjs-1.0`
 | 
			
		||||
GJS_JS_NATIVE_DIR=`$PKG_CONFIG --variable=jsnativedir gjs-1.0`
 | 
			
		||||
GJS_CONSOLE=`$PKG_CONFIG --variable=gjs_console gjs-1.0`
 | 
			
		||||
AC_SUBST(GJS_JS_DIR)
 | 
			
		||||
AC_SUBST(GJS_JS_NATIVE_DIR)
 | 
			
		||||
AC_SUBST(GJS_CONSOLE)
 | 
			
		||||
 | 
			
		||||
AC_CHECK_FUNCS(fdwalk)
 | 
			
		||||
@@ -196,15 +154,21 @@ if test "$enable_compile_warnings" != no ; then
 | 
			
		||||
fi
 | 
			
		||||
changequote([,])dnl
 | 
			
		||||
 | 
			
		||||
AC_ARG_ENABLE(jhbuild-wrapper-script,
 | 
			
		||||
  AS_HELP_STRING([--jhbuild-wrapper-script=yes],[Make "gnome-shell" script work for jhbuild]),,enable_jhbuild_wrapper_script=no)
 | 
			
		||||
AM_CONDITIONAL(USE_JHBUILD_WRAPPER_SCRIPT, test "x$enable_jhbuild_wrapper_script" = xyes)
 | 
			
		||||
AC_PATH_PROG(mutter, [mutter])
 | 
			
		||||
AC_SUBST(mutter)
 | 
			
		||||
 | 
			
		||||
AC_MSG_CHECKING([if mutter was compiled with GTK+-3.0])
 | 
			
		||||
if $PKG_CONFIG --libs libmutter-private | grep gtk-x11-3 >/dev/null; then
 | 
			
		||||
  AC_MSG_RESULT(yes)
 | 
			
		||||
else
 | 
			
		||||
  AC_MSG_RESULT(no)
 | 
			
		||||
  AC_MSG_ERROR([GNOME Shell requires Mutter to be compiled against GTK+-3.0])
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
AC_CONFIG_FILES([
 | 
			
		||||
  Makefile
 | 
			
		||||
  data/Makefile
 | 
			
		||||
  js/Makefile
 | 
			
		||||
  js/misc/config.js
 | 
			
		||||
  src/Makefile
 | 
			
		||||
  tests/Makefile
 | 
			
		||||
  po/Makefile.in
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
desktopdir=$(datadir)/applications
 | 
			
		||||
desktop_DATA = gnome-shell.desktop
 | 
			
		||||
desktop_DATA = gnome-shell.desktop gnome-shell-clock-preferences.desktop
 | 
			
		||||
 | 
			
		||||
# We substitute in bindir so it works as an autostart
 | 
			
		||||
# file when built in a non-system prefix
 | 
			
		||||
@@ -12,41 +12,49 @@ desktop_DATA = gnome-shell.desktop
 | 
			
		||||
%.desktop:%.desktop.in
 | 
			
		||||
	$(AM_V_GEN) sed s/^_// < $< > $@ || rm $@
 | 
			
		||||
 | 
			
		||||
searchprovidersdir = $(pkgdatadir)/search_providers
 | 
			
		||||
dist_searchproviders_DATA =				\
 | 
			
		||||
	search_providers/google.xml				\
 | 
			
		||||
	search_providers/wikipedia.xml
 | 
			
		||||
dist_pkgdata_DATA = clock-preferences.ui
 | 
			
		||||
 | 
			
		||||
imagesdir = $(pkgdatadir)/images
 | 
			
		||||
dist_images_DATA =				\
 | 
			
		||||
	close-black.svg				\
 | 
			
		||||
	magnifier.svg
 | 
			
		||||
 | 
			
		||||
themedir = $(pkgdatadir)/theme
 | 
			
		||||
dist_theme_DATA =				\
 | 
			
		||||
	theme/calendar-arrow-left.svg		\
 | 
			
		||||
	theme/calendar-arrow-right.svg		\
 | 
			
		||||
	theme/calendar-today.svg		\
 | 
			
		||||
	theme/add-workspace.svg			\
 | 
			
		||||
	theme/close-window.svg			\
 | 
			
		||||
	theme/close.svg				\
 | 
			
		||||
	theme/corner-ripple-ltr.png		\
 | 
			
		||||
	theme/corner-ripple-rtl.png		\
 | 
			
		||||
	theme/corner-ripple.png			\
 | 
			
		||||
	theme/dash-placeholder.svg		\
 | 
			
		||||
	theme/filter-selected-ltr.svg		\
 | 
			
		||||
	theme/filter-selected-rtl.svg		\
 | 
			
		||||
	theme/dialog-error.svg			\
 | 
			
		||||
	theme/gnome-shell.css			\
 | 
			
		||||
	theme/panel-border.svg			\
 | 
			
		||||
	theme/panel-button-border.svg		\
 | 
			
		||||
	theme/panel-button-highlight-narrow.svg	\
 | 
			
		||||
	theme/panel-button-highlight-wide.svg	\
 | 
			
		||||
	theme/process-working.svg		\
 | 
			
		||||
	theme/mosaic-view-active.svg		\
 | 
			
		||||
	theme/mosaic-view.svg			\
 | 
			
		||||
	theme/move-window-on-new.svg		\
 | 
			
		||||
	theme/process-working.png		\
 | 
			
		||||
	theme/remove-workspace.svg		\
 | 
			
		||||
	theme/running-indicator.svg		\
 | 
			
		||||
	theme/scroll-button-down-hover.png	\
 | 
			
		||||
	theme/scroll-button-down.png		\
 | 
			
		||||
	theme/scroll-button-up-hover.png	\
 | 
			
		||||
	theme/scroll-button-up.png		\
 | 
			
		||||
	theme/scroll-hhandle.svg		\
 | 
			
		||||
	theme/scroll-vhandle.svg		\
 | 
			
		||||
	theme/source-button-border.svg		\
 | 
			
		||||
	theme/section-more.svg			\
 | 
			
		||||
	theme/section-more-open.svg		\
 | 
			
		||||
	theme/separator-white.png		\
 | 
			
		||||
	theme/single-view-active.svg		\
 | 
			
		||||
	theme/single-view.svg			\
 | 
			
		||||
	theme/toggle-off-us.svg			\
 | 
			
		||||
	theme/toggle-off-intl.svg		\
 | 
			
		||||
	theme/toggle-on-us.svg			\
 | 
			
		||||
	theme/toggle-on-intl.svg		\
 | 
			
		||||
	theme/ws-switch-arrow-up.svg		\
 | 
			
		||||
	theme/ws-switch-arrow-down.svg
 | 
			
		||||
	theme/ws-switch-arrow-left.svg		\
 | 
			
		||||
	theme/ws-switch-arrow-right.svg
 | 
			
		||||
 | 
			
		||||
gsettings_SCHEMAS = org.gnome.shell.gschema.xml
 | 
			
		||||
gsettings_SCHEMAS =					\
 | 
			
		||||
	org.gnome.accessibility.magnifier.gschema.xml	\
 | 
			
		||||
	org.gnome.shell.gschema.xml
 | 
			
		||||
 | 
			
		||||
@INTLTOOL_XML_NOMERGE_RULE@
 | 
			
		||||
@GSETTINGS_RULES@
 | 
			
		||||
@@ -63,6 +71,11 @@ all-local: gschemas.compiled
 | 
			
		||||
gconfschemadir  = @GCONF_SCHEMA_FILE_DIR@
 | 
			
		||||
gconfschema_DATA = gnome-shell.schemas
 | 
			
		||||
 | 
			
		||||
menudir = $(sysconfdir)/xdg/menus
 | 
			
		||||
 | 
			
		||||
menu_DATA = \
 | 
			
		||||
	gs-applications.menu
 | 
			
		||||
 | 
			
		||||
shadersdir = $(pkgdatadir)/shaders
 | 
			
		||||
shaders_DATA = \
 | 
			
		||||
	shaders/dim-window.glsl
 | 
			
		||||
@@ -74,13 +87,16 @@ install-data-local:
 | 
			
		||||
 | 
			
		||||
EXTRA_DIST =						\
 | 
			
		||||
	gnome-shell.desktop.in.in			\
 | 
			
		||||
	gnome-shell-clock-preferences.desktop.in.in	\
 | 
			
		||||
	$(menu_DATA)					\
 | 
			
		||||
	$(gconfschema_DATA)				\
 | 
			
		||||
	$(shaders_DATA)					\
 | 
			
		||||
	org.gnome.accessibility.magnifier.gschema.xml.in \
 | 
			
		||||
	org.gnome.shell.gschema.xml.in
 | 
			
		||||
 | 
			
		||||
CLEANFILES =						\
 | 
			
		||||
	gnome-shell.desktop.in				\
 | 
			
		||||
	gnome-shell-clock-preferences.desktop.in	\
 | 
			
		||||
	$(desktop_DATA)					\
 | 
			
		||||
	$(gsettings_SCHEMAS)				\
 | 
			
		||||
	gschemas.compiled
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										188
									
								
								data/clock-preferences.ui
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,188 @@
 | 
			
		||||
<?xml version="1.0"?>
 | 
			
		||||
<interface domain="gnome-shell">
 | 
			
		||||
  <requires lib="gtk+" version="2.16"/>
 | 
			
		||||
  <!-- interface-naming-policy project-wide -->
 | 
			
		||||
  <object class="GtkDialog" id="prefs-dialog">
 | 
			
		||||
    <property name="border_width">5</property>
 | 
			
		||||
    <property name="title" translatable="yes">Clock Preferences</property>
 | 
			
		||||
    <property name="window_position">center</property>
 | 
			
		||||
    <property name="type_hint">normal</property>
 | 
			
		||||
    <property name="has_separator">False</property>
 | 
			
		||||
    <child internal-child="vbox">
 | 
			
		||||
      <object class="GtkVBox" id="dialog-vbox1">
 | 
			
		||||
        <property name="visible">True</property>
 | 
			
		||||
        <property name="orientation">vertical</property>
 | 
			
		||||
        <property name="spacing">2</property>
 | 
			
		||||
        <child>
 | 
			
		||||
          <object class="GtkVBox" id="vbox1">
 | 
			
		||||
            <property name="visible">True</property>
 | 
			
		||||
            <property name="orientation">vertical</property>
 | 
			
		||||
            <property name="spacing">18</property>
 | 
			
		||||
            <child>
 | 
			
		||||
              <object class="GtkFrame" id="frame1">
 | 
			
		||||
                <property name="visible">True</property>
 | 
			
		||||
                <property name="label_xalign">0</property>
 | 
			
		||||
                <property name="shadow_type">none</property>
 | 
			
		||||
                <child>
 | 
			
		||||
                  <object class="GtkAlignment" id="alignment1">
 | 
			
		||||
                    <property name="visible">True</property>
 | 
			
		||||
                    <property name="top_padding">6</property>
 | 
			
		||||
                    <property name="left_padding">12</property>
 | 
			
		||||
                    <property name="right_padding">6</property>
 | 
			
		||||
                    <child>
 | 
			
		||||
                      <object class="GtkHBox" id="hbox1">
 | 
			
		||||
                        <property name="visible">True</property>
 | 
			
		||||
                        <property name="spacing">12</property>
 | 
			
		||||
                        <child>
 | 
			
		||||
                          <object class="GtkRadioButton" id="12hr_radio">
 | 
			
		||||
                            <property name="label" translatable="yes">_12 hour format</property>
 | 
			
		||||
                            <property name="visible">True</property>
 | 
			
		||||
                            <property name="can_focus">True</property>
 | 
			
		||||
                            <property name="receives_default">False</property>
 | 
			
		||||
                            <property name="use_underline">True</property>
 | 
			
		||||
                            <property name="draw_indicator">True</property>
 | 
			
		||||
                          </object>
 | 
			
		||||
                          <packing>
 | 
			
		||||
                            <property name="expand">False</property>
 | 
			
		||||
                            <property name="fill">False</property>
 | 
			
		||||
                            <property name="position">0</property>
 | 
			
		||||
                          </packing>
 | 
			
		||||
                        </child>
 | 
			
		||||
                        <child>
 | 
			
		||||
                          <object class="GtkRadioButton" id="24hr_radio">
 | 
			
		||||
                            <property name="label" translatable="yes">_24 hour format</property>
 | 
			
		||||
                            <property name="visible">True</property>
 | 
			
		||||
                            <property name="can_focus">True</property>
 | 
			
		||||
                            <property name="receives_default">False</property>
 | 
			
		||||
                            <property name="use_underline">True</property>
 | 
			
		||||
                            <property name="draw_indicator">True</property>
 | 
			
		||||
                            <property name="group">12hr_radio</property>
 | 
			
		||||
                          </object>
 | 
			
		||||
                          <packing>
 | 
			
		||||
                            <property name="expand">False</property>
 | 
			
		||||
                            <property name="fill">False</property>
 | 
			
		||||
                            <property name="position">1</property>
 | 
			
		||||
                          </packing>
 | 
			
		||||
                        </child>
 | 
			
		||||
                      </object>
 | 
			
		||||
                    </child>
 | 
			
		||||
                  </object>
 | 
			
		||||
                </child>
 | 
			
		||||
                <child type="label">
 | 
			
		||||
                  <object class="GtkLabel" id="label_format">
 | 
			
		||||
                    <property name="visible">True</property>
 | 
			
		||||
                    <property name="label" translatable="yes">Clock Format</property>
 | 
			
		||||
                    <attributes>
 | 
			
		||||
                      <attribute name="weight" value="bold"/>
 | 
			
		||||
                    </attributes>
 | 
			
		||||
                  </object>
 | 
			
		||||
                </child>
 | 
			
		||||
              </object>
 | 
			
		||||
              <packing>
 | 
			
		||||
                <property name="expand">False</property>
 | 
			
		||||
                <property name="fill">False</property>
 | 
			
		||||
                <property name="position">0</property>
 | 
			
		||||
              </packing>
 | 
			
		||||
            </child>
 | 
			
		||||
            <child>
 | 
			
		||||
              <object class="GtkFrame" id="frame2">
 | 
			
		||||
                <property name="visible">True</property>
 | 
			
		||||
                <property name="label_xalign">0</property>
 | 
			
		||||
                <property name="shadow_type">none</property>
 | 
			
		||||
                <child>
 | 
			
		||||
                  <object class="GtkAlignment" id="alignment2">
 | 
			
		||||
                    <property name="visible">True</property>
 | 
			
		||||
                    <property name="top_padding">6</property>
 | 
			
		||||
                    <property name="left_padding">12</property>
 | 
			
		||||
                    <child>
 | 
			
		||||
                      <object class="GtkVBox" id="vbox2">
 | 
			
		||||
                        <property name="visible">True</property>
 | 
			
		||||
                        <property name="orientation">vertical</property>
 | 
			
		||||
                        <property name="spacing">6</property>
 | 
			
		||||
                        <child>
 | 
			
		||||
                          <object class="GtkCheckButton" id="date_check">
 | 
			
		||||
                            <property name="label" translatable="yes">Show the _date</property>
 | 
			
		||||
                            <property name="visible">True</property>
 | 
			
		||||
                            <property name="can_focus">True</property>
 | 
			
		||||
                            <property name="receives_default">False</property>
 | 
			
		||||
                            <property name="use_underline">True</property>
 | 
			
		||||
                            <property name="draw_indicator">True</property>
 | 
			
		||||
                          </object>
 | 
			
		||||
                          <packing>
 | 
			
		||||
                            <property name="position">0</property>
 | 
			
		||||
                          </packing>
 | 
			
		||||
                        </child>
 | 
			
		||||
                        <child>
 | 
			
		||||
                          <object class="GtkCheckButton" id="seconds_check">
 | 
			
		||||
                            <property name="label" translatable="yes">Show seco_nds</property>
 | 
			
		||||
                            <property name="visible">True</property>
 | 
			
		||||
                            <property name="can_focus">True</property>
 | 
			
		||||
                            <property name="receives_default">False</property>
 | 
			
		||||
                            <property name="use_underline">True</property>
 | 
			
		||||
                            <property name="draw_indicator">True</property>
 | 
			
		||||
                          </object>
 | 
			
		||||
                          <packing>
 | 
			
		||||
                            <property name="position">1</property>
 | 
			
		||||
                          </packing>
 | 
			
		||||
                        </child>
 | 
			
		||||
                      </object>
 | 
			
		||||
                    </child>
 | 
			
		||||
                  </object>
 | 
			
		||||
                </child>
 | 
			
		||||
                <child type="label">
 | 
			
		||||
                  <object class="GtkLabel" id="label_display">
 | 
			
		||||
                    <property name="visible">True</property>
 | 
			
		||||
                    <property name="label" translatable="yes">Panel Display</property>
 | 
			
		||||
                    <attributes>
 | 
			
		||||
                      <attribute name="weight" value="bold"/>
 | 
			
		||||
                    </attributes>
 | 
			
		||||
                  </object>
 | 
			
		||||
                </child>
 | 
			
		||||
              </object>
 | 
			
		||||
              <packing>
 | 
			
		||||
                <property name="expand">False</property>
 | 
			
		||||
                <property name="fill">False</property>
 | 
			
		||||
                <property name="position">1</property>
 | 
			
		||||
              </packing>
 | 
			
		||||
            </child>
 | 
			
		||||
          </object>
 | 
			
		||||
          <packing>
 | 
			
		||||
            <property name="padding">6</property>
 | 
			
		||||
            <property name="position">1</property>
 | 
			
		||||
          </packing>
 | 
			
		||||
        </child>
 | 
			
		||||
        <child internal-child="action_area">
 | 
			
		||||
          <object class="GtkHButtonBox" id="dialog-action_area1">
 | 
			
		||||
            <property name="visible">True</property>
 | 
			
		||||
            <property name="layout_style">end</property>
 | 
			
		||||
            <child>
 | 
			
		||||
              <placeholder/>
 | 
			
		||||
            </child>
 | 
			
		||||
            <child>
 | 
			
		||||
              <object class="GtkButton" id="prefs_close_button">
 | 
			
		||||
                <property name="label">gtk-close</property>
 | 
			
		||||
                <property name="visible">True</property>
 | 
			
		||||
                <property name="can_focus">True</property>
 | 
			
		||||
                <property name="receives_default">True</property>
 | 
			
		||||
                <property name="use_stock">True</property>
 | 
			
		||||
              </object>
 | 
			
		||||
              <packing>
 | 
			
		||||
                <property name="expand">False</property>
 | 
			
		||||
                <property name="fill">False</property>
 | 
			
		||||
                <property name="position">1</property>
 | 
			
		||||
              </packing>
 | 
			
		||||
            </child>
 | 
			
		||||
          </object>
 | 
			
		||||
          <packing>
 | 
			
		||||
            <property name="expand">False</property>
 | 
			
		||||
            <property name="pack_type">end</property>
 | 
			
		||||
            <property name="position">0</property>
 | 
			
		||||
          </packing>
 | 
			
		||||
        </child>
 | 
			
		||||
      </object>
 | 
			
		||||
    </child>
 | 
			
		||||
    <action-widgets>
 | 
			
		||||
      <action-widget response="0">prefs_close_button</action-widget>
 | 
			
		||||
    </action-widgets>
 | 
			
		||||
  </object>
 | 
			
		||||
</interface>
 | 
			
		||||
							
								
								
									
										66
									
								
								data/close-black.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,66 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<!-- Generator: Adobe Illustrator 13.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 14948)  -->
 | 
			
		||||
 | 
			
		||||
<svg
 | 
			
		||||
   xmlns:dc="http://purl.org/dc/elements/1.1/"
 | 
			
		||||
   xmlns:cc="http://creativecommons.org/ns#"
 | 
			
		||||
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 | 
			
		||||
   xmlns:svg="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 | 
			
		||||
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
 | 
			
		||||
   version="1.1"
 | 
			
		||||
   id="Foreground"
 | 
			
		||||
   x="0px"
 | 
			
		||||
   y="0px"
 | 
			
		||||
   width="16px"
 | 
			
		||||
   height="16px"
 | 
			
		||||
   viewBox="0 0 16 16"
 | 
			
		||||
   enable-background="new 0 0 16 16"
 | 
			
		||||
   xml:space="preserve"
 | 
			
		||||
   sodipodi:version="0.32"
 | 
			
		||||
   inkscape:version="0.46+devel"
 | 
			
		||||
   sodipodi:docname="close-black.svg"
 | 
			
		||||
   inkscape:output_extension="org.inkscape.output.svg.inkscape"><metadata
 | 
			
		||||
   id="metadata2399"><rdf:RDF><cc:Work
 | 
			
		||||
       rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
 | 
			
		||||
         rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
 | 
			
		||||
   id="defs2397"><linearGradient
 | 
			
		||||
     id="linearGradient3173"><stop
 | 
			
		||||
       style="stop-color:#c4c4c4;stop-opacity:1;"
 | 
			
		||||
       offset="0"
 | 
			
		||||
       id="stop3175" /><stop
 | 
			
		||||
       style="stop-color:#ffffff;stop-opacity:1;"
 | 
			
		||||
       offset="1"
 | 
			
		||||
       id="stop3177" /></linearGradient><inkscape:perspective
 | 
			
		||||
     sodipodi:type="inkscape:persp3d"
 | 
			
		||||
     inkscape:vp_x="0 : 8 : 1"
 | 
			
		||||
     inkscape:vp_y="0 : 1000 : 0"
 | 
			
		||||
     inkscape:vp_z="16 : 8 : 1"
 | 
			
		||||
     inkscape:persp3d-origin="8 : 5.3333333 : 1"
 | 
			
		||||
     id="perspective2401" /></defs><sodipodi:namedview
 | 
			
		||||
   inkscape:window-height="811"
 | 
			
		||||
   inkscape:window-width="1272"
 | 
			
		||||
   inkscape:pageshadow="2"
 | 
			
		||||
   inkscape:pageopacity="0.0"
 | 
			
		||||
   guidetolerance="10.0"
 | 
			
		||||
   gridtolerance="10.0"
 | 
			
		||||
   objecttolerance="10.0"
 | 
			
		||||
   borderopacity="1.0"
 | 
			
		||||
   bordercolor="#666666"
 | 
			
		||||
   pagecolor="#ffffff"
 | 
			
		||||
   id="base"
 | 
			
		||||
   showgrid="false"
 | 
			
		||||
   inkscape:zoom="32.125"
 | 
			
		||||
   inkscape:cx="8"
 | 
			
		||||
   inkscape:cy="10.440056"
 | 
			
		||||
   inkscape:window-x="40"
 | 
			
		||||
   inkscape:window-y="40"
 | 
			
		||||
   inkscape:current-layer="Foreground" />
 | 
			
		||||
<path
 | 
			
		||||
   fill-rule="evenodd"
 | 
			
		||||
   clip-rule="evenodd"
 | 
			
		||||
   d="M10.5,3.5l2,2L10,8l2.5,2.5l-2,2L8,10l-2.5,2.5l-2-2L6,8L3.5,5.5l2-2L8,6L10.5,3.5  z M0,8c0-4.418,3.582-8,8-8s8,3.582,8,8s-3.582,8-8,8S0,12.418,0,8z"
 | 
			
		||||
   id="path2394"
 | 
			
		||||
   style="fill-opacity:1;fill:#545454" />
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 2.3 KiB  | 
							
								
								
									
										15
									
								
								data/gnome-shell-clock-preferences.desktop.in.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,15 @@
 | 
			
		||||
[Desktop Entry]
 | 
			
		||||
_Name=Clock
 | 
			
		||||
_Comment=Customize the panel clock
 | 
			
		||||
Exec=@bindir@/gnome-shell-clock-preferences
 | 
			
		||||
Icon=gnome-panel-clock
 | 
			
		||||
Terminal=false
 | 
			
		||||
Type=Application
 | 
			
		||||
StartupNotify=true
 | 
			
		||||
Categories=GNOME;GTK;Settings;DesktopSettings;
 | 
			
		||||
OnlyShowIn=GNOME;
 | 
			
		||||
X-GNOME-ShellOnly=true
 | 
			
		||||
X-GNOME-Bugzilla-Bugzilla=GNOME
 | 
			
		||||
X-GNOME-Bugzilla-Product=gnome-shell
 | 
			
		||||
X-GNOME-Bugzilla-Component=general
 | 
			
		||||
X-GNOME-Bugzilla-Version=@VERSION@
 | 
			
		||||
@@ -7,10 +7,9 @@ X-GNOME-Bugzilla-Bugzilla=GNOME
 | 
			
		||||
X-GNOME-Bugzilla-Product=gnome-shell
 | 
			
		||||
X-GNOME-Bugzilla-Component=general
 | 
			
		||||
X-GNOME-Bugzilla-Version=@VERSION@
 | 
			
		||||
Categories=GNOME;GTK;Core;
 | 
			
		||||
Categories=GNOME;GTK;Utility;Core;
 | 
			
		||||
OnlyShowIn=GNOME;
 | 
			
		||||
NoDisplay=true
 | 
			
		||||
X-GNOME-Autostart-Phase=WindowManager
 | 
			
		||||
X-GNOME-Provides=panel;windowmanager;
 | 
			
		||||
X-GNOME-Autostart-Notify=true
 | 
			
		||||
X-GNOME-AutoRestart=true
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@
 | 
			
		||||
        <applyto>/desktop/gnome/shell/windows/button_layout</applyto>
 | 
			
		||||
        <owner>gnome-shell</owner>
 | 
			
		||||
        <type>string</type>
 | 
			
		||||
        <default>:close</default>
 | 
			
		||||
        <default>:minimize,maximize,close</default>
 | 
			
		||||
        <locale name="C">
 | 
			
		||||
           <short>Arrangement of buttons on the titlebar</short>
 | 
			
		||||
           <long>
 | 
			
		||||
@@ -44,20 +44,19 @@
 | 
			
		||||
      </schema>
 | 
			
		||||
 | 
			
		||||
      <schema>
 | 
			
		||||
        <key>/schemas/desktop/gnome/shell/windows/edge_tiling</key>
 | 
			
		||||
        <applyto>/desktop/gnome/shell/windows/edge_tiling</applyto>
 | 
			
		||||
        <key>/schemas/desktop/gnome/shell/windows/side_by_side_tiling</key>
 | 
			
		||||
        <applyto>/desktop/gnome/shell/windows/side_by_side_tiling</applyto>
 | 
			
		||||
        <owner>gnome-shell</owner>
 | 
			
		||||
        <type>bool</type>
 | 
			
		||||
        <default>true</default>
 | 
			
		||||
        <locale name="C">
 | 
			
		||||
          <short>enable edge tiling when dropping windows on screen edges</short>
 | 
			
		||||
          <short>enable side-by-side tiling when dropping windows on screen edges</short>
 | 
			
		||||
          <long>
 | 
			
		||||
             If enabled, dropping windows on vertical screen edges maximizes them
 | 
			
		||||
             If enabled, dropping windows on screen edges maximizes them
 | 
			
		||||
             vertically and resizes them horizontally to cover half of the
 | 
			
		||||
             available area. Dropping windows on the top screen edge maximizes them
 | 
			
		||||
             completely.
 | 
			
		||||
             available area.
 | 
			
		||||
 | 
			
		||||
             This key overrides /apps/metacity/general/edge_tiling when
 | 
			
		||||
             This key overrides /apps/metacity/general/side_by_side_tiling when
 | 
			
		||||
             running GNOME Shell.
 | 
			
		||||
          </long>
 | 
			
		||||
        </locale>
 | 
			
		||||
@@ -81,20 +80,5 @@
 | 
			
		||||
        </locale>
 | 
			
		||||
      </schema>
 | 
			
		||||
 | 
			
		||||
      <schema>
 | 
			
		||||
        <key>/schemas/desktop/gnome/shell/windows/workspaces_only_on_primary</key>
 | 
			
		||||
        <applyto>/desktop/gnome/shell/windows/workspaces_only_on_primary</applyto>
 | 
			
		||||
        <owner>gnome-shell</owner>
 | 
			
		||||
        <type>bool</type>
 | 
			
		||||
        <default>true</default>
 | 
			
		||||
        <locale name="C">
 | 
			
		||||
          <short>Workspaces only on primary monitor</short>
 | 
			
		||||
          <long>
 | 
			
		||||
             This key overrides /apps/mutter/general/workspaces_only_on_primary when
 | 
			
		||||
             running GNOME Shell.
 | 
			
		||||
          </long>
 | 
			
		||||
        </locale>
 | 
			
		||||
      </schema>
 | 
			
		||||
 | 
			
		||||
  </schemalist>
 | 
			
		||||
</gconfschemafile>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										45
									
								
								data/gs-applications.menu
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,45 @@
 | 
			
		||||
<Menu>
 | 
			
		||||
    <DefaultLayout>
 | 
			
		||||
        <Menuname>Apps</Menuname>
 | 
			
		||||
        <Menuname>Games</Menuname>
 | 
			
		||||
        <Menuname>Tools</Menuname>
 | 
			
		||||
    </DefaultLayout>
 | 
			
		||||
	<Name>Applications</Name>
 | 
			
		||||
	<AppDir>/usr/local/share/applications</AppDir>
 | 
			
		||||
	<DefaultAppDirs/>
 | 
			
		||||
	<Menu>
 | 
			
		||||
		<Name>Games</Name>
 | 
			
		||||
		<Include>
 | 
			
		||||
			<And>
 | 
			
		||||
				<Category>Game</Category>
 | 
			
		||||
			</And>
 | 
			
		||||
		</Include>
 | 
			
		||||
	</Menu>
 | 
			
		||||
	<Menu>
 | 
			
		||||
		<Name>Tools</Name>
 | 
			
		||||
		<Include>
 | 
			
		||||
			<Category>Development</Category>
 | 
			
		||||
			<And>
 | 
			
		||||
				<Category>System</Category>
 | 
			
		||||
				<Not>
 | 
			
		||||
					<Category>Settings</Category>
 | 
			
		||||
				</Not>
 | 
			
		||||
			</And>
 | 
			
		||||
			<Category>Utility</Category>
 | 
			
		||||
		</Include>
 | 
			
		||||
	</Menu>
 | 
			
		||||
	<Menu>
 | 
			
		||||
		<Name>Apps</Name>
 | 
			
		||||
		<OnlyUnallocated/>
 | 
			
		||||
		<Include>
 | 
			
		||||
			<And>
 | 
			
		||||
				<Or>
 | 
			
		||||
					<Category>Documentation</Category>
 | 
			
		||||
					<Not><Category>Core</Category></Not>
 | 
			
		||||
				</Or>
 | 
			
		||||
				<Not><Category>Settings</Category></Not>
 | 
			
		||||
				<Not><Category>Screensaver</Category></Not>
 | 
			
		||||
			</And>
 | 
			
		||||
		</Include>
 | 
			
		||||
	</Menu>
 | 
			
		||||
</Menu>
 | 
			
		||||
							
								
								
									
										80
									
								
								data/magnifier.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,80 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<!-- Generator: Adobe Illustrator 13.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 14948)  -->
 | 
			
		||||
 | 
			
		||||
<svg
 | 
			
		||||
   xmlns:dc="http://purl.org/dc/elements/1.1/"
 | 
			
		||||
   xmlns:cc="http://creativecommons.org/ns#"
 | 
			
		||||
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 | 
			
		||||
   xmlns:svg="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 | 
			
		||||
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
 | 
			
		||||
   version="1.0"
 | 
			
		||||
   id="Foreground"
 | 
			
		||||
   x="0px"
 | 
			
		||||
   y="0px"
 | 
			
		||||
   width="18"
 | 
			
		||||
   height="18"
 | 
			
		||||
   viewBox="0 0 18 18"
 | 
			
		||||
   enable-background="new 0 0 29 18"
 | 
			
		||||
   xml:space="preserve"
 | 
			
		||||
   sodipodi:version="0.32"
 | 
			
		||||
   inkscape:version="0.46+devel"
 | 
			
		||||
   sodipodi:docname="magnifier.svg"
 | 
			
		||||
   inkscape:output_extension="org.inkscape.output.svg.inkscape"><metadata
 | 
			
		||||
   id="metadata16"><rdf:RDF><cc:Work
 | 
			
		||||
       rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
 | 
			
		||||
         rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
 | 
			
		||||
   id="defs14"><inkscape:perspective
 | 
			
		||||
     sodipodi:type="inkscape:persp3d"
 | 
			
		||||
     inkscape:vp_x="0 : 9 : 1"
 | 
			
		||||
     inkscape:vp_y="0 : 1000 : 0"
 | 
			
		||||
     inkscape:vp_z="29 : 9 : 1"
 | 
			
		||||
     inkscape:persp3d-origin="14.5 : 6 : 1"
 | 
			
		||||
     id="perspective18" /></defs><sodipodi:namedview
 | 
			
		||||
   inkscape:window-height="728"
 | 
			
		||||
   inkscape:window-width="1103"
 | 
			
		||||
   inkscape:pageshadow="2"
 | 
			
		||||
   inkscape:pageopacity="1"
 | 
			
		||||
   guidetolerance="10.0"
 | 
			
		||||
   gridtolerance="10.0"
 | 
			
		||||
   objecttolerance="10.0"
 | 
			
		||||
   borderopacity="1.0"
 | 
			
		||||
   bordercolor="#666666"
 | 
			
		||||
   pagecolor="#000000"
 | 
			
		||||
   id="base"
 | 
			
		||||
   showgrid="true"
 | 
			
		||||
   inkscape:zoom="27.260185"
 | 
			
		||||
   inkscape:cx="9.5844061"
 | 
			
		||||
   inkscape:cy="9.4435574"
 | 
			
		||||
   inkscape:window-x="142"
 | 
			
		||||
   inkscape:window-y="26"
 | 
			
		||||
   inkscape:current-layer="Foreground"
 | 
			
		||||
   inkscape:snap-global="true"
 | 
			
		||||
   showguides="false"><inkscape:grid
 | 
			
		||||
     type="xygrid"
 | 
			
		||||
     id="grid2391"
 | 
			
		||||
     empspacing="5"
 | 
			
		||||
     visible="true"
 | 
			
		||||
     enabled="true"
 | 
			
		||||
     snapvisiblegridlinesonly="true" /></sodipodi:namedview>
 | 
			
		||||
 | 
			
		||||
<g
 | 
			
		||||
   id="g5"
 | 
			
		||||
   style="fill:#ffffff;fill-opacity:1"
 | 
			
		||||
   transform="translate(-4,-0.023114)">
 | 
			
		||||
	<path
 | 
			
		||||
   d="m 6.246,13.98 c -0.319,-0.319 -0.319,-0.837 0,-1.157 L 9.963,9.106 c 0.319,-0.319 0.837,-0.319 1.157,0 l 0.786,0.787 c 0.32,0.319 0.32,0.837 0,1.157 l -3.717,3.717 c -0.32,0.319 -0.838,0.319 -1.157,0 l -0.786,-0.787 0,0 z"
 | 
			
		||||
   id="path7"
 | 
			
		||||
   style="fill:#ffffff;fill-opacity:1" />
 | 
			
		||||
	<path
 | 
			
		||||
   d="M 9.076,11.937"
 | 
			
		||||
   id="path9"
 | 
			
		||||
   style="fill:#ffffff;fill-opacity:1" />
 | 
			
		||||
</g>
 | 
			
		||||
<path
 | 
			
		||||
   clip-rule="evenodd"
 | 
			
		||||
   d="m 7.25,7.476886 c 0,-1.243 1.007,-2.25 2.2499998,-2.25 1.2430002,0 2.2500002,1.007 2.2500002,2.25 0,1.243 -1.007,2.25 -2.2500002,2.25 C 8.257,9.726886 7.25,8.719886 7.25,7.476886 z m -2.25,0 c 0,-2.485 2.015,-4.5 4.4999998,-4.5 2.4850002,0 4.5000002,2.015 4.5000002,4.5 0,2.4849998 -2.015,4.5 -4.5000002,4.5 C 7.015,11.976886 5,9.9618858 5,7.476886 z"
 | 
			
		||||
   id="path11"
 | 
			
		||||
   style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd" />
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 2.9 KiB  | 
							
								
								
									
										133
									
								
								data/org.gnome.accessibility.magnifier.gschema.xml.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,133 @@
 | 
			
		||||
<schemalist>
 | 
			
		||||
 | 
			
		||||
  <enum id="MouseTrackingMode">
 | 
			
		||||
    <value nick="none"         value="0"/>
 | 
			
		||||
    <value nick="centered"     value="1"/>
 | 
			
		||||
    <value nick="proportional" value="2"/>
 | 
			
		||||
    <value nick="push"         value="3"/>
 | 
			
		||||
  </enum>
 | 
			
		||||
 | 
			
		||||
  <enum id="ScreenPosition">
 | 
			
		||||
    <value nick="none"        value="0"/>
 | 
			
		||||
    <value nick="full-screen" value="1"/>
 | 
			
		||||
    <value nick="top-half"    value="2"/>
 | 
			
		||||
    <value nick="bottom-half" value="3"/>
 | 
			
		||||
    <value nick="left-half"   value="4"/>
 | 
			
		||||
    <value nick="right-half"  value="5"/>
 | 
			
		||||
  </enum>
 | 
			
		||||
 | 
			
		||||
  <schema id="org.gnome.accessibility.magnifier"
 | 
			
		||||
          path="/desktop/gnome/accessibility/magnifier/"
 | 
			
		||||
          gettext-domain="@GETTEXT_PACKAGE@">
 | 
			
		||||
    <key name="show-magnifier" type="b">
 | 
			
		||||
      <default>false</default>
 | 
			
		||||
      <_summary>Show or hide the magnifier</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        Show or hide the magnifier and all of its zoom regions.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="mouse-tracking" enum="MouseTrackingMode">
 | 
			
		||||
      <default>'proportional'</default>
 | 
			
		||||
      <_summary>Mouse Tracking Mode</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        Determines the position of the magnified mouse image within the
 | 
			
		||||
        magnified view and how it reacts to system mouse movement. The values
 | 
			
		||||
        are
 | 
			
		||||
        - none: no mouse tracking;
 | 
			
		||||
        - centered: the mouse image is
 | 
			
		||||
        displayed at the center of the zoom region (which also represents
 | 
			
		||||
        the point under the system mouse) and the magnified contents are
 | 
			
		||||
        scrolled as the system mouse moves;
 | 
			
		||||
        - proportional: the position of the magnified mouse in the zoom region
 | 
			
		||||
        is proportionally the same as the position of the system mouse on screen;
 | 
			
		||||
        - push: when the magnified mouse intersects a boundary of the zoom
 | 
			
		||||
        region, the contents are scrolled into view.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="screen-position" enum="ScreenPosition">
 | 
			
		||||
      <default>'full-screen'</default>
 | 
			
		||||
      <_summary>Screen position</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        The magnified view either fills the entire screen, or occupies the
 | 
			
		||||
        top-half, bottom-half, left-half, or right-half of the screen.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="mag-factor" type="d">
 | 
			
		||||
      <default>2.0</default>
 | 
			
		||||
      <_summary>Magnification factor</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        The power of the magnification. A value of 1.0 means no magnification.
 | 
			
		||||
        A value of 2.0 doubles the size.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="lens-mode" type="b">
 | 
			
		||||
      <default>false</default>
 | 
			
		||||
      <_summary>Enable lens mode</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        Whether the magnified view should be centered over the location of
 | 
			
		||||
        the system mouse and move with it.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="scroll-at-edges" type="b">
 | 
			
		||||
      <default>false</default>
 | 
			
		||||
      <_summary>
 | 
			
		||||
        Scroll magnified contents beyond the edges of the desktop
 | 
			
		||||
      </_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        For centered mouse tracking, when the system pointer is at or near the
 | 
			
		||||
        edge of the screen, the magnified contents continue to scroll such that
 | 
			
		||||
        the screen edge moves into the magnified view.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
 | 
			
		||||
    <!-- Cross-hairs -->
 | 
			
		||||
    <key name="show-cross-hairs" type="b">
 | 
			
		||||
      <default>false</default>
 | 
			
		||||
      <_summary>Show or hide crosshairs</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        Enables/disables display of crosshairs centered on the magnified
 | 
			
		||||
        mouse sprite.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="cross-hairs-thickness" type="i">
 | 
			
		||||
      <default>8</default>
 | 
			
		||||
      <_summary>Thickness of the crosshairs</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        Width of the vertical and horizontal lines that make up the crosshairs.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="cross-hairs-color" type="s">
 | 
			
		||||
      <default>'#ff0000'</default>
 | 
			
		||||
      <_summary>Color of the crosshairs</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        The color of the the vertical and horizontal lines that make up
 | 
			
		||||
        the crosshairs.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="cross-hairs-opacity" type="i">
 | 
			
		||||
      <default>169</default>
 | 
			
		||||
      <_summary>Opacity of the crosshairs</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        Determines the transparency of the crosshairs, from fully opaque
 | 
			
		||||
        to fully transparent.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="cross-hairs-length" type="i">
 | 
			
		||||
      <default>4096</default>
 | 
			
		||||
      <_summary>Length of the crosshairs</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        Determines the length of the vertical and horizontal lines that
 | 
			
		||||
        make up the crosshairs.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="cross-hairs-clip" type="b">
 | 
			
		||||
      <default>false</default>
 | 
			
		||||
      <_summary>Clip the crosshairs at the center</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        Determines whether the crosshairs intersect the magnified mouse sprite,
 | 
			
		||||
        or are clipped such that the ends of the horizontal and vertical lines
 | 
			
		||||
        surround the mouse image.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
  </schema>
 | 
			
		||||
</schemalist>
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<schemalist>
 | 
			
		||||
  <schema id="org.gnome.shell" path="/org/gnome/shell/"
 | 
			
		||||
  <schema id="org.gnome.shell" path="/apps/gnome-shell/"
 | 
			
		||||
          gettext-domain="@GETTEXT_PACKAGE@">
 | 
			
		||||
    <key name="development-tools" type="b">
 | 
			
		||||
      <default>true</default>
 | 
			
		||||
@@ -15,18 +15,8 @@
 | 
			
		||||
      <default>[]</default>
 | 
			
		||||
      <_summary>Uuids of extensions to disable</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        GNOME Shell extensions have a uuid property; this key lists extensions
 | 
			
		||||
        which should not be loaded.  This setting overrides enabled-extensions
 | 
			
		||||
        for extensions that appear in both lists.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="enabled-extensions" type="as">
 | 
			
		||||
      <default>[]</default>
 | 
			
		||||
      <_summary>Uuids of extensions to enable</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        GNOME Shell extensions have a uuid property; this key lists extensions
 | 
			
		||||
        which should be loaded.  disabled-extensions overrides this setting for
 | 
			
		||||
        extensions that appear in both lists.
 | 
			
		||||
        GNOME Shell extensions have a uuid property;
 | 
			
		||||
        this key lists extensions which should not be loaded.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="enable-app-monitoring" type="b">
 | 
			
		||||
@@ -40,31 +30,23 @@
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="favorite-apps" type="as">
 | 
			
		||||
      <default>[ 'mozilla-firefox.desktop', 'evolution.desktop', 'empathy.desktop', 'rhythmbox.desktop', 'shotwell.desktop', 'openoffice.org-writer.desktop', 'nautilus.desktop' ]</default>
 | 
			
		||||
      <default>[ 'mozilla-firefox.desktop', 'evolution.desktop', 'openoffice.org-writer.desktop' ]</default>
 | 
			
		||||
      <_summary>List of desktop file IDs for favorite applications</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        The applications corresponding to these identifiers
 | 
			
		||||
        will be displayed in the favorites area.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="disabled-open-search-providers" type="as">
 | 
			
		||||
      <default>[]</default>
 | 
			
		||||
      <_summary>disabled OpenSearch providers</_summary>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="command-history" type="as">
 | 
			
		||||
      <default>[]</default>
 | 
			
		||||
      <_summary>History for command (Alt-F2) dialog</_summary>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="looking-glass-history" type="as">
 | 
			
		||||
      <default>[]</default>
 | 
			
		||||
      <_summary>History for the looking glass dialog</_summary>
 | 
			
		||||
    </key>
 | 
			
		||||
    <child name="clock" schema="org.gnome.shell.clock"/>
 | 
			
		||||
    <child name="calendar" schema="org.gnome.shell.calendar"/>
 | 
			
		||||
    <child name="recorder" schema="org.gnome.shell.recorder"/>
 | 
			
		||||
  </schema>
 | 
			
		||||
 | 
			
		||||
  <schema id="org.gnome.shell.calendar" path="/org/gnome/shell/calendar/"
 | 
			
		||||
  <schema id="org.gnome.shell.calendar" path="/apps/gnome-shell/calendar/"
 | 
			
		||||
          gettext-domain="@GETTEXT_PACKAGE@">
 | 
			
		||||
    <key name="show-weekdate" type="b">
 | 
			
		||||
      <default>false</default>
 | 
			
		||||
@@ -75,25 +57,58 @@
 | 
			
		||||
      </key>
 | 
			
		||||
  </schema>
 | 
			
		||||
 | 
			
		||||
  <schema id="org.gnome.shell.clock" path="/org/gnome/shell/clock/"
 | 
			
		||||
  <schema id="org.gnome.shell.clock" path="/apps/gnome-shell/clock/"
 | 
			
		||||
          gettext-domain="@GETTEXT_PACKAGE@">
 | 
			
		||||
    <key name="format" type="s">
 | 
			
		||||
      <default l10n="messages" context="hour_format">
 | 
			
		||||
      <!-- TRANSLATORS: This is the default hour format, choose ONLY '12-hour' or '24-hour'. -->
 | 
			
		||||
        "12-hour"
 | 
			
		||||
      </default>
 | 
			
		||||
      <_summary>Hour format</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        This key specifies the hour format used by the panel clock.
 | 
			
		||||
        Possible values are "12-hour", "24-hour", "unix" and "custom". If set
 | 
			
		||||
        to "unix", the clock will display time in seconds since Epoch,
 | 
			
		||||
        i.e. 1970-01-01. If set to "custom", the clock will display time
 | 
			
		||||
        according to the format specified in the custom_format key. Note that
 | 
			
		||||
        if set to either "unix" or "custom", the show_date and show_seconds
 | 
			
		||||
        keys are ignored.
 | 
			
		||||
      </_description>
 | 
			
		||||
      <choices>
 | 
			
		||||
        <choice value="12-hour"/>
 | 
			
		||||
        <choice value="24-hour"/>
 | 
			
		||||
        <choice value="unix"/>
 | 
			
		||||
        <choice value="custom"/>
 | 
			
		||||
      </choices>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="custom-format" type="s">
 | 
			
		||||
      <default>''</default>
 | 
			
		||||
      <_summary>Custom format of the clock</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        This key specifies the format used by the panel clock when the format
 | 
			
		||||
        key is set to "custom". You can use conversion specifiers understood
 | 
			
		||||
        by strftime() to obtain a specific format. See the strftime() manual
 | 
			
		||||
        for more information.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="show-seconds" type="b">
 | 
			
		||||
      <default>false</default>
 | 
			
		||||
      <_summary>Show time with seconds</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        If true, display seconds in time.
 | 
			
		||||
        If true and format is either "12-hour" or "24-hour", display seconds in time.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="show-date" type="b">
 | 
			
		||||
      <default>false</default>
 | 
			
		||||
      <_summary>Show date in clock</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        If true, display date in the clock, in addition to time.
 | 
			
		||||
        If true and format is either "12-hour" or "24-hour",
 | 
			
		||||
        display date in the clock, in addition to time.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
  </schema>
 | 
			
		||||
 | 
			
		||||
  <schema id="org.gnome.shell.recorder" path="/org/gnome/shell/recorder/"
 | 
			
		||||
  <schema id="org.gnome.shell.recorder" path="/apps/gnome-shell/recorder/"
 | 
			
		||||
          gettext-domain="@GETTEXT_PACKAGE@">
 | 
			
		||||
    <key name="framerate" type="i">
 | 
			
		||||
      <default>15</default>
 | 
			
		||||
@@ -115,13 +130,11 @@
 | 
			
		||||
        take care of its own output - this might be used to send the output
 | 
			
		||||
        to an icecast server via shout2send or similar. When unset or set
 | 
			
		||||
        to an empty value, the default pipeline will be used. This is currently
 | 
			
		||||
        'videorate ! vp8enc quality=10 speed=2 threads=%T ! queue ! webmmux'
 | 
			
		||||
        and records to WEBM using the VP8 codec. %T is used as a placeholder
 | 
			
		||||
        for a guess at the optimal thread count on the system.
 | 
			
		||||
        'videorate ! theoraenc ! oggmux' and records to Ogg Theora.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="file-extension" type="s">
 | 
			
		||||
      <default>'webm'</default>
 | 
			
		||||
      <default>'ogv'</default>
 | 
			
		||||
      <_summary>File extension used for storing the screencast</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        The filename for recorded screencasts will be a unique filename
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +0,0 @@
 | 
			
		||||
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
 | 
			
		||||
<ShortName>Google</ShortName>
 | 
			
		||||
<Description>Google Search</Description>
 | 
			
		||||
<InputEncoding>UTF-8</InputEncoding>
 | 
			
		||||
<Image width="16" height="16">data:image/x-icon;base64,AAABAAEAEBAAAAEAGABoAwAAFgAAACgAAAAQAAAAIAAAAAEAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADs9Pt8xetPtu9FsfFNtu%2BTzvb2%2B%2Fne4dFJeBw0egA%2FfAJAfAA8ewBBegAAAAD%2B%2FPtft98Mp%2BwWsfAVsvEbs%2FQeqvF8xO7%2F%2F%2F63yqkxdgM7gwE%2FggM%2BfQA%2BegBDeQDe7PIbotgQufcMufEPtfIPsvAbs%2FQvq%2Bfz%2Bf%2F%2B%2B%2FZKhR05hgBBhQI8hgBAgAI9ewD0%2B%2Fg3pswAtO8Cxf4Kw%2FsJvvYAqupKsNv%2B%2Fv7%2F%2FP5VkSU0iQA7jQA9hgBDgQU%2BfQH%2F%2Ff%2FQ6fM4sM4KsN8AteMCruIqqdbZ7PH8%2Fv%2Fg6Nc%2Fhg05kAA8jAM9iQI%2BhQA%2BgQDQu6b97uv%2F%2F%2F7V8Pqw3eiWz97q8%2Ff%2F%2F%2F%2F7%2FPptpkkqjQE4kwA7kAA5iwI8iAA8hQCOSSKdXjiyflbAkG7u2s%2F%2B%2F%2F39%2F%2F7r8utrqEYtjQE8lgA7kwA7kwA9jwA9igA9hACiWSekVRyeSgiYSBHx6N%2F%2B%2Fv7k7OFRmiYtlAA5lwI7lwI4lAA7kgI9jwE9iwI4iQCoVhWcTxCmb0K%2BooT8%2Fv%2F7%2F%2F%2FJ2r8fdwI1mwA3mQA3mgA8lAE8lAE4jwA9iwE%2BhwGfXifWvqz%2B%2Ff%2F58u%2Fev6Dt4tr%2B%2F%2F2ZuIUsggA7mgM6mAM3lgA5lgA6kQE%2FkwBChwHt4dv%2F%2F%2F728ei1bCi7VAC5XQ7kz7n%2F%2F%2F6bsZkgcB03lQA9lgM7kwA2iQktZToPK4r9%2F%2F%2F9%2F%2F%2FSqYK5UwDKZAS9WALIkFn%2B%2F%2F3%2F%2BP8oKccGGcIRJrERILYFEMwAAuEAAdX%2F%2Ff7%2F%2FP%2B%2BfDvGXQLIZgLEWgLOjlf7%2F%2F%2F%2F%2F%2F9QU90EAPQAAf8DAP0AAfMAAOUDAtr%2F%2F%2F%2F7%2B%2Fu2bCTIYwDPZgDBWQDSr4P%2F%2Fv%2F%2F%2FP5GRuABAPkAA%2FwBAfkDAPAAAesAAN%2F%2F%2B%2Fz%2F%2F%2F64g1C5VwDMYwK8Yg7y5tz8%2Fv%2FV1PYKDOcAAP0DAf4AAf0AAfYEAOwAAuAAAAD%2F%2FPvi28ymXyChTATRrIb8%2F%2F3v8fk6P8MAAdUCAvoAAP0CAP0AAfYAAO4AAACAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAQAA</Image>
 | 
			
		||||
<Url type="text/html" method="GET" template="http://www.google.com/search?q={searchTerms}"/>
 | 
			
		||||
</OpenSearchDescription>
 | 
			
		||||
@@ -1,44 +0,0 @@
 | 
			
		||||
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
 | 
			
		||||
<ShortName>Wikipedia</ShortName>
 | 
			
		||||
<Description>Wikipedia, the free encyclopedia</Description>
 | 
			
		||||
<InputEncoding>UTF-8</InputEncoding>
 | 
			
		||||
<Image width="16" height="16">data:image/x-icon;base64,AAABAAEAEBAQAAEABAAoAQAAFgAAACgAAAAQAAAAIAAAAAEABAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAEAgQAhIOEAMjHyABIR0gA6ejpAGlqaQCpqKkAKCgoAPz9%2FAAZGBkAmJiYANjZ2ABXWFcAent6ALm6uQA8OjwAiIiIiIiIiIiIiI4oiL6IiIiIgzuIV4iIiIhndo53KIiIiB%2FWvXoYiIiIfEZfWBSIiIEGi%2FfoqoiIgzuL84i9iIjpGIoMiEHoiMkos3FojmiLlUipYliEWIF%2BiDe0GoRa7D6GPbjcu1yIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA</Image>
 | 
			
		||||
<Url type="text/html" method="GET" template="http://{language}.wikipedia.org/wiki/Special:Search?search={searchTerms}"/>
 | 
			
		||||
<!-- The criterion for being below is being listed with more than 100,000
 | 
			
		||||
articles on http://meta.wikimedia.org/wiki/List_of_Wikipedias -->
 | 
			
		||||
<Language>ar</Language>
 | 
			
		||||
<Language>bg</Language>
 | 
			
		||||
<Language>ca</Language>
 | 
			
		||||
<Language>cs</Language>
 | 
			
		||||
<Language>da</Language>
 | 
			
		||||
<Language>de</Language>
 | 
			
		||||
<Language>en</Language>
 | 
			
		||||
<Language>eo</Language>
 | 
			
		||||
<Language>es</Language>
 | 
			
		||||
<Language>fa</Language>
 | 
			
		||||
<Language>fi</Language>
 | 
			
		||||
<Language>fr</Language>
 | 
			
		||||
<Language>he</Language>
 | 
			
		||||
<Language>hu</Language>
 | 
			
		||||
<Language>id</Language>
 | 
			
		||||
<Language>it</Language>
 | 
			
		||||
<Language>ja</Language>
 | 
			
		||||
<Language>ko</Language>
 | 
			
		||||
<Language>lt</Language>
 | 
			
		||||
<Language>nl</Language>
 | 
			
		||||
<Language>no</Language>
 | 
			
		||||
<Language>pl</Language>
 | 
			
		||||
<Language>pt</Language>
 | 
			
		||||
<Language>ro</Language>
 | 
			
		||||
<Language>ru</Language>
 | 
			
		||||
<Language>sk</Language>
 | 
			
		||||
<Language>sl</Language>
 | 
			
		||||
<Language>sr</Language>
 | 
			
		||||
<Language>sv</Language>
 | 
			
		||||
<Language>tr</Language>
 | 
			
		||||
<Language>uk</Language>
 | 
			
		||||
<Language>vi</Language>
 | 
			
		||||
<Language>vo</Language>
 | 
			
		||||
<Language>war</Language>
 | 
			
		||||
<Language>zh</Language>
 | 
			
		||||
</OpenSearchDescription>
 | 
			
		||||
							
								
								
									
										98
									
								
								data/theme/add-workspace.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,98 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
 | 
			
		||||
 | 
			
		||||
<svg
 | 
			
		||||
   xmlns:dc="http://purl.org/dc/elements/1.1/"
 | 
			
		||||
   xmlns:cc="http://creativecommons.org/ns#"
 | 
			
		||||
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 | 
			
		||||
   xmlns:svg="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 | 
			
		||||
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
 | 
			
		||||
   width="23"
 | 
			
		||||
   height="15"
 | 
			
		||||
   id="svg6375"
 | 
			
		||||
   version="1.1"
 | 
			
		||||
   inkscape:version="0.47pre4 r22446"
 | 
			
		||||
   sodipodi:docname="New document 13">
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs6377">
 | 
			
		||||
    <inkscape:perspective
 | 
			
		||||
       sodipodi:type="inkscape:persp3d"
 | 
			
		||||
       inkscape:vp_x="0 : 16 : 1"
 | 
			
		||||
       inkscape:vp_y="0 : 1000 : 0"
 | 
			
		||||
       inkscape:vp_z="32 : 16 : 1"
 | 
			
		||||
       inkscape:persp3d-origin="16 : 10.666667 : 1"
 | 
			
		||||
       id="perspective6383" />
 | 
			
		||||
    <inkscape:perspective
 | 
			
		||||
       id="perspective6366"
 | 
			
		||||
       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
 | 
			
		||||
       inkscape:vp_z="1 : 0.5 : 1"
 | 
			
		||||
       inkscape:vp_y="0 : 1000 : 0"
 | 
			
		||||
       inkscape:vp_x="0 : 0.5 : 1"
 | 
			
		||||
       sodipodi:type="inkscape:persp3d" />
 | 
			
		||||
  </defs>
 | 
			
		||||
  <sodipodi:namedview
 | 
			
		||||
     id="base"
 | 
			
		||||
     pagecolor="#ffffff"
 | 
			
		||||
     bordercolor="#666666"
 | 
			
		||||
     borderopacity="1.0"
 | 
			
		||||
     inkscape:pageopacity="0.0"
 | 
			
		||||
     inkscape:pageshadow="2"
 | 
			
		||||
     inkscape:zoom="11.197802"
 | 
			
		||||
     inkscape:cx="16"
 | 
			
		||||
     inkscape:cy="16"
 | 
			
		||||
     inkscape:current-layer="layer1"
 | 
			
		||||
     showgrid="true"
 | 
			
		||||
     inkscape:grid-bbox="true"
 | 
			
		||||
     inkscape:document-units="px"
 | 
			
		||||
     inkscape:window-width="1680"
 | 
			
		||||
     inkscape:window-height="997"
 | 
			
		||||
     inkscape:window-x="0"
 | 
			
		||||
     inkscape:window-y="26"
 | 
			
		||||
     inkscape:window-maximized="1" />
 | 
			
		||||
  <metadata
 | 
			
		||||
     id="metadata6380">
 | 
			
		||||
    <rdf:RDF>
 | 
			
		||||
      <cc:Work
 | 
			
		||||
         rdf:about="">
 | 
			
		||||
        <dc:format>image/svg+xml</dc:format>
 | 
			
		||||
        <dc:type
 | 
			
		||||
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
 | 
			
		||||
        <dc:title></dc:title>
 | 
			
		||||
      </cc:Work>
 | 
			
		||||
    </rdf:RDF>
 | 
			
		||||
  </metadata>
 | 
			
		||||
  <g
 | 
			
		||||
     id="layer1"
 | 
			
		||||
     inkscape:label="Layer 1"
 | 
			
		||||
     inkscape:groupmode="layer"
 | 
			
		||||
     transform="translate(0,-17)">
 | 
			
		||||
    <g
 | 
			
		||||
       style="display:inline"
 | 
			
		||||
       id="g6243"
 | 
			
		||||
       transform="translate(-986.28859,-658.2796)">
 | 
			
		||||
      <rect
 | 
			
		||||
         style="fill:#000000;fill-opacity:0.98770495;stroke:#666666;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
 | 
			
		||||
         id="rect5318"
 | 
			
		||||
         width="22"
 | 
			
		||||
         height="14"
 | 
			
		||||
         x="986.89801"
 | 
			
		||||
         y="675.86743"
 | 
			
		||||
         rx="0.49999979"
 | 
			
		||||
         ry="0.5" />
 | 
			
		||||
      <g
 | 
			
		||||
         id="g5320"
 | 
			
		||||
         transform="translate(402.77304,-12.882544)">
 | 
			
		||||
        <path
 | 
			
		||||
           id="path5322"
 | 
			
		||||
           d="m 595.125,692.53048 0,6.43903"
 | 
			
		||||
           style="fill:none;stroke:#666666;stroke-width:1.99999952;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
 | 
			
		||||
        <path
 | 
			
		||||
           id="path5324"
 | 
			
		||||
           d="m 598.34451,695.75 -6.43902,0"
 | 
			
		||||
           style="fill:none;stroke:#666666;stroke-width:1.99999952;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" />
 | 
			
		||||
      </g>
 | 
			
		||||
    </g>
 | 
			
		||||
  </g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 3.2 KiB  | 
@@ -1,187 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
 | 
			
		||||
 | 
			
		||||
<svg
 | 
			
		||||
   xmlns:dc="http://purl.org/dc/elements/1.1/"
 | 
			
		||||
   xmlns:cc="http://creativecommons.org/ns#"
 | 
			
		||||
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 | 
			
		||||
   xmlns:svg="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns:xlink="http://www.w3.org/1999/xlink"
 | 
			
		||||
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 | 
			
		||||
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
 | 
			
		||||
   width="28"
 | 
			
		||||
   height="25"
 | 
			
		||||
   id="svg10621"
 | 
			
		||||
   version="1.1"
 | 
			
		||||
   inkscape:version="0.48.1 r9760"
 | 
			
		||||
   sodipodi:docname="calendar-today.svg">
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs10623">
 | 
			
		||||
    <radialGradient
 | 
			
		||||
       inkscape:collect="always"
 | 
			
		||||
       xlink:href="#linearGradient34508-1-3"
 | 
			
		||||
       id="radialGradient99561-1"
 | 
			
		||||
       gradientUnits="userSpaceOnUse"
 | 
			
		||||
       gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
 | 
			
		||||
       cx="51"
 | 
			
		||||
       cy="30"
 | 
			
		||||
       fx="51"
 | 
			
		||||
       fy="30"
 | 
			
		||||
       r="42" />
 | 
			
		||||
    <linearGradient
 | 
			
		||||
       inkscape:collect="always"
 | 
			
		||||
       id="linearGradient34508-1-3">
 | 
			
		||||
      <stop
 | 
			
		||||
         style="stop-color:#ffffff;stop-opacity:1;"
 | 
			
		||||
         offset="0"
 | 
			
		||||
         id="stop34510-1-9" />
 | 
			
		||||
      <stop
 | 
			
		||||
         style="stop-color:#ffffff;stop-opacity:0;"
 | 
			
		||||
         offset="1"
 | 
			
		||||
         id="stop34512-4-5" />
 | 
			
		||||
    </linearGradient>
 | 
			
		||||
    <radialGradient
 | 
			
		||||
       r="42"
 | 
			
		||||
       fy="30"
 | 
			
		||||
       fx="51"
 | 
			
		||||
       cy="30"
 | 
			
		||||
       cx="51"
 | 
			
		||||
       gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
 | 
			
		||||
       gradientUnits="userSpaceOnUse"
 | 
			
		||||
       id="radialGradient10592"
 | 
			
		||||
       xlink:href="#linearGradient34508-1-3"
 | 
			
		||||
       inkscape:collect="always" />
 | 
			
		||||
    <radialGradient
 | 
			
		||||
       inkscape:collect="always"
 | 
			
		||||
       xlink:href="#linearGradient34508-1-3"
 | 
			
		||||
       id="radialGradient3770"
 | 
			
		||||
       gradientUnits="userSpaceOnUse"
 | 
			
		||||
       gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
 | 
			
		||||
       cx="51"
 | 
			
		||||
       cy="30"
 | 
			
		||||
       fx="51"
 | 
			
		||||
       fy="30"
 | 
			
		||||
       r="42" />
 | 
			
		||||
    <radialGradient
 | 
			
		||||
       inkscape:collect="always"
 | 
			
		||||
       xlink:href="#linearGradient34508-1-3"
 | 
			
		||||
       id="radialGradient3001"
 | 
			
		||||
       gradientUnits="userSpaceOnUse"
 | 
			
		||||
       gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
 | 
			
		||||
       cx="51"
 | 
			
		||||
       cy="30"
 | 
			
		||||
       fx="51"
 | 
			
		||||
       fy="30"
 | 
			
		||||
       r="42" />
 | 
			
		||||
    <radialGradient
 | 
			
		||||
       inkscape:collect="always"
 | 
			
		||||
       xlink:href="#linearGradient34508-1-3"
 | 
			
		||||
       id="radialGradient3007"
 | 
			
		||||
       gradientUnits="userSpaceOnUse"
 | 
			
		||||
       gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
 | 
			
		||||
       cx="51"
 | 
			
		||||
       cy="30"
 | 
			
		||||
       fx="51"
 | 
			
		||||
       fy="30"
 | 
			
		||||
       r="42" />
 | 
			
		||||
    <radialGradient
 | 
			
		||||
       inkscape:collect="always"
 | 
			
		||||
       xlink:href="#linearGradient34508-1-3"
 | 
			
		||||
       id="radialGradient3067"
 | 
			
		||||
       gradientUnits="userSpaceOnUse"
 | 
			
		||||
       gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
 | 
			
		||||
       cx="51"
 | 
			
		||||
       cy="30"
 | 
			
		||||
       fx="51"
 | 
			
		||||
       fy="30"
 | 
			
		||||
       r="42" />
 | 
			
		||||
    <radialGradient
 | 
			
		||||
       inkscape:collect="always"
 | 
			
		||||
       xlink:href="#linearGradient34508-1-3"
 | 
			
		||||
       id="radialGradient3072"
 | 
			
		||||
       gradientUnits="userSpaceOnUse"
 | 
			
		||||
       gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
 | 
			
		||||
       cx="51"
 | 
			
		||||
       cy="30"
 | 
			
		||||
       fx="51"
 | 
			
		||||
       fy="30"
 | 
			
		||||
       r="42" />
 | 
			
		||||
    <radialGradient
 | 
			
		||||
       inkscape:collect="always"
 | 
			
		||||
       xlink:href="#linearGradient34508-1-3"
 | 
			
		||||
       id="radialGradient2997"
 | 
			
		||||
       gradientUnits="userSpaceOnUse"
 | 
			
		||||
       gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
 | 
			
		||||
       cx="51"
 | 
			
		||||
       cy="30"
 | 
			
		||||
       fx="51"
 | 
			
		||||
       fy="30"
 | 
			
		||||
       r="42" />
 | 
			
		||||
  </defs>
 | 
			
		||||
  <sodipodi:namedview
 | 
			
		||||
     id="base"
 | 
			
		||||
     pagecolor="#000000"
 | 
			
		||||
     bordercolor="#666666"
 | 
			
		||||
     borderopacity="1.0"
 | 
			
		||||
     inkscape:pageopacity="0"
 | 
			
		||||
     inkscape:pageshadow="2"
 | 
			
		||||
     inkscape:zoom="15.839192"
 | 
			
		||||
     inkscape:cx="8.3750933"
 | 
			
		||||
     inkscape:cy="8.0837211"
 | 
			
		||||
     inkscape:document-units="px"
 | 
			
		||||
     inkscape:current-layer="layer1"
 | 
			
		||||
     showgrid="false"
 | 
			
		||||
     fit-margin-top="0"
 | 
			
		||||
     fit-margin-left="0"
 | 
			
		||||
     fit-margin-right="0"
 | 
			
		||||
     fit-margin-bottom="0"
 | 
			
		||||
     inkscape:window-width="1440"
 | 
			
		||||
     inkscape:window-height="843"
 | 
			
		||||
     inkscape:window-x="0"
 | 
			
		||||
     inkscape:window-y="26"
 | 
			
		||||
     inkscape:window-maximized="1" />
 | 
			
		||||
  <metadata
 | 
			
		||||
     id="metadata10626">
 | 
			
		||||
    <rdf:RDF>
 | 
			
		||||
      <cc:Work
 | 
			
		||||
         rdf:about="">
 | 
			
		||||
        <dc:format>image/svg+xml</dc:format>
 | 
			
		||||
        <dc:type
 | 
			
		||||
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
 | 
			
		||||
        <dc:title />
 | 
			
		||||
      </cc:Work>
 | 
			
		||||
    </rdf:RDF>
 | 
			
		||||
  </metadata>
 | 
			
		||||
  <g
 | 
			
		||||
     inkscape:label="Layer 1"
 | 
			
		||||
     inkscape:groupmode="layer"
 | 
			
		||||
     id="layer1"
 | 
			
		||||
     transform="translate(-469.08263,-536.99307)">
 | 
			
		||||
    <g
 | 
			
		||||
       id="g3003">
 | 
			
		||||
      <path
 | 
			
		||||
         inkscape:export-ydpi="90"
 | 
			
		||||
         inkscape:export-xdpi="90"
 | 
			
		||||
         inkscape:export-filename="/home/jimmac/src/cvs/gnome/gnome-shell-design/mockups/motion/textures/panel.png"
 | 
			
		||||
         transform="matrix(0.43692393,0,0,1.3783114,460.60467,517.48289)"
 | 
			
		||||
         sodipodi:end="6.2831853"
 | 
			
		||||
         sodipodi:start="3.1415927"
 | 
			
		||||
         d="M 9,29.999999 C 9.0000011,21.163443 27.804042,14 51.000002,14 74.195961,14 93,21.163444 93,30 l -42,0 z"
 | 
			
		||||
         sodipodi:ry="16"
 | 
			
		||||
         sodipodi:rx="42"
 | 
			
		||||
         sodipodi:cy="30"
 | 
			
		||||
         sodipodi:cx="51"
 | 
			
		||||
         id="path34506-3"
 | 
			
		||||
         style="opacity:0.4625;color:#000000;fill:url(#radialGradient2997);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
 | 
			
		||||
         sodipodi:type="arc" />
 | 
			
		||||
      <rect
 | 
			
		||||
         y="558.85046"
 | 
			
		||||
         x="468.96878"
 | 
			
		||||
         height="3.1425927"
 | 
			
		||||
         width="28.149134"
 | 
			
		||||
         id="rect2996"
 | 
			
		||||
         style="fill:#ffffff;fill-opacity:0.50196078;stroke-width:0.43599999;stroke-miterlimit:4;stroke-dasharray:none" />
 | 
			
		||||
    </g>
 | 
			
		||||
  </g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 5.7 KiB  | 
@@ -1,26 +1,24 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<!-- Generator: Adobe Illustrator 13.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 14948)  -->
 | 
			
		||||
 | 
			
		||||
<svg
 | 
			
		||||
   xmlns:dc="http://purl.org/dc/elements/1.1/"
 | 
			
		||||
   xmlns:cc="http://creativecommons.org/ns#"
 | 
			
		||||
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 | 
			
		||||
   xmlns:svg="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns:xlink="http://www.w3.org/1999/xlink"
 | 
			
		||||
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 | 
			
		||||
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
 | 
			
		||||
   version="1.0"
 | 
			
		||||
   id="Foreground"
 | 
			
		||||
   x="0px"
 | 
			
		||||
   y="0px"
 | 
			
		||||
   width="32"
 | 
			
		||||
   height="32"
 | 
			
		||||
   viewBox="0 0 23.272727 23.272727"
 | 
			
		||||
   width="22"
 | 
			
		||||
   height="22"
 | 
			
		||||
   viewBox="0 0 16 16"
 | 
			
		||||
   enable-background="new 0 0 16 16"
 | 
			
		||||
   xml:space="preserve"
 | 
			
		||||
   sodipodi:version="0.32"
 | 
			
		||||
   inkscape:version="0.48+devel r10081 custom"
 | 
			
		||||
   inkscape:version="0.46"
 | 
			
		||||
   sodipodi:docname="close-window.svg"
 | 
			
		||||
   inkscape:output_extension="org.inkscape.output.svg.inkscape"><metadata
 | 
			
		||||
   id="metadata2399"><rdf:RDF><cc:Work
 | 
			
		||||
@@ -39,49 +37,11 @@
 | 
			
		||||
     inkscape:vp_y="0 : 1000 : 0"
 | 
			
		||||
     inkscape:vp_z="16 : 8 : 1"
 | 
			
		||||
     inkscape:persp3d-origin="8 : 5.3333333 : 1"
 | 
			
		||||
     id="perspective2401" /><filter
 | 
			
		||||
     color-interpolation-filters="sRGB"
 | 
			
		||||
     inkscape:collect="always"
 | 
			
		||||
     id="filter16494-4"
 | 
			
		||||
     x="-0.20989846"
 | 
			
		||||
     width="1.4197969"
 | 
			
		||||
     y="-0.20903821"
 | 
			
		||||
     height="1.4180764"><feGaussianBlur
 | 
			
		||||
       inkscape:collect="always"
 | 
			
		||||
       stdDeviation="1.3282637"
 | 
			
		||||
       id="feGaussianBlur16496-8" /></filter><radialGradient
 | 
			
		||||
     inkscape:collect="always"
 | 
			
		||||
     xlink:href="#linearGradient16498-6"
 | 
			
		||||
     id="radialGradient16504-1"
 | 
			
		||||
     cx="7.6582627"
 | 
			
		||||
     cy="5.8191104"
 | 
			
		||||
     fx="7.6582627"
 | 
			
		||||
     fy="5.8191104"
 | 
			
		||||
     r="8.6928644"
 | 
			
		||||
     gradientTransform="matrix(1.0474339,0,0,1.0517402,-0.3632615,-0.42032492)"
 | 
			
		||||
     gradientUnits="userSpaceOnUse" /><linearGradient
 | 
			
		||||
     inkscape:collect="always"
 | 
			
		||||
     id="linearGradient16498-6"><stop
 | 
			
		||||
       style="stop-color:#7b7b7b;stop-opacity:1"
 | 
			
		||||
       offset="0"
 | 
			
		||||
       id="stop16500-8" /><stop
 | 
			
		||||
       style="stop-color:#101010;stop-opacity:1"
 | 
			
		||||
       offset="1"
 | 
			
		||||
       id="stop16502-0" /></linearGradient><filter
 | 
			
		||||
     color-interpolation-filters="sRGB"
 | 
			
		||||
     inkscape:collect="always"
 | 
			
		||||
     id="filter16524-9"
 | 
			
		||||
     x="-0.212979"
 | 
			
		||||
     width="1.425958"
 | 
			
		||||
     y="-0.21305652"
 | 
			
		||||
     height="1.426113"><feGaussianBlur
 | 
			
		||||
       inkscape:collect="always"
 | 
			
		||||
       stdDeviation="0.71020915"
 | 
			
		||||
       id="feGaussianBlur16526-0" /></filter></defs><sodipodi:namedview
 | 
			
		||||
   inkscape:window-height="1114"
 | 
			
		||||
   inkscape:window-width="1463"
 | 
			
		||||
     id="perspective2401" /></defs><sodipodi:namedview
 | 
			
		||||
   inkscape:window-height="999"
 | 
			
		||||
   inkscape:window-width="1680"
 | 
			
		||||
   inkscape:pageshadow="2"
 | 
			
		||||
   inkscape:pageopacity="0"
 | 
			
		||||
   inkscape:pageopacity="1"
 | 
			
		||||
   guidetolerance="10.0"
 | 
			
		||||
   gridtolerance="10.0"
 | 
			
		||||
   objecttolerance="10.0"
 | 
			
		||||
@@ -90,63 +50,27 @@
 | 
			
		||||
   pagecolor="#000000"
 | 
			
		||||
   id="base"
 | 
			
		||||
   showgrid="false"
 | 
			
		||||
   inkscape:zoom="1"
 | 
			
		||||
   inkscape:cx="10.720189"
 | 
			
		||||
   inkscape:cy="13.739577"
 | 
			
		||||
   inkscape:zoom="25.648691"
 | 
			
		||||
   inkscape:cx="8.8097603"
 | 
			
		||||
   inkscape:cy="9.0472789"
 | 
			
		||||
   inkscape:window-x="0"
 | 
			
		||||
   inkscape:window-y="26"
 | 
			
		||||
   inkscape:current-layer="Foreground"
 | 
			
		||||
   showguides="true"
 | 
			
		||||
   inkscape:guide-bbox="true"
 | 
			
		||||
   borderlayer="true"
 | 
			
		||||
   inkscape:showpageshadow="false"
 | 
			
		||||
   inkscape:window-maximized="0"><inkscape:grid
 | 
			
		||||
     type="xygrid"
 | 
			
		||||
     id="grid11246"
 | 
			
		||||
     empspacing="5"
 | 
			
		||||
     visible="true"
 | 
			
		||||
     enabled="true"
 | 
			
		||||
     snapvisiblegridlinesonly="true" /></sodipodi:namedview>
 | 
			
		||||
   inkscape:guide-bbox="true" />
 | 
			
		||||
 | 
			
		||||
<g
 | 
			
		||||
   style="display:inline"
 | 
			
		||||
   id="g16402-8"
 | 
			
		||||
   transform="translate(4.7533483,2.8238929)"><g
 | 
			
		||||
     id="g3175-4"><path
 | 
			
		||||
       sodipodi:type="inkscape:offset"
 | 
			
		||||
       inkscape:radius="0"
 | 
			
		||||
       inkscape:original="M 7.65625 0.125 C 3.2589349 0.125 -0.3125 3.7070002 -0.3125 8.125 C -0.3125 12.543001 3.2589349 16.125 7.65625 16.125 C 12.053566 16.125 15.625 12.543001 15.625 8.125 C 15.625 3.7070002 12.053566 0.125 7.65625 0.125 z "
 | 
			
		||||
       xlink:href="#path2394-32"
 | 
			
		||||
       style="opacity:0.52994014;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2.18181825;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter16494-4);enable-background:accumulate"
 | 
			
		||||
       id="path16480-5"
 | 
			
		||||
       inkscape:href="#path2394-32"
 | 
			
		||||
       d="m 7.65625,0.125 c -4.3973151,0 -7.96875,3.5820002 -7.96875,8 0,4.418001 3.5714349,8 7.96875,8 4.397316,0 7.96875,-3.581999 7.96875,-8 0,-4.4179998 -3.571434,-8 -7.96875,-8 z"
 | 
			
		||||
       transform="translate(0,1.028519)" /><path
 | 
			
		||||
       clip-rule="evenodd"
 | 
			
		||||
       d="m -0.30428257,8.1237596 c 0,-4.4179998 3.56522987,-7.9999996 7.96254497,-7.9999996 4.3973156,0 7.9625456,3.5819998 7.9625456,7.9999996 0,4.4180014 -3.56523,8.0000004 -7.9625456,8.0000004 -4.3973151,0 -7.96254497,-3.581999 -7.96254497,-8.0000004 z"
 | 
			
		||||
       id="path2394-32"
 | 
			
		||||
       style="color:#000000;fill:url(#radialGradient16504-1);fill-opacity:1;fill-rule:nonzero;stroke:#eeeeec;stroke-width:1.4545455;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
 | 
			
		||||
       sodipodi:nodetypes="csssc"
 | 
			
		||||
       inkscape:connector-curvature="0" /><g
 | 
			
		||||
       id="g3172-6" /></g><g
 | 
			
		||||
     transform="matrix(0.72727273,0,0,0.72727273,2.368236,2.1803254)"
 | 
			
		||||
     style="fill:#ffffff;fill-opacity:1;display:inline"
 | 
			
		||||
     id="g27275-6-6"
 | 
			
		||||
     inkscape:label="window-close"><g
 | 
			
		||||
       style="fill:#ffffff;fill-opacity:1;display:inline"
 | 
			
		||||
       id="g27277-1-1"
 | 
			
		||||
       transform="translate(-41,-760)"><path
 | 
			
		||||
         sodipodi:type="inkscape:offset"
 | 
			
		||||
         inkscape:radius="0"
 | 
			
		||||
         inkscape:original="M 44.21875 764.1875 L 44.21875 765.1875 C 44.19684 765.46825 44.289258 765.74287 44.5 765.9375 L 46.78125 768.21875 L 44.5 770.46875 C 44.31181 770.65692 44.218747 770.92221 44.21875 771.1875 L 44.21875 772.1875 L 45.21875 772.1875 C 45.48404 772.1875 45.749336 772.09444 45.9375 771.90625 L 48.21875 769.625 L 50.5 771.90625 C 50.688164 772.0944 50.953449 772.18749 51.21875 772.1875 L 52.21875 772.1875 L 52.21875 771.1875 C 52.218742 770.9222 52.125688 770.65692 51.9375 770.46875 L 49.6875 768.21875 L 51.96875 765.9375 C 52.18441 765.73815 52.21875 765.47397 52.21875 765.1875 L 52.21875 764.1875 L 51.21875 764.1875 C 50.977922 764.1945 50.796875 764.2695 50.53125 764.5 L 48.21875 766.78125 L 45.9375 764.5 C 45.75987 764.31608 45.504951 764.1987 45.25 764.1875 C 45.23954 764.18704 45.22912 764.18738 45.21875 764.1875 L 44.21875 764.1875 z "
 | 
			
		||||
         xlink:href="#path27279-0-5"
 | 
			
		||||
         style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;color:#bebebe;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.78124988;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter16524-9);enable-background:new;font-family:Andale Mono;-inkscape-font-specification:Andale Mono"
 | 
			
		||||
         id="path16506-5"
 | 
			
		||||
         inkscape:href="#path27279-0-5"
 | 
			
		||||
         d="m 44.21875,764.1875 0,1 c -0.02191,0.28075 0.07051,0.55537 0.28125,0.75 l 2.28125,2.28125 -2.28125,2.25 c -0.18819,0.18817 -0.281253,0.45346 -0.28125,0.71875 l 0,1 1,0 c 0.26529,0 0.530586,-0.0931 0.71875,-0.28125 L 48.21875,769.625 50.5,771.90625 c 0.188164,0.18815 0.453449,0.28124 0.71875,0.28125 l 1,0 0,-1 c -8e-6,-0.2653 -0.09306,-0.53058 -0.28125,-0.71875 l -2.25,-2.25 2.28125,-2.28125 c 0.21566,-0.19935 0.25,-0.46353 0.25,-0.75 l 0,-1 -1,0 c -0.240828,0.007 -0.421875,0.082 -0.6875,0.3125 l -2.3125,2.28125 L 45.9375,764.5 c -0.17763,-0.18392 -0.432549,-0.3013 -0.6875,-0.3125 -0.01046,-4.6e-4 -0.02088,-1.2e-4 -0.03125,0 l -1,0 z"
 | 
			
		||||
         transform="translate(0,1.3535534)" /><path
 | 
			
		||||
         sodipodi:nodetypes="ccsccccccccccccccccccccccc"
 | 
			
		||||
         style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;color:#bebebe;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.78124988;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:new;font-family:Andale Mono;-inkscape-font-specification:Andale Mono"
 | 
			
		||||
         id="path27279-0-5"
 | 
			
		||||
         inkscape:connector-curvature="0"
 | 
			
		||||
         d="m 44.226475,764.17222 1,0 c 0.01037,-1.2e-4 0.02079,-4.6e-4 0.03125,0 0.254951,0.0112 0.50987,0.12858 0.6875,0.3125 l 2.28125,2.28125 2.3125,-2.28125 c 0.265625,-0.2305 0.446672,-0.3055 0.6875,-0.3125 l 1,0 0,1 c 0,0.28647 -0.03434,0.55065 -0.25,0.75 l -2.28125,2.28125 2.25,2.25 c 0.188188,0.18817 0.281242,0.45345 0.28125,0.71875 l 0,1 -1,0 c -0.265301,-1e-5 -0.530586,-0.0931 -0.71875,-0.28125 l -2.28125,-2.28125 -2.28125,2.28125 c -0.188164,0.18819 -0.45346,0.28125 -0.71875,0.28125 l -1,0 0,-1 c -3e-6,-0.26529 0.09306,-0.53058 0.28125,-0.71875 l 2.28125,-2.25 -2.28125,-2.28125 c -0.210742,-0.19463 -0.30316,-0.46925 -0.28125,-0.75 l 0,-1 z" /></g></g></g></svg>
 | 
			
		||||
   id="g3175"><path
 | 
			
		||||
     sodipodi:nodetypes="csssc"
 | 
			
		||||
     style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.59217799;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
 | 
			
		||||
     id="path2394"
 | 
			
		||||
     d="M 0.83987936,8.0425327 C 0.83987936,4.0805265 4.0712155,0.86823453 8.0567103,0.86823453 C 12.042205,0.86823453 15.273542,4.0805265 15.273542,8.0425327 C 15.273542,12.004539 12.042205,15.216831 8.0567103,15.216831 C 4.0712155,15.216831 0.83987936,12.004539 0.83987936,8.0425327 z"
 | 
			
		||||
     clip-rule="evenodd" /><g
 | 
			
		||||
     id="g3172"><path
 | 
			
		||||
       style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.67127273;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
 | 
			
		||||
       d="M 5.4242673,5.3313047 L 10.515414,10.421272 L 10.714004,10.646491"
 | 
			
		||||
       id="path3152" /></g></g><path
 | 
			
		||||
   style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.67127273;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
 | 
			
		||||
   d="M 5.4402527,10.650392 L 10.688082,5.3573033"
 | 
			
		||||
   id="path3154"
 | 
			
		||||
   sodipodi:nodetypes="cc" /></svg>
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 3.2 KiB  | 
| 
		 Before Width: | Height: | Size: 2.3 KiB  | 
| 
		 Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB  | 
							
								
								
									
										222
									
								
								data/theme/dialog-error.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,222 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
 | 
			
		||||
 | 
			
		||||
<svg
 | 
			
		||||
   xmlns:dc="http://purl.org/dc/elements/1.1/"
 | 
			
		||||
   xmlns:cc="http://creativecommons.org/ns#"
 | 
			
		||||
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 | 
			
		||||
   xmlns:svg="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns:xlink="http://www.w3.org/1999/xlink"
 | 
			
		||||
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 | 
			
		||||
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
 | 
			
		||||
   width="24"
 | 
			
		||||
   height="24"
 | 
			
		||||
   id="svg4908"
 | 
			
		||||
   sodipodi:version="0.32"
 | 
			
		||||
   inkscape:version="0.47 r22583"
 | 
			
		||||
   sodipodi:docname="dialog-error.svg"
 | 
			
		||||
   inkscape:output_extension="org.inkscape.output.svg.inkscape"
 | 
			
		||||
   inkscape:export-filename="/home/andreas/project/gnome-icon-theme/scalable/actions/process-stop.png"
 | 
			
		||||
   inkscape:export-xdpi="90"
 | 
			
		||||
   inkscape:export-ydpi="90"
 | 
			
		||||
   version="1.0">
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs4910">
 | 
			
		||||
    <inkscape:perspective
 | 
			
		||||
       sodipodi:type="inkscape:persp3d"
 | 
			
		||||
       inkscape:vp_x="0 : 24 : 1"
 | 
			
		||||
       inkscape:vp_y="0 : 1000 : 0"
 | 
			
		||||
       inkscape:vp_z="48 : 24 : 1"
 | 
			
		||||
       inkscape:persp3d-origin="24 : 16 : 1"
 | 
			
		||||
       id="perspective25" />
 | 
			
		||||
    <radialGradient
 | 
			
		||||
       gradientTransform="matrix(1.349881,0,0,1.349881,-3.498814,-1.810859)"
 | 
			
		||||
       gradientUnits="userSpaceOnUse"
 | 
			
		||||
       r="9.7183542"
 | 
			
		||||
       fy="4.9892726"
 | 
			
		||||
       fx="9.6893959"
 | 
			
		||||
       cy="4.9892726"
 | 
			
		||||
       cx="9.6893959"
 | 
			
		||||
       id="radialGradient5177"
 | 
			
		||||
       xlink:href="#linearGradient5171"
 | 
			
		||||
       inkscape:collect="always" />
 | 
			
		||||
    <radialGradient
 | 
			
		||||
       gradientTransform="matrix(2.417917,0,0,2.417917,-14.17917,-4.903184)"
 | 
			
		||||
       gradientUnits="userSpaceOnUse"
 | 
			
		||||
       r="9.7785711"
 | 
			
		||||
       fy="3.458019"
 | 
			
		||||
       fx="10"
 | 
			
		||||
       cy="3.458019"
 | 
			
		||||
       cx="10"
 | 
			
		||||
       id="radialGradient5157"
 | 
			
		||||
       xlink:href="#linearGradient5151"
 | 
			
		||||
       inkscape:collect="always" />
 | 
			
		||||
    <radialGradient
 | 
			
		||||
       gradientUnits="userSpaceOnUse"
 | 
			
		||||
       gradientTransform="matrix(0.928125,0,0,0.3143011,0.7718789,12.358015)"
 | 
			
		||||
       r="9.0598059"
 | 
			
		||||
       fy="18.022524"
 | 
			
		||||
       fx="10.739184"
 | 
			
		||||
       cy="18.022524"
 | 
			
		||||
       cx="10.739184"
 | 
			
		||||
       id="radialGradient5145"
 | 
			
		||||
       xlink:href="#linearGradient5139"
 | 
			
		||||
       inkscape:collect="always" />
 | 
			
		||||
    <linearGradient
 | 
			
		||||
       id="linearGradient5139"
 | 
			
		||||
       inkscape:collect="always">
 | 
			
		||||
      <stop
 | 
			
		||||
         id="stop5141"
 | 
			
		||||
         offset="0"
 | 
			
		||||
         style="stop-color:black;stop-opacity:1;" />
 | 
			
		||||
      <stop
 | 
			
		||||
         id="stop5143"
 | 
			
		||||
         offset="1"
 | 
			
		||||
         style="stop-color:black;stop-opacity:0;" />
 | 
			
		||||
    </linearGradient>
 | 
			
		||||
    <linearGradient
 | 
			
		||||
       id="linearGradient5151"
 | 
			
		||||
       inkscape:collect="always">
 | 
			
		||||
      <stop
 | 
			
		||||
         id="stop5153"
 | 
			
		||||
         offset="0"
 | 
			
		||||
         style="stop-color:white;stop-opacity:1;" />
 | 
			
		||||
      <stop
 | 
			
		||||
         id="stop5155"
 | 
			
		||||
         offset="1"
 | 
			
		||||
         style="stop-color:white;stop-opacity:0;" />
 | 
			
		||||
    </linearGradient>
 | 
			
		||||
    <linearGradient
 | 
			
		||||
       id="linearGradient5171">
 | 
			
		||||
      <stop
 | 
			
		||||
         id="stop5173"
 | 
			
		||||
         offset="0"
 | 
			
		||||
         style="stop-color:#fe3a00;stop-opacity:1" />
 | 
			
		||||
      <stop
 | 
			
		||||
         id="stop5175"
 | 
			
		||||
         offset="1"
 | 
			
		||||
         style="stop-color:#c00;stop-opacity:1;" />
 | 
			
		||||
    </linearGradient>
 | 
			
		||||
  </defs>
 | 
			
		||||
  <sodipodi:namedview
 | 
			
		||||
     id="base"
 | 
			
		||||
     pagecolor="#ffffff"
 | 
			
		||||
     bordercolor="#666666"
 | 
			
		||||
     borderopacity="1.0"
 | 
			
		||||
     inkscape:pageopacity="0.0"
 | 
			
		||||
     inkscape:pageshadow="2"
 | 
			
		||||
     inkscape:zoom="22.627417"
 | 
			
		||||
     inkscape:cx="24.442987"
 | 
			
		||||
     inkscape:cy="10.142308"
 | 
			
		||||
     inkscape:current-layer="g7001"
 | 
			
		||||
     showgrid="false"
 | 
			
		||||
     inkscape:grid-bbox="true"
 | 
			
		||||
     inkscape:document-units="px"
 | 
			
		||||
     showguides="true"
 | 
			
		||||
     inkscape:guide-bbox="true"
 | 
			
		||||
     inkscape:window-width="1674"
 | 
			
		||||
     inkscape:window-height="970"
 | 
			
		||||
     inkscape:window-x="0"
 | 
			
		||||
     inkscape:window-y="26"
 | 
			
		||||
     width="48px"
 | 
			
		||||
     height="48px"
 | 
			
		||||
     inkscape:window-maximized="0" />
 | 
			
		||||
  <metadata
 | 
			
		||||
     id="metadata4913">
 | 
			
		||||
    <rdf:RDF>
 | 
			
		||||
      <cc:Work
 | 
			
		||||
         rdf:about="">
 | 
			
		||||
        <dc:format>image/svg+xml</dc:format>
 | 
			
		||||
        <dc:type
 | 
			
		||||
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
 | 
			
		||||
        <dc:title>Stop Process</dc:title>
 | 
			
		||||
        <dc:date>December 2006</dc:date>
 | 
			
		||||
        <dc:creator>
 | 
			
		||||
          <cc:Agent>
 | 
			
		||||
            <dc:title>Jakub Steiner</dc:title>
 | 
			
		||||
          </cc:Agent>
 | 
			
		||||
        </dc:creator>
 | 
			
		||||
        <dc:contributor>
 | 
			
		||||
          <cc:Agent>
 | 
			
		||||
            <dc:title>Andreas Nilsson</dc:title>
 | 
			
		||||
          </cc:Agent>
 | 
			
		||||
        </dc:contributor>
 | 
			
		||||
        <cc:license
 | 
			
		||||
           rdf:resource="http://creativecommons.org/licenses/GPL/2.0/" />
 | 
			
		||||
        <dc:subject>
 | 
			
		||||
          <rdf:Bag>
 | 
			
		||||
            <rdf:li>stop</rdf:li>
 | 
			
		||||
            <rdf:li>halt</rdf:li>
 | 
			
		||||
          </rdf:Bag>
 | 
			
		||||
        </dc:subject>
 | 
			
		||||
      </cc:Work>
 | 
			
		||||
      <cc:License
 | 
			
		||||
         rdf:about="http://creativecommons.org/licenses/GPL/2.0/">
 | 
			
		||||
        <cc:permits
 | 
			
		||||
           rdf:resource="http://web.resource.org/cc/Reproduction" />
 | 
			
		||||
        <cc:permits
 | 
			
		||||
           rdf:resource="http://web.resource.org/cc/Distribution" />
 | 
			
		||||
        <cc:requires
 | 
			
		||||
           rdf:resource="http://web.resource.org/cc/Notice" />
 | 
			
		||||
        <cc:permits
 | 
			
		||||
           rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
 | 
			
		||||
        <cc:requires
 | 
			
		||||
           rdf:resource="http://web.resource.org/cc/ShareAlike" />
 | 
			
		||||
        <cc:requires
 | 
			
		||||
           rdf:resource="http://web.resource.org/cc/SourceCode" />
 | 
			
		||||
      </cc:License>
 | 
			
		||||
    </rdf:RDF>
 | 
			
		||||
  </metadata>
 | 
			
		||||
  <g
 | 
			
		||||
     id="layer1"
 | 
			
		||||
     inkscape:label="Layer 1"
 | 
			
		||||
     inkscape:groupmode="layer"
 | 
			
		||||
     transform="translate(0,-24)">
 | 
			
		||||
    <g
 | 
			
		||||
       inkscape:label="Layer 1"
 | 
			
		||||
       id="g7001"
 | 
			
		||||
       transform="matrix(1.4566048,0,0,1.4455352,0.4112881,1.2324709)">
 | 
			
		||||
      <path
 | 
			
		||||
         transform="matrix(0.91468137,0,0,0.70055266,-1.8812476,17.474032)"
 | 
			
		||||
         d="m 19.79899,18.022524 a 9.0598059,3.0935922 0 1 1 -18.1196115,0 9.0598059,3.0935922 0 1 1 18.1196115,0 z"
 | 
			
		||||
         sodipodi:ry="3.0935922"
 | 
			
		||||
         sodipodi:rx="9.0598059"
 | 
			
		||||
         sodipodi:cy="18.022524"
 | 
			
		||||
         sodipodi:cx="10.739184"
 | 
			
		||||
         id="path5137"
 | 
			
		||||
         style="color:#000000;fill:url(#radialGradient5145);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
 | 
			
		||||
         sodipodi:type="arc" />
 | 
			
		||||
      <path
 | 
			
		||||
         transform="matrix(0.87347736,0,0,0.83068052,-0.79308842,15.602788)"
 | 
			
		||||
         d="m 19.25,9.625 a 9.25,9.25 0 1 1 -18.5,0 9.25,9.25 0 1 1 18.5,0 z"
 | 
			
		||||
         sodipodi:ry="9.25"
 | 
			
		||||
         sodipodi:rx="9.25"
 | 
			
		||||
         sodipodi:cy="9.625"
 | 
			
		||||
         sodipodi:cx="10"
 | 
			
		||||
         id="path4262"
 | 
			
		||||
         style="color:#000000;fill:url(#radialGradient5177);fill-opacity:1;fill-rule:nonzero;stroke:#a40000;stroke-width:0.47435912;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
 | 
			
		||||
         sodipodi:type="arc" />
 | 
			
		||||
      <path
 | 
			
		||||
         sodipodi:type="arc"
 | 
			
		||||
         style="opacity:0.35393258;color:#000000;fill:none;stroke:url(#radialGradient5157);stroke-width:0.49999994;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
 | 
			
		||||
         id="path5149"
 | 
			
		||||
         sodipodi:cx="10"
 | 
			
		||||
         sodipodi:cy="9.625"
 | 
			
		||||
         sodipodi:rx="9.25"
 | 
			
		||||
         sodipodi:ry="9.25"
 | 
			
		||||
         d="m 19.25,9.625 a 9.25,9.25 0 1 1 -18.5,0 9.25,9.25 0 1 1 18.5,0 z"
 | 
			
		||||
         transform="matrix(0.82868359,0,0,0.78808147,-0.34515141,16.012803)" />
 | 
			
		||||
      <path
 | 
			
		||||
         sodipodi:nodetypes="cc"
 | 
			
		||||
         id="path5159"
 | 
			
		||||
         d="m 4.834121,20.642783 6.215127,5.91061"
 | 
			
		||||
         style="color:#000000;fill:none;stroke:#ffffff;stroke-width:1.21219134;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" />
 | 
			
		||||
      <path
 | 
			
		||||
         style="color:#000000;fill:none;stroke:#ffffff;stroke-width:1.21219146;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
 | 
			
		||||
         d="M 11.04925,20.622826 4.8159529,26.553393"
 | 
			
		||||
         id="path5161"
 | 
			
		||||
         sodipodi:nodetypes="cc" />
 | 
			
		||||
    </g>
 | 
			
		||||
  </g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 8.0 KiB  | 
							
								
								
									
										113
									
								
								data/theme/mosaic-view-active.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,113 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
 | 
			
		||||
 | 
			
		||||
<svg
 | 
			
		||||
   xmlns:dc="http://purl.org/dc/elements/1.1/"
 | 
			
		||||
   xmlns:cc="http://creativecommons.org/ns#"
 | 
			
		||||
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 | 
			
		||||
   xmlns:svg="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 | 
			
		||||
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
 | 
			
		||||
   width="24"
 | 
			
		||||
   height="16"
 | 
			
		||||
   id="svg6503"
 | 
			
		||||
   version="1.1"
 | 
			
		||||
   inkscape:version="0.47pre4 r22446"
 | 
			
		||||
   sodipodi:docname="mosaic-view-active.svg">
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs6505">
 | 
			
		||||
    <inkscape:perspective
 | 
			
		||||
       sodipodi:type="inkscape:persp3d"
 | 
			
		||||
       inkscape:vp_x="0 : 16 : 1"
 | 
			
		||||
       inkscape:vp_y="0 : 1000 : 0"
 | 
			
		||||
       inkscape:vp_z="32 : 16 : 1"
 | 
			
		||||
       inkscape:persp3d-origin="16 : 10.666667 : 1"
 | 
			
		||||
       id="perspective6511" />
 | 
			
		||||
    <inkscape:perspective
 | 
			
		||||
       id="perspective6494"
 | 
			
		||||
       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
 | 
			
		||||
       inkscape:vp_z="1 : 0.5 : 1"
 | 
			
		||||
       inkscape:vp_y="0 : 1000 : 0"
 | 
			
		||||
       inkscape:vp_x="0 : 0.5 : 1"
 | 
			
		||||
       sodipodi:type="inkscape:persp3d" />
 | 
			
		||||
  </defs>
 | 
			
		||||
  <sodipodi:namedview
 | 
			
		||||
     id="base"
 | 
			
		||||
     pagecolor="#ffffff"
 | 
			
		||||
     bordercolor="#666666"
 | 
			
		||||
     borderopacity="1.0"
 | 
			
		||||
     inkscape:pageopacity="0.0"
 | 
			
		||||
     inkscape:pageshadow="2"
 | 
			
		||||
     inkscape:zoom="11.197802"
 | 
			
		||||
     inkscape:cx="-15.97056"
 | 
			
		||||
     inkscape:cy="16"
 | 
			
		||||
     inkscape:current-layer="layer1"
 | 
			
		||||
     showgrid="true"
 | 
			
		||||
     inkscape:grid-bbox="true"
 | 
			
		||||
     inkscape:document-units="px"
 | 
			
		||||
     inkscape:window-width="1680"
 | 
			
		||||
     inkscape:window-height="997"
 | 
			
		||||
     inkscape:window-x="0"
 | 
			
		||||
     inkscape:window-y="26"
 | 
			
		||||
     inkscape:window-maximized="1" />
 | 
			
		||||
  <metadata
 | 
			
		||||
     id="metadata6508">
 | 
			
		||||
    <rdf:RDF>
 | 
			
		||||
      <cc:Work
 | 
			
		||||
         rdf:about="">
 | 
			
		||||
        <dc:format>image/svg+xml</dc:format>
 | 
			
		||||
        <dc:type
 | 
			
		||||
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
 | 
			
		||||
        <dc:title />
 | 
			
		||||
      </cc:Work>
 | 
			
		||||
    </rdf:RDF>
 | 
			
		||||
  </metadata>
 | 
			
		||||
  <g
 | 
			
		||||
     id="layer1"
 | 
			
		||||
     inkscape:label="Layer 1"
 | 
			
		||||
     inkscape:groupmode="layer"
 | 
			
		||||
     transform="translate(0,-16)">
 | 
			
		||||
    <g
 | 
			
		||||
       style="display:inline;fill:#cbcbcb;fill-opacity:1"
 | 
			
		||||
       transform="translate(-449.85476,-685.85869)"
 | 
			
		||||
       id="g5306">
 | 
			
		||||
      <rect
 | 
			
		||||
         style="fill:#cbcbcb;fill-opacity:1;stroke:#000000;stroke-width:0.99999970000000005;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0.44262299999999999;stroke-dasharray:none"
 | 
			
		||||
         id="rect5308"
 | 
			
		||||
         width="11"
 | 
			
		||||
         height="7"
 | 
			
		||||
         x="450.5"
 | 
			
		||||
         y="710.5"
 | 
			
		||||
         rx="0.99999958"
 | 
			
		||||
         ry="1" />
 | 
			
		||||
      <rect
 | 
			
		||||
         style="fill:#cbcbcb;fill-opacity:1;stroke:#000000;stroke-width:0.99999970000000005;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0.44262299999999999;stroke-dasharray:none;display:inline"
 | 
			
		||||
         id="rect5310"
 | 
			
		||||
         width="11"
 | 
			
		||||
         height="7"
 | 
			
		||||
         x="462.5"
 | 
			
		||||
         y="702.5"
 | 
			
		||||
         rx="0.99999958"
 | 
			
		||||
         ry="1" />
 | 
			
		||||
      <rect
 | 
			
		||||
         style="fill:#cbcbcb;fill-opacity:1;stroke:#000000;stroke-width:0.99999976000000002;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0.44262299999999999;stroke-dasharray:none;display:inline"
 | 
			
		||||
         id="rect5312"
 | 
			
		||||
         width="11"
 | 
			
		||||
         height="7"
 | 
			
		||||
         x="450.5"
 | 
			
		||||
         y="702.5"
 | 
			
		||||
         rx="0.99999958"
 | 
			
		||||
         ry="1" />
 | 
			
		||||
      <rect
 | 
			
		||||
         style="fill:#cbcbcb;fill-opacity:1;stroke:#000000;stroke-width:0.99999970000000005;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0.44262299999999999;stroke-dasharray:none;display:inline"
 | 
			
		||||
         id="rect5314"
 | 
			
		||||
         width="11"
 | 
			
		||||
         height="7"
 | 
			
		||||
         x="462.5"
 | 
			
		||||
         y="710.5"
 | 
			
		||||
         rx="0.99999958"
 | 
			
		||||
         ry="1" />
 | 
			
		||||
    </g>
 | 
			
		||||
  </g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 3.7 KiB  | 
							
								
								
									
										113
									
								
								data/theme/mosaic-view.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,113 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
 | 
			
		||||
 | 
			
		||||
<svg
 | 
			
		||||
   xmlns:dc="http://purl.org/dc/elements/1.1/"
 | 
			
		||||
   xmlns:cc="http://creativecommons.org/ns#"
 | 
			
		||||
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 | 
			
		||||
   xmlns:svg="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 | 
			
		||||
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
 | 
			
		||||
   width="24"
 | 
			
		||||
   height="16"
 | 
			
		||||
   id="svg6503"
 | 
			
		||||
   version="1.1"
 | 
			
		||||
   inkscape:version="0.47pre4 r22446"
 | 
			
		||||
   sodipodi:docname="New document 19">
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs6505">
 | 
			
		||||
    <inkscape:perspective
 | 
			
		||||
       sodipodi:type="inkscape:persp3d"
 | 
			
		||||
       inkscape:vp_x="0 : 16 : 1"
 | 
			
		||||
       inkscape:vp_y="0 : 1000 : 0"
 | 
			
		||||
       inkscape:vp_z="32 : 16 : 1"
 | 
			
		||||
       inkscape:persp3d-origin="16 : 10.666667 : 1"
 | 
			
		||||
       id="perspective6511" />
 | 
			
		||||
    <inkscape:perspective
 | 
			
		||||
       id="perspective6494"
 | 
			
		||||
       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
 | 
			
		||||
       inkscape:vp_z="1 : 0.5 : 1"
 | 
			
		||||
       inkscape:vp_y="0 : 1000 : 0"
 | 
			
		||||
       inkscape:vp_x="0 : 0.5 : 1"
 | 
			
		||||
       sodipodi:type="inkscape:persp3d" />
 | 
			
		||||
  </defs>
 | 
			
		||||
  <sodipodi:namedview
 | 
			
		||||
     id="base"
 | 
			
		||||
     pagecolor="#ffffff"
 | 
			
		||||
     bordercolor="#666666"
 | 
			
		||||
     borderopacity="1.0"
 | 
			
		||||
     inkscape:pageopacity="0.0"
 | 
			
		||||
     inkscape:pageshadow="2"
 | 
			
		||||
     inkscape:zoom="11.197802"
 | 
			
		||||
     inkscape:cx="16"
 | 
			
		||||
     inkscape:cy="16"
 | 
			
		||||
     inkscape:current-layer="layer1"
 | 
			
		||||
     showgrid="true"
 | 
			
		||||
     inkscape:grid-bbox="true"
 | 
			
		||||
     inkscape:document-units="px"
 | 
			
		||||
     inkscape:window-width="1680"
 | 
			
		||||
     inkscape:window-height="997"
 | 
			
		||||
     inkscape:window-x="0"
 | 
			
		||||
     inkscape:window-y="26"
 | 
			
		||||
     inkscape:window-maximized="1" />
 | 
			
		||||
  <metadata
 | 
			
		||||
     id="metadata6508">
 | 
			
		||||
    <rdf:RDF>
 | 
			
		||||
      <cc:Work
 | 
			
		||||
         rdf:about="">
 | 
			
		||||
        <dc:format>image/svg+xml</dc:format>
 | 
			
		||||
        <dc:type
 | 
			
		||||
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
 | 
			
		||||
        <dc:title></dc:title>
 | 
			
		||||
      </cc:Work>
 | 
			
		||||
    </rdf:RDF>
 | 
			
		||||
  </metadata>
 | 
			
		||||
  <g
 | 
			
		||||
     id="layer1"
 | 
			
		||||
     inkscape:label="Layer 1"
 | 
			
		||||
     inkscape:groupmode="layer"
 | 
			
		||||
     transform="translate(0,-16)">
 | 
			
		||||
    <g
 | 
			
		||||
       style="display:inline"
 | 
			
		||||
       transform="translate(-449.85476,-685.85869)"
 | 
			
		||||
       id="g5306">
 | 
			
		||||
      <rect
 | 
			
		||||
         style="fill:#666666;fill-opacity:1;stroke:#000000;stroke-width:0.9999997;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0.442623;stroke-dasharray:none"
 | 
			
		||||
         id="rect5308"
 | 
			
		||||
         width="11"
 | 
			
		||||
         height="7"
 | 
			
		||||
         x="450.5"
 | 
			
		||||
         y="710.5"
 | 
			
		||||
         rx="0.99999958"
 | 
			
		||||
         ry="1" />
 | 
			
		||||
      <rect
 | 
			
		||||
         style="fill:#666666;fill-opacity:1;stroke:#000000;stroke-width:0.9999997;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0.442623;stroke-dasharray:none;display:inline"
 | 
			
		||||
         id="rect5310"
 | 
			
		||||
         width="11"
 | 
			
		||||
         height="7"
 | 
			
		||||
         x="462.5"
 | 
			
		||||
         y="702.5"
 | 
			
		||||
         rx="0.99999958"
 | 
			
		||||
         ry="1" />
 | 
			
		||||
      <rect
 | 
			
		||||
         style="fill:#666666;fill-opacity:1;stroke:#000000;stroke-width:0.99999976;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0.442623;stroke-dasharray:none;display:inline"
 | 
			
		||||
         id="rect5312"
 | 
			
		||||
         width="11"
 | 
			
		||||
         height="7"
 | 
			
		||||
         x="450.5"
 | 
			
		||||
         y="702.5"
 | 
			
		||||
         rx="0.99999958"
 | 
			
		||||
         ry="1" />
 | 
			
		||||
      <rect
 | 
			
		||||
         style="fill:#666666;fill-opacity:1;stroke:#000000;stroke-width:0.9999997;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0.442623;stroke-dasharray:none;display:inline"
 | 
			
		||||
         id="rect5314"
 | 
			
		||||
         width="11"
 | 
			
		||||
         height="7"
 | 
			
		||||
         x="462.5"
 | 
			
		||||
         y="710.5"
 | 
			
		||||
         rx="0.99999958"
 | 
			
		||||
         ry="1" />
 | 
			
		||||
    </g>
 | 
			
		||||
  </g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 3.6 KiB  | 
							
								
								
									
										89
									
								
								data/theme/move-window-on-new.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,89 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
 | 
			
		||||
 | 
			
		||||
<svg
 | 
			
		||||
   xmlns:dc="http://purl.org/dc/elements/1.1/"
 | 
			
		||||
   xmlns:cc="http://creativecommons.org/ns#"
 | 
			
		||||
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 | 
			
		||||
   xmlns:svg="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 | 
			
		||||
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
 | 
			
		||||
   width="98"
 | 
			
		||||
   height="98"
 | 
			
		||||
   id="svg6375"
 | 
			
		||||
   version="1.1"
 | 
			
		||||
   inkscape:version="0.47 r22583"
 | 
			
		||||
   sodipodi:docname="add-workspace.svg">
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs6377">
 | 
			
		||||
    <inkscape:perspective
 | 
			
		||||
       sodipodi:type="inkscape:persp3d"
 | 
			
		||||
       inkscape:vp_x="0 : 16 : 1"
 | 
			
		||||
       inkscape:vp_y="0 : 1000 : 0"
 | 
			
		||||
       inkscape:vp_z="32 : 16 : 1"
 | 
			
		||||
       inkscape:persp3d-origin="16 : 10.666667 : 1"
 | 
			
		||||
       id="perspective6383" />
 | 
			
		||||
    <inkscape:perspective
 | 
			
		||||
       id="perspective6366"
 | 
			
		||||
       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
 | 
			
		||||
       inkscape:vp_z="1 : 0.5 : 1"
 | 
			
		||||
       inkscape:vp_y="0 : 1000 : 0"
 | 
			
		||||
       inkscape:vp_x="0 : 0.5 : 1"
 | 
			
		||||
       sodipodi:type="inkscape:persp3d" />
 | 
			
		||||
  </defs>
 | 
			
		||||
  <sodipodi:namedview
 | 
			
		||||
     id="base"
 | 
			
		||||
     pagecolor="#ffffff"
 | 
			
		||||
     bordercolor="#666666"
 | 
			
		||||
     borderopacity="1.0"
 | 
			
		||||
     inkscape:pageopacity="0.0"
 | 
			
		||||
     inkscape:pageshadow="2"
 | 
			
		||||
     inkscape:zoom="3.9590209"
 | 
			
		||||
     inkscape:cx="56.650687"
 | 
			
		||||
     inkscape:cy="20.635343"
 | 
			
		||||
     inkscape:current-layer="layer1"
 | 
			
		||||
     showgrid="true"
 | 
			
		||||
     inkscape:grid-bbox="true"
 | 
			
		||||
     inkscape:document-units="px"
 | 
			
		||||
     inkscape:window-width="1680"
 | 
			
		||||
     inkscape:window-height="997"
 | 
			
		||||
     inkscape:window-x="0"
 | 
			
		||||
     inkscape:window-y="26"
 | 
			
		||||
     inkscape:window-maximized="1" />
 | 
			
		||||
  <metadata
 | 
			
		||||
     id="metadata6380">
 | 
			
		||||
    <rdf:RDF>
 | 
			
		||||
      <cc:Work
 | 
			
		||||
         rdf:about="">
 | 
			
		||||
        <dc:format>image/svg+xml</dc:format>
 | 
			
		||||
        <dc:type
 | 
			
		||||
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
 | 
			
		||||
        <dc:title></dc:title>
 | 
			
		||||
      </cc:Work>
 | 
			
		||||
    </rdf:RDF>
 | 
			
		||||
  </metadata>
 | 
			
		||||
  <g
 | 
			
		||||
     id="layer1"
 | 
			
		||||
     inkscape:label="Layer 1"
 | 
			
		||||
     inkscape:groupmode="layer"
 | 
			
		||||
     transform="translate(0,66)">
 | 
			
		||||
    <g
 | 
			
		||||
       id="g2824"
 | 
			
		||||
       transform="matrix(11.568551,0,0,11.698271,-78.828159,-304.81518)">
 | 
			
		||||
      <path
 | 
			
		||||
         style="fill:none;stroke:#666666;stroke-width:1.99999952;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
 | 
			
		||||
         d="m 11.07363,21.36834 0,6.43903"
 | 
			
		||||
         id="path5322" />
 | 
			
		||||
      <path
 | 
			
		||||
         style="fill:none;stroke:#666666;stroke-width:1.99999952;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
 | 
			
		||||
         d="m 14.29314,24.58786 -6.43902,0"
 | 
			
		||||
         id="path5324" />
 | 
			
		||||
    </g>
 | 
			
		||||
    <path
 | 
			
		||||
       style="fill:#000000;fill-opacity:0.98823529"
 | 
			
		||||
       d="m 48.239516,97.908047 c -0.41677,-0.05102 -1.269253,-0.222408 -1.894408,-0.380859 -4.088493,-1.036262 -7.520781,-4.753234 -8.330163,-9.021094 -0.154947,-0.817026 -0.257819,-6.68112 -0.257819,-14.696556 l 0,-13.337088 -13.829177,-0.08909 C 10.802042,60.298796 10.026884,60.268266 8.6851548,59.783022 3.6288503,57.954375 0.62673331,53.828648 0.62673331,48.708554 c 0,-5.625522 4.25936019,-10.425065 9.97721469,-11.242548 0.987903,-0.141242 7.368912,-0.254994 14.460646,-0.257791 l 12.692532,-0.005 0,-13.586668 c 0,-14.6441583 0.03287,-15.0698926 1.364686,-17.6753047 2.185477,-4.2754229 6.938193,-6.75739913 11.687647,-6.10355607 3.382776,0.46569661 6.737962,2.72496967 8.414081,5.66577137 1.480816,2.5981315 1.519067,3.0522448 1.519067,18.0333334 l 0,13.666424 12.692533,0.005 c 7.091733,0.0028 13.472742,0.116549 14.460646,0.257791 6.395303,0.914337 10.804785,6.623716 9.941157,12.871766 -0.698243,5.051565 -4.792685,9.104635 -9.941157,9.840713 -0.987904,0.141242 -7.368913,0.254995 -14.460646,0.257791 l -12.692533,0.005 0,13.801945 c 0,13.031417 -0.02798,13.895893 -0.501177,15.484801 -1.526902,5.127058 -6.919246,8.802262 -12.001914,8.18002 z"
 | 
			
		||||
       id="path2828"
 | 
			
		||||
       transform="translate(0,-66)" />
 | 
			
		||||
  </g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 4.0 KiB  | 
@@ -1,33 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
 | 
			
		||||
 | 
			
		||||
<svg
 | 
			
		||||
   xmlns:svg="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
   width="3"
 | 
			
		||||
   height="10"
 | 
			
		||||
   id="svg2"
 | 
			
		||||
   version="1.1">
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs4" />
 | 
			
		||||
  <metadata
 | 
			
		||||
     id="metadata7">
 | 
			
		||||
  </metadata>
 | 
			
		||||
  <g
 | 
			
		||||
     id="layer1">
 | 
			
		||||
    <rect
 | 
			
		||||
       style="fill:#000000;fill-opacity:1;stroke-width:0.43599999000000000;stroke-miterlimit:4;stroke-dasharray:none"
 | 
			
		||||
       id="rect3779"
 | 
			
		||||
       width="3"
 | 
			
		||||
       height="10"
 | 
			
		||||
       x="0"
 | 
			
		||||
       y="0" />
 | 
			
		||||
    <rect
 | 
			
		||||
       style="fill:#536272;fill-opacity:1;stroke-width:0.43599999;stroke-miterlimit:4;stroke-dasharray:none"
 | 
			
		||||
       id="rect3796"
 | 
			
		||||
       width="3"
 | 
			
		||||
       height="1"
 | 
			
		||||
       x="0"
 | 
			
		||||
       y="9" />
 | 
			
		||||
  </g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 787 B  | 
@@ -1,74 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
 | 
			
		||||
 | 
			
		||||
<svg
 | 
			
		||||
   xmlns:dc="http://purl.org/dc/elements/1.1/"
 | 
			
		||||
   xmlns:cc="http://creativecommons.org/ns#"
 | 
			
		||||
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 | 
			
		||||
   xmlns:svg="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 | 
			
		||||
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
 | 
			
		||||
   width="21"
 | 
			
		||||
   height="10"
 | 
			
		||||
   id="svg2"
 | 
			
		||||
   version="1.1"
 | 
			
		||||
   inkscape:version="0.48.1 r9760"
 | 
			
		||||
   sodipodi:docname="panel-button-border.svg">
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs4" />
 | 
			
		||||
  <sodipodi:namedview
 | 
			
		||||
     id="base"
 | 
			
		||||
     pagecolor="#000000"
 | 
			
		||||
     bordercolor="#666666"
 | 
			
		||||
     borderopacity="1.0"
 | 
			
		||||
     inkscape:pageopacity="0"
 | 
			
		||||
     inkscape:pageshadow="2"
 | 
			
		||||
     inkscape:zoom="44.8"
 | 
			
		||||
     inkscape:cx="8.6594891"
 | 
			
		||||
     inkscape:cy="5.7029946"
 | 
			
		||||
     inkscape:document-units="px"
 | 
			
		||||
     inkscape:current-layer="layer1"
 | 
			
		||||
     showgrid="true"
 | 
			
		||||
     showguides="true"
 | 
			
		||||
     inkscape:guide-bbox="true"
 | 
			
		||||
     inkscape:window-width="1440"
 | 
			
		||||
     inkscape:window-height="843"
 | 
			
		||||
     inkscape:window-x="0"
 | 
			
		||||
     inkscape:window-y="26"
 | 
			
		||||
     inkscape:window-maximized="1"
 | 
			
		||||
     guidetolerance="10000"
 | 
			
		||||
     objecttolerance="10000">
 | 
			
		||||
    <inkscape:grid
 | 
			
		||||
       type="xygrid"
 | 
			
		||||
       id="grid3792"
 | 
			
		||||
       empspacing="10"
 | 
			
		||||
       visible="true"
 | 
			
		||||
       enabled="true"
 | 
			
		||||
       snapvisiblegridlinesonly="true" />
 | 
			
		||||
  </sodipodi:namedview>
 | 
			
		||||
  <metadata
 | 
			
		||||
     id="metadata7">
 | 
			
		||||
    <rdf:RDF>
 | 
			
		||||
      <cc:Work
 | 
			
		||||
         rdf:about="">
 | 
			
		||||
        <dc:format>image/svg+xml</dc:format>
 | 
			
		||||
        <dc:type
 | 
			
		||||
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
 | 
			
		||||
        <dc:title />
 | 
			
		||||
      </cc:Work>
 | 
			
		||||
    </rdf:RDF>
 | 
			
		||||
  </metadata>
 | 
			
		||||
  <g
 | 
			
		||||
     inkscape:label="Layer 1"
 | 
			
		||||
     inkscape:groupmode="layer"
 | 
			
		||||
     id="layer1">
 | 
			
		||||
    <rect
 | 
			
		||||
       style="opacity:0.8;fill:#ffffff;fill-opacity:1;stroke-width:0.43599999;stroke-miterlimit:4;stroke-dasharray:none"
 | 
			
		||||
       id="rect3796"
 | 
			
		||||
       width="3"
 | 
			
		||||
       height="2"
 | 
			
		||||
       x="9"
 | 
			
		||||
       y="8" />
 | 
			
		||||
  </g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 2.0 KiB  | 
@@ -1,111 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
 | 
			
		||||
 | 
			
		||||
<svg
 | 
			
		||||
   xmlns:dc="http://purl.org/dc/elements/1.1/"
 | 
			
		||||
   xmlns:cc="http://creativecommons.org/ns#"
 | 
			
		||||
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 | 
			
		||||
   xmlns:svg="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns:xlink="http://www.w3.org/1999/xlink"
 | 
			
		||||
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 | 
			
		||||
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
 | 
			
		||||
   width="30"
 | 
			
		||||
   height="25"
 | 
			
		||||
   id="svg10621"
 | 
			
		||||
   version="1.1"
 | 
			
		||||
   inkscape:version="0.48.1 r9760"
 | 
			
		||||
   sodipodi:docname="panel-button-highlight-narrow.svg">
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs10623">
 | 
			
		||||
    <radialGradient
 | 
			
		||||
       inkscape:collect="always"
 | 
			
		||||
       xlink:href="#linearGradient34508-1-3"
 | 
			
		||||
       id="radialGradient99561-1"
 | 
			
		||||
       gradientUnits="userSpaceOnUse"
 | 
			
		||||
       gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
 | 
			
		||||
       cx="51"
 | 
			
		||||
       cy="30"
 | 
			
		||||
       fx="51"
 | 
			
		||||
       fy="30"
 | 
			
		||||
       r="42" />
 | 
			
		||||
    <linearGradient
 | 
			
		||||
       inkscape:collect="always"
 | 
			
		||||
       id="linearGradient34508-1-3">
 | 
			
		||||
      <stop
 | 
			
		||||
         style="stop-color:#ffffff;stop-opacity:1;"
 | 
			
		||||
         offset="0"
 | 
			
		||||
         id="stop34510-1-9" />
 | 
			
		||||
      <stop
 | 
			
		||||
         style="stop-color:#ffffff;stop-opacity:0;"
 | 
			
		||||
         offset="1"
 | 
			
		||||
         id="stop34512-4-5" />
 | 
			
		||||
    </linearGradient>
 | 
			
		||||
    <radialGradient
 | 
			
		||||
       r="42"
 | 
			
		||||
       fy="30"
 | 
			
		||||
       fx="51"
 | 
			
		||||
       cy="30"
 | 
			
		||||
       cx="51"
 | 
			
		||||
       gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
 | 
			
		||||
       gradientUnits="userSpaceOnUse"
 | 
			
		||||
       id="radialGradient10592"
 | 
			
		||||
       xlink:href="#linearGradient34508-1-3"
 | 
			
		||||
       inkscape:collect="always" />
 | 
			
		||||
  </defs>
 | 
			
		||||
  <sodipodi:namedview
 | 
			
		||||
     id="base"
 | 
			
		||||
     pagecolor="#000000"
 | 
			
		||||
     bordercolor="#666666"
 | 
			
		||||
     borderopacity="1.0"
 | 
			
		||||
     inkscape:pageopacity="0"
 | 
			
		||||
     inkscape:pageshadow="2"
 | 
			
		||||
     inkscape:zoom="1.979899"
 | 
			
		||||
     inkscape:cx="-171.36384"
 | 
			
		||||
     inkscape:cy="-53.255157"
 | 
			
		||||
     inkscape:document-units="px"
 | 
			
		||||
     inkscape:current-layer="layer1"
 | 
			
		||||
     showgrid="false"
 | 
			
		||||
     fit-margin-top="0"
 | 
			
		||||
     fit-margin-left="0"
 | 
			
		||||
     fit-margin-right="0"
 | 
			
		||||
     fit-margin-bottom="0"
 | 
			
		||||
     inkscape:window-width="1440"
 | 
			
		||||
     inkscape:window-height="843"
 | 
			
		||||
     inkscape:window-x="0"
 | 
			
		||||
     inkscape:window-y="26"
 | 
			
		||||
     inkscape:window-maximized="1" />
 | 
			
		||||
  <metadata
 | 
			
		||||
     id="metadata10626">
 | 
			
		||||
    <rdf:RDF>
 | 
			
		||||
      <cc:Work
 | 
			
		||||
         rdf:about="">
 | 
			
		||||
        <dc:format>image/svg+xml</dc:format>
 | 
			
		||||
        <dc:type
 | 
			
		||||
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
 | 
			
		||||
        <dc:title />
 | 
			
		||||
      </cc:Work>
 | 
			
		||||
    </rdf:RDF>
 | 
			
		||||
  </metadata>
 | 
			
		||||
  <g
 | 
			
		||||
     inkscape:label="Layer 1"
 | 
			
		||||
     inkscape:groupmode="layer"
 | 
			
		||||
     id="layer1"
 | 
			
		||||
     transform="translate(-468.08632,-537.03477)">
 | 
			
		||||
    <path
 | 
			
		||||
       sodipodi:type="arc"
 | 
			
		||||
       style="opacity:0.4625;color:#000000;fill:url(#radialGradient10592);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
 | 
			
		||||
       id="path34506-3"
 | 
			
		||||
       sodipodi:cx="51"
 | 
			
		||||
       sodipodi:cy="30"
 | 
			
		||||
       sodipodi:rx="42"
 | 
			
		||||
       sodipodi:ry="16"
 | 
			
		||||
       d="M 9,29.999999 C 9.0000011,21.163443 27.804042,14 51.000002,14 74.195961,14 93,21.163444 93,30 l -42,0 z"
 | 
			
		||||
       sodipodi:start="3.1415927"
 | 
			
		||||
       sodipodi:end="6.2831853"
 | 
			
		||||
       transform="matrix(0.35714286,0,0,1.5625,464.87203,515.15977)"
 | 
			
		||||
       inkscape:export-filename="/home/jimmac/src/cvs/gnome/gnome-shell-design/mockups/motion/textures/panel.png"
 | 
			
		||||
       inkscape:export-xdpi="90"
 | 
			
		||||
       inkscape:export-ydpi="90" />
 | 
			
		||||
  </g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 3.5 KiB  | 
@@ -1,111 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
 | 
			
		||||
 | 
			
		||||
<svg
 | 
			
		||||
   xmlns:dc="http://purl.org/dc/elements/1.1/"
 | 
			
		||||
   xmlns:cc="http://creativecommons.org/ns#"
 | 
			
		||||
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 | 
			
		||||
   xmlns:svg="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns:xlink="http://www.w3.org/1999/xlink"
 | 
			
		||||
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 | 
			
		||||
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
 | 
			
		||||
   width="84"
 | 
			
		||||
   height="25"
 | 
			
		||||
   id="svg10621"
 | 
			
		||||
   version="1.1"
 | 
			
		||||
   inkscape:version="0.48.0 r9654"
 | 
			
		||||
   sodipodi:docname="panel-button-highlight-wide.svg">
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs10623">
 | 
			
		||||
    <radialGradient
 | 
			
		||||
       inkscape:collect="always"
 | 
			
		||||
       xlink:href="#linearGradient34508-1-3"
 | 
			
		||||
       id="radialGradient99561-1"
 | 
			
		||||
       gradientUnits="userSpaceOnUse"
 | 
			
		||||
       gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
 | 
			
		||||
       cx="51"
 | 
			
		||||
       cy="30"
 | 
			
		||||
       fx="51"
 | 
			
		||||
       fy="30"
 | 
			
		||||
       r="42" />
 | 
			
		||||
    <linearGradient
 | 
			
		||||
       inkscape:collect="always"
 | 
			
		||||
       id="linearGradient34508-1-3">
 | 
			
		||||
      <stop
 | 
			
		||||
         style="stop-color:#ffffff;stop-opacity:1;"
 | 
			
		||||
         offset="0"
 | 
			
		||||
         id="stop34510-1-9" />
 | 
			
		||||
      <stop
 | 
			
		||||
         style="stop-color:#ffffff;stop-opacity:0;"
 | 
			
		||||
         offset="1"
 | 
			
		||||
         id="stop34512-4-5" />
 | 
			
		||||
    </linearGradient>
 | 
			
		||||
    <radialGradient
 | 
			
		||||
       r="42"
 | 
			
		||||
       fy="30"
 | 
			
		||||
       fx="51"
 | 
			
		||||
       cy="30"
 | 
			
		||||
       cx="51"
 | 
			
		||||
       gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)"
 | 
			
		||||
       gradientUnits="userSpaceOnUse"
 | 
			
		||||
       id="radialGradient10592"
 | 
			
		||||
       xlink:href="#linearGradient34508-1-3"
 | 
			
		||||
       inkscape:collect="always" />
 | 
			
		||||
  </defs>
 | 
			
		||||
  <sodipodi:namedview
 | 
			
		||||
     id="base"
 | 
			
		||||
     pagecolor="#000000"
 | 
			
		||||
     bordercolor="#666666"
 | 
			
		||||
     borderopacity="1.0"
 | 
			
		||||
     inkscape:pageopacity="0"
 | 
			
		||||
     inkscape:pageshadow="2"
 | 
			
		||||
     inkscape:zoom="1.979899"
 | 
			
		||||
     inkscape:cx="-118.50071"
 | 
			
		||||
     inkscape:cy="27.304508"
 | 
			
		||||
     inkscape:document-units="px"
 | 
			
		||||
     inkscape:current-layer="layer1"
 | 
			
		||||
     showgrid="false"
 | 
			
		||||
     fit-margin-top="0"
 | 
			
		||||
     fit-margin-left="0"
 | 
			
		||||
     fit-margin-right="0"
 | 
			
		||||
     fit-margin-bottom="0"
 | 
			
		||||
     inkscape:window-width="1440"
 | 
			
		||||
     inkscape:window-height="843"
 | 
			
		||||
     inkscape:window-x="0"
 | 
			
		||||
     inkscape:window-y="26"
 | 
			
		||||
     inkscape:window-maximized="1" />
 | 
			
		||||
  <metadata
 | 
			
		||||
     id="metadata10626">
 | 
			
		||||
    <rdf:RDF>
 | 
			
		||||
      <cc:Work
 | 
			
		||||
         rdf:about="">
 | 
			
		||||
        <dc:format>image/svg+xml</dc:format>
 | 
			
		||||
        <dc:type
 | 
			
		||||
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
 | 
			
		||||
        <dc:title />
 | 
			
		||||
      </cc:Work>
 | 
			
		||||
    </rdf:RDF>
 | 
			
		||||
  </metadata>
 | 
			
		||||
  <g
 | 
			
		||||
     inkscape:label="Layer 1"
 | 
			
		||||
     inkscape:groupmode="layer"
 | 
			
		||||
     id="layer1"
 | 
			
		||||
     transform="translate(-441.08632,-537.03477)">
 | 
			
		||||
    <path
 | 
			
		||||
       sodipodi:type="arc"
 | 
			
		||||
       style="opacity:0.4625;color:#000000;fill:url(#radialGradient10592);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
 | 
			
		||||
       id="path34506-3"
 | 
			
		||||
       sodipodi:cx="51"
 | 
			
		||||
       sodipodi:cy="30"
 | 
			
		||||
       sodipodi:rx="42"
 | 
			
		||||
       sodipodi:ry="16"
 | 
			
		||||
       d="M 9,29.999999 C 9.0000011,21.163443 27.804042,14 51.000002,14 74.195961,14 93,21.163444 93,30 l -42,0 z"
 | 
			
		||||
       sodipodi:start="3.1415927"
 | 
			
		||||
       sodipodi:end="6.2831853"
 | 
			
		||||
       transform="matrix(1,0,0,1.5625,432.08632,515.15977)"
 | 
			
		||||
       inkscape:export-filename="/home/jimmac/src/cvs/gnome/gnome-shell-design/mockups/motion/textures/panel.png"
 | 
			
		||||
       inkscape:export-xdpi="90"
 | 
			
		||||
       inkscape:export-ydpi="90" />
 | 
			
		||||
  </g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 3.5 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								data/theme/process-working.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 4.0 KiB  | 
@@ -1,261 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
 | 
			
		||||
 | 
			
		||||
<svg
 | 
			
		||||
   xmlns:dc="http://purl.org/dc/elements/1.1/"
 | 
			
		||||
   xmlns:cc="http://creativecommons.org/ns#"
 | 
			
		||||
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 | 
			
		||||
   xmlns:svg="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns:xlink="http://www.w3.org/1999/xlink"
 | 
			
		||||
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 | 
			
		||||
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
 | 
			
		||||
   id="svg5369"
 | 
			
		||||
   version="1.1"
 | 
			
		||||
   inkscape:version="0.48+devel r10053 custom"
 | 
			
		||||
   width="96"
 | 
			
		||||
   height="48"
 | 
			
		||||
   sodipodi:docname="process-working.svg"
 | 
			
		||||
   style="display:inline">
 | 
			
		||||
  <metadata
 | 
			
		||||
     id="metadata5375">
 | 
			
		||||
    <rdf:RDF>
 | 
			
		||||
      <cc:Work
 | 
			
		||||
         rdf:about="">
 | 
			
		||||
        <dc:format>image/svg+xml</dc:format>
 | 
			
		||||
        <dc:type
 | 
			
		||||
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
 | 
			
		||||
        <dc:title></dc:title>
 | 
			
		||||
      </cc:Work>
 | 
			
		||||
    </rdf:RDF>
 | 
			
		||||
  </metadata>
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs5373" />
 | 
			
		||||
  <sodipodi:namedview
 | 
			
		||||
     pagecolor="#808080"
 | 
			
		||||
     bordercolor="#666666"
 | 
			
		||||
     borderopacity="1"
 | 
			
		||||
     objecttolerance="10"
 | 
			
		||||
     gridtolerance="10"
 | 
			
		||||
     guidetolerance="10"
 | 
			
		||||
     inkscape:pageopacity="0"
 | 
			
		||||
     inkscape:pageshadow="2"
 | 
			
		||||
     inkscape:window-width="1975"
 | 
			
		||||
     inkscape:window-height="1098"
 | 
			
		||||
     id="namedview5371"
 | 
			
		||||
     showgrid="true"
 | 
			
		||||
     borderlayer="true"
 | 
			
		||||
     inkscape:showpageshadow="false"
 | 
			
		||||
     inkscape:zoom="16"
 | 
			
		||||
     inkscape:cx="53.997662"
 | 
			
		||||
     inkscape:cy="22.367695"
 | 
			
		||||
     inkscape:window-x="1600"
 | 
			
		||||
     inkscape:window-y="33"
 | 
			
		||||
     inkscape:window-maximized="0"
 | 
			
		||||
     inkscape:current-layer="layer2">
 | 
			
		||||
    <inkscape:grid
 | 
			
		||||
       type="xygrid"
 | 
			
		||||
       id="grid11933"
 | 
			
		||||
       empspacing="5"
 | 
			
		||||
       visible="true"
 | 
			
		||||
       enabled="true"
 | 
			
		||||
       snapvisiblegridlinesonly="true" />
 | 
			
		||||
  </sodipodi:namedview>
 | 
			
		||||
  <g
 | 
			
		||||
     inkscape:groupmode="layer"
 | 
			
		||||
     id="layer1"
 | 
			
		||||
     inkscape:label="tiles"
 | 
			
		||||
     style="display:none">
 | 
			
		||||
    <rect
 | 
			
		||||
       style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
 | 
			
		||||
       id="rect12451"
 | 
			
		||||
       width="24"
 | 
			
		||||
       height="24"
 | 
			
		||||
       x="0"
 | 
			
		||||
       y="0" />
 | 
			
		||||
    <rect
 | 
			
		||||
       y="24"
 | 
			
		||||
       x="0"
 | 
			
		||||
       height="24"
 | 
			
		||||
       width="24"
 | 
			
		||||
       id="rect12453"
 | 
			
		||||
       style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
 | 
			
		||||
    <rect
 | 
			
		||||
       y="0"
 | 
			
		||||
       x="24"
 | 
			
		||||
       height="24"
 | 
			
		||||
       width="24"
 | 
			
		||||
       id="rect12455"
 | 
			
		||||
       style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
 | 
			
		||||
    <rect
 | 
			
		||||
       style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
 | 
			
		||||
       id="rect12457"
 | 
			
		||||
       width="24"
 | 
			
		||||
       height="24"
 | 
			
		||||
       x="24"
 | 
			
		||||
       y="24" />
 | 
			
		||||
    <rect
 | 
			
		||||
       style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
 | 
			
		||||
       id="rect12459"
 | 
			
		||||
       width="24"
 | 
			
		||||
       height="24"
 | 
			
		||||
       x="48"
 | 
			
		||||
       y="0" />
 | 
			
		||||
    <rect
 | 
			
		||||
       y="24"
 | 
			
		||||
       x="48"
 | 
			
		||||
       height="24"
 | 
			
		||||
       width="24"
 | 
			
		||||
       id="rect12461"
 | 
			
		||||
       style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
 | 
			
		||||
    <rect
 | 
			
		||||
       y="0"
 | 
			
		||||
       x="72"
 | 
			
		||||
       height="24"
 | 
			
		||||
       width="24"
 | 
			
		||||
       id="rect12463"
 | 
			
		||||
       style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
 | 
			
		||||
    <rect
 | 
			
		||||
       style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
 | 
			
		||||
       id="rect12465"
 | 
			
		||||
       width="24"
 | 
			
		||||
       height="24"
 | 
			
		||||
       x="72"
 | 
			
		||||
       y="24" />
 | 
			
		||||
  </g>
 | 
			
		||||
  <g
 | 
			
		||||
     inkscape:groupmode="layer"
 | 
			
		||||
     id="layer2"
 | 
			
		||||
     inkscape:label="spinner">
 | 
			
		||||
    <g
 | 
			
		||||
       transform="matrix(0.28240106,0,0,0.28240106,146.92015,-382.52444)"
 | 
			
		||||
       id="g10450-5"
 | 
			
		||||
       style="display:inline">
 | 
			
		||||
      <path
 | 
			
		||||
         inkscape:connector-curvature="0"
 | 
			
		||||
         style="opacity:0.6;color:#000000;fill:none;stroke:#ffffff;stroke-width:7.08212566;stroke-linecap:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
 | 
			
		||||
         d="m -477.76072,1373.3569 0,9.4717"
 | 
			
		||||
         id="path18768"
 | 
			
		||||
         sodipodi:nodetypes="cc"
 | 
			
		||||
         inkscape:transform-center-y="-4.6808838" />
 | 
			
		||||
      <path
 | 
			
		||||
         inkscape:connector-curvature="0"
 | 
			
		||||
         inkscape:transform-center-y="-3.3099227"
 | 
			
		||||
         sodipodi:nodetypes="cc"
 | 
			
		||||
         id="path18770"
 | 
			
		||||
         d="m -461.0171,1380.2922 -7.23427,7.3824"
 | 
			
		||||
         style="opacity:0.7;color:#000000;fill:none;stroke:#ffffff;stroke-width:7.08212566;stroke-linecap:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
 | 
			
		||||
         inkscape:transform-center-x="-3.3098966" />
 | 
			
		||||
      <path
 | 
			
		||||
         inkscape:connector-curvature="0"
 | 
			
		||||
         inkscape:transform-center-x="-4.6808962"
 | 
			
		||||
         style="opacity:0.8;color:#000000;fill:none;stroke:#ffffff;stroke-width:7.08212566;stroke-linecap:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
 | 
			
		||||
         d="m -454.08163,1397.0359 -9.47165,0"
 | 
			
		||||
         id="path18772"
 | 
			
		||||
         sodipodi:nodetypes="cc"
 | 
			
		||||
         inkscape:transform-center-y="-2.6596956e-05" />
 | 
			
		||||
      <path
 | 
			
		||||
         inkscape:connector-curvature="0"
 | 
			
		||||
         sodipodi:nodetypes="cc"
 | 
			
		||||
         id="path18774"
 | 
			
		||||
         d="m -461.01709,1413.7796 -6.93831,-7.0864"
 | 
			
		||||
         style="opacity:0.9;color:#000000;fill:none;stroke:#ffffff;stroke-width:7.08212566;stroke-linecap:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
 | 
			
		||||
         inkscape:transform-center-x="-3.3098966"
 | 
			
		||||
         inkscape:transform-center-y="3.3098652" />
 | 
			
		||||
      <path
 | 
			
		||||
         inkscape:connector-curvature="0"
 | 
			
		||||
         inkscape:transform-center-y="4.6808757"
 | 
			
		||||
         style="color:#000000;fill:none;stroke:#ffffff;stroke-width:7.08212566;stroke-linecap:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
 | 
			
		||||
         d="m -477.76074,1420.715 9e-5,-9.4716"
 | 
			
		||||
         id="path18776"
 | 
			
		||||
         sodipodi:nodetypes="cc" />
 | 
			
		||||
      <path
 | 
			
		||||
         inkscape:connector-curvature="0"
 | 
			
		||||
         sodipodi:nodetypes="cc"
 | 
			
		||||
         id="path18778"
 | 
			
		||||
         d="m -494.50442,1413.7796 6.79048,-6.9384"
 | 
			
		||||
         style="opacity:0.3;color:#000000;fill:none;stroke:#ffffff;stroke-width:7.08212566;stroke-linecap:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
 | 
			
		||||
         inkscape:transform-center-y="3.3098769"
 | 
			
		||||
         inkscape:transform-center-x="3.3098883" />
 | 
			
		||||
      <path
 | 
			
		||||
         inkscape:connector-curvature="0"
 | 
			
		||||
         inkscape:transform-center-x="4.6808941"
 | 
			
		||||
         style="opacity:0.4;color:#000000;fill:none;stroke:#ffffff;stroke-width:7.08212566;stroke-linecap:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
 | 
			
		||||
         d="m -501.43987,1397.0359 9.47174,0"
 | 
			
		||||
         id="path18780"
 | 
			
		||||
         sodipodi:nodetypes="cc"
 | 
			
		||||
         inkscape:transform-center-y="-2.6596956e-05" />
 | 
			
		||||
      <path
 | 
			
		||||
         inkscape:connector-curvature="0"
 | 
			
		||||
         sodipodi:nodetypes="cc"
 | 
			
		||||
         id="path18782"
 | 
			
		||||
         d="m -494.5044,1380.2922 6.64243,6.9384"
 | 
			
		||||
         style="opacity:0.5;color:#000000;fill:none;stroke:#ffffff;stroke-width:7.08212566;stroke-linecap:round;stroke-opacity:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
 | 
			
		||||
         inkscape:transform-center-x="3.3098902"
 | 
			
		||||
         inkscape:transform-center-y="-3.3099302" />
 | 
			
		||||
    </g>
 | 
			
		||||
    <use
 | 
			
		||||
       style="display:inline"
 | 
			
		||||
       x="0"
 | 
			
		||||
       y="0"
 | 
			
		||||
       xlink:href="#g10450-5"
 | 
			
		||||
       id="use4981"
 | 
			
		||||
       transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,36,-4.9705636)"
 | 
			
		||||
       width="400"
 | 
			
		||||
       height="400" />
 | 
			
		||||
    <use
 | 
			
		||||
       style="display:inline"
 | 
			
		||||
       x="0"
 | 
			
		||||
       y="0"
 | 
			
		||||
       xlink:href="#use4981"
 | 
			
		||||
       id="use4983"
 | 
			
		||||
       transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,43.032478,-21.909695)"
 | 
			
		||||
       width="400"
 | 
			
		||||
       height="400" />
 | 
			
		||||
    <use
 | 
			
		||||
       style="display:inline"
 | 
			
		||||
       x="0"
 | 
			
		||||
       y="0"
 | 
			
		||||
       xlink:href="#use4983"
 | 
			
		||||
       id="use4985"
 | 
			
		||||
       transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,50.081986,-38.904617)"
 | 
			
		||||
       width="400"
 | 
			
		||||
       height="400" />
 | 
			
		||||
    <use
 | 
			
		||||
       style="display:inline"
 | 
			
		||||
       x="0"
 | 
			
		||||
       y="0"
 | 
			
		||||
       xlink:href="#use4985"
 | 
			
		||||
       id="use4987"
 | 
			
		||||
       transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,-38.919996,-31.872139)"
 | 
			
		||||
       width="400"
 | 
			
		||||
       height="400" />
 | 
			
		||||
    <use
 | 
			
		||||
       style="display:inline"
 | 
			
		||||
       x="0"
 | 
			
		||||
       y="0"
 | 
			
		||||
       xlink:href="#use4987"
 | 
			
		||||
       id="use4989"
 | 
			
		||||
       transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,52.986628,2.0890543)"
 | 
			
		||||
       width="400"
 | 
			
		||||
       height="400" />
 | 
			
		||||
    <use
 | 
			
		||||
       style="display:inline"
 | 
			
		||||
       x="0"
 | 
			
		||||
       y="0"
 | 
			
		||||
       xlink:href="#use4989"
 | 
			
		||||
       id="use4991"
 | 
			
		||||
       transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,60.013026,-14.912936)"
 | 
			
		||||
       width="400"
 | 
			
		||||
       height="400" />
 | 
			
		||||
    <use
 | 
			
		||||
       style="display:inline"
 | 
			
		||||
       x="0"
 | 
			
		||||
       y="0"
 | 
			
		||||
       xlink:href="#use4991"
 | 
			
		||||
       id="use4993"
 | 
			
		||||
       transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,67.022396,-31.859127)"
 | 
			
		||||
       width="400"
 | 
			
		||||
       height="400" />
 | 
			
		||||
  </g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 9.8 KiB  | 
							
								
								
									
										92
									
								
								data/theme/remove-workspace.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,92 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
 | 
			
		||||
 | 
			
		||||
<svg
 | 
			
		||||
   xmlns:dc="http://purl.org/dc/elements/1.1/"
 | 
			
		||||
   xmlns:cc="http://creativecommons.org/ns#"
 | 
			
		||||
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 | 
			
		||||
   xmlns:svg="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 | 
			
		||||
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
 | 
			
		||||
   width="23"
 | 
			
		||||
   height="15"
 | 
			
		||||
   id="svg5501"
 | 
			
		||||
   version="1.1"
 | 
			
		||||
   inkscape:version="0.47pre4 r22446"
 | 
			
		||||
   sodipodi:docname="add-workspace.svg">
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs5503">
 | 
			
		||||
    <inkscape:perspective
 | 
			
		||||
       sodipodi:type="inkscape:persp3d"
 | 
			
		||||
       inkscape:vp_x="0 : 16 : 1"
 | 
			
		||||
       inkscape:vp_y="0 : 1000 : 0"
 | 
			
		||||
       inkscape:vp_z="32 : 16 : 1"
 | 
			
		||||
       inkscape:persp3d-origin="16 : 10.666667 : 1"
 | 
			
		||||
       id="perspective5509" />
 | 
			
		||||
    <inkscape:perspective
 | 
			
		||||
       id="perspective5314"
 | 
			
		||||
       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
 | 
			
		||||
       inkscape:vp_z="1 : 0.5 : 1"
 | 
			
		||||
       inkscape:vp_y="0 : 1000 : 0"
 | 
			
		||||
       inkscape:vp_x="0 : 0.5 : 1"
 | 
			
		||||
       sodipodi:type="inkscape:persp3d" />
 | 
			
		||||
  </defs>
 | 
			
		||||
  <sodipodi:namedview
 | 
			
		||||
     id="base"
 | 
			
		||||
     pagecolor="#ffffff"
 | 
			
		||||
     bordercolor="#666666"
 | 
			
		||||
     borderopacity="1.0"
 | 
			
		||||
     inkscape:pageopacity="0.0"
 | 
			
		||||
     inkscape:pageshadow="2"
 | 
			
		||||
     inkscape:zoom="11.197802"
 | 
			
		||||
     inkscape:cx="-0.074583208"
 | 
			
		||||
     inkscape:cy="16"
 | 
			
		||||
     inkscape:current-layer="layer1"
 | 
			
		||||
     showgrid="true"
 | 
			
		||||
     inkscape:grid-bbox="true"
 | 
			
		||||
     inkscape:document-units="px"
 | 
			
		||||
     inkscape:window-width="1680"
 | 
			
		||||
     inkscape:window-height="997"
 | 
			
		||||
     inkscape:window-x="0"
 | 
			
		||||
     inkscape:window-y="26"
 | 
			
		||||
     inkscape:window-maximized="1"
 | 
			
		||||
     inkscape:snap-grids="true"
 | 
			
		||||
     inkscape:snap-bbox="true" />
 | 
			
		||||
  <metadata
 | 
			
		||||
     id="metadata5506">
 | 
			
		||||
    <rdf:RDF>
 | 
			
		||||
      <cc:Work
 | 
			
		||||
         rdf:about="">
 | 
			
		||||
        <dc:format>image/svg+xml</dc:format>
 | 
			
		||||
        <dc:type
 | 
			
		||||
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
 | 
			
		||||
        <dc:title></dc:title>
 | 
			
		||||
      </cc:Work>
 | 
			
		||||
    </rdf:RDF>
 | 
			
		||||
  </metadata>
 | 
			
		||||
  <g
 | 
			
		||||
     id="layer1"
 | 
			
		||||
     inkscape:label="Layer 1"
 | 
			
		||||
     inkscape:groupmode="layer"
 | 
			
		||||
     transform="translate(0,-17)">
 | 
			
		||||
    <g
 | 
			
		||||
       style="display:inline"
 | 
			
		||||
       id="g6239"
 | 
			
		||||
       transform="translate(-953.97989,-657.32287)">
 | 
			
		||||
      <rect
 | 
			
		||||
         style="fill:#000000;fill-opacity:0.98770495;stroke:#666666;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
 | 
			
		||||
         id="rect5318-6"
 | 
			
		||||
         width="22"
 | 
			
		||||
         height="14"
 | 
			
		||||
         x="954.5"
 | 
			
		||||
         y="675"
 | 
			
		||||
         rx="0.49999979"
 | 
			
		||||
         ry="0.5" />
 | 
			
		||||
      <path
 | 
			
		||||
         style="fill:none;stroke:#666666;stroke-width:1.99999952;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
 | 
			
		||||
         d="m 968.71951,682 -6.43902,0"
 | 
			
		||||
         id="path5324-5" />
 | 
			
		||||
    </g>
 | 
			
		||||
  </g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 2.9 KiB  | 
@@ -2,62 +2,24 @@
 | 
			
		||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
 | 
			
		||||
 | 
			
		||||
<svg
 | 
			
		||||
   xmlns:dc="http://purl.org/dc/elements/1.1/"
 | 
			
		||||
   xmlns:cc="http://creativecommons.org/ns#"
 | 
			
		||||
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 | 
			
		||||
   xmlns:svg="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns:xlink="http://www.w3.org/1999/xlink"
 | 
			
		||||
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 | 
			
		||||
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
 | 
			
		||||
   width="119.97824"
 | 
			
		||||
   height="119.97824"
 | 
			
		||||
   width="74.01342"
 | 
			
		||||
   height="74.006706"
 | 
			
		||||
   id="svg7355"
 | 
			
		||||
   version="1.1"
 | 
			
		||||
   inkscape:version="0.48.1 r9760"
 | 
			
		||||
   sodipodi:docname="running-indicator.svg">
 | 
			
		||||
  <metadata
 | 
			
		||||
     id="metadata4175">
 | 
			
		||||
    <rdf:RDF>
 | 
			
		||||
      <cc:Work
 | 
			
		||||
         rdf:about="">
 | 
			
		||||
        <dc:format>image/svg+xml</dc:format>
 | 
			
		||||
        <dc:type
 | 
			
		||||
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
 | 
			
		||||
      </cc:Work>
 | 
			
		||||
    </rdf:RDF>
 | 
			
		||||
  </metadata>
 | 
			
		||||
  <sodipodi:namedview
 | 
			
		||||
     pagecolor="#2c1cff"
 | 
			
		||||
     bordercolor="#666666"
 | 
			
		||||
     borderopacity="1"
 | 
			
		||||
     objecttolerance="10"
 | 
			
		||||
     gridtolerance="10"
 | 
			
		||||
     guidetolerance="10"
 | 
			
		||||
     inkscape:pageopacity="1"
 | 
			
		||||
     inkscape:pageshadow="2"
 | 
			
		||||
     inkscape:window-width="1920"
 | 
			
		||||
     inkscape:window-height="1141"
 | 
			
		||||
     id="namedview4173"
 | 
			
		||||
     showgrid="false"
 | 
			
		||||
     inkscape:zoom="8.1348081"
 | 
			
		||||
     inkscape:cx="81.120662"
 | 
			
		||||
     inkscape:cy="58.117986"
 | 
			
		||||
     inkscape:window-x="0"
 | 
			
		||||
     inkscape:window-y="26"
 | 
			
		||||
     inkscape:window-maximized="1"
 | 
			
		||||
     inkscape:current-layer="g30864" />
 | 
			
		||||
   version="1.1">
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs7357">
 | 
			
		||||
    <radialGradient
 | 
			
		||||
       xlink:href="#linearGradient36429"
 | 
			
		||||
       id="radialGradient7461"
 | 
			
		||||
       gradientUnits="userSpaceOnUse"
 | 
			
		||||
       gradientTransform="matrix(1.011539,0,0,0.57582113,-0.39262194,71.83807)"
 | 
			
		||||
       cx="47.428951"
 | 
			
		||||
       cy="167.16817"
 | 
			
		||||
       fx="47.428951"
 | 
			
		||||
       fy="167.16817"
 | 
			
		||||
       gradientTransform="matrix(1.0525552,0,0,1.0525552,-2.5162753,-9.0000838)"
 | 
			
		||||
       cx="47.878681"
 | 
			
		||||
       cy="171.25"
 | 
			
		||||
       fx="47.878681"
 | 
			
		||||
       fy="171.25"
 | 
			
		||||
       r="37" />
 | 
			
		||||
    <linearGradient
 | 
			
		||||
       id="linearGradient36429">
 | 
			
		||||
@@ -97,34 +59,31 @@
 | 
			
		||||
       fx="49.067139"
 | 
			
		||||
       cy="242.50381"
 | 
			
		||||
       cx="49.067139"
 | 
			
		||||
       gradientTransform="matrix(1.1891549,0,0,0.15252127,-9.281289,132.52772)"
 | 
			
		||||
       gradientTransform="matrix(1.1891549,0,0,0.55513246,-9.281289,36.12653)"
 | 
			
		||||
       gradientUnits="userSpaceOnUse"
 | 
			
		||||
       id="radialGradient7488"
 | 
			
		||||
       xlink:href="#linearGradient36471" />
 | 
			
		||||
  </defs>
 | 
			
		||||
  <g
 | 
			
		||||
     id="layer1"
 | 
			
		||||
     transform="matrix(1.6213276,0,0,1.6213276,-431.6347,-272.5745)">
 | 
			
		||||
     transform="translate(-266.21629,-168.11809)">
 | 
			
		||||
    <g
 | 
			
		||||
       style="display:inline"
 | 
			
		||||
       id="g30864"
 | 
			
		||||
       transform="translate(255.223,70.118091)">
 | 
			
		||||
      <rect
 | 
			
		||||
         ry="3.4593496"
 | 
			
		||||
         rx="3.4593496"
 | 
			
		||||
         y="99.596962"
 | 
			
		||||
         x="12.596948"
 | 
			
		||||
         height="71.116341"
 | 
			
		||||
         width="71.116341"
 | 
			
		||||
         ry="3.5996203"
 | 
			
		||||
         rx="3.5996203"
 | 
			
		||||
         y="98"
 | 
			
		||||
         x="11"
 | 
			
		||||
         height="74"
 | 
			
		||||
         width="74"
 | 
			
		||||
         id="rect14000"
 | 
			
		||||
         style="opacity:0.37187500000000001;fill:url(#radialGradient7461);fill-opacity:1;stroke:none" />
 | 
			
		||||
         style="opacity:0.371875;fill:url(#radialGradient7461);fill-opacity:1;stroke:none" />
 | 
			
		||||
      <path
 | 
			
		||||
         id="rect34520"
 | 
			
		||||
         d="m 83.273151,166.72152 c 0,1.96759 -1.584022,3.55163 -3.551629,3.55163 l -63.443032,0 c -1.967608,0 -3.551648,-1.58402 -3.551643,-3.55164 0,-5.85318 0,-5.85318 0,0"
 | 
			
		||||
         style="opacity:0.35;fill:none;stroke:url(#radialGradient7488);stroke-width:1;stroke-opacity:1"
 | 
			
		||||
         connector-curvature="0"
 | 
			
		||||
         inkscape:connector-curvature="0"
 | 
			
		||||
         sodipodi:nodetypes="ccscc" />
 | 
			
		||||
         d="m 84.506708,167.95508 c 6e-6,1.96759 -1.584022,3.55162 -3.551629,3.55163 l -65.910146,0 c -1.967608,-1e-5 -3.551648,-1.58402 -3.551643,-3.55164"
 | 
			
		||||
         style="opacity:0.2;fill:none;stroke:url(#radialGradient7488);stroke-width:1;stroke-opacity:1" />
 | 
			
		||||
    </g>
 | 
			
		||||
  </g>
 | 
			
		||||
</svg>
 | 
			
		||||
 
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 2.6 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								data/theme/scroll-button-down-hover.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 225 B  | 
							
								
								
									
										
											BIN
										
									
								
								data/theme/scroll-button-down.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 225 B  | 
							
								
								
									
										
											BIN
										
									
								
								data/theme/scroll-button-up-hover.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 211 B  | 
							
								
								
									
										
											BIN
										
									
								
								data/theme/scroll-button-up.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 211 B  | 
@@ -9,14 +9,29 @@
 | 
			
		||||
   xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 | 
			
		||||
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
 | 
			
		||||
   width="16"
 | 
			
		||||
   height="16"
 | 
			
		||||
   id="svg2"
 | 
			
		||||
   width="5.8600588"
 | 
			
		||||
   height="9"
 | 
			
		||||
   id="svg3647"
 | 
			
		||||
   version="1.1"
 | 
			
		||||
   inkscape:version="0.48+devel r9942 custom"
 | 
			
		||||
   sodipodi:docname="New document 4">
 | 
			
		||||
   inkscape:version="0.47 r22583"
 | 
			
		||||
   sodipodi:docname="section-more.svg">
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs4" />
 | 
			
		||||
     id="defs3649">
 | 
			
		||||
    <inkscape:perspective
 | 
			
		||||
       sodipodi:type="inkscape:persp3d"
 | 
			
		||||
       inkscape:vp_x="0 : 526.18109 : 1"
 | 
			
		||||
       inkscape:vp_y="0 : 1000 : 0"
 | 
			
		||||
       inkscape:vp_z="744.09448 : 526.18109 : 1"
 | 
			
		||||
       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
 | 
			
		||||
       id="perspective3655" />
 | 
			
		||||
    <inkscape:perspective
 | 
			
		||||
       id="perspective3603"
 | 
			
		||||
       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
 | 
			
		||||
       inkscape:vp_z="1 : 0.5 : 1"
 | 
			
		||||
       inkscape:vp_y="0 : 1000 : 0"
 | 
			
		||||
       inkscape:vp_x="0 : 0.5 : 1"
 | 
			
		||||
       sodipodi:type="inkscape:persp3d" />
 | 
			
		||||
  </defs>
 | 
			
		||||
  <sodipodi:namedview
 | 
			
		||||
     id="base"
 | 
			
		||||
     pagecolor="#ffffff"
 | 
			
		||||
@@ -24,29 +39,19 @@
 | 
			
		||||
     borderopacity="1.0"
 | 
			
		||||
     inkscape:pageopacity="0.0"
 | 
			
		||||
     inkscape:pageshadow="2"
 | 
			
		||||
     inkscape:zoom="1"
 | 
			
		||||
     inkscape:cx="8.984481"
 | 
			
		||||
     inkscape:cy="5.6224906"
 | 
			
		||||
     inkscape:zoom="82.777778"
 | 
			
		||||
     inkscape:cx="2.9300294"
 | 
			
		||||
     inkscape:cy="5.466443"
 | 
			
		||||
     inkscape:document-units="px"
 | 
			
		||||
     inkscape:current-layer="layer1"
 | 
			
		||||
     showgrid="false"
 | 
			
		||||
     borderlayer="true"
 | 
			
		||||
     inkscape:showpageshadow="false"
 | 
			
		||||
     inkscape:window-width="930"
 | 
			
		||||
     inkscape:window-height="681"
 | 
			
		||||
     inkscape:window-x="1892"
 | 
			
		||||
     inkscape:window-y="272"
 | 
			
		||||
     inkscape:window-maximized="0">
 | 
			
		||||
    <inkscape:grid
 | 
			
		||||
       type="xygrid"
 | 
			
		||||
       id="grid17403"
 | 
			
		||||
       empspacing="5"
 | 
			
		||||
       visible="true"
 | 
			
		||||
       enabled="true"
 | 
			
		||||
       snapvisiblegridlinesonly="true" />
 | 
			
		||||
  </sodipodi:namedview>
 | 
			
		||||
     inkscape:window-width="1680"
 | 
			
		||||
     inkscape:window-height="997"
 | 
			
		||||
     inkscape:window-x="0"
 | 
			
		||||
     inkscape:window-y="26"
 | 
			
		||||
     inkscape:window-maximized="1" />
 | 
			
		||||
  <metadata
 | 
			
		||||
     id="metadata7">
 | 
			
		||||
     id="metadata3652">
 | 
			
		||||
    <rdf:RDF>
 | 
			
		||||
      <cc:Work
 | 
			
		||||
         rdf:about="">
 | 
			
		||||
@@ -61,22 +66,22 @@
 | 
			
		||||
     inkscape:label="Layer 1"
 | 
			
		||||
     inkscape:groupmode="layer"
 | 
			
		||||
     id="layer1"
 | 
			
		||||
     transform="translate(0,-1036.3622)">
 | 
			
		||||
     transform="translate(-262.78425,-490.71933)">
 | 
			
		||||
    <path
 | 
			
		||||
       sodipodi:type="star"
 | 
			
		||||
       style="fill:#5f5f5f;fill-opacity:1;stroke:#5f5f5f;stroke-width:0.43015847;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
 | 
			
		||||
       id="path18028"
 | 
			
		||||
       sodipodi:sides="3"
 | 
			
		||||
       sodipodi:cx="84.5"
 | 
			
		||||
       sodipodi:cy="337.5"
 | 
			
		||||
       sodipodi:r1="5"
 | 
			
		||||
       sodipodi:r2="2.5"
 | 
			
		||||
       sodipodi:arg1="0.52359878"
 | 
			
		||||
       sodipodi:arg2="1.5707963"
 | 
			
		||||
       inkscape:flatsided="true"
 | 
			
		||||
       inkscape:rounded="0"
 | 
			
		||||
       transform="matrix(0,-0.98149546,0.71467449,0,25.404986,578.15569)"
 | 
			
		||||
       d="M 88.830127,340 80.169873,340 84.5,332.5 88.830127,340 z"
 | 
			
		||||
       inkscape:randomized="0"
 | 
			
		||||
       d="M 88.830127,340 80.169873,340 84.5,332.5 z"
 | 
			
		||||
       transform="matrix(0,1.3621708,0.99186247,0,-325.48222,929.32667)" />
 | 
			
		||||
       inkscape:rounded="0"
 | 
			
		||||
       inkscape:flatsided="true"
 | 
			
		||||
       sodipodi:arg2="1.5707963"
 | 
			
		||||
       sodipodi:arg1="0.52359878"
 | 
			
		||||
       sodipodi:r2="2.5"
 | 
			
		||||
       sodipodi:r1="5"
 | 
			
		||||
       sodipodi:cy="337.5"
 | 
			
		||||
       sodipodi:cx="84.5"
 | 
			
		||||
       sodipodi:sides="3"
 | 
			
		||||
       id="path5497-5"
 | 
			
		||||
       style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.59699643;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
 | 
			
		||||
       sodipodi:type="star" />
 | 
			
		||||
  </g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.8 KiB  | 
							
								
								
									
										85
									
								
								data/theme/calendar-arrow-right.svg → data/theme/section-more.svg
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						@@ -9,14 +9,29 @@
 | 
			
		||||
   xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 | 
			
		||||
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
 | 
			
		||||
   width="16"
 | 
			
		||||
   height="16"
 | 
			
		||||
   id="svg2"
 | 
			
		||||
   width="5.8600588"
 | 
			
		||||
   height="9"
 | 
			
		||||
   id="svg3647"
 | 
			
		||||
   version="1.1"
 | 
			
		||||
   inkscape:version="0.48+devel r9942 custom"
 | 
			
		||||
   sodipodi:docname="arrow-left.svg">
 | 
			
		||||
   inkscape:version="0.46+devel"
 | 
			
		||||
   sodipodi:docname="New document 6">
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs4" />
 | 
			
		||||
     id="defs3649">
 | 
			
		||||
    <inkscape:perspective
 | 
			
		||||
       sodipodi:type="inkscape:persp3d"
 | 
			
		||||
       inkscape:vp_x="0 : 526.18109 : 1"
 | 
			
		||||
       inkscape:vp_y="0 : 1000 : 0"
 | 
			
		||||
       inkscape:vp_z="744.09448 : 526.18109 : 1"
 | 
			
		||||
       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
 | 
			
		||||
       id="perspective3655" />
 | 
			
		||||
    <inkscape:perspective
 | 
			
		||||
       id="perspective3603"
 | 
			
		||||
       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
 | 
			
		||||
       inkscape:vp_z="1 : 0.5 : 1"
 | 
			
		||||
       inkscape:vp_y="0 : 1000 : 0"
 | 
			
		||||
       inkscape:vp_x="0 : 0.5 : 1"
 | 
			
		||||
       sodipodi:type="inkscape:persp3d" />
 | 
			
		||||
  </defs>
 | 
			
		||||
  <sodipodi:namedview
 | 
			
		||||
     id="base"
 | 
			
		||||
     pagecolor="#ffffff"
 | 
			
		||||
@@ -24,29 +39,19 @@
 | 
			
		||||
     borderopacity="1.0"
 | 
			
		||||
     inkscape:pageopacity="0.0"
 | 
			
		||||
     inkscape:pageshadow="2"
 | 
			
		||||
     inkscape:zoom="1"
 | 
			
		||||
     inkscape:cx="7.7366092"
 | 
			
		||||
     inkscape:cy="6.4536271"
 | 
			
		||||
     inkscape:zoom="0.35"
 | 
			
		||||
     inkscape:cx="112.21575"
 | 
			
		||||
     inkscape:cy="-32.642856"
 | 
			
		||||
     inkscape:document-units="px"
 | 
			
		||||
     inkscape:current-layer="layer1"
 | 
			
		||||
     showgrid="false"
 | 
			
		||||
     borderlayer="true"
 | 
			
		||||
     inkscape:showpageshadow="false"
 | 
			
		||||
     inkscape:window-width="930"
 | 
			
		||||
     inkscape:window-height="681"
 | 
			
		||||
     inkscape:window-x="1892"
 | 
			
		||||
     inkscape:window-y="272"
 | 
			
		||||
     inkscape:window-maximized="0">
 | 
			
		||||
    <inkscape:grid
 | 
			
		||||
       type="xygrid"
 | 
			
		||||
       id="grid17403"
 | 
			
		||||
       empspacing="5"
 | 
			
		||||
       visible="true"
 | 
			
		||||
       enabled="true"
 | 
			
		||||
       snapvisiblegridlinesonly="true" />
 | 
			
		||||
  </sodipodi:namedview>
 | 
			
		||||
     inkscape:window-width="609"
 | 
			
		||||
     inkscape:window-height="501"
 | 
			
		||||
     inkscape:window-x="164"
 | 
			
		||||
     inkscape:window-y="26"
 | 
			
		||||
     inkscape:window-maximized="0" />
 | 
			
		||||
  <metadata
 | 
			
		||||
     id="metadata7">
 | 
			
		||||
     id="metadata3652">
 | 
			
		||||
    <rdf:RDF>
 | 
			
		||||
      <cc:Work
 | 
			
		||||
         rdf:about="">
 | 
			
		||||
@@ -61,22 +66,22 @@
 | 
			
		||||
     inkscape:label="Layer 1"
 | 
			
		||||
     inkscape:groupmode="layer"
 | 
			
		||||
     id="layer1"
 | 
			
		||||
     transform="translate(0,-1036.3622)">
 | 
			
		||||
     transform="translate(-262.78425,-490.71933)">
 | 
			
		||||
    <path
 | 
			
		||||
       sodipodi:type="star"
 | 
			
		||||
       style="fill:#5f5f5f;fill-opacity:1;stroke:#5f5f5f;stroke-width:0.43015847;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
 | 
			
		||||
       id="path18028"
 | 
			
		||||
       sodipodi:sides="3"
 | 
			
		||||
       sodipodi:cx="84.5"
 | 
			
		||||
       sodipodi:cy="337.5"
 | 
			
		||||
       sodipodi:r1="5"
 | 
			
		||||
       sodipodi:r2="2.5"
 | 
			
		||||
       sodipodi:arg1="0.52359878"
 | 
			
		||||
       sodipodi:arg2="1.5707963"
 | 
			
		||||
       inkscape:flatsided="true"
 | 
			
		||||
       inkscape:rounded="0"
 | 
			
		||||
       transform="matrix(0,0.98149546,-0.71467449,0,506.02358,412.28296)"
 | 
			
		||||
       d="M 88.830127,340 80.169873,340 84.5,332.5 88.830127,340 z"
 | 
			
		||||
       inkscape:randomized="0"
 | 
			
		||||
       d="M 88.830127,340 80.169873,340 84.5,332.5 z"
 | 
			
		||||
       transform="matrix(0,1.3621708,-0.99186247,0,342.48324,929.32667)" />
 | 
			
		||||
       inkscape:rounded="0"
 | 
			
		||||
       inkscape:flatsided="true"
 | 
			
		||||
       sodipodi:arg2="1.5707963"
 | 
			
		||||
       sodipodi:arg1="0.52359878"
 | 
			
		||||
       sodipodi:r2="2.5"
 | 
			
		||||
       sodipodi:r1="5"
 | 
			
		||||
       sodipodi:cy="337.5"
 | 
			
		||||
       sodipodi:cx="84.5"
 | 
			
		||||
       sodipodi:sides="3"
 | 
			
		||||
       id="path5497-5"
 | 
			
		||||
       style="fill:#5f5f5f;fill-opacity:1;stroke:#5f5f5f;stroke-width:0.59699643;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
 | 
			
		||||
       sodipodi:type="star" />
 | 
			
		||||
  </g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.8 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								data/theme/separator-white.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 531 B  | 
@@ -9,23 +9,23 @@
 | 
			
		||||
   xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 | 
			
		||||
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
 | 
			
		||||
   width="10"
 | 
			
		||||
   height="20"
 | 
			
		||||
   id="svg10003"
 | 
			
		||||
   width="24"
 | 
			
		||||
   height="16"
 | 
			
		||||
   id="svg6446"
 | 
			
		||||
   version="1.1"
 | 
			
		||||
   inkscape:version="0.47 r22583"
 | 
			
		||||
   sodipodi:docname="filter-selected.svg">
 | 
			
		||||
   inkscape:version="0.47pre4 r22446"
 | 
			
		||||
   sodipodi:docname="single-view-active.svg">
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs10005">
 | 
			
		||||
     id="defs6448">
 | 
			
		||||
    <inkscape:perspective
 | 
			
		||||
       sodipodi:type="inkscape:persp3d"
 | 
			
		||||
       inkscape:vp_x="0 : 32 : 1"
 | 
			
		||||
       inkscape:vp_x="0 : 16 : 1"
 | 
			
		||||
       inkscape:vp_y="0 : 1000 : 0"
 | 
			
		||||
       inkscape:vp_z="64 : 32 : 1"
 | 
			
		||||
       inkscape:persp3d-origin="32 : 21.333333 : 1"
 | 
			
		||||
       id="perspective10011" />
 | 
			
		||||
       inkscape:vp_z="32 : 16 : 1"
 | 
			
		||||
       inkscape:persp3d-origin="16 : 10.666667 : 1"
 | 
			
		||||
       id="perspective6454" />
 | 
			
		||||
    <inkscape:perspective
 | 
			
		||||
       id="perspective9998"
 | 
			
		||||
       id="perspective6441"
 | 
			
		||||
       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
 | 
			
		||||
       inkscape:vp_z="1 : 0.5 : 1"
 | 
			
		||||
       inkscape:vp_y="0 : 1000 : 0"
 | 
			
		||||
@@ -39,20 +39,20 @@
 | 
			
		||||
     borderopacity="1.0"
 | 
			
		||||
     inkscape:pageopacity="0.0"
 | 
			
		||||
     inkscape:pageshadow="2"
 | 
			
		||||
     inkscape:zoom="5.5"
 | 
			
		||||
     inkscape:cx="32"
 | 
			
		||||
     inkscape:cy="10.181818"
 | 
			
		||||
     inkscape:zoom="11.197802"
 | 
			
		||||
     inkscape:cx="0.014720032"
 | 
			
		||||
     inkscape:cy="16"
 | 
			
		||||
     inkscape:current-layer="layer1"
 | 
			
		||||
     showgrid="true"
 | 
			
		||||
     inkscape:document-units="px"
 | 
			
		||||
     inkscape:grid-bbox="true"
 | 
			
		||||
     inkscape:document-units="px"
 | 
			
		||||
     inkscape:window-width="1680"
 | 
			
		||||
     inkscape:window-height="994"
 | 
			
		||||
     inkscape:window-height="997"
 | 
			
		||||
     inkscape:window-x="0"
 | 
			
		||||
     inkscape:window-y="26"
 | 
			
		||||
     inkscape:window-maximized="1" />
 | 
			
		||||
  <metadata
 | 
			
		||||
     id="metadata10008">
 | 
			
		||||
     id="metadata6451">
 | 
			
		||||
    <rdf:RDF>
 | 
			
		||||
      <cc:Work
 | 
			
		||||
         rdf:about="">
 | 
			
		||||
@@ -67,15 +67,15 @@
 | 
			
		||||
     id="layer1"
 | 
			
		||||
     inkscape:label="Layer 1"
 | 
			
		||||
     inkscape:groupmode="layer"
 | 
			
		||||
     transform="translate(0,-44)">
 | 
			
		||||
    <path
 | 
			
		||||
       inkscape:export-ydpi="90"
 | 
			
		||||
       inkscape:export-xdpi="90"
 | 
			
		||||
       inkscape:export-filename="/home/jimmac/src/cvs/gnome/gnome-shell-design/mockups/app-picker.png"
 | 
			
		||||
       sodipodi:nodetypes="cccc"
 | 
			
		||||
       inkscape:connector-curvature="0"
 | 
			
		||||
       id="rect34320"
 | 
			
		||||
       d="m -0.18726572,54.181804 10.55634072,10.55636 10e-6,-21.11269 z"
 | 
			
		||||
       style="opacity:0.21000001;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.99999988;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
 | 
			
		||||
     transform="translate(0,-17)">
 | 
			
		||||
    <rect
 | 
			
		||||
       ry="0.5"
 | 
			
		||||
       rx="0.49999979"
 | 
			
		||||
       y="17.483809"
 | 
			
		||||
       x="0.53483802"
 | 
			
		||||
       height="15"
 | 
			
		||||
       width="23"
 | 
			
		||||
       id="rect5304"
 | 
			
		||||
       style="fill:#cccccc;fill-opacity:1;stroke:#cccccc;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" />
 | 
			
		||||
  </g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.4 KiB  | 
@@ -9,23 +9,23 @@
 | 
			
		||||
   xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 | 
			
		||||
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
 | 
			
		||||
   width="10"
 | 
			
		||||
   height="20"
 | 
			
		||||
   id="svg10003"
 | 
			
		||||
   width="24"
 | 
			
		||||
   height="16"
 | 
			
		||||
   id="svg6446"
 | 
			
		||||
   version="1.1"
 | 
			
		||||
   inkscape:version="0.48.1 r9760"
 | 
			
		||||
   sodipodi:docname="filter-selected-ltr.svg">
 | 
			
		||||
   inkscape:version="0.47pre4 r22446"
 | 
			
		||||
   sodipodi:docname="single-view.svg">
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs10005">
 | 
			
		||||
     id="defs6448">
 | 
			
		||||
    <inkscape:perspective
 | 
			
		||||
       sodipodi:type="inkscape:persp3d"
 | 
			
		||||
       inkscape:vp_x="0 : 32 : 1"
 | 
			
		||||
       inkscape:vp_x="0 : 16 : 1"
 | 
			
		||||
       inkscape:vp_y="0 : 1000 : 0"
 | 
			
		||||
       inkscape:vp_z="64 : 32 : 1"
 | 
			
		||||
       inkscape:persp3d-origin="32 : 21.333333 : 1"
 | 
			
		||||
       id="perspective10011" />
 | 
			
		||||
       inkscape:vp_z="32 : 16 : 1"
 | 
			
		||||
       inkscape:persp3d-origin="16 : 10.666667 : 1"
 | 
			
		||||
       id="perspective6454" />
 | 
			
		||||
    <inkscape:perspective
 | 
			
		||||
       id="perspective9998"
 | 
			
		||||
       id="perspective6441"
 | 
			
		||||
       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
 | 
			
		||||
       inkscape:vp_z="1 : 0.5 : 1"
 | 
			
		||||
       inkscape:vp_y="0 : 1000 : 0"
 | 
			
		||||
@@ -37,29 +37,29 @@
 | 
			
		||||
     pagecolor="#ffffff"
 | 
			
		||||
     bordercolor="#666666"
 | 
			
		||||
     borderopacity="1.0"
 | 
			
		||||
     inkscape:pageopacity="0"
 | 
			
		||||
     inkscape:pageopacity="0.0"
 | 
			
		||||
     inkscape:pageshadow="2"
 | 
			
		||||
     inkscape:zoom="5.5"
 | 
			
		||||
     inkscape:cx="32.363636"
 | 
			
		||||
     inkscape:cy="10.181818"
 | 
			
		||||
     inkscape:zoom="11.197802"
 | 
			
		||||
     inkscape:cx="0.014720032"
 | 
			
		||||
     inkscape:cy="16"
 | 
			
		||||
     inkscape:current-layer="layer1"
 | 
			
		||||
     showgrid="true"
 | 
			
		||||
     inkscape:document-units="px"
 | 
			
		||||
     inkscape:grid-bbox="true"
 | 
			
		||||
     inkscape:window-width="1440"
 | 
			
		||||
     inkscape:window-height="839"
 | 
			
		||||
     inkscape:document-units="px"
 | 
			
		||||
     inkscape:window-width="1680"
 | 
			
		||||
     inkscape:window-height="997"
 | 
			
		||||
     inkscape:window-x="0"
 | 
			
		||||
     inkscape:window-y="26"
 | 
			
		||||
     inkscape:window-maximized="1" />
 | 
			
		||||
  <metadata
 | 
			
		||||
     id="metadata10008">
 | 
			
		||||
     id="metadata6451">
 | 
			
		||||
    <rdf:RDF>
 | 
			
		||||
      <cc:Work
 | 
			
		||||
         rdf:about="">
 | 
			
		||||
        <dc:format>image/svg+xml</dc:format>
 | 
			
		||||
        <dc:type
 | 
			
		||||
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
 | 
			
		||||
        <dc:title></dc:title>
 | 
			
		||||
        <dc:title />
 | 
			
		||||
      </cc:Work>
 | 
			
		||||
    </rdf:RDF>
 | 
			
		||||
  </metadata>
 | 
			
		||||
@@ -67,15 +67,15 @@
 | 
			
		||||
     id="layer1"
 | 
			
		||||
     inkscape:label="Layer 1"
 | 
			
		||||
     inkscape:groupmode="layer"
 | 
			
		||||
     transform="translate(0,-44)">
 | 
			
		||||
    <path
 | 
			
		||||
       inkscape:export-ydpi="90"
 | 
			
		||||
       inkscape:export-xdpi="90"
 | 
			
		||||
       inkscape:export-filename="/home/jimmac/src/cvs/gnome/gnome-shell-design/mockups/app-picker.png"
 | 
			
		||||
       sodipodi:nodetypes="cccc"
 | 
			
		||||
       inkscape:connector-curvature="0"
 | 
			
		||||
       id="rect34320"
 | 
			
		||||
       d="m 10.369085,54.181804 -10.55634072,10.55636 -1e-5,-21.11269 z"
 | 
			
		||||
       style="opacity:0.21000001;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.99999988;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
 | 
			
		||||
     transform="translate(0,-17)">
 | 
			
		||||
    <rect
 | 
			
		||||
       ry="0.5"
 | 
			
		||||
       rx="0.49999979"
 | 
			
		||||
       y="17.483809"
 | 
			
		||||
       x="0.53483802"
 | 
			
		||||
       height="15"
 | 
			
		||||
       width="23"
 | 
			
		||||
       id="rect5304"
 | 
			
		||||
       style="fill:#626262;fill-opacity:1;stroke:#cccccc;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" />
 | 
			
		||||
  </g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.4 KiB  | 
@@ -1,74 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
 | 
			
		||||
 | 
			
		||||
<svg
 | 
			
		||||
   xmlns:dc="http://purl.org/dc/elements/1.1/"
 | 
			
		||||
   xmlns:cc="http://creativecommons.org/ns#"
 | 
			
		||||
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 | 
			
		||||
   xmlns:svg="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 | 
			
		||||
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
 | 
			
		||||
   width="21"
 | 
			
		||||
   height="10"
 | 
			
		||||
   id="svg2"
 | 
			
		||||
   version="1.1"
 | 
			
		||||
   inkscape:version="0.48.0 r9654"
 | 
			
		||||
   sodipodi:docname="source-button-border.svg">
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs4" />
 | 
			
		||||
  <sodipodi:namedview
 | 
			
		||||
     id="base"
 | 
			
		||||
     pagecolor="#000000"
 | 
			
		||||
     bordercolor="#666666"
 | 
			
		||||
     borderopacity="1.0"
 | 
			
		||||
     inkscape:pageopacity="0"
 | 
			
		||||
     inkscape:pageshadow="2"
 | 
			
		||||
     inkscape:zoom="44.8"
 | 
			
		||||
     inkscape:cx="8.704132"
 | 
			
		||||
     inkscape:cy="5.7029946"
 | 
			
		||||
     inkscape:document-units="px"
 | 
			
		||||
     inkscape:current-layer="layer1"
 | 
			
		||||
     showgrid="true"
 | 
			
		||||
     showguides="true"
 | 
			
		||||
     inkscape:guide-bbox="true"
 | 
			
		||||
     inkscape:window-width="1600"
 | 
			
		||||
     inkscape:window-height="1145"
 | 
			
		||||
     inkscape:window-x="0"
 | 
			
		||||
     inkscape:window-y="26"
 | 
			
		||||
     inkscape:window-maximized="1"
 | 
			
		||||
     guidetolerance="10000"
 | 
			
		||||
     objecttolerance="10000">
 | 
			
		||||
    <inkscape:grid
 | 
			
		||||
       type="xygrid"
 | 
			
		||||
       id="grid3792"
 | 
			
		||||
       empspacing="10"
 | 
			
		||||
       visible="true"
 | 
			
		||||
       enabled="true"
 | 
			
		||||
       snapvisiblegridlinesonly="true" />
 | 
			
		||||
  </sodipodi:namedview>
 | 
			
		||||
  <metadata
 | 
			
		||||
     id="metadata7">
 | 
			
		||||
    <rdf:RDF>
 | 
			
		||||
      <cc:Work
 | 
			
		||||
         rdf:about="">
 | 
			
		||||
        <dc:format>image/svg+xml</dc:format>
 | 
			
		||||
        <dc:type
 | 
			
		||||
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
 | 
			
		||||
        <dc:title></dc:title>
 | 
			
		||||
      </cc:Work>
 | 
			
		||||
    </rdf:RDF>
 | 
			
		||||
  </metadata>
 | 
			
		||||
  <g
 | 
			
		||||
     inkscape:label="Layer 1"
 | 
			
		||||
     inkscape:groupmode="layer"
 | 
			
		||||
     id="layer1">
 | 
			
		||||
    <rect
 | 
			
		||||
       style="opacity:0.8;fill:#ffffff;fill-opacity:1;stroke-width:0.43599999;stroke-miterlimit:4;stroke-dasharray:none"
 | 
			
		||||
       id="rect3796"
 | 
			
		||||
       width="19"
 | 
			
		||||
       height="2"
 | 
			
		||||
       x="1"
 | 
			
		||||
       y="8" />
 | 
			
		||||
  </g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 2.0 KiB  | 
@@ -13,8 +13,8 @@
 | 
			
		||||
   height="22"
 | 
			
		||||
   id="svg3199"
 | 
			
		||||
   version="1.1"
 | 
			
		||||
   inkscape:version="0.48.1 r9760"
 | 
			
		||||
   sodipodi:docname="toggle-on-intl.svg">
 | 
			
		||||
   inkscape:version="0.47 r22583"
 | 
			
		||||
   sodipodi:docname="New document 11">
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs3201">
 | 
			
		||||
    <inkscape:perspective
 | 
			
		||||
@@ -39,14 +39,14 @@
 | 
			
		||||
     borderopacity="1.0"
 | 
			
		||||
     inkscape:pageopacity="0.0"
 | 
			
		||||
     inkscape:pageshadow="2"
 | 
			
		||||
     inkscape:zoom="1"
 | 
			
		||||
     inkscape:cx="49.147112"
 | 
			
		||||
     inkscape:cy="17.532036"
 | 
			
		||||
     inkscape:zoom="0.35"
 | 
			
		||||
     inkscape:cx="32.500004"
 | 
			
		||||
     inkscape:cy="10.999997"
 | 
			
		||||
     inkscape:document-units="px"
 | 
			
		||||
     inkscape:current-layer="layer1"
 | 
			
		||||
     showgrid="false"
 | 
			
		||||
     inkscape:window-width="1412"
 | 
			
		||||
     inkscape:window-height="1067"
 | 
			
		||||
     inkscape:window-width="609"
 | 
			
		||||
     inkscape:window-height="501"
 | 
			
		||||
     inkscape:window-x="0"
 | 
			
		||||
     inkscape:window-y="26"
 | 
			
		||||
     inkscape:window-maximized="0" />
 | 
			
		||||
@@ -58,7 +58,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>
 | 
			
		||||
@@ -72,7 +72,7 @@
 | 
			
		||||
       transform="translate(-453.5,448.36218)"
 | 
			
		||||
       id="g16453">
 | 
			
		||||
      <rect
 | 
			
		||||
         style="color:#000000;fill:#4a90d9;fill-opacity:1;fill-rule:nonzero;stroke:#3465a4;stroke-width:0.99999994000000003;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
 | 
			
		||||
         style="color:#000000;fill:#204a87;fill-opacity:1;fill-rule:nonzero;stroke:#3465a4;stroke-width:0.99999994;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
 | 
			
		||||
         id="rect16256-9-4"
 | 
			
		||||
         width="63.000004"
 | 
			
		||||
         height="19"
 | 
			
		||||
 
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.5 KiB  | 
@@ -13,8 +13,8 @@
 | 
			
		||||
   height="22"
 | 
			
		||||
   id="svg2857"
 | 
			
		||||
   version="1.1"
 | 
			
		||||
   inkscape:version="0.48.1 r9760"
 | 
			
		||||
   sodipodi:docname="toggle-on-us.svg">
 | 
			
		||||
   inkscape:version="0.47 r22583"
 | 
			
		||||
   sodipodi:docname="New document 2">
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs2859">
 | 
			
		||||
    <inkscape:perspective
 | 
			
		||||
@@ -40,18 +40,16 @@
 | 
			
		||||
     inkscape:pageopacity="0.0"
 | 
			
		||||
     inkscape:pageshadow="2"
 | 
			
		||||
     inkscape:zoom="1"
 | 
			
		||||
     inkscape:cx="19.689855"
 | 
			
		||||
     inkscape:cy="2.0517979"
 | 
			
		||||
     inkscape:cx="-69.642856"
 | 
			
		||||
     inkscape:cy="42.428569"
 | 
			
		||||
     inkscape:document-units="px"
 | 
			
		||||
     inkscape:current-layer="layer1"
 | 
			
		||||
     showgrid="false"
 | 
			
		||||
     inkscape:window-width="941"
 | 
			
		||||
     inkscape:window-height="751"
 | 
			
		||||
     inkscape:window-x="2577"
 | 
			
		||||
     inkscape:window-y="206"
 | 
			
		||||
     inkscape:window-maximized="0"
 | 
			
		||||
     borderlayer="true"
 | 
			
		||||
     inkscape:showpageshadow="false" />
 | 
			
		||||
     inkscape:window-width="609"
 | 
			
		||||
     inkscape:window-height="501"
 | 
			
		||||
     inkscape:window-x="0"
 | 
			
		||||
     inkscape:window-y="26"
 | 
			
		||||
     inkscape:window-maximized="0" />
 | 
			
		||||
  <metadata
 | 
			
		||||
     id="metadata2862">
 | 
			
		||||
    <rdf:RDF>
 | 
			
		||||
@@ -60,7 +58,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>
 | 
			
		||||
@@ -74,7 +72,7 @@
 | 
			
		||||
       transform="translate(-351.35714,708.36218)"
 | 
			
		||||
       id="g16453">
 | 
			
		||||
      <rect
 | 
			
		||||
         style="color:#000000;fill:#4a90d9;fill-opacity:1;fill-rule:nonzero;stroke:#3465a4;stroke-width:0.99999994000000003;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
 | 
			
		||||
         style="color:#000000;fill:#204a87;fill-opacity:1;fill-rule:nonzero;stroke:#3465a4;stroke-width:0.99999994;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
 | 
			
		||||
         id="rect16256-9-4"
 | 
			
		||||
         width="63.000004"
 | 
			
		||||
         height="19"
 | 
			
		||||
 
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.7 KiB  | 
							
								
								
									
										96
									
								
								data/theme/ws-switch-arrow-left.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,96 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
 | 
			
		||||
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="96" height="96" id="svg25070" version="1.1" inkscape:version="0.47 r22583" sodipodi:docname="dark-arrow-larger.svg">
 | 
			
		||||
  <defs id="defs25072">
 | 
			
		||||
    <inkscape:perspective sodipodi:type="inkscape:persp3d" inkscape:vp_x="0 : 24 : 1" inkscape:vp_y="0 : 1000 : 0" inkscape:vp_z="48 : 24 : 1" inkscape:persp3d-origin="24 : 16 : 1" id="perspective25078"/>
 | 
			
		||||
    <inkscape:perspective id="perspective24985" inkscape:persp3d-origin="0.5 : 0.33333333 : 1" inkscape:vp_z="1 : 0.5 : 1" inkscape:vp_y="0 : 1000 : 0" inkscape:vp_x="0 : 0.5 : 1" sodipodi:type="inkscape:persp3d"/>
 | 
			
		||||
    <linearGradient inkscape:collect="always" xlink:href="#linearGradient4034-0-4" id="linearGradient24957" gradientUnits="userSpaceOnUse" gradientTransform="translate(6)" x1="-86.552246" y1="185.439" x2="-83.37072" y2="197.31261"/>
 | 
			
		||||
    <linearGradient inkscape:collect="always" id="linearGradient4034-0-4">
 | 
			
		||||
      <stop style="stop-color: rgb(238, 238, 236); stop-opacity: 1;" offset="0" id="stop4036-5-7"/>
 | 
			
		||||
      <stop style="stop-color: rgb(186, 189, 182); stop-opacity: 1;" offset="1" id="stop4038-9-6"/>
 | 
			
		||||
    </linearGradient>
 | 
			
		||||
    <filter id="filter24765" inkscape:label="Invert" x="0" y="0" width="1" height="1" inkscape:menu="Color" inkscape:menu-tooltip="Invert colors" color-interpolation-filters="sRGB">
 | 
			
		||||
      <feColorMatrix id="feColorMatrix24767" type="saturate" values="1" result="fbSourceGraphic"/>
 | 
			
		||||
      <feColorMatrix id="feColorMatrix24769" in="fbSourceGraphic" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"/>
 | 
			
		||||
    </filter>
 | 
			
		||||
    <linearGradient inkscape:collect="always" xlink:href="#linearGradient4632-1-3-9-3-2" id="linearGradient24955" gradientUnits="userSpaceOnUse" gradientTransform="translate(-5)" x1="-74.520325" y1="169.06032" x2="-74.520325" y2="205.94189"/>
 | 
			
		||||
    <linearGradient id="linearGradient4632-1-3-9-3-2">
 | 
			
		||||
      <stop style="stop-color: rgb(238, 238, 236); stop-opacity: 1;" offset="0" id="stop4634-1-8-3-9-0"/>
 | 
			
		||||
      <stop id="stop4636-1-9-9-8-8" offset="0.0274937" style="stop-color: rgb(255, 255, 255); stop-opacity: 1;"/>
 | 
			
		||||
      <stop id="stop4638-8-3-9-6-6" offset="0.274937" style="stop-color: rgb(242, 242, 242); stop-opacity: 1;"/>
 | 
			
		||||
      <stop id="stop4640-8-5-7-8-9" offset="0.38707438" style="stop-color: rgb(238, 238, 236); stop-opacity: 1;"/>
 | 
			
		||||
      <stop id="stop4642-5-41-9-6-9" offset="0.66528589" style="stop-color: rgb(217, 218, 216); stop-opacity: 1;"/>
 | 
			
		||||
      <stop id="stop4644-5-2-7-9-2" offset="0.76745707" style="stop-color: rgb(223, 224, 221); stop-opacity: 1;"/>
 | 
			
		||||
      <stop style="stop-color: rgb(240, 240, 240); stop-opacity: 1;" offset="1" id="stop4646-3-2-3-7-3"/>
 | 
			
		||||
    </linearGradient>
 | 
			
		||||
    <radialGradient inkscape:collect="always" xlink:href="#linearGradient4869-4-1" id="radialGradient24959" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1.0075, 0, 0, 1.0075, -5.4544, -1.25141)" cx="-33.412369" cy="185.74171" fx="-33.412369" fy="185.74171" r="2.3554697"/>
 | 
			
		||||
    <linearGradient id="linearGradient4869-4-1">
 | 
			
		||||
      <stop style="stop-color: rgb(255, 255, 255); stop-opacity: 1;" offset="0" id="stop4871-6-2"/>
 | 
			
		||||
      <stop id="stop4879-7-4" offset="0.31807542" style="stop-color: rgb(238, 238, 236); stop-opacity: 1;"/>
 | 
			
		||||
      <stop id="stop4877-6-1" offset="0.74691135" style="stop-color: rgb(200, 201, 198); stop-opacity: 1;"/>
 | 
			
		||||
      <stop style="stop-color: rgb(211, 215, 207); stop-opacity: 1;" offset="1" id="stop4873-1-0"/>
 | 
			
		||||
    </linearGradient>
 | 
			
		||||
    <filter id="filter25011" inkscape:label="Invert" x="0" y="0" width="1" height="1" inkscape:menu="Color" inkscape:menu-tooltip="Invert colors" color-interpolation-filters="sRGB">
 | 
			
		||||
      <feColorMatrix id="feColorMatrix25013" type="saturate" values="1" result="fbSourceGraphic"/>
 | 
			
		||||
      <feColorMatrix id="feColorMatrix25015" in="fbSourceGraphic" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"/>
 | 
			
		||||
    </filter>
 | 
			
		||||
    <radialGradient inkscape:collect="always" xlink:href="#linearGradient4869-4-0" id="radialGradient24961" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1.0075, 0, 0, 1.0075, -5.4544, -1.25141)" cx="-33.412369" cy="185.74171" fx="-33.412369" fy="185.74171" r="2.3554697"/>
 | 
			
		||||
    <linearGradient id="linearGradient4869-4-0">
 | 
			
		||||
      <stop style="stop-color: rgb(255, 255, 255); stop-opacity: 1;" offset="0" id="stop4871-6-8"/>
 | 
			
		||||
      <stop id="stop4879-7-5" offset="0.31807542" style="stop-color: rgb(238, 238, 236); stop-opacity: 1;"/>
 | 
			
		||||
      <stop id="stop4877-6-5" offset="0.74691135" style="stop-color: rgb(200, 201, 198); stop-opacity: 1;"/>
 | 
			
		||||
      <stop style="stop-color: rgb(211, 215, 207); stop-opacity: 1;" offset="1" id="stop4873-1-4"/>
 | 
			
		||||
    </linearGradient>
 | 
			
		||||
    <filter id="filter25023" inkscape:label="Invert" x="0" y="0" width="1" height="1" inkscape:menu="Color" inkscape:menu-tooltip="Invert colors" color-interpolation-filters="sRGB">
 | 
			
		||||
      <feColorMatrix id="feColorMatrix25025" type="saturate" values="1" result="fbSourceGraphic"/>
 | 
			
		||||
      <feColorMatrix id="feColorMatrix25027" in="fbSourceGraphic" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"/>
 | 
			
		||||
    </filter>
 | 
			
		||||
    <linearGradient inkscape:collect="always" xlink:href="#linearGradient4941" id="linearGradient24963" gradientUnits="userSpaceOnUse" x1="-39.858727" y1="184.61784" x2="-38.244785" y2="188.84898"/>
 | 
			
		||||
    <linearGradient inkscape:collect="always" id="linearGradient4941">
 | 
			
		||||
      <stop style="stop-color: rgb(255, 255, 255); stop-opacity: 1;" offset="0" id="stop4943"/>
 | 
			
		||||
      <stop style="stop-color: rgb(255, 255, 255); stop-opacity: 0;" offset="1" id="stop4945"/>
 | 
			
		||||
    </linearGradient>
 | 
			
		||||
    <filter id="filter25033" inkscape:label="Invert" x="0" y="0" width="1" height="1" inkscape:menu="Color" inkscape:menu-tooltip="Invert colors" color-interpolation-filters="sRGB">
 | 
			
		||||
      <feColorMatrix id="feColorMatrix25035" type="saturate" values="1" result="fbSourceGraphic"/>
 | 
			
		||||
      <feColorMatrix id="feColorMatrix25037" in="fbSourceGraphic" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"/>
 | 
			
		||||
    </filter>
 | 
			
		||||
    <linearGradient inkscape:collect="always" xlink:href="#linearGradient4941-7" id="linearGradient24965" gradientUnits="userSpaceOnUse" x1="-39.858727" y1="184.61784" x2="-38.244785" y2="188.84898"/>
 | 
			
		||||
    <linearGradient inkscape:collect="always" id="linearGradient4941-7">
 | 
			
		||||
      <stop style="stop-color: rgb(255, 255, 255); stop-opacity: 1;" offset="0" id="stop4943-2"/>
 | 
			
		||||
      <stop style="stop-color: rgb(255, 255, 255); stop-opacity: 0;" offset="1" id="stop4945-5"/>
 | 
			
		||||
    </linearGradient>
 | 
			
		||||
    <filter id="filter25043" inkscape:label="Invert" x="0" y="0" width="1" height="1" inkscape:menu="Color" inkscape:menu-tooltip="Invert colors" color-interpolation-filters="sRGB">
 | 
			
		||||
      <feColorMatrix id="feColorMatrix25045" type="saturate" values="1" result="fbSourceGraphic"/>
 | 
			
		||||
      <feColorMatrix id="feColorMatrix25047" in="fbSourceGraphic" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"/>
 | 
			
		||||
    </filter>
 | 
			
		||||
    <filter id="filter25049" inkscape:label="Invert" x="0" y="0" width="1" height="1" inkscape:menu="Color" inkscape:menu-tooltip="Invert colors" color-interpolation-filters="sRGB">
 | 
			
		||||
      <feColorMatrix id="feColorMatrix25051" type="saturate" values="1" result="fbSourceGraphic"/>
 | 
			
		||||
      <feColorMatrix id="feColorMatrix25053" in="fbSourceGraphic" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"/>
 | 
			
		||||
    </filter>
 | 
			
		||||
    <filter id="filter25055" inkscape:label="Invert" x="0" y="0" width="1" height="1" inkscape:menu="Color" inkscape:menu-tooltip="Invert colors" color-interpolation-filters="sRGB">
 | 
			
		||||
      <feColorMatrix id="feColorMatrix25057" type="saturate" values="1" result="fbSourceGraphic"/>
 | 
			
		||||
      <feColorMatrix id="feColorMatrix25059" in="fbSourceGraphic" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"/>
 | 
			
		||||
    </filter>
 | 
			
		||||
  </defs>
 | 
			
		||||
  <sodipodi:namedview id="base" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="2.8284271" inkscape:cx="48.631638" inkscape:cy="57.536221" inkscape:current-layer="layer1" showgrid="true" inkscape:grid-bbox="true" inkscape:document-units="px" inkscape:window-width="1200" inkscape:window-height="851" inkscape:window-x="0" inkscape:window-y="52" inkscape:window-maximized="0"/>
 | 
			
		||||
  <metadata id="metadata25075">
 | 
			
		||||
    <rdf:RDF>
 | 
			
		||||
      <cc:Work rdf:about="">
 | 
			
		||||
        <dc:format>image/svg+xml</dc:format>
 | 
			
		||||
        <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
 | 
			
		||||
        <dc:title/>
 | 
			
		||||
      </cc:Work>
 | 
			
		||||
    </rdf:RDF>
 | 
			
		||||
  </metadata>
 | 
			
		||||
  <g id="layer1" inkscape:label="Layer 1" inkscape:groupmode="layer" transform="translate(0, 48)">
 | 
			
		||||
    <g id="g4030-1-8" transform="matrix(2, 0, 0, 2, 193.25, -374.967)" style="stroke: rgb(0, 0, 0); display: inline; stroke-opacity: 1;">
 | 
			
		||||
      <path sodipodi:nodetypes="ccc" id="path3165-7-3" d="m -72.5,173.5 -14,14 14,14" style="overflow: visible; marker: none; color: rgb(0, 0, 0); fill: none; stroke: rgb(0, 0, 0); stroke-width: 7; stroke-linecap: round; stroke-linejoin: miter; stroke-miterlimit: 4; stroke-opacity: 1; stroke-dasharray: none; stroke-dashoffset: 0pt; visibility: visible; display: inline;"/>
 | 
			
		||||
    </g>
 | 
			
		||||
    <path sodipodi:type="arc" style="overflow: visible; marker: none; color: rgb(0, 0, 0); fill: rgb(0, 0, 0); fill-opacity: 1; fill-rule: nonzero; stroke: none; stroke-width: 0.523439; visibility: visible; display: inline;" id="path4050-2-7-9-4" sodipodi:cx="-38.59375" sodipodi:cy="186.40625" sodipodi:rx="2.09375" sodipodi:ry="2.09375" d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z" transform="matrix(3.34328, 0, 0, 3.34328, 185.28, -623.176)"/>
 | 
			
		||||
    <path sodipodi:type="arc" style="overflow: visible; marker: none; color: rgb(0, 0, 0); fill: rgb(0, 0, 0); fill-opacity: 1; fill-rule: nonzero; stroke: none; stroke-width: 0.523439; visibility: visible; display: inline;" id="path4050-2-7-9-4-8" sodipodi:cx="-38.59375" sodipodi:cy="186.40625" sodipodi:rx="2.09375" sodipodi:ry="2.09375" d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z" transform="matrix(3.34328, 0, 0, 3.34328, 207.28, -623.176)"/>
 | 
			
		||||
    <path sodipodi:type="arc" style="overflow: visible; marker: none; color: rgb(0, 0, 0); fill: none; stroke: rgb(0, 0, 0); stroke-width: 0.697921; stroke-linecap: round; stroke-linejoin: miter; stroke-miterlimit: 4; stroke-opacity: 1; stroke-dasharray: none; stroke-dashoffset: 0pt; visibility: visible; display: inline;" id="path4050-2-7-9-4-0" sodipodi:cx="-38.59375" sodipodi:cy="186.40625" sodipodi:rx="2.09375" sodipodi:ry="2.09375" d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z" transform="matrix(2.86565, 0, 0, 2.86565, 166.846, -534.143)"/>
 | 
			
		||||
    <path sodipodi:type="arc" style="overflow: visible; marker: none; color: rgb(0, 0, 0); fill: none; stroke: rgb(0, 0, 0); stroke-width: 0.697921; stroke-linecap: round; stroke-linejoin: miter; stroke-miterlimit: 4; stroke-opacity: 1; stroke-dasharray: none; stroke-dashoffset: 0pt; visibility: visible; display: inline;" id="path4050-2-7-9-4-0-9" sodipodi:cx="-38.59375" sodipodi:cy="186.40625" sodipodi:rx="2.09375" sodipodi:ry="2.09375" d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z" transform="matrix(2.86565, 0, 0, 2.86565, 188.846, -534.143)"/>
 | 
			
		||||
    <path style="overflow: visible; marker: none; font-size: medium; font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; text-indent: 0pt; text-align: start; text-decoration: none; line-height: normal; letter-spacing: normal; word-spacing: normal; text-transform: none; direction: ltr; text-anchor: start; opacity: 0.35; color: rgb(0, 0, 0); fill: none; stroke: rgb(0, 0, 0); stroke-width: 1; stroke-miterlimit: 4; stroke-dasharray: none; visibility: visible; display: inline; font-family: Bitstream Vera Sans; stroke-opacity: 1;" d="m 317.06251,365.96875 c -0.76948,0.0224 -1.52555,0.35464 -2.0625,0.90625 l -16.125,16.125 16.125,16.125 c 1.11265,1.11265 3.13735,1.11265 4.25,0 1.11265,-1.11264 1.11265,-3.13735 0,-4.25 l -11.875,-11.875 11.875,-11.875 c 0.86584,-0.83655 1.1475,-2.22114 0.6773,-3.32947 -0.47021,-1.10834 -1.66156,-1.86802 -2.8648,-1.82678 z" id="path3165-7-3-1" sodipodi:nodetypes="ccccscccsc" transform="matrix(2, 0, 0, 2, -586, -765.967)"/>
 | 
			
		||||
    <path style="overflow: visible; marker: none; font-size: medium; font-style: normal; font-variant: normal; font-weight: normal; font-stretch: normal; text-indent: 0pt; text-align: start; text-decoration: none; line-height: normal; letter-spacing: normal; word-spacing: normal; text-transform: none; direction: ltr; text-anchor: start; color: rgb(0, 0, 0); fill: none; stroke: rgb(0, 0, 0); stroke-width: 1; stroke-linecap: round; stroke-miterlimit: 4; stroke-dasharray: none; visibility: visible; display: inline; font-family: Bitstream Vera Sans; stroke-opacity: 1;" d="m 320.08435,397.03059 c 0.007,-0.79449 -0.27079,-1.59203 -0.83434,-2.15559 L 307.37501,383 m 12.5523,-15.20447 c -0.47021,-1.10834 -1.66156,-1.86802 -2.8648,-1.82678 -0.76948,0.0224 -1.52555,0.35464 -2.0625,0.90625 L 298.87501,383" id="path3165-7-3-1-9" sodipodi:nodetypes="ccccccc" transform="matrix(2, 0, 0, 2, -586, -765.967)"/>
 | 
			
		||||
  </g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 13 KiB  | 
@@ -2,51 +2,13 @@
 | 
			
		||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
 | 
			
		||||
 | 
			
		||||
<svg
 | 
			
		||||
   xmlns:dc="http://purl.org/dc/elements/1.1/"
 | 
			
		||||
   xmlns:cc="http://creativecommons.org/ns#"
 | 
			
		||||
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 | 
			
		||||
   xmlns:svg="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns:xlink="http://www.w3.org/1999/xlink"
 | 
			
		||||
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 | 
			
		||||
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
 | 
			
		||||
   version="1.1"
 | 
			
		||||
   width="96"
 | 
			
		||||
   height="96"
 | 
			
		||||
   id="svg25070"
 | 
			
		||||
   inkscape:version="0.48.0 r9654"
 | 
			
		||||
   sodipodi:docname="ws-switch-arrow-down.svg">
 | 
			
		||||
  <metadata
 | 
			
		||||
     id="metadata3353">
 | 
			
		||||
    <rdf:RDF>
 | 
			
		||||
      <cc:Work
 | 
			
		||||
         rdf:about="">
 | 
			
		||||
        <dc:format>image/svg+xml</dc:format>
 | 
			
		||||
        <dc:type
 | 
			
		||||
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
 | 
			
		||||
      </cc:Work>
 | 
			
		||||
    </rdf:RDF>
 | 
			
		||||
  </metadata>
 | 
			
		||||
  <sodipodi:namedview
 | 
			
		||||
     pagecolor="#ffffff"
 | 
			
		||||
     bordercolor="#666666"
 | 
			
		||||
     borderopacity="1"
 | 
			
		||||
     objecttolerance="10"
 | 
			
		||||
     gridtolerance="10"
 | 
			
		||||
     guidetolerance="10"
 | 
			
		||||
     inkscape:pageopacity="0"
 | 
			
		||||
     inkscape:pageshadow="2"
 | 
			
		||||
     inkscape:window-width="718"
 | 
			
		||||
     inkscape:window-height="480"
 | 
			
		||||
     id="namedview3351"
 | 
			
		||||
     showgrid="false"
 | 
			
		||||
     inkscape:zoom="2.6979167"
 | 
			
		||||
     inkscape:cx="48"
 | 
			
		||||
     inkscape:cy="48"
 | 
			
		||||
     inkscape:window-x="0"
 | 
			
		||||
     inkscape:window-y="26"
 | 
			
		||||
     inkscape:window-maximized="0"
 | 
			
		||||
     inkscape:current-layer="svg25070" />
 | 
			
		||||
   id="svg25070">
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs25072">
 | 
			
		||||
    <linearGradient
 | 
			
		||||
@@ -326,7 +288,7 @@
 | 
			
		||||
    </filter>
 | 
			
		||||
  </defs>
 | 
			
		||||
  <g
 | 
			
		||||
     transform="matrix(0,1,-1,0,48.0003,4.1307112e-7)"
 | 
			
		||||
     transform="translate(0,48)"
 | 
			
		||||
     id="layer1">
 | 
			
		||||
    <g
 | 
			
		||||
       transform="matrix(-2,0,0,2,-97.2497,-374.967)"
 | 
			
		||||
@@ -335,42 +297,35 @@
 | 
			
		||||
      <path
 | 
			
		||||
         d="m -72.5,173.5 -14,14 14,14"
 | 
			
		||||
         id="path3165-7-3"
 | 
			
		||||
         style="color:#000000;fill:none;stroke:#000000;stroke-width:7;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
 | 
			
		||||
         inkscape:connector-curvature="0" />
 | 
			
		||||
         style="color:#000000;fill:none;stroke:#000000;stroke-width:7;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" />
 | 
			
		||||
    </g>
 | 
			
		||||
    <path
 | 
			
		||||
       d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z"
 | 
			
		||||
       transform="matrix(-3.34328,0,0,3.34328,-89.2797,-623.176)"
 | 
			
		||||
       id="path4050-2-7-9-4"
 | 
			
		||||
       style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible"
 | 
			
		||||
       inkscape:connector-curvature="0" />
 | 
			
		||||
       style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible" />
 | 
			
		||||
    <path
 | 
			
		||||
       d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z"
 | 
			
		||||
       transform="matrix(-3.34328,0,0,3.34328,-111.2797,-623.176)"
 | 
			
		||||
       id="path4050-2-7-9-4-8"
 | 
			
		||||
       style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible"
 | 
			
		||||
       inkscape:connector-curvature="0" />
 | 
			
		||||
       style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible" />
 | 
			
		||||
    <path
 | 
			
		||||
       d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z"
 | 
			
		||||
       transform="matrix(-2.86565,0,0,2.86565,-70.8457,-534.143)"
 | 
			
		||||
       id="path4050-2-7-9-4-0"
 | 
			
		||||
       style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
 | 
			
		||||
       inkscape:connector-curvature="0" />
 | 
			
		||||
       style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" />
 | 
			
		||||
    <path
 | 
			
		||||
       d="m -36.5,186.40625 a 2.09375,2.09375 0 1 1 -4.1875,0 2.09375,2.09375 0 1 1 4.1875,0 z"
 | 
			
		||||
       transform="matrix(-2.86565,0,0,2.86565,-92.8457,-534.143)"
 | 
			
		||||
       id="path4050-2-7-9-4-0-9"
 | 
			
		||||
       style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
 | 
			
		||||
       inkscape:connector-curvature="0" />
 | 
			
		||||
       style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" />
 | 
			
		||||
    <path
 | 
			
		||||
       d="m 47.87528,-34.0295 c 1.53896,0.0448 3.0511,0.70928 4.125,1.8125 l 32.25,32.25 -32.25,32.25 c -2.2253,2.2253 -6.2747,2.2253 -8.5,0 -2.2253,-2.22528 -2.2253,-6.2747 0,-8.5 l 23.75,-23.75 -23.75,-23.75 c -1.73168,-1.6731 -2.295,-4.44228 -1.3546,-6.65894 0.94042,-2.21668 3.32312,-3.73604 5.7296,-3.65356 z"
 | 
			
		||||
       id="path3165-7-3-1"
 | 
			
		||||
       style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;opacity:0.35;color:#000000;fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans"
 | 
			
		||||
       inkscape:connector-curvature="0" />
 | 
			
		||||
       style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;opacity:0.35;color:#000000;fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans" />
 | 
			
		||||
    <path
 | 
			
		||||
       d="m 41.8316,28.09418 c -0.014,-1.58898 0.54158,-3.18406 1.66868,-4.31118 l 23.75,-23.75 m -25.1046,-30.40894 c 0.94042,-2.21668 3.32312,-3.73604 5.7296,-3.65356 1.53896,0.0448 3.0511,0.70928 4.125,1.8125 l 32.25,32.25"
 | 
			
		||||
       id="path3165-7-3-1-9"
 | 
			
		||||
       style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;color:#000000;fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans"
 | 
			
		||||
       inkscape:connector-curvature="0" />
 | 
			
		||||
       style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;color:#000000;fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans" />
 | 
			
		||||
  </g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 12 KiB  | 
@@ -1,447 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
 | 
			
		||||
 | 
			
		||||
<svg
 | 
			
		||||
   xmlns:dc="http://purl.org/dc/elements/1.1/"
 | 
			
		||||
   xmlns:cc="http://creativecommons.org/ns#"
 | 
			
		||||
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 | 
			
		||||
   xmlns:svg="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
   xmlns:xlink="http://www.w3.org/1999/xlink"
 | 
			
		||||
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 | 
			
		||||
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
 | 
			
		||||
   width="96"
 | 
			
		||||
   height="96"
 | 
			
		||||
   id="svg25070"
 | 
			
		||||
   version="1.1"
 | 
			
		||||
   inkscape:version="0.48.0 r9654"
 | 
			
		||||
   sodipodi:docname="ws-switch-arrow-up.svg">
 | 
			
		||||
  <defs
 | 
			
		||||
     id="defs25072">
 | 
			
		||||
    <inkscape:perspective
 | 
			
		||||
       sodipodi:type="inkscape:persp3d"
 | 
			
		||||
       inkscape:vp_x="0 : 24 : 1"
 | 
			
		||||
       inkscape:vp_y="0 : 1000 : 0"
 | 
			
		||||
       inkscape:vp_z="48 : 24 : 1"
 | 
			
		||||
       inkscape:persp3d-origin="24 : 16 : 1"
 | 
			
		||||
       id="perspective25078" />
 | 
			
		||||
    <inkscape:perspective
 | 
			
		||||
       id="perspective24985"
 | 
			
		||||
       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
 | 
			
		||||
       inkscape:vp_z="1 : 0.5 : 1"
 | 
			
		||||
       inkscape:vp_y="0 : 1000 : 0"
 | 
			
		||||
       inkscape:vp_x="0 : 0.5 : 1"
 | 
			
		||||
       sodipodi:type="inkscape:persp3d" />
 | 
			
		||||
    <linearGradient
 | 
			
		||||
       inkscape:collect="always"
 | 
			
		||||
       xlink:href="#linearGradient4034-0-4"
 | 
			
		||||
       id="linearGradient24957"
 | 
			
		||||
       gradientUnits="userSpaceOnUse"
 | 
			
		||||
       gradientTransform="translate(6)"
 | 
			
		||||
       x1="-86.552246"
 | 
			
		||||
       y1="185.439"
 | 
			
		||||
       x2="-83.37072"
 | 
			
		||||
       y2="197.31261" />
 | 
			
		||||
    <linearGradient
 | 
			
		||||
       inkscape:collect="always"
 | 
			
		||||
       id="linearGradient4034-0-4">
 | 
			
		||||
      <stop
 | 
			
		||||
         style="stop-color: rgb(238, 238, 236); stop-opacity: 1;"
 | 
			
		||||
         offset="0"
 | 
			
		||||
         id="stop4036-5-7" />
 | 
			
		||||
      <stop
 | 
			
		||||
         style="stop-color: rgb(186, 189, 182); stop-opacity: 1;"
 | 
			
		||||
         offset="1"
 | 
			
		||||
         id="stop4038-9-6" />
 | 
			
		||||
    </linearGradient>
 | 
			
		||||
    <filter
 | 
			
		||||
       id="filter24765"
 | 
			
		||||
       inkscape:label="Invert"
 | 
			
		||||
       x="0"
 | 
			
		||||
       y="0"
 | 
			
		||||
       width="1"
 | 
			
		||||
       height="1"
 | 
			
		||||
       inkscape:menu="Color"
 | 
			
		||||
       inkscape:menu-tooltip="Invert colors"
 | 
			
		||||
       color-interpolation-filters="sRGB">
 | 
			
		||||
      <feColorMatrix
 | 
			
		||||
         id="feColorMatrix24767"
 | 
			
		||||
         type="saturate"
 | 
			
		||||
         values="1"
 | 
			
		||||
         result="fbSourceGraphic" />
 | 
			
		||||
      <feColorMatrix
 | 
			
		||||
         id="feColorMatrix24769"
 | 
			
		||||
         in="fbSourceGraphic"
 | 
			
		||||
         values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
 | 
			
		||||
    </filter>
 | 
			
		||||
    <linearGradient
 | 
			
		||||
       inkscape:collect="always"
 | 
			
		||||
       xlink:href="#linearGradient4632-1-3-9-3-2"
 | 
			
		||||
       id="linearGradient24955"
 | 
			
		||||
       gradientUnits="userSpaceOnUse"
 | 
			
		||||
       gradientTransform="translate(-5)"
 | 
			
		||||
       x1="-74.520325"
 | 
			
		||||
       y1="169.06032"
 | 
			
		||||
       x2="-74.520325"
 | 
			
		||||
       y2="205.94189" />
 | 
			
		||||
    <linearGradient
 | 
			
		||||
       id="linearGradient4632-1-3-9-3-2">
 | 
			
		||||
      <stop
 | 
			
		||||
         style="stop-color: rgb(238, 238, 236); stop-opacity: 1;"
 | 
			
		||||
         offset="0"
 | 
			
		||||
         id="stop4634-1-8-3-9-0" />
 | 
			
		||||
      <stop
 | 
			
		||||
         id="stop4636-1-9-9-8-8"
 | 
			
		||||
         offset="0.0274937"
 | 
			
		||||
         style="stop-color: rgb(255, 255, 255); stop-opacity: 1;" />
 | 
			
		||||
      <stop
 | 
			
		||||
         id="stop4638-8-3-9-6-6"
 | 
			
		||||
         offset="0.274937"
 | 
			
		||||
         style="stop-color: rgb(242, 242, 242); stop-opacity: 1;" />
 | 
			
		||||
      <stop
 | 
			
		||||
         id="stop4640-8-5-7-8-9"
 | 
			
		||||
         offset="0.38707438"
 | 
			
		||||
         style="stop-color: rgb(238, 238, 236); stop-opacity: 1;" />
 | 
			
		||||
      <stop
 | 
			
		||||
         id="stop4642-5-41-9-6-9"
 | 
			
		||||
         offset="0.66528589"
 | 
			
		||||
         style="stop-color: rgb(217, 218, 216); stop-opacity: 1;" />
 | 
			
		||||
      <stop
 | 
			
		||||
         id="stop4644-5-2-7-9-2"
 | 
			
		||||
         offset="0.76745707"
 | 
			
		||||
         style="stop-color: rgb(223, 224, 221); stop-opacity: 1;" />
 | 
			
		||||
      <stop
 | 
			
		||||
         style="stop-color: rgb(240, 240, 240); stop-opacity: 1;"
 | 
			
		||||
         offset="1"
 | 
			
		||||
         id="stop4646-3-2-3-7-3" />
 | 
			
		||||
    </linearGradient>
 | 
			
		||||
    <radialGradient
 | 
			
		||||
       inkscape:collect="always"
 | 
			
		||||
       xlink:href="#linearGradient4869-4-1"
 | 
			
		||||
       id="radialGradient24959"
 | 
			
		||||
       gradientUnits="userSpaceOnUse"
 | 
			
		||||
       gradientTransform="matrix(1.0075, 0, 0, 1.0075, -5.4544, -1.25141)"
 | 
			
		||||
       cx="-33.412369"
 | 
			
		||||
       cy="185.74171"
 | 
			
		||||
       fx="-33.412369"
 | 
			
		||||
       fy="185.74171"
 | 
			
		||||
       r="2.3554697" />
 | 
			
		||||
    <linearGradient
 | 
			
		||||
       id="linearGradient4869-4-1">
 | 
			
		||||
      <stop
 | 
			
		||||
         style="stop-color: rgb(255, 255, 255); stop-opacity: 1;"
 | 
			
		||||
         offset="0"
 | 
			
		||||
         id="stop4871-6-2" />
 | 
			
		||||
      <stop
 | 
			
		||||
         id="stop4879-7-4"
 | 
			
		||||
         offset="0.31807542"
 | 
			
		||||
         style="stop-color: rgb(238, 238, 236); stop-opacity: 1;" />
 | 
			
		||||
      <stop
 | 
			
		||||
         id="stop4877-6-1"
 | 
			
		||||
         offset="0.74691135"
 | 
			
		||||
         style="stop-color: rgb(200, 201, 198); stop-opacity: 1;" />
 | 
			
		||||
      <stop
 | 
			
		||||
         style="stop-color: rgb(211, 215, 207); stop-opacity: 1;"
 | 
			
		||||
         offset="1"
 | 
			
		||||
         id="stop4873-1-0" />
 | 
			
		||||
    </linearGradient>
 | 
			
		||||
    <filter
 | 
			
		||||
       id="filter25011"
 | 
			
		||||
       inkscape:label="Invert"
 | 
			
		||||
       x="0"
 | 
			
		||||
       y="0"
 | 
			
		||||
       width="1"
 | 
			
		||||
       height="1"
 | 
			
		||||
       inkscape:menu="Color"
 | 
			
		||||
       inkscape:menu-tooltip="Invert colors"
 | 
			
		||||
       color-interpolation-filters="sRGB">
 | 
			
		||||
      <feColorMatrix
 | 
			
		||||
         id="feColorMatrix25013"
 | 
			
		||||
         type="saturate"
 | 
			
		||||
         values="1"
 | 
			
		||||
         result="fbSourceGraphic" />
 | 
			
		||||
      <feColorMatrix
 | 
			
		||||
         id="feColorMatrix25015"
 | 
			
		||||
         in="fbSourceGraphic"
 | 
			
		||||
         values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
 | 
			
		||||
    </filter>
 | 
			
		||||
    <radialGradient
 | 
			
		||||
       inkscape:collect="always"
 | 
			
		||||
       xlink:href="#linearGradient4869-4-0"
 | 
			
		||||
       id="radialGradient24961"
 | 
			
		||||
       gradientUnits="userSpaceOnUse"
 | 
			
		||||
       gradientTransform="matrix(1.0075, 0, 0, 1.0075, -5.4544, -1.25141)"
 | 
			
		||||
       cx="-33.412369"
 | 
			
		||||
       cy="185.74171"
 | 
			
		||||
       fx="-33.412369"
 | 
			
		||||
       fy="185.74171"
 | 
			
		||||
       r="2.3554697" />
 | 
			
		||||
    <linearGradient
 | 
			
		||||
       id="linearGradient4869-4-0">
 | 
			
		||||
      <stop
 | 
			
		||||
         style="stop-color: rgb(255, 255, 255); stop-opacity: 1;"
 | 
			
		||||
         offset="0"
 | 
			
		||||
         id="stop4871-6-8" />
 | 
			
		||||
      <stop
 | 
			
		||||
         id="stop4879-7-5"
 | 
			
		||||
         offset="0.31807542"
 | 
			
		||||
         style="stop-color: rgb(238, 238, 236); stop-opacity: 1;" />
 | 
			
		||||
      <stop
 | 
			
		||||
         id="stop4877-6-5"
 | 
			
		||||
         offset="0.74691135"
 | 
			
		||||
         style="stop-color: rgb(200, 201, 198); stop-opacity: 1;" />
 | 
			
		||||
      <stop
 | 
			
		||||
         style="stop-color: rgb(211, 215, 207); stop-opacity: 1;"
 | 
			
		||||
         offset="1"
 | 
			
		||||
         id="stop4873-1-4" />
 | 
			
		||||
    </linearGradient>
 | 
			
		||||
    <filter
 | 
			
		||||
       id="filter25023"
 | 
			
		||||
       inkscape:label="Invert"
 | 
			
		||||
       x="0"
 | 
			
		||||
       y="0"
 | 
			
		||||
       width="1"
 | 
			
		||||
       height="1"
 | 
			
		||||
       inkscape:menu="Color"
 | 
			
		||||
       inkscape:menu-tooltip="Invert colors"
 | 
			
		||||
       color-interpolation-filters="sRGB">
 | 
			
		||||
      <feColorMatrix
 | 
			
		||||
         id="feColorMatrix25025"
 | 
			
		||||
         type="saturate"
 | 
			
		||||
         values="1"
 | 
			
		||||
         result="fbSourceGraphic" />
 | 
			
		||||
      <feColorMatrix
 | 
			
		||||
         id="feColorMatrix25027"
 | 
			
		||||
         in="fbSourceGraphic"
 | 
			
		||||
         values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
 | 
			
		||||
    </filter>
 | 
			
		||||
    <linearGradient
 | 
			
		||||
       inkscape:collect="always"
 | 
			
		||||
       xlink:href="#linearGradient4941"
 | 
			
		||||
       id="linearGradient24963"
 | 
			
		||||
       gradientUnits="userSpaceOnUse"
 | 
			
		||||
       x1="-39.858727"
 | 
			
		||||
       y1="184.61784"
 | 
			
		||||
       x2="-38.244785"
 | 
			
		||||
       y2="188.84898" />
 | 
			
		||||
    <linearGradient
 | 
			
		||||
       inkscape:collect="always"
 | 
			
		||||
       id="linearGradient4941">
 | 
			
		||||
      <stop
 | 
			
		||||
         style="stop-color: rgb(255, 255, 255); stop-opacity: 1;"
 | 
			
		||||
         offset="0"
 | 
			
		||||
         id="stop4943" />
 | 
			
		||||
      <stop
 | 
			
		||||
         style="stop-color: rgb(255, 255, 255); stop-opacity: 0;"
 | 
			
		||||
         offset="1"
 | 
			
		||||
         id="stop4945" />
 | 
			
		||||
    </linearGradient>
 | 
			
		||||
    <filter
 | 
			
		||||
       id="filter25033"
 | 
			
		||||
       inkscape:label="Invert"
 | 
			
		||||
       x="0"
 | 
			
		||||
       y="0"
 | 
			
		||||
       width="1"
 | 
			
		||||
       height="1"
 | 
			
		||||
       inkscape:menu="Color"
 | 
			
		||||
       inkscape:menu-tooltip="Invert colors"
 | 
			
		||||
       color-interpolation-filters="sRGB">
 | 
			
		||||
      <feColorMatrix
 | 
			
		||||
         id="feColorMatrix25035"
 | 
			
		||||
         type="saturate"
 | 
			
		||||
         values="1"
 | 
			
		||||
         result="fbSourceGraphic" />
 | 
			
		||||
      <feColorMatrix
 | 
			
		||||
         id="feColorMatrix25037"
 | 
			
		||||
         in="fbSourceGraphic"
 | 
			
		||||
         values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
 | 
			
		||||
    </filter>
 | 
			
		||||
    <linearGradient
 | 
			
		||||
       inkscape:collect="always"
 | 
			
		||||
       xlink:href="#linearGradient4941-7"
 | 
			
		||||
       id="linearGradient24965"
 | 
			
		||||
       gradientUnits="userSpaceOnUse"
 | 
			
		||||
       x1="-39.858727"
 | 
			
		||||
       y1="184.61784"
 | 
			
		||||
       x2="-38.244785"
 | 
			
		||||
       y2="188.84898" />
 | 
			
		||||
    <linearGradient
 | 
			
		||||
       inkscape:collect="always"
 | 
			
		||||
       id="linearGradient4941-7">
 | 
			
		||||
      <stop
 | 
			
		||||
         style="stop-color: rgb(255, 255, 255); stop-opacity: 1;"
 | 
			
		||||
         offset="0"
 | 
			
		||||
         id="stop4943-2" />
 | 
			
		||||
      <stop
 | 
			
		||||
         style="stop-color: rgb(255, 255, 255); stop-opacity: 0;"
 | 
			
		||||
         offset="1"
 | 
			
		||||
         id="stop4945-5" />
 | 
			
		||||
    </linearGradient>
 | 
			
		||||
    <filter
 | 
			
		||||
       id="filter25043"
 | 
			
		||||
       inkscape:label="Invert"
 | 
			
		||||
       x="0"
 | 
			
		||||
       y="0"
 | 
			
		||||
       width="1"
 | 
			
		||||
       height="1"
 | 
			
		||||
       inkscape:menu="Color"
 | 
			
		||||
       inkscape:menu-tooltip="Invert colors"
 | 
			
		||||
       color-interpolation-filters="sRGB">
 | 
			
		||||
      <feColorMatrix
 | 
			
		||||
         id="feColorMatrix25045"
 | 
			
		||||
         type="saturate"
 | 
			
		||||
         values="1"
 | 
			
		||||
         result="fbSourceGraphic" />
 | 
			
		||||
      <feColorMatrix
 | 
			
		||||
         id="feColorMatrix25047"
 | 
			
		||||
         in="fbSourceGraphic"
 | 
			
		||||
         values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
 | 
			
		||||
    </filter>
 | 
			
		||||
    <filter
 | 
			
		||||
       id="filter25049"
 | 
			
		||||
       inkscape:label="Invert"
 | 
			
		||||
       x="0"
 | 
			
		||||
       y="0"
 | 
			
		||||
       width="1"
 | 
			
		||||
       height="1"
 | 
			
		||||
       inkscape:menu="Color"
 | 
			
		||||
       inkscape:menu-tooltip="Invert colors"
 | 
			
		||||
       color-interpolation-filters="sRGB">
 | 
			
		||||
      <feColorMatrix
 | 
			
		||||
         id="feColorMatrix25051"
 | 
			
		||||
         type="saturate"
 | 
			
		||||
         values="1"
 | 
			
		||||
         result="fbSourceGraphic" />
 | 
			
		||||
      <feColorMatrix
 | 
			
		||||
         id="feColorMatrix25053"
 | 
			
		||||
         in="fbSourceGraphic"
 | 
			
		||||
         values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
 | 
			
		||||
    </filter>
 | 
			
		||||
    <filter
 | 
			
		||||
       id="filter25055"
 | 
			
		||||
       inkscape:label="Invert"
 | 
			
		||||
       x="0"
 | 
			
		||||
       y="0"
 | 
			
		||||
       width="1"
 | 
			
		||||
       height="1"
 | 
			
		||||
       inkscape:menu="Color"
 | 
			
		||||
       inkscape:menu-tooltip="Invert colors"
 | 
			
		||||
       color-interpolation-filters="sRGB">
 | 
			
		||||
      <feColorMatrix
 | 
			
		||||
         id="feColorMatrix25057"
 | 
			
		||||
         type="saturate"
 | 
			
		||||
         values="1"
 | 
			
		||||
         result="fbSourceGraphic" />
 | 
			
		||||
      <feColorMatrix
 | 
			
		||||
         id="feColorMatrix25059"
 | 
			
		||||
         in="fbSourceGraphic"
 | 
			
		||||
         values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" />
 | 
			
		||||
    </filter>
 | 
			
		||||
  </defs>
 | 
			
		||||
  <sodipodi:namedview
 | 
			
		||||
     id="base"
 | 
			
		||||
     pagecolor="#ffffff"
 | 
			
		||||
     bordercolor="#666666"
 | 
			
		||||
     borderopacity="1.0"
 | 
			
		||||
     inkscape:pageopacity="0.0"
 | 
			
		||||
     inkscape:pageshadow="2"
 | 
			
		||||
     inkscape:zoom="2.8284271"
 | 
			
		||||
     inkscape:cx="-12.356322"
 | 
			
		||||
     inkscape:cy="57.536221"
 | 
			
		||||
     inkscape:current-layer="layer1"
 | 
			
		||||
     showgrid="true"
 | 
			
		||||
     inkscape:grid-bbox="true"
 | 
			
		||||
     inkscape:document-units="px"
 | 
			
		||||
     inkscape:window-width="1200"
 | 
			
		||||
     inkscape:window-height="840"
 | 
			
		||||
     inkscape:window-x="0"
 | 
			
		||||
     inkscape:window-y="26"
 | 
			
		||||
     inkscape:window-maximized="0" />
 | 
			
		||||
  <metadata
 | 
			
		||||
     id="metadata25075">
 | 
			
		||||
    <rdf:RDF>
 | 
			
		||||
      <cc:Work
 | 
			
		||||
         rdf:about="">
 | 
			
		||||
        <dc:format>image/svg+xml</dc:format>
 | 
			
		||||
        <dc:type
 | 
			
		||||
           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
 | 
			
		||||
        <dc:title />
 | 
			
		||||
      </cc:Work>
 | 
			
		||||
    </rdf:RDF>
 | 
			
		||||
  </metadata>
 | 
			
		||||
  <g
 | 
			
		||||
     id="layer1"
 | 
			
		||||
     inkscape:label="Layer 1"
 | 
			
		||||
     inkscape:groupmode="layer"
 | 
			
		||||
     transform="translate(0, 48)">
 | 
			
		||||
    <g
 | 
			
		||||
       id="g3181"
 | 
			
		||||
       transform="matrix(0,1,-1,0,48.0003,-48)">
 | 
			
		||||
      <g
 | 
			
		||||
         style="stroke:#000000;stroke-opacity:1;display:inline"
 | 
			
		||||
         transform="matrix(2,0,0,2,193.25,-374.967)"
 | 
			
		||||
         id="g4030-1-8">
 | 
			
		||||
        <path
 | 
			
		||||
           style="color:#000000;fill:none;stroke:#000000;stroke-width:7;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
 | 
			
		||||
           d="m -72.5,173.5 -14,14 14,14"
 | 
			
		||||
           id="path3165-7-3"
 | 
			
		||||
           sodipodi:nodetypes="ccc"
 | 
			
		||||
           inkscape:connector-curvature="0" />
 | 
			
		||||
      </g>
 | 
			
		||||
      <path
 | 
			
		||||
         transform="matrix(3.34328,0,0,3.34328,185.28,-623.176)"
 | 
			
		||||
         d="m -36.5,186.40625 c 0,1.15635 -0.937404,2.09375 -2.09375,2.09375 -1.156346,0 -2.09375,-0.9374 -2.09375,-2.09375 0,-1.15635 0.937404,-2.09375 2.09375,-2.09375 1.156346,0 2.09375,0.9374 2.09375,2.09375 z"
 | 
			
		||||
         sodipodi:ry="2.09375"
 | 
			
		||||
         sodipodi:rx="2.09375"
 | 
			
		||||
         sodipodi:cy="186.40625"
 | 
			
		||||
         sodipodi:cx="-38.59375"
 | 
			
		||||
         id="path4050-2-7-9-4"
 | 
			
		||||
         style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible"
 | 
			
		||||
         sodipodi:type="arc" />
 | 
			
		||||
      <path
 | 
			
		||||
         transform="matrix(3.34328,0,0,3.34328,207.28,-623.176)"
 | 
			
		||||
         d="m -36.5,186.40625 c 0,1.15635 -0.937404,2.09375 -2.09375,2.09375 -1.156346,0 -2.09375,-0.9374 -2.09375,-2.09375 0,-1.15635 0.937404,-2.09375 2.09375,-2.09375 1.156346,0 2.09375,0.9374 2.09375,2.09375 z"
 | 
			
		||||
         sodipodi:ry="2.09375"
 | 
			
		||||
         sodipodi:rx="2.09375"
 | 
			
		||||
         sodipodi:cy="186.40625"
 | 
			
		||||
         sodipodi:cx="-38.59375"
 | 
			
		||||
         id="path4050-2-7-9-4-8"
 | 
			
		||||
         style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.52343899;marker:none;visibility:visible;display:inline;overflow:visible"
 | 
			
		||||
         sodipodi:type="arc" />
 | 
			
		||||
      <path
 | 
			
		||||
         transform="matrix(2.86565,0,0,2.86565,166.846,-534.143)"
 | 
			
		||||
         d="m -36.5,186.40625 c 0,1.15635 -0.937404,2.09375 -2.09375,2.09375 -1.156346,0 -2.09375,-0.9374 -2.09375,-2.09375 0,-1.15635 0.937404,-2.09375 2.09375,-2.09375 1.156346,0 2.09375,0.9374 2.09375,2.09375 z"
 | 
			
		||||
         sodipodi:ry="2.09375"
 | 
			
		||||
         sodipodi:rx="2.09375"
 | 
			
		||||
         sodipodi:cy="186.40625"
 | 
			
		||||
         sodipodi:cx="-38.59375"
 | 
			
		||||
         id="path4050-2-7-9-4-0"
 | 
			
		||||
         style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
 | 
			
		||||
         sodipodi:type="arc" />
 | 
			
		||||
      <path
 | 
			
		||||
         transform="matrix(2.86565,0,0,2.86565,188.846,-534.143)"
 | 
			
		||||
         d="m -36.5,186.40625 c 0,1.15635 -0.937404,2.09375 -2.09375,2.09375 -1.156346,0 -2.09375,-0.9374 -2.09375,-2.09375 0,-1.15635 0.937404,-2.09375 2.09375,-2.09375 1.156346,0 2.09375,0.9374 2.09375,2.09375 z"
 | 
			
		||||
         sodipodi:ry="2.09375"
 | 
			
		||||
         sodipodi:rx="2.09375"
 | 
			
		||||
         sodipodi:cy="186.40625"
 | 
			
		||||
         sodipodi:cx="-38.59375"
 | 
			
		||||
         id="path4050-2-7-9-4-0-9"
 | 
			
		||||
         style="color:#000000;fill:none;stroke:#000000;stroke-width:0.69792098;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
 | 
			
		||||
         sodipodi:type="arc" />
 | 
			
		||||
      <path
 | 
			
		||||
         transform="matrix(2,0,0,2,-586,-765.967)"
 | 
			
		||||
         sodipodi:nodetypes="ccccscccsc"
 | 
			
		||||
         id="path3165-7-3-1"
 | 
			
		||||
         d="m 317.06251,365.96875 c -0.76948,0.0224 -1.52555,0.35464 -2.0625,0.90625 l -16.125,16.125 16.125,16.125 c 1.11265,1.11265 3.13735,1.11265 4.25,0 1.11265,-1.11264 1.11265,-3.13735 0,-4.25 l -11.875,-11.875 11.875,-11.875 c 0.86584,-0.83655 1.1475,-2.22114 0.6773,-3.32947 -0.47021,-1.10834 -1.66156,-1.86802 -2.8648,-1.82678 z"
 | 
			
		||||
         style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;opacity:0.35;color:#000000;fill:none;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans"
 | 
			
		||||
         inkscape:connector-curvature="0" />
 | 
			
		||||
      <path
 | 
			
		||||
         transform="matrix(2,0,0,2,-586,-765.967)"
 | 
			
		||||
         sodipodi:nodetypes="ccccccc"
 | 
			
		||||
         id="path3165-7-3-1-9"
 | 
			
		||||
         d="m 320.08435,397.03059 c 0.007,-0.79449 -0.27079,-1.59203 -0.83434,-2.15559 L 307.37501,383 m 12.5523,-15.20447 c -0.47021,-1.10834 -1.66156,-1.86802 -2.8648,-1.82678 -0.76948,0.0224 -1.52555,0.35464 -2.0625,0.90625 L 298.87501,383"
 | 
			
		||||
         style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0pt;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;text-anchor:start;color:#000000;fill:none;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;font-family:Bitstream Vera Sans"
 | 
			
		||||
         inkscape:connector-curvature="0" />
 | 
			
		||||
    </g>
 | 
			
		||||
  </g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 16 KiB  | 
@@ -6,22 +6,6 @@
 | 
			
		||||
 | 
			
		||||
  <name xml:lang="en">GNOME Shell</name>
 | 
			
		||||
  <shortdesc xml:lang="en">Next generation GNOME desktop shell</shortdesc>
 | 
			
		||||
  <description>GNOME Shell provides core user interface functions for the GNOME 3
 | 
			
		||||
desktop, like switching to windows and launching applications.
 | 
			
		||||
GNOME Shell takes advantage of the capabilities of modern graphics
 | 
			
		||||
hardware and introduces innovative user interface concepts to
 | 
			
		||||
provide a visually attractive and easy to use experience.
 | 
			
		||||
 | 
			
		||||
Tarball releases are provided largely for distributions to build
 | 
			
		||||
packages. If you are interested in building GNOME Shell from source,
 | 
			
		||||
we would recommend building from version control using the build
 | 
			
		||||
script described at:
 | 
			
		||||
 | 
			
		||||
 http://live.gnome.org/GnomeShell
 | 
			
		||||
 | 
			
		||||
Not only will that give you the very latest version of this rapidly
 | 
			
		||||
changing project, it will be much easier than get GNOME Shell and
 | 
			
		||||
its dependencies to build from tarballs.</description>
 | 
			
		||||
  <!--
 | 
			
		||||
  <homepage rdf:resource="http://live.gnome.org/GnomeShell" />
 | 
			
		||||
  -->
 | 
			
		||||
@@ -49,7 +33,7 @@ its dependencies to build from tarballs.</description>
 | 
			
		||||
    <foaf:Person>
 | 
			
		||||
      <foaf:name>Colin Walters</foaf:name>
 | 
			
		||||
      <foaf:mbox rdf:resource="mailto:walters@verbum.org" />
 | 
			
		||||
      <gnome:userid>walters</gnome:userid>
 | 
			
		||||
      <gnome:userid>cwalters</gnome:userid>
 | 
			
		||||
    </foaf:Person>
 | 
			
		||||
  </maintainer>
 | 
			
		||||
  <maintainer>
 | 
			
		||||
 
 | 
			
		||||
@@ -2,35 +2,27 @@
 | 
			
		||||
jsdir = $(pkgdatadir)/js
 | 
			
		||||
 | 
			
		||||
nobase_dist_js_DATA = 	\
 | 
			
		||||
	misc/config.js		\
 | 
			
		||||
	misc/docInfo.js		\
 | 
			
		||||
	misc/fileUtils.js	\
 | 
			
		||||
	misc/format.js		\
 | 
			
		||||
	misc/gnomeSession.js	\
 | 
			
		||||
	misc/history.js		\
 | 
			
		||||
	misc/modemManager.js	\
 | 
			
		||||
	misc/params.js		\
 | 
			
		||||
	misc/screenSaver.js     \
 | 
			
		||||
	misc/util.js		\
 | 
			
		||||
	misc/telepathy.js	\
 | 
			
		||||
	perf/core.js		\
 | 
			
		||||
	prefs/clockPreferences.js \
 | 
			
		||||
	ui/altTab.js		\
 | 
			
		||||
	ui/appDisplay.js	\
 | 
			
		||||
	ui/appFavorites.js	\
 | 
			
		||||
	ui/automountManager.js  \
 | 
			
		||||
	ui/autorunManager.js    \
 | 
			
		||||
	ui/boxpointer.js	\
 | 
			
		||||
	ui/calendar.js		\
 | 
			
		||||
	ui/chrome.js		\
 | 
			
		||||
	ui/ctrlAltTab.js	\
 | 
			
		||||
	ui/dash.js		\
 | 
			
		||||
	ui/dateMenu.js		\
 | 
			
		||||
	ui/dnd.js		\
 | 
			
		||||
	ui/docDisplay.js	\
 | 
			
		||||
	ui/endSessionDialog.js	\
 | 
			
		||||
	ui/environment.js	\
 | 
			
		||||
	ui/extensionSystem.js	\
 | 
			
		||||
	ui/genericDisplay.js	\
 | 
			
		||||
	ui/iconGrid.js		\
 | 
			
		||||
	ui/layout.js		\
 | 
			
		||||
	ui/lightbox.js		\
 | 
			
		||||
	ui/link.js		\
 | 
			
		||||
	ui/lookingGlass.js	\
 | 
			
		||||
@@ -38,14 +30,11 @@ nobase_dist_js_DATA = 	\
 | 
			
		||||
	ui/magnifierDBus.js	\
 | 
			
		||||
	ui/main.js		\
 | 
			
		||||
	ui/messageTray.js	\
 | 
			
		||||
	ui/modalDialog.js	\
 | 
			
		||||
	ui/shellMountOperation.js \
 | 
			
		||||
	ui/notificationDaemon.js \
 | 
			
		||||
	ui/overview.js		\
 | 
			
		||||
	ui/panel.js		\
 | 
			
		||||
	ui/panelMenu.js		\
 | 
			
		||||
	ui/placeDisplay.js	\
 | 
			
		||||
	ui/polkitAuthenticationAgent.js \
 | 
			
		||||
	ui/popupMenu.js		\
 | 
			
		||||
	ui/runDialog.js		\
 | 
			
		||||
	ui/scripting.js		\
 | 
			
		||||
@@ -55,18 +44,13 @@ nobase_dist_js_DATA = 	\
 | 
			
		||||
	ui/statusIconDispatcher.js	\
 | 
			
		||||
	ui/statusMenu.js	\
 | 
			
		||||
	ui/status/accessibility.js	\
 | 
			
		||||
	ui/status/keyboard.js	\
 | 
			
		||||
	ui/status/network.js	\
 | 
			
		||||
	ui/status/power.js	\
 | 
			
		||||
	ui/status/volume.js	\
 | 
			
		||||
	ui/status/bluetooth.js	\
 | 
			
		||||
	ui/telepathyClient.js	\
 | 
			
		||||
	ui/tweener.js		\
 | 
			
		||||
	ui/viewSelector.js	\
 | 
			
		||||
	ui/windowAttentionHandler.js	\
 | 
			
		||||
	ui/windowManager.js	\
 | 
			
		||||
	ui/workspace.js		\
 | 
			
		||||
	ui/workspaceThumbnail.js	\
 | 
			
		||||
	ui/workspacesView.js	\
 | 
			
		||||
	ui/workspaceSwitcherPopup.js    \
 | 
			
		||||
	ui/xdndHandler.js
 | 
			
		||||
	ui/workspaceSwitcherPopup.js
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +0,0 @@
 | 
			
		||||
/* mode: js2; indent-tabs-mode: nil; tab-size: 4 */
 | 
			
		||||
/* The name of this package (not localized) */
 | 
			
		||||
const PACKAGE_NAME = '@PACKAGE_NAME@';
 | 
			
		||||
/* The version of this package */
 | 
			
		||||
const PACKAGE_VERSION = '@PACKAGE_VERSION@';
 | 
			
		||||
/* The version of GJS we're linking to */
 | 
			
		||||
const GJS_VERSION = '@GJS_VERSION@';
 | 
			
		||||
/* 1 if gnome-bluetooth is available, 0 otherwise */
 | 
			
		||||
const HAVE_BLUETOOTH = @HAVE_BLUETOOTH@;
 | 
			
		||||
 | 
			
		||||
@@ -29,8 +29,8 @@ DocInfo.prototype = {
 | 
			
		||||
        return St.TextureCache.get_default().load_recent_thumbnail(size, this.recentInfo);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    launch : function(workspaceIndex) {
 | 
			
		||||
        Shell.DocSystem.get_default().open(this.recentInfo, workspaceIndex);
 | 
			
		||||
    launch : function() {
 | 
			
		||||
        Shell.DocSystem.get_default().open(this.recentInfo);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    matchTerms: function(terms) {
 | 
			
		||||
@@ -60,7 +60,8 @@ function getDocManager() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * DocManager wraps the DocSystem, primarily to expose DocInfo objects.
 | 
			
		||||
 * DocManager wraps the DocSystem, primarily to expose DocInfo objects
 | 
			
		||||
 * which conform to the GenericDisplay item API.
 | 
			
		||||
 */
 | 
			
		||||
function DocManager() {
 | 
			
		||||
    this._init();
 | 
			
		||||
 
 | 
			
		||||
@@ -2,13 +2,11 @@
 | 
			
		||||
 | 
			
		||||
const DBus = imports.dbus;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
 | 
			
		||||
const PresenceIface = {
 | 
			
		||||
    name: 'org.gnome.SessionManager.Presence',
 | 
			
		||||
    methods: [{ name: 'SetStatus',
 | 
			
		||||
                inSignature: 'u',
 | 
			
		||||
                outSignature: '' }],
 | 
			
		||||
                inSignature: 'u' }],
 | 
			
		||||
    properties: [{ name: 'status',
 | 
			
		||||
                   signature: 'u',
 | 
			
		||||
                   access: 'readwrite' }],
 | 
			
		||||
@@ -45,82 +43,3 @@ Presence.prototype = {
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
DBus.proxifyPrototype(Presence.prototype, PresenceIface);
 | 
			
		||||
 | 
			
		||||
// Note inhibitors are immutable objects, so they don't
 | 
			
		||||
// change at runtime (changes always come in the form
 | 
			
		||||
// of new inhibitors)
 | 
			
		||||
const InhibitorIface = {
 | 
			
		||||
    name: 'org.gnome.SessionManager.Inhibitor',
 | 
			
		||||
    properties: [{ name: 'app_id',
 | 
			
		||||
                   signature: 's',
 | 
			
		||||
                   access: 'readonly' },
 | 
			
		||||
                 { name: 'client_id',
 | 
			
		||||
                   signature: 's',
 | 
			
		||||
                   access: 'readonly' },
 | 
			
		||||
                 { name: 'reason',
 | 
			
		||||
                   signature: 's',
 | 
			
		||||
                   access: 'readonly' },
 | 
			
		||||
                 { name: 'flags',
 | 
			
		||||
                   signature: 'u',
 | 
			
		||||
                   access: 'readonly' },
 | 
			
		||||
                 { name: 'toplevel_xid',
 | 
			
		||||
                   signature: 'u',
 | 
			
		||||
                   access: 'readonly' },
 | 
			
		||||
                 { name: 'cookie',
 | 
			
		||||
                   signature: 'u',
 | 
			
		||||
                   access: 'readonly' }],
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function Inhibitor(objectPath) {
 | 
			
		||||
    this._init(objectPath);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Inhibitor.prototype = {
 | 
			
		||||
    _init: function(objectPath) {
 | 
			
		||||
        DBus.session.proxifyObject(this,
 | 
			
		||||
                                   "org.gnome.SessionManager",
 | 
			
		||||
                                   objectPath);
 | 
			
		||||
        this.isLoaded = false;
 | 
			
		||||
        this._loadingPropertiesCount = InhibitorIface.properties.length;
 | 
			
		||||
        for (let i = 0; i < InhibitorIface.properties.length; i++) {
 | 
			
		||||
            let propertyName = InhibitorIface.properties[i].name;
 | 
			
		||||
            this.GetRemote(propertyName, Lang.bind(this,
 | 
			
		||||
                function(value, exception) {
 | 
			
		||||
                    if (exception)
 | 
			
		||||
                        return;
 | 
			
		||||
 | 
			
		||||
                    this[propertyName] = value;
 | 
			
		||||
                    this._loadingPropertiesCount--;
 | 
			
		||||
 | 
			
		||||
                    if (this._loadingPropertiesCount == 0) {
 | 
			
		||||
                        this.isLoaded = true;
 | 
			
		||||
                        this.emit("is-loaded");
 | 
			
		||||
                    }
 | 
			
		||||
                }));
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
DBus.proxifyPrototype(Inhibitor.prototype, InhibitorIface);
 | 
			
		||||
Signals.addSignalMethods(Inhibitor.prototype);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// Not the full interface, only the methods we use
 | 
			
		||||
const SessionManagerIface = {
 | 
			
		||||
    name: 'org.gnome.SessionManager',
 | 
			
		||||
    methods: [
 | 
			
		||||
        { name: 'Logout', inSignature: 'u', outSignature: '' },
 | 
			
		||||
        { name: 'Shutdown', inSignature: '', outSignature: '' },
 | 
			
		||||
        { name: 'CanShutdown', inSignature: '', outSignature: 'b' }
 | 
			
		||||
    ]
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function SessionManager() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SessionManager.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        DBus.session.proxifyObject(this, 'org.gnome.SessionManager', '/org/gnome/SessionManager');
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
DBus.proxifyPrototype(SessionManager.prototype, SessionManagerIface);
 | 
			
		||||
@@ -1,115 +0,0 @@
 | 
			
		||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
 | 
			
		||||
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
 | 
			
		||||
const DEFAULT_LIMIT = 512;
 | 
			
		||||
 | 
			
		||||
function HistoryManager(params) {
 | 
			
		||||
    this._init(params);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
HistoryManager.prototype = {
 | 
			
		||||
    _init: function(params) {
 | 
			
		||||
        params = Params.parse(params, { gsettingsKey: null,
 | 
			
		||||
                                        limit: DEFAULT_LIMIT,
 | 
			
		||||
                                        entry: null });
 | 
			
		||||
 | 
			
		||||
        this._key = params.gsettingsKey;
 | 
			
		||||
        this._limit = params.limit;
 | 
			
		||||
 | 
			
		||||
        this._historyIndex = 0;
 | 
			
		||||
        if (this._key) {
 | 
			
		||||
            this._history = global.settings.get_strv(this._key);
 | 
			
		||||
            global.settings.connect('changed::' + this._key,
 | 
			
		||||
                                    Lang.bind(this, this._historyChanged));
 | 
			
		||||
 | 
			
		||||
        } else {
 | 
			
		||||
            this._history = [];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._entry = params.entry;
 | 
			
		||||
 | 
			
		||||
        if (this._entry) {
 | 
			
		||||
            this._entry.connect('key-press-event', 
 | 
			
		||||
                                Lang.bind(this, this._onEntryKeyPress));
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _historyChanged: function() {
 | 
			
		||||
        this._history = global.settings.get_strv(this._key);
 | 
			
		||||
        this._historyIndex = this._history.length;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    prevItem: function(text) {
 | 
			
		||||
        if (this._historyIndex <= 0)
 | 
			
		||||
            return text;
 | 
			
		||||
 | 
			
		||||
        if (text)
 | 
			
		||||
            this._history[this._historyIndex] = text;
 | 
			
		||||
        this._historyIndex--;
 | 
			
		||||
        return this._indexChanged();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    nextItem: function(text) {
 | 
			
		||||
        if (this._historyIndex >= this._history.length)
 | 
			
		||||
            return text;
 | 
			
		||||
 | 
			
		||||
        if (text)
 | 
			
		||||
            this._history[this._historyIndex] = text;
 | 
			
		||||
        this._historyIndex++;
 | 
			
		||||
        return this._indexChanged();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    lastItem: function() {
 | 
			
		||||
        if (this._historyIndex != this._history.length) {
 | 
			
		||||
            this._historyIndex = this._history.length;
 | 
			
		||||
            this._indexChanged();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return this._historyIndex[this._history.length];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addItem: function(input) {
 | 
			
		||||
        if (this._history.length == 0 ||
 | 
			
		||||
            this._history[this._history.length - 1] != input) {
 | 
			
		||||
 | 
			
		||||
            this._history.push(input);
 | 
			
		||||
            this._save();
 | 
			
		||||
        }
 | 
			
		||||
        this._historyIndex = this._history.length;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onEntryKeyPress: function(entry, event) {
 | 
			
		||||
        let symbol = event.get_key_symbol();
 | 
			
		||||
        if (symbol == Clutter.KEY_Up) {
 | 
			
		||||
            this.prevItem(entry.get_text());
 | 
			
		||||
            return true;
 | 
			
		||||
        } else if (symbol == Clutter.KEY_Down) {
 | 
			
		||||
            this.nextItem(entry.get_text());
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _indexChanged: function() {
 | 
			
		||||
        let current = this._history[this._historyIndex] || '';
 | 
			
		||||
        this.emit('changed', current);
 | 
			
		||||
 | 
			
		||||
        if (this._entry)
 | 
			
		||||
            this._entry.set_text(current);
 | 
			
		||||
 | 
			
		||||
        return current;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _save: function() {
 | 
			
		||||
        if (this._history.length > this._limit)
 | 
			
		||||
            this._history.splice(0, this._history.length - this._limit);
 | 
			
		||||
 | 
			
		||||
        if (this._key)
 | 
			
		||||
            global.settings.set_strv(this._key, this._history);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
Signals.addSignalMethods(HistoryManager.prototype);
 | 
			
		||||
@@ -1,225 +0,0 @@
 | 
			
		||||
// -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*-
 | 
			
		||||
 | 
			
		||||
const DBus = imports.dbus;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
 | 
			
		||||
// The following are not the complete interfaces, just the methods we need
 | 
			
		||||
// (or may need in the future)
 | 
			
		||||
 | 
			
		||||
const ModemGsmNetworkInterface = {
 | 
			
		||||
    name: 'org.freedesktop.ModemManager.Modem.Gsm.Network',
 | 
			
		||||
    methods: [
 | 
			
		||||
        { name: 'GetRegistrationInfo', inSignature: '', outSignature: 'uss' },
 | 
			
		||||
        { name: 'GetSignalQuality', inSignature: '', outSignature: 'u' }
 | 
			
		||||
    ],
 | 
			
		||||
    properties: [
 | 
			
		||||
        { name: 'AccessTechnology', signature: 'u', access: 'read' }
 | 
			
		||||
    ],
 | 
			
		||||
    signals: [
 | 
			
		||||
        { name: 'SignalQuality', inSignature: 'u' },
 | 
			
		||||
        { name: 'RegistrationInfo', inSignature: 'uss' }
 | 
			
		||||
    ]
 | 
			
		||||
};
 | 
			
		||||
const ModemGsmNetworkProxy = DBus.makeProxyClass(ModemGsmNetworkInterface);
 | 
			
		||||
 | 
			
		||||
const ModemCdmaInterface = {
 | 
			
		||||
    name: 'org.freedesktop.ModemManager.Modem.Cdma',
 | 
			
		||||
    methods: [
 | 
			
		||||
        { name: 'GetSignalQuality', inSignature: '', outSignature: 'u' },
 | 
			
		||||
        { name: 'GetServingSystem', inSignature: '', outSignature: 'usu' }
 | 
			
		||||
    ],
 | 
			
		||||
    signals: [
 | 
			
		||||
        { name: 'SignalQuality', inSignature: 'u' }
 | 
			
		||||
    ]
 | 
			
		||||
};
 | 
			
		||||
const ModemCdmaProxy = DBus.makeProxyClass(ModemCdmaInterface);
 | 
			
		||||
 | 
			
		||||
let _providersTable;
 | 
			
		||||
function _getProvidersTable() {
 | 
			
		||||
    if (_providersTable)
 | 
			
		||||
        return _providersTable;
 | 
			
		||||
    let [providers, countryCodes] = Shell.mobile_providers_parse();
 | 
			
		||||
    return _providersTable = providers;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function ModemGsm() {
 | 
			
		||||
    this._init.apply(this, arguments);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ModemGsm.prototype = {
 | 
			
		||||
    _init: function(path) {
 | 
			
		||||
        this._proxy = new ModemGsmNetworkProxy(DBus.system, 'org.freedesktop.ModemManager', path);
 | 
			
		||||
 | 
			
		||||
        this.signal_quality = 0;
 | 
			
		||||
        this.operator_name = null;
 | 
			
		||||
 | 
			
		||||
        // Code is duplicated because the function have different signatures
 | 
			
		||||
        this._proxy.connect('SignalQuality', Lang.bind(this, function(proxy, quality) {
 | 
			
		||||
            this.signal_quality = quality;
 | 
			
		||||
            this.emit('notify::signal-quality');
 | 
			
		||||
        }));
 | 
			
		||||
        this._proxy.connect('RegistrationInfo', Lang.bind(this, function(proxy, status, code, name) {
 | 
			
		||||
            this.operator_name = this._findOperatorName(name, code);
 | 
			
		||||
            this.emit('notify::operator-name');
 | 
			
		||||
        }));
 | 
			
		||||
        this._proxy.GetRegistrationInfoRemote(Lang.bind(this, function(result, err) {
 | 
			
		||||
            if (err) {
 | 
			
		||||
                log(err);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let [status, code, name] = result;
 | 
			
		||||
            this.operator_name = this._findOperatorName(name, code);
 | 
			
		||||
            this.emit('notify::operator-name');
 | 
			
		||||
        }));
 | 
			
		||||
        this._proxy.GetSignalQualityRemote(Lang.bind(this, function(result, err) {
 | 
			
		||||
            if (err) {
 | 
			
		||||
                // it will return an error if the device is not connected
 | 
			
		||||
                this.signal_quality = 0;
 | 
			
		||||
            } else {
 | 
			
		||||
                let [quality] = result;
 | 
			
		||||
                this.signal_quality = quality;
 | 
			
		||||
            }
 | 
			
		||||
            this.emit('notify::signal-quality');
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _findOperatorName: function(name, opCode) {
 | 
			
		||||
        if (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.length == 0 && opCode)
 | 
			
		||||
            needle = opCode;
 | 
			
		||||
        else if (name.length == 6 || name.length == 5)
 | 
			
		||||
            needle = name;
 | 
			
		||||
        else // nothing to search
 | 
			
		||||
            return null;
 | 
			
		||||
 | 
			
		||||
        return this._findProviderForMCCMNC(needle);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _findProviderForMCCMNC: function(needle) {
 | 
			
		||||
        let table = _getProvidersTable();
 | 
			
		||||
        let needlemcc = needle.substring(0, 3);
 | 
			
		||||
        let needlemnc = needle.substring(3, needle.length);
 | 
			
		||||
 | 
			
		||||
        let name2, name3;
 | 
			
		||||
        for (let iter in table) {
 | 
			
		||||
            let providers = table[iter];
 | 
			
		||||
 | 
			
		||||
            // 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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
Signals.addSignalMethods(ModemGsm.prototype);
 | 
			
		||||
 | 
			
		||||
function ModemCdma() {
 | 
			
		||||
    this._init.apply(this, arguments);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ModemCdma.prototype = {
 | 
			
		||||
    _init: function(path) {
 | 
			
		||||
        this._proxy = new ModemCdmaProxy(DBus.system, 'org.freedesktop.ModemManager', path);
 | 
			
		||||
 | 
			
		||||
        this.signal_quality = 0;
 | 
			
		||||
        this.operator_name = null;
 | 
			
		||||
        this._proxy.connect('SignalQuality', Lang.bind(this, function(proxy, quality) {
 | 
			
		||||
            this.signal_quality = quality;
 | 
			
		||||
            this.emit('notify::signal-quality');
 | 
			
		||||
 | 
			
		||||
            // receiving this signal means the device got activated
 | 
			
		||||
            // and we can finally call GetServingSystem
 | 
			
		||||
            if (this.operator_name == null)
 | 
			
		||||
                this._refreshServingSystem();
 | 
			
		||||
        }));
 | 
			
		||||
        this._proxy.GetSignalQualityRemote(Lang.bind(this, function(result, err) {
 | 
			
		||||
            if (err) {
 | 
			
		||||
                // it will return an error if the device is not connected
 | 
			
		||||
                this.signal_quality = 0;
 | 
			
		||||
            } else {
 | 
			
		||||
                let [quality] = result;
 | 
			
		||||
                this.signal_quality = quality;
 | 
			
		||||
            }
 | 
			
		||||
            this.emit('notify::signal-quality');
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _refreshServingSystem: function() {
 | 
			
		||||
        this._proxy.GetServingSystemRemote(Lang.bind(this, function(result, err) {
 | 
			
		||||
            if (err) {
 | 
			
		||||
                // it will return an error if the device is not connected
 | 
			
		||||
                this.operator_name = null;
 | 
			
		||||
            } else {
 | 
			
		||||
                let [bandClass, band, id] = result;
 | 
			
		||||
                if (name.length > 0)
 | 
			
		||||
                    this.operator_name = this._findProviderForSid(id);
 | 
			
		||||
                else
 | 
			
		||||
                    this.operator_name = null;
 | 
			
		||||
            }
 | 
			
		||||
            this.emit('notify::operator-name');
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _findProviderForSid: function(sid) {
 | 
			
		||||
        if (sid == 0)
 | 
			
		||||
            return null;
 | 
			
		||||
 | 
			
		||||
        let table = _getProvidersTable();
 | 
			
		||||
 | 
			
		||||
        // Search through each country
 | 
			
		||||
        for (let iter in table) {
 | 
			
		||||
            let providers = table[iter];
 | 
			
		||||
 | 
			
		||||
            // 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;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
Signals.addSignalMethods(ModemCdma.prototype);
 | 
			
		||||
@@ -1,53 +0,0 @@
 | 
			
		||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
 | 
			
		||||
 | 
			
		||||
const DBus = imports.dbus;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
 | 
			
		||||
const ScreenSaverIface = {
 | 
			
		||||
    name: 'org.gnome.ScreenSaver',
 | 
			
		||||
    methods: [{ name: 'GetActive',
 | 
			
		||||
                inSignature: '',
 | 
			
		||||
                outSignature: 'b' },
 | 
			
		||||
              { name: 'Lock',
 | 
			
		||||
                inSignature: '' },
 | 
			
		||||
              { name: 'SetActive',
 | 
			
		||||
                inSignature: 'b' }],
 | 
			
		||||
    signals: [{ name: 'ActiveChanged',
 | 
			
		||||
                inSignature: 'b' }]
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function ScreenSaverProxy() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ScreenSaverProxy.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        DBus.session.proxifyObject(this,
 | 
			
		||||
                                   'org.gnome.ScreenSaver',
 | 
			
		||||
                                   '/org/gnome/ScreenSaver');
 | 
			
		||||
 | 
			
		||||
        DBus.session.watch_name('org.gnome.ScreenSaver',
 | 
			
		||||
                                false, // do not launch a name-owner if none exists
 | 
			
		||||
                                Lang.bind(this, this._onSSAppeared),
 | 
			
		||||
                                Lang.bind(this, this._onSSVanished));
 | 
			
		||||
 | 
			
		||||
        this.screenSaverActive = false;
 | 
			
		||||
        this.connect('ActiveChanged',
 | 
			
		||||
                     Lang.bind(this, this._onActiveChanged));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onSSAppeared: function(owner) {
 | 
			
		||||
        this.GetActiveRemote(Lang.bind(this, function(isActive) {
 | 
			
		||||
            this.screenSaverActive = isActive;
 | 
			
		||||
        }))
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onSSVanished: function(oldOwner) {
 | 
			
		||||
        this.screenSaverActive = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onActiveChanged: function(object, isActive) {
 | 
			
		||||
        this.screenSaverActive = isActive;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
DBus.proxifyPrototype(ScreenSaverProxy.prototype, ScreenSaverIface);
 | 
			
		||||
							
								
								
									
										361
									
								
								js/misc/telepathy.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,361 @@
 | 
			
		||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
 | 
			
		||||
 | 
			
		||||
const DBus = imports.dbus;
 | 
			
		||||
 | 
			
		||||
// D-Bus utils
 | 
			
		||||
function nameToPath(name) {
 | 
			
		||||
    return '/' + name.replace(/\./g, '/');
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function pathToName(path) {
 | 
			
		||||
    if (path[0] != '/')
 | 
			
		||||
        throw new Error('not a D-Bus path: ' + path);
 | 
			
		||||
    return path.substr(1).replace(/\//g, '.');
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// This is tp_escape_as_identifier() from telepathy-glib
 | 
			
		||||
function escapeAsIdentifier(name) {
 | 
			
		||||
    if (!name)
 | 
			
		||||
        return '_';
 | 
			
		||||
 | 
			
		||||
    // first char is replaced with _XX if it's non-alpha,
 | 
			
		||||
    // later chars are replaced with _XX if they're non-alphanumeric
 | 
			
		||||
    if (name.length == 1) {
 | 
			
		||||
        return name.replace(/[^a-zA-Z]/, _hexEscape);
 | 
			
		||||
    } else {
 | 
			
		||||
        return (name[0].replace(/[^a-zA-Z]/, _hexEscape) +
 | 
			
		||||
                name.substring(1).replace(/[^a-zA-Z0-9]/g, _hexEscape));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _hexEscape(ch) {
 | 
			
		||||
    return '_' + ch.charCodeAt(0).toString(16);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Telepathy D-Bus interface definitions. Note that most of these are
 | 
			
		||||
// incomplete, and only cover the methods/properties/signals that
 | 
			
		||||
// we're currently using.
 | 
			
		||||
 | 
			
		||||
const TELEPATHY = 'org.freedesktop.Telepathy';
 | 
			
		||||
 | 
			
		||||
const CLIENT_NAME = TELEPATHY + '.Client';
 | 
			
		||||
const ClientIface = {
 | 
			
		||||
    name: CLIENT_NAME,
 | 
			
		||||
    properties: [
 | 
			
		||||
        { name: 'Interfaces',
 | 
			
		||||
          signature: 'as',
 | 
			
		||||
          access: 'read' }
 | 
			
		||||
    ]
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const CLIENT_APPROVER_NAME = TELEPATHY + '.Client.Approver';
 | 
			
		||||
const ClientApproverIface = {
 | 
			
		||||
    name: CLIENT_APPROVER_NAME,
 | 
			
		||||
    methods: [
 | 
			
		||||
        { name: 'AddDispatchOperation',
 | 
			
		||||
          inSignature: 'a(oa{sv})oa{sv}',
 | 
			
		||||
          outSignature: '' }
 | 
			
		||||
    ],
 | 
			
		||||
    properties: [
 | 
			
		||||
        { name: 'ApproverChannelFilter',
 | 
			
		||||
          signature: 'aa{sv}',
 | 
			
		||||
          access: 'read' }
 | 
			
		||||
    ]
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const CLIENT_HANDLER_NAME = TELEPATHY + '.Client.Handler';
 | 
			
		||||
const ClientHandlerIface = {
 | 
			
		||||
    name: CLIENT_HANDLER_NAME,
 | 
			
		||||
    methods: [
 | 
			
		||||
        { name: 'HandleChannels',
 | 
			
		||||
          inSignature: 'ooa(oa{sv})aota{sv}',
 | 
			
		||||
          outSignature: '' }
 | 
			
		||||
    ],
 | 
			
		||||
    properties: [
 | 
			
		||||
        { name: 'HandlerChannelFilter',
 | 
			
		||||
          signature: 'aa{sv}',
 | 
			
		||||
          access: 'read' }
 | 
			
		||||
    ]
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const CLIENT_OBSERVER_NAME = TELEPATHY + '.Client.Observer';
 | 
			
		||||
const ClientObserverIface = {
 | 
			
		||||
    name: CLIENT_OBSERVER_NAME,
 | 
			
		||||
    methods: [
 | 
			
		||||
        { name: 'ObserveChannels',
 | 
			
		||||
          inSignature: 'ooa(oa{sv})oaoa{sv}',
 | 
			
		||||
          outSignature: '' }
 | 
			
		||||
    ],
 | 
			
		||||
    properties: [
 | 
			
		||||
        { name: 'ObserverChannelFilter',
 | 
			
		||||
          signature: 'aa{sv}',
 | 
			
		||||
          access: 'read' }
 | 
			
		||||
    ]
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const CHANNEL_DISPATCH_OPERATION_NAME = TELEPATHY + '.ChannelDispatchOperation';
 | 
			
		||||
const ChannelDispatchOperationIface = {
 | 
			
		||||
    name: CHANNEL_DISPATCH_OPERATION_NAME,
 | 
			
		||||
    methods: [
 | 
			
		||||
        { name: 'HandleWith',
 | 
			
		||||
          inSignature: 's',
 | 
			
		||||
          outSignature: '' },
 | 
			
		||||
        { name: 'Claim',
 | 
			
		||||
          inSignature: '',
 | 
			
		||||
          outSignature: '' }
 | 
			
		||||
    ]
 | 
			
		||||
};
 | 
			
		||||
let ChannelDispatchOperation = DBus.makeProxyClass(ChannelDispatchOperationIface);
 | 
			
		||||
 | 
			
		||||
const CONNECTION_NAME = TELEPATHY + '.Connection';
 | 
			
		||||
const ConnectionIface = {
 | 
			
		||||
    name: CONNECTION_NAME,
 | 
			
		||||
    signals: [
 | 
			
		||||
        { name: 'StatusChanged',
 | 
			
		||||
          inSignature: 'uu' }
 | 
			
		||||
    ]
 | 
			
		||||
};
 | 
			
		||||
let Connection = DBus.makeProxyClass(ConnectionIface);
 | 
			
		||||
 | 
			
		||||
const ConnectionStatus = {
 | 
			
		||||
    CONNECTED:    0,
 | 
			
		||||
    CONNECTING:   1,
 | 
			
		||||
    DISCONNECTED: 2
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const CONNECTION_ALIASING_NAME = CONNECTION_NAME + '.Interface.Aliasing';
 | 
			
		||||
const ConnectionAliasingIface = {
 | 
			
		||||
    name: CONNECTION_ALIASING_NAME,
 | 
			
		||||
    methods: [
 | 
			
		||||
        { name: 'RequestAliases',
 | 
			
		||||
          inSignature: 'au',
 | 
			
		||||
          outSignature: 'as'
 | 
			
		||||
        }
 | 
			
		||||
    ],
 | 
			
		||||
    signals: [
 | 
			
		||||
        { name: 'AliasesChanged',
 | 
			
		||||
          inSignature: 'a(us)' }
 | 
			
		||||
    ]
 | 
			
		||||
};
 | 
			
		||||
let ConnectionAliasing = DBus.makeProxyClass(ConnectionAliasingIface);
 | 
			
		||||
 | 
			
		||||
const CONNECTION_AVATARS_NAME = CONNECTION_NAME + '.Interface.Avatars';
 | 
			
		||||
const ConnectionAvatarsIface = {
 | 
			
		||||
    name: CONNECTION_AVATARS_NAME,
 | 
			
		||||
    methods: [
 | 
			
		||||
        { name: 'GetKnownAvatarTokens',
 | 
			
		||||
          inSignature: 'au',
 | 
			
		||||
          outSignature: 'a{us}'
 | 
			
		||||
        },
 | 
			
		||||
        { name: 'RequestAvatars',
 | 
			
		||||
          inSignature: 'au',
 | 
			
		||||
          outSignature: ''
 | 
			
		||||
        }
 | 
			
		||||
    ],
 | 
			
		||||
    signals: [
 | 
			
		||||
        { name: 'AvatarRetrieved',
 | 
			
		||||
          inSignature: 'usays'
 | 
			
		||||
        },
 | 
			
		||||
        { name: 'AvatarUpdated',
 | 
			
		||||
          inSignature: 'us'
 | 
			
		||||
        }
 | 
			
		||||
    ]
 | 
			
		||||
};
 | 
			
		||||
let ConnectionAvatars = DBus.makeProxyClass(ConnectionAvatarsIface);
 | 
			
		||||
 | 
			
		||||
const CONNECTION_CONTACTS_NAME = CONNECTION_NAME + '.Interface.Contacts';
 | 
			
		||||
const ConnectionContactsIface = {
 | 
			
		||||
    name: CONNECTION_CONTACTS_NAME,
 | 
			
		||||
    methods: [
 | 
			
		||||
        { name: 'GetContactAttributes',
 | 
			
		||||
          inSignature: 'auasb',
 | 
			
		||||
          outSignature: 'a{ua{sv}}'
 | 
			
		||||
        }
 | 
			
		||||
    ]
 | 
			
		||||
};
 | 
			
		||||
let ConnectionContacts = DBus.makeProxyClass(ConnectionContactsIface);
 | 
			
		||||
 | 
			
		||||
const CONNECTION_REQUESTS_NAME = CONNECTION_NAME + '.Interface.Requests';
 | 
			
		||||
const ConnectionRequestsIface = {
 | 
			
		||||
    name: CONNECTION_REQUESTS_NAME,
 | 
			
		||||
    methods: [
 | 
			
		||||
        { name: 'CreateChannel',
 | 
			
		||||
          inSignature: 'a{sv}',
 | 
			
		||||
          outSignature: 'oa{sv}'
 | 
			
		||||
        },
 | 
			
		||||
        { name: 'EnsureChannel',
 | 
			
		||||
          inSignature: 'a{sv}',
 | 
			
		||||
          outSignature: 'boa{sv}'
 | 
			
		||||
        }
 | 
			
		||||
    ],
 | 
			
		||||
    properties: [
 | 
			
		||||
        { name: 'Channels',
 | 
			
		||||
          signature: 'a(oa{sv})',
 | 
			
		||||
          access: 'read' }
 | 
			
		||||
    ],
 | 
			
		||||
    signals: [
 | 
			
		||||
        { name: 'NewChannels',
 | 
			
		||||
          inSignature: 'a(oa{sv})'
 | 
			
		||||
        },
 | 
			
		||||
        { name: 'ChannelClosed',
 | 
			
		||||
          inSignature: 'o'
 | 
			
		||||
        }
 | 
			
		||||
    ]
 | 
			
		||||
};
 | 
			
		||||
let ConnectionRequests = DBus.makeProxyClass(ConnectionRequestsIface);
 | 
			
		||||
 | 
			
		||||
const CONNECTION_SIMPLE_PRESENCE_NAME = CONNECTION_NAME + '.Interface.SimplePresence';
 | 
			
		||||
const ConnectionSimplePresenceIface = {
 | 
			
		||||
    name: CONNECTION_SIMPLE_PRESENCE_NAME,
 | 
			
		||||
    methods: [
 | 
			
		||||
        { name: 'SetPresence',
 | 
			
		||||
          inSignature: 'ss'
 | 
			
		||||
        },
 | 
			
		||||
        { name: 'GetPresences',
 | 
			
		||||
          inSignature: 'au',
 | 
			
		||||
          outSignature: 'a{u(uss)}'
 | 
			
		||||
        }
 | 
			
		||||
    ],
 | 
			
		||||
    signals: [
 | 
			
		||||
        { name: 'PresencesChanged',
 | 
			
		||||
          inSignature: 'a{u(uss)}' }
 | 
			
		||||
    ]
 | 
			
		||||
};
 | 
			
		||||
let ConnectionSimplePresence = DBus.makeProxyClass(ConnectionSimplePresenceIface);
 | 
			
		||||
 | 
			
		||||
const ConnectionPresenceType = {
 | 
			
		||||
    UNSET:         0,
 | 
			
		||||
    OFFLINE:       1,
 | 
			
		||||
    AVAILABLE:     2,
 | 
			
		||||
    AWAY:          3,
 | 
			
		||||
    EXTENDED_AWAY: 4,
 | 
			
		||||
    HIDDEN:        5,
 | 
			
		||||
    BUSY:          6,
 | 
			
		||||
    UNKNOWN:       7,
 | 
			
		||||
    ERROR:         8
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const HandleType = {
 | 
			
		||||
    NONE:    0,
 | 
			
		||||
    CONTACT: 1,
 | 
			
		||||
    ROOM:    2,
 | 
			
		||||
    LIST:    3,
 | 
			
		||||
    GROUP:   4
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const CHANNEL_NAME = TELEPATHY + '.Channel';
 | 
			
		||||
const ChannelIface = {
 | 
			
		||||
    name: CHANNEL_NAME,
 | 
			
		||||
    signals: [
 | 
			
		||||
        { name: 'Closed',
 | 
			
		||||
          inSignature: '' }
 | 
			
		||||
    ]
 | 
			
		||||
};
 | 
			
		||||
let Channel = DBus.makeProxyClass(ChannelIface);
 | 
			
		||||
 | 
			
		||||
const CHANNEL_TEXT_NAME = CHANNEL_NAME + '.Type.Text';
 | 
			
		||||
const ChannelTextIface = {
 | 
			
		||||
    name: CHANNEL_TEXT_NAME,
 | 
			
		||||
    methods: [
 | 
			
		||||
        { name: 'ListPendingMessages',
 | 
			
		||||
          inSignature: 'b',
 | 
			
		||||
          outSignature: 'a(uuuuus)'
 | 
			
		||||
        },
 | 
			
		||||
        { name: 'AcknowledgePendingMessages',
 | 
			
		||||
          inSignature: 'au',
 | 
			
		||||
          outSignature: ''
 | 
			
		||||
        },
 | 
			
		||||
        { name: 'Send',
 | 
			
		||||
          inSignature: 'us',
 | 
			
		||||
          outSignature: ''
 | 
			
		||||
        }
 | 
			
		||||
    ],
 | 
			
		||||
    signals: [
 | 
			
		||||
        { name: 'Received',
 | 
			
		||||
          inSignature: 'uuuuus' },
 | 
			
		||||
        { name: 'Sent',
 | 
			
		||||
          inSignature: 'uus' }
 | 
			
		||||
    ]
 | 
			
		||||
};
 | 
			
		||||
let ChannelText = DBus.makeProxyClass(ChannelTextIface);
 | 
			
		||||
 | 
			
		||||
const ChannelTextMessageType = {
 | 
			
		||||
    NORMAL: 0,
 | 
			
		||||
    ACTION: 1,
 | 
			
		||||
    NOTICE: 2,
 | 
			
		||||
    AUTO_REPLY: 3,
 | 
			
		||||
    DELIVERY_REPORT: 4
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const CHANNEL_CONTACT_LIST_NAME = CHANNEL_NAME + '.Type.ContactList';
 | 
			
		||||
// There is no interface associated with ContactList; it's just a
 | 
			
		||||
// special kind of Channel.Interface.Group
 | 
			
		||||
 | 
			
		||||
const CHANNEL_GROUP_NAME = CHANNEL_NAME + '.Interface.Group';
 | 
			
		||||
const ChannelGroupIface = {
 | 
			
		||||
    name: CHANNEL_GROUP_NAME,
 | 
			
		||||
    properties: [
 | 
			
		||||
        { name: 'Members',
 | 
			
		||||
          signature: 'au',
 | 
			
		||||
          access: 'read' }
 | 
			
		||||
    ],
 | 
			
		||||
    signals: [
 | 
			
		||||
        { name: 'MembersChanged',
 | 
			
		||||
          inSignature: 'sauauauauuu' }
 | 
			
		||||
    ]
 | 
			
		||||
};
 | 
			
		||||
let ChannelGroup = DBus.makeProxyClass(ChannelGroupIface);
 | 
			
		||||
 | 
			
		||||
const ACCOUNT_MANAGER_NAME = TELEPATHY + '.AccountManager';
 | 
			
		||||
const AccountManagerIface = {
 | 
			
		||||
    name: ACCOUNT_MANAGER_NAME,
 | 
			
		||||
    properties: [
 | 
			
		||||
        { name: 'ValidAccounts',
 | 
			
		||||
          signature: 'ao',
 | 
			
		||||
          access: 'read' }
 | 
			
		||||
    ],
 | 
			
		||||
    signals: [
 | 
			
		||||
        { name: 'AccountValidityChanged',
 | 
			
		||||
          inSignature: 'ob' }
 | 
			
		||||
    ]
 | 
			
		||||
};
 | 
			
		||||
let AccountManager = DBus.makeProxyClass(AccountManagerIface);
 | 
			
		||||
 | 
			
		||||
const ACCOUNT_NAME = TELEPATHY + '.Account';
 | 
			
		||||
const AccountIface = {
 | 
			
		||||
    name: ACCOUNT_NAME,
 | 
			
		||||
    properties: [
 | 
			
		||||
        { name: 'Connection',
 | 
			
		||||
          signature: 'o',
 | 
			
		||||
          access: 'read' }
 | 
			
		||||
    ]
 | 
			
		||||
};
 | 
			
		||||
let Account = DBus.makeProxyClass(AccountIface);
 | 
			
		||||
 | 
			
		||||
const CHANNEL_DISPATCHER_NAME = TELEPATHY + '.ChannelDispatcher';
 | 
			
		||||
const ChannelDispatcherIface = {
 | 
			
		||||
    name: CHANNEL_DISPATCHER_NAME,
 | 
			
		||||
    methods: [
 | 
			
		||||
        { name: 'EnsureChannel',
 | 
			
		||||
          inSignature: 'oa{sv}xs',
 | 
			
		||||
          outSignature: 'o' }
 | 
			
		||||
    ]
 | 
			
		||||
};
 | 
			
		||||
let ChannelDispatcher = DBus.makeProxyClass(ChannelDispatcherIface);
 | 
			
		||||
 | 
			
		||||
const CHANNEL_REQUEST_NAME = TELEPATHY + '.ChannelRequest';
 | 
			
		||||
const ChannelRequestIface = {
 | 
			
		||||
    name: CHANNEL_REQUEST_NAME,
 | 
			
		||||
    methods: [
 | 
			
		||||
        { name: 'Proceed',
 | 
			
		||||
          inSignature: '',
 | 
			
		||||
          outSignature: '' }
 | 
			
		||||
    ],
 | 
			
		||||
    signals: [
 | 
			
		||||
        { name: 'Failed',
 | 
			
		||||
          signature: 'ss' },
 | 
			
		||||
        { name: 'Succeeded',
 | 
			
		||||
          signature: '' }
 | 
			
		||||
    ]
 | 
			
		||||
};
 | 
			
		||||
let ChannelRequest = DBus.makeProxyClass(ChannelRequestIface);
 | 
			
		||||
							
								
								
									
										210
									
								
								js/misc/util.js
									
									
									
									
									
								
							
							
						
						@@ -1,210 +0,0 @@
 | 
			
		||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
 | 
			
		||||
 | 
			
		||||
const Gdk = imports.gi.Gdk;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
 | 
			
		||||
/* http://daringfireball.net/2010/07/improved_regex_for_matching_urls */
 | 
			
		||||
const _urlRegexp = new RegExp('\\b(([a-z][\\w-]+:(/{1,3}|[a-z0-9%])|www\\d{0,3}[.]|[a-z0-9.\\-]+[.][a-z]{2,4}/)([^\\s()<>]+|\\(([^\\s()<>]+|(\\([^\\s()<>]+\\)))*\\))+(\\(([^\\s()<>]+|(\\([^\\s()<>]+\\)))*\\)|[^\\s`!()\\[\\]{};:\'\\".,<>?«»“”‘’]))', 'gi');
 | 
			
		||||
 | 
			
		||||
// findUrls:
 | 
			
		||||
// @str: string to find URLs in
 | 
			
		||||
//
 | 
			
		||||
// Searches @str for URLs and returns an array of objects with %url
 | 
			
		||||
// properties showing the matched URL string, and %pos properties indicating
 | 
			
		||||
// the position within @str where the URL was found.
 | 
			
		||||
//
 | 
			
		||||
// Return value: the list of match objects, as described above
 | 
			
		||||
function findUrls(str) {
 | 
			
		||||
    let res = [], match;
 | 
			
		||||
    while ((match = _urlRegexp.exec(str)))
 | 
			
		||||
        res.push({ url: match[0], pos: match.index });
 | 
			
		||||
    return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// spawn:
 | 
			
		||||
// @argv: an argv array
 | 
			
		||||
//
 | 
			
		||||
// Runs @argv in the background, handling any errors that occur
 | 
			
		||||
// when trying to start the program.
 | 
			
		||||
function spawn(argv) {
 | 
			
		||||
    try {
 | 
			
		||||
        trySpawn(argv);
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
        _handleSpawnError(argv[0], err);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// spawnCommandLine:
 | 
			
		||||
// @command_line: a command line
 | 
			
		||||
//
 | 
			
		||||
// Runs @command_line in the background, handling any errors that
 | 
			
		||||
// occur when trying to parse or start the program.
 | 
			
		||||
function spawnCommandLine(command_line) {
 | 
			
		||||
    try {
 | 
			
		||||
        let [success, argv] = GLib.shell_parse_argv(command_line);
 | 
			
		||||
        trySpawn(argv);
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
        _handleSpawnError(command_line, err);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// trySpawn:
 | 
			
		||||
// @argv: an argv array
 | 
			
		||||
//
 | 
			
		||||
// Runs @argv in the background. If launching @argv fails,
 | 
			
		||||
// this will throw an error.
 | 
			
		||||
function trySpawn(argv)
 | 
			
		||||
{
 | 
			
		||||
    try {
 | 
			
		||||
        GLib.spawn_async(null, argv, null,
 | 
			
		||||
                         GLib.SpawnFlags.SEARCH_PATH,
 | 
			
		||||
                         null, null);
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
        if (err.code == GLib.SpawnError.G_SPAWN_ERROR_NOENT) {
 | 
			
		||||
            err.message = _("Command not found");
 | 
			
		||||
        } else {
 | 
			
		||||
            // The exception from gjs contains an error string like:
 | 
			
		||||
            //   Error invoking GLib.spawn_command_line_async: Failed to
 | 
			
		||||
            //   execute child process "foo" (No such file or directory)
 | 
			
		||||
            // We are only interested in the part in the parentheses. (And
 | 
			
		||||
            // we can't pattern match the text, since it gets localized.)
 | 
			
		||||
            err.message = err.message.replace(/.*\((.+)\)/, '$1');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        throw err;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// trySpawnCommandLine:
 | 
			
		||||
// @command_line: a command line
 | 
			
		||||
//
 | 
			
		||||
// Runs @command_line in the background. If launching @command_line
 | 
			
		||||
// fails, this will throw an error.
 | 
			
		||||
function trySpawnCommandLine(command_line) {
 | 
			
		||||
    let success, argv;
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        [success, argv] = GLib.shell_parse_argv(command_line);
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
        // Replace "Error invoking GLib.shell_parse_argv: " with
 | 
			
		||||
        // something nicer
 | 
			
		||||
        err.message = err.message.replace(/[^:]*: /, _("Could not parse command:") + "\n");
 | 
			
		||||
        throw err;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    trySpawn(argv);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _handleSpawnError(command, err) {
 | 
			
		||||
    let title = _("Execution of '%s' failed:").format(command);
 | 
			
		||||
    Main.notifyError(title, err.message);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// killall:
 | 
			
		||||
// @processName: a process name
 | 
			
		||||
//
 | 
			
		||||
// Kills @processName. If no process with the given name is found,
 | 
			
		||||
// this will fail silently.
 | 
			
		||||
function killall(processName) {
 | 
			
		||||
    try {
 | 
			
		||||
        // pkill is more portable than killall, but on Linux at least
 | 
			
		||||
        // it won't match if you pass more than 15 characters of the
 | 
			
		||||
        // process name... However, if you use the '-f' flag to match
 | 
			
		||||
        // the entire command line, it will work, but we have to be
 | 
			
		||||
        // careful in that case that we can match
 | 
			
		||||
        // '/usr/bin/processName' but not 'gedit processName.c' or
 | 
			
		||||
        // whatever...
 | 
			
		||||
 | 
			
		||||
        let argv = ['pkill', '-f', '^([^ ]*/)?' + processName + '($| )'];
 | 
			
		||||
        GLib.spawn_sync(null, argv, null, GLib.SpawnFlags.SEARCH_PATH, null, null);
 | 
			
		||||
        // It might be useful to return success/failure, but we'd need
 | 
			
		||||
        // a wrapper around WIFEXITED and WEXITSTATUS. Since none of
 | 
			
		||||
        // the current callers care, we don't bother.
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
        logError(e, 'Failed to kill ' + processName);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// This was ported from network-manager-applet
 | 
			
		||||
// Copyright 2007 - 2011 Red Hat, Inc.
 | 
			
		||||
// Author: Dan Williams <dcbw@redhat.com>
 | 
			
		||||
 | 
			
		||||
const _IGNORED_WORDS = [
 | 
			
		||||
        'Semiconductor',
 | 
			
		||||
        'Components',
 | 
			
		||||
        'Corporation',
 | 
			
		||||
        'Communications',
 | 
			
		||||
        'Company',
 | 
			
		||||
        'Corp.',
 | 
			
		||||
        'Corp',
 | 
			
		||||
        'Co.',
 | 
			
		||||
        'Inc.',
 | 
			
		||||
        'Inc',
 | 
			
		||||
        'Incorporated',
 | 
			
		||||
        'Ltd.',
 | 
			
		||||
        'Limited.',
 | 
			
		||||
        'Intel',
 | 
			
		||||
        'chipset',
 | 
			
		||||
        'adapter',
 | 
			
		||||
        '[hex]',
 | 
			
		||||
        'NDIS',
 | 
			
		||||
        'Module'
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
const _IGNORED_PHRASES = [
 | 
			
		||||
        'Multiprotocol MAC/baseband processor',
 | 
			
		||||
        'Wireless LAN Controller',
 | 
			
		||||
        'Wireless LAN Adapter',
 | 
			
		||||
        'Wireless Adapter',
 | 
			
		||||
        'Network Connection',
 | 
			
		||||
        'Wireless Cardbus Adapter',
 | 
			
		||||
        'Wireless CardBus Adapter',
 | 
			
		||||
        '54 Mbps Wireless PC Card',
 | 
			
		||||
        'Wireless PC Card',
 | 
			
		||||
        'Wireless PC',
 | 
			
		||||
        'PC Card with XJACK(r) Antenna',
 | 
			
		||||
        'Wireless cardbus',
 | 
			
		||||
        'Wireless LAN PC Card',
 | 
			
		||||
        'Technology Group Ltd.',
 | 
			
		||||
        'Communication S.p.A.',
 | 
			
		||||
        'Business Mobile Networks BV',
 | 
			
		||||
        'Mobile Broadband Minicard Composite Device',
 | 
			
		||||
        'Mobile Communications AB',
 | 
			
		||||
        '(PC-Suite Mode)'
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
function fixupPCIDescription(desc) {
 | 
			
		||||
    desc = desc.replace(/[_,]/, ' ');
 | 
			
		||||
 | 
			
		||||
    /* Attempt to shorten ID by ignoring certain phrases */
 | 
			
		||||
    for (let i = 0; i < _IGNORED_PHRASES.length; i++) {
 | 
			
		||||
        let item = _IGNORED_PHRASES[i];
 | 
			
		||||
        let pos = desc.indexOf(item);
 | 
			
		||||
        if (pos != -1) {
 | 
			
		||||
            let before = desc.substring(0, pos);
 | 
			
		||||
            let after = desc.substring(pos + item.length, desc.length);
 | 
			
		||||
            desc = before + after;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Attmept to shorten ID by ignoring certain individual words */
 | 
			
		||||
    let words = desc.split(' ');
 | 
			
		||||
    let out = [ ];
 | 
			
		||||
    for (let i = 0; i < words.length; i++) {
 | 
			
		||||
        let item = words[i];
 | 
			
		||||
 | 
			
		||||
        // skip empty items (that come out from consecutive spaces)
 | 
			
		||||
        if (item.length == 0)
 | 
			
		||||
            continue;
 | 
			
		||||
 | 
			
		||||
        if (_IGNORED_WORDS.indexOf(item) == -1) {
 | 
			
		||||
            out.push(item);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return out.join(' ');
 | 
			
		||||
}
 | 
			
		||||
@@ -21,77 +21,26 @@ let METRICS = {
 | 
			
		||||
    overviewFpsSubsequent:
 | 
			
		||||
    { description: "Frames rate when going to the overview, second time",
 | 
			
		||||
      units: "frames / s" },
 | 
			
		||||
    overviewFps5Windows:
 | 
			
		||||
    { description: "Frames rate when going to the overview, 5 windows open",
 | 
			
		||||
      units: "frames / s" },
 | 
			
		||||
    overviewFps10Windows:
 | 
			
		||||
    { description: "Frames rate when going to the overview, 10 windows open",
 | 
			
		||||
      units: "frames / s" },
 | 
			
		||||
    overviewFps5Maximized:
 | 
			
		||||
    { description: "Frames rate when going to the overview, 5 maximized windows open",
 | 
			
		||||
      units: "frames / s" },
 | 
			
		||||
    overviewFps10Maximized:
 | 
			
		||||
    { description: "Frames rate when going to the overview, 10 maximized windows open",
 | 
			
		||||
      units: "frames / s" },
 | 
			
		||||
    overviewFps5Alpha:
 | 
			
		||||
    { description: "Frames rate when going to the overview, 5 alpha-transparent windows open",
 | 
			
		||||
      units: "frames / s" },
 | 
			
		||||
    overviewFps10Alpha:
 | 
			
		||||
    { description: "Frames rate when going to the overview, 10 alpha-transparent windows open",
 | 
			
		||||
      units: "frames / s" },
 | 
			
		||||
    usedAfterOverview:
 | 
			
		||||
    { description: "Malloc'ed bytes after the overview is shown once",
 | 
			
		||||
      units: "B" },
 | 
			
		||||
    leakedAfterOverview:
 | 
			
		||||
    { description: "Additional malloc'ed bytes the second time the overview is shown",
 | 
			
		||||
      units: "B" },
 | 
			
		||||
    applicationsShowTimeFirst:
 | 
			
		||||
    { description: "Time to switch to applications view, first time",
 | 
			
		||||
      units: "us" },
 | 
			
		||||
    applicationsShowTimeSubsequent:
 | 
			
		||||
    { description: "Time to switch to applications view, second time",
 | 
			
		||||
      units: "us"}
 | 
			
		||||
      units: "B" }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
let WINDOW_CONFIGS = [
 | 
			
		||||
    { width: 640, height: 480, alpha: false, maximized: false, count: 1,  metric: 'overviewFpsSubsequent' },
 | 
			
		||||
    { width: 640, height: 480, alpha: false, maximized: false, count: 5,  metric: 'overviewFps5Windows'  },
 | 
			
		||||
    { width: 640, height: 480, alpha: false, maximized: false, count: 10, metric: 'overviewFps10Windows'  },
 | 
			
		||||
    { width: 640, height: 480, alpha: false, maximized: true,  count: 5,  metric: 'overviewFps5Maximized' },
 | 
			
		||||
    { width: 640, height: 480, alpha: false, maximized: true,  count: 10, metric: 'overviewFps10Maximized' },
 | 
			
		||||
    { width: 640, height: 480, alpha: true,  maximized: false, count: 5,  metric: 'overviewFps5Alpha' },
 | 
			
		||||
    { width: 640, height: 480, alpha: true,  maximized: false, count: 10, metric: 'overviewFps10Alpha' }
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
function run() {
 | 
			
		||||
    Scripting.defineScriptEvent("overviewShowStart", "Starting to show the overview");
 | 
			
		||||
    Scripting.defineScriptEvent("overviewShowDone", "Overview finished showing");
 | 
			
		||||
    Scripting.defineScriptEvent("afterShowHide", "After a show/hide cycle for the overview");
 | 
			
		||||
    Scripting.defineScriptEvent("applicationsShowStart", "Starting to switch to applications view");
 | 
			
		||||
    Scripting.defineScriptEvent("applicationsShowDone", "Done switching to applications view");
 | 
			
		||||
 | 
			
		||||
    Main.overview.connect('shown', function() {
 | 
			
		||||
                              Scripting.scriptEvent('overviewShowDone');
 | 
			
		||||
                          });
 | 
			
		||||
 | 
			
		||||
    yield Scripting.sleep(1000);
 | 
			
		||||
 | 
			
		||||
    for (let i = 0; i < 2 * WINDOW_CONFIGS.length; i++) {
 | 
			
		||||
        // We go to the overview twice for each configuration; the first time
 | 
			
		||||
        // to calculate the mipmaps for the windows, the second time to get
 | 
			
		||||
        // a clean set of numbers.
 | 
			
		||||
        if ((i % 2) == 0) {
 | 
			
		||||
            let config = WINDOW_CONFIGS[i / 2];
 | 
			
		||||
            yield Scripting.destroyTestWindows();
 | 
			
		||||
 | 
			
		||||
            for (let k = 0; k < config.count; k++)
 | 
			
		||||
                yield Scripting.createTestWindow(config.width, config.height, config.alpha, config.maximized);
 | 
			
		||||
 | 
			
		||||
            yield Scripting.waitTestWindows();
 | 
			
		||||
            yield Scripting.sleep(1000);
 | 
			
		||||
            yield Scripting.waitLeisure();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    yield Scripting.waitLeisure();
 | 
			
		||||
    for (let i = 0; i < 2; i++) {
 | 
			
		||||
        Scripting.scriptEvent('overviewShowStart');
 | 
			
		||||
        Main.overview.show();
 | 
			
		||||
 | 
			
		||||
@@ -104,21 +53,6 @@ function run() {
 | 
			
		||||
        Scripting.collectStatistics();
 | 
			
		||||
        Scripting.scriptEvent('afterShowHide');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    yield Scripting.destroyTestWindows();
 | 
			
		||||
    yield Scripting.sleep(1000);
 | 
			
		||||
 | 
			
		||||
    Main.overview.show();
 | 
			
		||||
    yield Scripting.waitLeisure();
 | 
			
		||||
 | 
			
		||||
    for (let i = 0; i < 2; i++) {
 | 
			
		||||
        Scripting.scriptEvent('applicationsShowStart');
 | 
			
		||||
        Main.overview.viewSelector.switchTab('applications');
 | 
			
		||||
        yield Scripting.waitLeisure();
 | 
			
		||||
        Scripting.scriptEvent('applicationsShowDone');
 | 
			
		||||
        Main.overview.viewSelector.switchTab('windows');
 | 
			
		||||
        yield Scripting.waitLeisure();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
let showingOverview = false;
 | 
			
		||||
@@ -130,8 +64,6 @@ let mallocUsedSize = 0;
 | 
			
		||||
let overviewShowCount = 0;
 | 
			
		||||
let firstOverviewUsedSize;
 | 
			
		||||
let haveSwapComplete = false;
 | 
			
		||||
let applicationsShowStart;
 | 
			
		||||
let applicationsShowCount = 0;
 | 
			
		||||
 | 
			
		||||
function script_overviewShowStart(time) {
 | 
			
		||||
    showingOverview = true;
 | 
			
		||||
@@ -147,18 +79,6 @@ function script_overviewShowDone(time) {
 | 
			
		||||
    finishedShowingOverview = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function script_applicationsShowStart(time) {
 | 
			
		||||
    applicationsShowStart = time;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function script_applicationsShowDone(time) {
 | 
			
		||||
    applicationsShowCount++;
 | 
			
		||||
    if (applicationsShowCount == 1)
 | 
			
		||||
        METRICS.applicationsShowTimeFirst.value = time - applicationsShowStart;
 | 
			
		||||
    else
 | 
			
		||||
        METRICS.applicationsShowTimeSubsequent.value = time - applicationsShowStart;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function script_afterShowHide(time) {
 | 
			
		||||
    if (overviewShowCount == 1) {
 | 
			
		||||
        METRICS.usedAfterOverview.value = mallocUsedSize;
 | 
			
		||||
@@ -193,15 +113,9 @@ function _frameDone(time) {
 | 
			
		||||
        if (overviewShowCount == 1) {
 | 
			
		||||
            METRICS.overviewLatencyFirst.value = overviewLatency;
 | 
			
		||||
            METRICS.overviewFpsFirst.value = fps;
 | 
			
		||||
        } else if (overviewShowCount == 2) {
 | 
			
		||||
        } else {
 | 
			
		||||
            METRICS.overviewLatencySubsequent.value = overviewLatency;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Other than overviewFpsFirst, we collect FPS metrics the second
 | 
			
		||||
        // we show each window configuration. overviewShowCount is 1,2,3...
 | 
			
		||||
        if (overviewShowCount % 2 == 0) {
 | 
			
		||||
            let config = WINDOW_CONFIGS[(overviewShowCount / 2) - 1];
 | 
			
		||||
            METRICS[config.metric].value = fps;
 | 
			
		||||
            METRICS.overviewFpsSubsequent.value = fps;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										97
									
								
								js/prefs/clockPreferences.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,97 @@
 | 
			
		||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
 | 
			
		||||
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Gtk = imports.gi.Gtk;
 | 
			
		||||
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
 | 
			
		||||
const Gettext = imports.gettext;
 | 
			
		||||
 | 
			
		||||
const FORMAT_KEY       = 'format';
 | 
			
		||||
const SHOW_DATE_KEY    = 'show-date';
 | 
			
		||||
const SHOW_SECONDS_KEY = 'show-seconds';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
function ClockPreferences(uiFile) {
 | 
			
		||||
    this._init(uiFile);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
ClockPreferences.prototype = {
 | 
			
		||||
    _init: function(uiFile) {
 | 
			
		||||
        let builder = new Gtk.Builder();
 | 
			
		||||
        builder.add_from_file(uiFile);
 | 
			
		||||
 | 
			
		||||
        this._dialog = builder.get_object('prefs-dialog');
 | 
			
		||||
        this._dialog.connect('response', Lang.bind(this, this._onResponse));
 | 
			
		||||
 | 
			
		||||
        this._12hrRadio = builder.get_object('12hr_radio');
 | 
			
		||||
        this._24hrRadio = builder.get_object('24hr_radio');
 | 
			
		||||
        this._dateCheck = builder.get_object('date_check');
 | 
			
		||||
        this._secondsCheck = builder.get_object('seconds_check');
 | 
			
		||||
 | 
			
		||||
        delete builder;
 | 
			
		||||
 | 
			
		||||
        this._settings = new Gio.Settings({ schema: 'org.gnome.shell.clock' });
 | 
			
		||||
        this._notifyId = this._settings.connect('changed',
 | 
			
		||||
                                                Lang.bind(this,
 | 
			
		||||
                                                          this._updateDialog));
 | 
			
		||||
 | 
			
		||||
        this._12hrRadio.connect('toggled', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                let format = this._12hrRadio.active ? '12-hour' : '24-hour';
 | 
			
		||||
                this._settings.set_string(FORMAT_KEY, format);
 | 
			
		||||
            }));
 | 
			
		||||
        this._dateCheck.connect('toggled', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                this._settings.set_boolean(SHOW_DATE_KEY,
 | 
			
		||||
                                           this._dateCheck.active);
 | 
			
		||||
            }));
 | 
			
		||||
        this._secondsCheck.connect('toggled', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                this._settings.set_boolean(SHOW_SECONDS_KEY,
 | 
			
		||||
                                           this._secondsCheck.active);
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
        this._updateDialog();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    show: function() {
 | 
			
		||||
        this._dialog.show_all();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateDialog: function() {
 | 
			
		||||
        let format = this._settings.get_string(FORMAT_KEY);
 | 
			
		||||
        this._12hrRadio.active = (format == "12-hour");
 | 
			
		||||
        this._24hrRadio.active = (format == "24-hour");
 | 
			
		||||
 | 
			
		||||
        this._dateCheck.active = this._settings.get_boolean(SHOW_DATE_KEY);
 | 
			
		||||
        this._secondsCheck.active = this._settings.get_boolean(SHOW_SECONDS_KEY);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onResponse: function() {
 | 
			
		||||
        this._dialog.destroy();
 | 
			
		||||
        this._settings.disconnect(this._notifyId);
 | 
			
		||||
        this.emit('destroy');
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
Signals.addSignalMethods(ClockPreferences.prototype);
 | 
			
		||||
 | 
			
		||||
function main(params) {
 | 
			
		||||
    if ('progName' in params)
 | 
			
		||||
        GLib.set_prgname(params['progName']);
 | 
			
		||||
    if ('localeDir' in params)
 | 
			
		||||
        Gettext.bindtextdomain('gnome-shell', params['localeDir']);
 | 
			
		||||
 | 
			
		||||
    Gtk.init(null, null);
 | 
			
		||||
 | 
			
		||||
    let clockPrefs = new ClockPreferences(params['uiFile']);
 | 
			
		||||
    clockPrefs.connect('destroy',
 | 
			
		||||
                       function() {
 | 
			
		||||
                           Gtk.main_quit();
 | 
			
		||||
                       });
 | 
			
		||||
    clockPrefs.show();
 | 
			
		||||
 | 
			
		||||
    Gtk.main();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										318
									
								
								js/ui/altTab.js
									
									
									
									
									
								
							
							
						
						@@ -4,7 +4,6 @@ const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Gdk = imports.gi.Gdk;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Mainloop = imports.mainloop;
 | 
			
		||||
const Meta = imports.gi.Meta;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
@@ -14,10 +13,7 @@ const Tweener = imports.ui.tweener;
 | 
			
		||||
 | 
			
		||||
const POPUP_APPICON_SIZE = 96;
 | 
			
		||||
const POPUP_SCROLL_TIME = 0.10; // seconds
 | 
			
		||||
const POPUP_FADE_IN_TIME = 0.4; // seconds
 | 
			
		||||
const POPUP_FADE_OUT_TIME = 0.1; // seconds
 | 
			
		||||
 | 
			
		||||
const APP_ICON_HOVER_TIMEOUT = 200; // milliseconds
 | 
			
		||||
const POPUP_FADE_TIME = 0.1; // seconds
 | 
			
		||||
 | 
			
		||||
const DISABLE_HOVER_TIMEOUT = 500; // milliseconds
 | 
			
		||||
 | 
			
		||||
@@ -38,8 +34,7 @@ function AltTabPopup() {
 | 
			
		||||
AltTabPopup.prototype = {
 | 
			
		||||
    _init : function() {
 | 
			
		||||
        this.actor = new Shell.GenericContainer({ name: 'altTabPopup',
 | 
			
		||||
                                                  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));
 | 
			
		||||
@@ -54,8 +49,6 @@ AltTabPopup.prototype = {
 | 
			
		||||
        this._thumbnailTimeoutId = 0;
 | 
			
		||||
        this._motionTimeoutId = 0;
 | 
			
		||||
 | 
			
		||||
        this.thumbnailsVisible = false;
 | 
			
		||||
 | 
			
		||||
        // Initially disable hover so we ignore the enter-event if
 | 
			
		||||
        // the switcher appears underneath the current pointer location
 | 
			
		||||
        this._disableHover();
 | 
			
		||||
@@ -75,7 +68,7 @@ AltTabPopup.prototype = {
 | 
			
		||||
 | 
			
		||||
    _allocate: function (actor, box, flags) {
 | 
			
		||||
        let childBox = new Clutter.ActorBox();
 | 
			
		||||
        let primary = Main.layoutManager.primaryMonitor;
 | 
			
		||||
        let primary = global.get_primary_monitor();
 | 
			
		||||
 | 
			
		||||
        let leftPadding = this.actor.get_theme_node().get_padding(St.Side.LEFT);
 | 
			
		||||
        let rightPadding = this.actor.get_theme_node().get_padding(St.Side.RIGHT);
 | 
			
		||||
@@ -88,7 +81,7 @@ AltTabPopup.prototype = {
 | 
			
		||||
        let [childMinHeight, childNaturalHeight] = this._appSwitcher.actor.get_preferred_height(primary.width - hPadding);
 | 
			
		||||
        let [childMinWidth, childNaturalWidth] = this._appSwitcher.actor.get_preferred_width(childNaturalHeight);
 | 
			
		||||
        childBox.x1 = Math.max(primary.x + leftPadding, primary.x + Math.floor((primary.width - childNaturalWidth) / 2));
 | 
			
		||||
        childBox.x2 = Math.min(primary.x + primary.width - rightPadding, childBox.x1 + childNaturalWidth);
 | 
			
		||||
        childBox.x2 = Math.min(childBox.x1 + primary.width - hPadding, childBox.x1 + childNaturalWidth);
 | 
			
		||||
        childBox.y1 = primary.y + Math.floor((primary.height - childNaturalHeight) / 2);
 | 
			
		||||
        childBox.y2 = childBox.y1 + childNaturalHeight;
 | 
			
		||||
        this._appSwitcher.actor.allocate(childBox, flags);
 | 
			
		||||
@@ -98,6 +91,8 @@ AltTabPopup.prototype = {
 | 
			
		||||
        // those calculations
 | 
			
		||||
        if (this._thumbnails) {
 | 
			
		||||
            let icon = this._appIcons[this._currentApp].actor;
 | 
			
		||||
            // Force a stage relayout to make sure we get the correct position
 | 
			
		||||
            global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, 0, 0);
 | 
			
		||||
            let [posX, posY] = icon.get_transformed_position();
 | 
			
		||||
            let thumbnailCenter = posX + icon.width / 2;
 | 
			
		||||
            let [childMinWidth, childNaturalWidth] = this._thumbnails.actor.get_preferred_width(-1);
 | 
			
		||||
@@ -120,7 +115,7 @@ AltTabPopup.prototype = {
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    show : function(backward, binding) {
 | 
			
		||||
    show : function(backward) {
 | 
			
		||||
        let tracker = Shell.WindowTracker.get_default();
 | 
			
		||||
        let apps = tracker.get_running_apps ('');
 | 
			
		||||
 | 
			
		||||
@@ -131,45 +126,44 @@ AltTabPopup.prototype = {
 | 
			
		||||
            return false;
 | 
			
		||||
        this._haveModal = true;
 | 
			
		||||
 | 
			
		||||
        this.actor.connect('key-press-event', Lang.bind(this, this._keyPressEvent));
 | 
			
		||||
        this.actor.connect('key-release-event', Lang.bind(this, this._keyReleaseEvent));
 | 
			
		||||
        this._keyPressEventId = global.stage.connect('key-press-event', Lang.bind(this, this._keyPressEvent));
 | 
			
		||||
        this._keyReleaseEventId = global.stage.connect('key-release-event', Lang.bind(this, this._keyReleaseEvent));
 | 
			
		||||
 | 
			
		||||
        this.actor.connect('button-press-event', Lang.bind(this, this._clickedOutside));
 | 
			
		||||
        this.actor.connect('scroll-event', Lang.bind(this, this._onScroll));
 | 
			
		||||
 | 
			
		||||
        this._appSwitcher = new AppSwitcher(apps, this);
 | 
			
		||||
        this._appSwitcher = new AppSwitcher(apps);
 | 
			
		||||
        this.actor.add_actor(this._appSwitcher.actor);
 | 
			
		||||
        this._appSwitcher.connect('item-activated', Lang.bind(this, this._appActivated));
 | 
			
		||||
        this._appSwitcher.connect('item-entered', Lang.bind(this, this._appEntered));
 | 
			
		||||
 | 
			
		||||
        this._appIcons = this._appSwitcher.icons;
 | 
			
		||||
 | 
			
		||||
        // Need to force an allocation so we can figure out whether we
 | 
			
		||||
        // need to scroll when selecting
 | 
			
		||||
        this.actor.opacity = 0;
 | 
			
		||||
        this.actor.show();
 | 
			
		||||
        this.actor.get_allocation_box();
 | 
			
		||||
 | 
			
		||||
        // Make the initial selection
 | 
			
		||||
        if (binding == 'switch_group') {
 | 
			
		||||
            if (backward) {
 | 
			
		||||
                this._select(0, this._appIcons[0].cachedWindows.length - 1);
 | 
			
		||||
            } else {
 | 
			
		||||
                if (this._appIcons[0].cachedWindows.length > 1)
 | 
			
		||||
                    this._select(0, 1);
 | 
			
		||||
                else
 | 
			
		||||
                    this._select(0, 0);
 | 
			
		||||
            }
 | 
			
		||||
        } else if (binding == 'switch_group_backward') {
 | 
			
		||||
            this._select(0, this._appIcons[0].cachedWindows.length - 1);
 | 
			
		||||
        } else if (binding == 'switch_windows_backward') {
 | 
			
		||||
            this._select(this._appIcons.length - 1);
 | 
			
		||||
        } else if (this._appIcons.length == 1) {
 | 
			
		||||
            this._select(0);
 | 
			
		||||
        if (this._appIcons.length == 1) {
 | 
			
		||||
            if (!backward && this._appIcons[0].cachedWindows.length > 1) {
 | 
			
		||||
                // For compatibility with the multi-app case below
 | 
			
		||||
                this._select(0, 1, true);
 | 
			
		||||
            } else
 | 
			
		||||
                this._select(0);
 | 
			
		||||
        } else if (backward) {
 | 
			
		||||
            this._select(this._appIcons.length - 1);
 | 
			
		||||
        } else {
 | 
			
		||||
            this._select(1);
 | 
			
		||||
            let firstWindows = this._appIcons[0].cachedWindows;
 | 
			
		||||
            if (firstWindows.length > 1) {
 | 
			
		||||
                let curAppNextWindow = firstWindows[1];
 | 
			
		||||
                let nextAppWindow = this._appIcons[1].cachedWindows[0];
 | 
			
		||||
 | 
			
		||||
                // If the next window of the current app is more-recently-used
 | 
			
		||||
                // than the first window of the next app, then select it.
 | 
			
		||||
                if (curAppNextWindow.get_workspace() == global.screen.get_active_workspace() &&
 | 
			
		||||
                    curAppNextWindow.get_user_time() > nextAppWindow.get_user_time())
 | 
			
		||||
                    this._select(0, 1, true);
 | 
			
		||||
                else
 | 
			
		||||
                    this._select(1);
 | 
			
		||||
            } else {
 | 
			
		||||
                this._select(1);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // There's a race condition; if the user released Alt before
 | 
			
		||||
@@ -183,15 +177,12 @@ AltTabPopup.prototype = {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Using easeInOutExpo over 400ms gives us 150ms of "nearly
 | 
			
		||||
        // invisible" (less than 10% opacity), followed by a 100ms
 | 
			
		||||
        // tween in (to 90% opacity, with the last 10% coming over the
 | 
			
		||||
        // next 150ms). So if the user releases Alt quickly after we
 | 
			
		||||
        // start tweening, they'll never see the switcher.
 | 
			
		||||
        this.actor.opacity = 0;
 | 
			
		||||
        this.actor.show();
 | 
			
		||||
        Tweener.addTween(this.actor,
 | 
			
		||||
                         { opacity: 255,
 | 
			
		||||
                           time: POPUP_FADE_IN_TIME,
 | 
			
		||||
                           transition: 'easeInOutExpo'
 | 
			
		||||
                           time: POPUP_FADE_TIME,
 | 
			
		||||
                           transition: 'easeOutQuad'
 | 
			
		||||
                         });
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
@@ -221,31 +212,42 @@ AltTabPopup.prototype = {
 | 
			
		||||
 | 
			
		||||
    _keyPressEvent : function(actor, event) {
 | 
			
		||||
        let keysym = event.get_key_symbol();
 | 
			
		||||
        let event_state = Shell.get_event_state(event);
 | 
			
		||||
        let backwards = event_state & Clutter.ModifierType.SHIFT_MASK;
 | 
			
		||||
        let action = global.screen.get_display().get_keybinding_action(event.get_key_code(), event_state);
 | 
			
		||||
        let shift = (Shell.get_event_state(event) & Clutter.ModifierType.SHIFT_MASK);
 | 
			
		||||
        // X allows servers to represent Shift+Tab in two different ways
 | 
			
		||||
        if (shift && keysym == Clutter.Tab)
 | 
			
		||||
            keysym = Clutter.ISO_Left_Tab;
 | 
			
		||||
 | 
			
		||||
        this._disableHover();
 | 
			
		||||
 | 
			
		||||
        if (keysym == Clutter.Escape) {
 | 
			
		||||
            this.destroy();
 | 
			
		||||
        } else if (action == Meta.KeyBindingAction.SWITCH_GROUP) {
 | 
			
		||||
            this._select(this._currentApp, backwards ? this._previousWindow() : this._nextWindow());
 | 
			
		||||
        } else if (action == Meta.KeyBindingAction.SWITCH_GROUP_BACKWARD) {
 | 
			
		||||
        if (keysym == Clutter.grave)
 | 
			
		||||
            this._select(this._currentApp, this._nextWindow());
 | 
			
		||||
        else if (keysym == Clutter.asciitilde)
 | 
			
		||||
            this._select(this._currentApp, this._previousWindow());
 | 
			
		||||
        } else if (action == Meta.KeyBindingAction.SWITCH_WINDOWS) {
 | 
			
		||||
            this._select(backwards ? this._previousApp() : this._nextApp());
 | 
			
		||||
        } else if (action == Meta.KeyBindingAction.SWITCH_WINDOWS_BACKWARD) {
 | 
			
		||||
            this._select(this._previousApp());
 | 
			
		||||
        } else if (this._thumbnailsFocused) {
 | 
			
		||||
            if (keysym == Clutter.Left)
 | 
			
		||||
        else if (keysym == Clutter.Escape)
 | 
			
		||||
            this.destroy();
 | 
			
		||||
        else if (this._thumbnailsFocused) {
 | 
			
		||||
            if (keysym == Clutter.Tab) {
 | 
			
		||||
                if (this._currentWindow == this._appIcons[this._currentApp].cachedWindows.length - 1)
 | 
			
		||||
                    this._select(this._nextApp());
 | 
			
		||||
                else
 | 
			
		||||
                    this._select(this._currentApp, this._nextWindow());
 | 
			
		||||
            } else if (keysym == Clutter.ISO_Left_Tab) {
 | 
			
		||||
                if (this._currentWindow == 0 || this._currentWindow == -1)
 | 
			
		||||
                    this._select(this._previousApp());
 | 
			
		||||
                else
 | 
			
		||||
                    this._select(this._currentApp, this._previousWindow());
 | 
			
		||||
            } else if (keysym == Clutter.Left)
 | 
			
		||||
                this._select(this._currentApp, this._previousWindow());
 | 
			
		||||
            else if (keysym == Clutter.Right)
 | 
			
		||||
                this._select(this._currentApp, this._nextWindow());
 | 
			
		||||
            else if (keysym == Clutter.Up)
 | 
			
		||||
                this._select(this._currentApp, null, true);
 | 
			
		||||
        } else {
 | 
			
		||||
            if (keysym == Clutter.Left)
 | 
			
		||||
            if (keysym == Clutter.Tab)
 | 
			
		||||
                this._select(this._nextApp());
 | 
			
		||||
            else if (keysym == Clutter.ISO_Left_Tab)
 | 
			
		||||
                this._select(this._previousApp());
 | 
			
		||||
            else if (keysym == Clutter.Left)
 | 
			
		||||
                this._select(this._previousApp());
 | 
			
		||||
            else if (keysym == Clutter.Right)
 | 
			
		||||
                this._select(this._nextApp());
 | 
			
		||||
@@ -362,35 +364,30 @@ AltTabPopup.prototype = {
 | 
			
		||||
        this.destroy();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _popModal: function() {
 | 
			
		||||
        if (this._haveModal) {
 | 
			
		||||
            Main.popModal(this.actor);
 | 
			
		||||
            this._haveModal = false;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroy : function() {
 | 
			
		||||
        this._popModal();
 | 
			
		||||
        if (this.actor.visible) {
 | 
			
		||||
            Tweener.addTween(this.actor,
 | 
			
		||||
                             { opacity: 0,
 | 
			
		||||
                               time: POPUP_FADE_OUT_TIME,
 | 
			
		||||
                               transition: 'easeOutQuad',
 | 
			
		||||
                               onComplete: Lang.bind(this,
 | 
			
		||||
                                   function() {
 | 
			
		||||
                                       this.actor.destroy();
 | 
			
		||||
                                   })
 | 
			
		||||
                             });
 | 
			
		||||
        } else
 | 
			
		||||
            this.actor.destroy();
 | 
			
		||||
        Tweener.addTween(this.actor,
 | 
			
		||||
                         { opacity: 0,
 | 
			
		||||
                           time: POPUP_FADE_TIME,
 | 
			
		||||
                           transition: 'easeOutQuad',
 | 
			
		||||
                           onComplete: Lang.bind(this,
 | 
			
		||||
                               function() {
 | 
			
		||||
                                   this.actor.destroy();
 | 
			
		||||
                               })
 | 
			
		||||
                         });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDestroy : function() {
 | 
			
		||||
        this._popModal();
 | 
			
		||||
        if (this._haveModal)
 | 
			
		||||
            Main.popModal(this.actor);
 | 
			
		||||
 | 
			
		||||
        if (this._thumbnails)
 | 
			
		||||
            this._destroyThumbnails();
 | 
			
		||||
 | 
			
		||||
        if (this._keyPressEventId)
 | 
			
		||||
            global.stage.disconnect(this._keyPressEventId);
 | 
			
		||||
        if (this._keyReleaseEventId)
 | 
			
		||||
            global.stage.disconnect(this._keyReleaseEventId);
 | 
			
		||||
 | 
			
		||||
        if (this._motionTimeoutId != 0)
 | 
			
		||||
            Mainloop.source_remove(this._motionTimeoutId);
 | 
			
		||||
        if (this._thumbnailTimeoutId != 0)
 | 
			
		||||
@@ -461,15 +458,11 @@ AltTabPopup.prototype = {
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _destroyThumbnails : function() {
 | 
			
		||||
        let thumbnailsActor = this._thumbnails.actor;
 | 
			
		||||
        Tweener.addTween(thumbnailsActor,
 | 
			
		||||
        Tweener.addTween(this._thumbnails.actor,
 | 
			
		||||
                         { opacity: 0,
 | 
			
		||||
                           time: THUMBNAIL_FADE_TIME,
 | 
			
		||||
                           transition: 'easeOutQuad',
 | 
			
		||||
                           onComplete: Lang.bind(this, function() {
 | 
			
		||||
                                                            thumbnailsActor.destroy();
 | 
			
		||||
                                                            this.thumbnailsVisible = false;
 | 
			
		||||
                                                        })
 | 
			
		||||
                           onComplete: function() { this.destroy(); }
 | 
			
		||||
                         });
 | 
			
		||||
        this._thumbnails = null;
 | 
			
		||||
    },
 | 
			
		||||
@@ -485,8 +478,7 @@ AltTabPopup.prototype = {
 | 
			
		||||
        Tweener.addTween(this._thumbnails.actor,
 | 
			
		||||
                         { opacity: 255,
 | 
			
		||||
                           time: THUMBNAIL_FADE_TIME,
 | 
			
		||||
                           transition: 'easeOutQuad',
 | 
			
		||||
                           onComplete: Lang.bind(this, function () { this.thumbnailsVisible = true; })
 | 
			
		||||
                           transition: 'easeOutQuad'
 | 
			
		||||
                         });
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
@@ -527,11 +519,16 @@ SwitcherList.prototype = {
 | 
			
		||||
        this._leftArrow = new St.DrawingArea({ style_class: 'switcher-arrow',
 | 
			
		||||
                                               pseudo_class: 'highlighted' });
 | 
			
		||||
        this._leftArrow.connect('repaint', Lang.bind(this,
 | 
			
		||||
            function() { _drawArrow(this._leftArrow, St.Side.LEFT); }));
 | 
			
		||||
                                            function (area) {
 | 
			
		||||
                                                Shell.draw_box_pointer(area, Shell.PointerDirection.LEFT);
 | 
			
		||||
                                            }));
 | 
			
		||||
 | 
			
		||||
        this._rightArrow = new St.DrawingArea({ style_class: 'switcher-arrow',
 | 
			
		||||
                                                pseudo_class: 'highlighted' });
 | 
			
		||||
        this._rightArrow.connect('repaint', Lang.bind(this,
 | 
			
		||||
            function() { _drawArrow(this._rightArrow, St.Side.RIGHT); }));
 | 
			
		||||
                                            function (area) {
 | 
			
		||||
                                                Shell.draw_box_pointer(area, Shell.PointerDirection.RIGHT);
 | 
			
		||||
                                            }));
 | 
			
		||||
 | 
			
		||||
        this.actor.add_actor(this._leftArrow);
 | 
			
		||||
        this.actor.add_actor(this._rightArrow);
 | 
			
		||||
@@ -587,30 +584,24 @@ SwitcherList.prototype = {
 | 
			
		||||
        this._rightArrow.opacity = this._rightGradient.opacity;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addItem : function(item, label) {
 | 
			
		||||
        let bbox = new St.Button({ style_class: 'item-box',
 | 
			
		||||
                                   reactive: true });
 | 
			
		||||
    addItem : function(item) {
 | 
			
		||||
        let bbox = new St.Clickable({ style_class: 'item-box',
 | 
			
		||||
                                      reactive: true });
 | 
			
		||||
 | 
			
		||||
        bbox.set_child(item);
 | 
			
		||||
        this._list.add_actor(bbox);
 | 
			
		||||
 | 
			
		||||
        let n = this._items.length;
 | 
			
		||||
        bbox.connect('clicked', Lang.bind(this, function() { this._onItemClicked(n); }));
 | 
			
		||||
        bbox.connect('enter-event', Lang.bind(this, function() { this._onItemEnter(n); }));
 | 
			
		||||
 | 
			
		||||
        bbox.label_actor = label;
 | 
			
		||||
        bbox.connect('clicked', Lang.bind(this, function () {
 | 
			
		||||
                                               this._itemActivated(n);
 | 
			
		||||
                                          }));
 | 
			
		||||
        bbox.connect('enter-event', Lang.bind(this, function () {
 | 
			
		||||
                                                  this._itemEntered(n);
 | 
			
		||||
                                              }));
 | 
			
		||||
 | 
			
		||||
        this._items.push(bbox);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onItemClicked: function (index) {
 | 
			
		||||
        this._itemActivated(index);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onItemEnter: function (index) {
 | 
			
		||||
        this._itemEntered(index);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addSeparator: function () {
 | 
			
		||||
        let box = new St.Bin({ style_class: 'separator' });
 | 
			
		||||
        this._separator = box;
 | 
			
		||||
@@ -618,21 +609,19 @@ SwitcherList.prototype = {
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    highlight: function(index, justOutline) {
 | 
			
		||||
        if (this._highlighted != -1) {
 | 
			
		||||
            this._items[this._highlighted].remove_style_pseudo_class('outlined');
 | 
			
		||||
            this._items[this._highlighted].remove_style_pseudo_class('selected');
 | 
			
		||||
        }
 | 
			
		||||
        if (this._highlighted != -1)
 | 
			
		||||
            this._items[this._highlighted].style_class = 'item-box';
 | 
			
		||||
 | 
			
		||||
        this._highlighted = index;
 | 
			
		||||
 | 
			
		||||
        if (this._highlighted != -1) {
 | 
			
		||||
            if (justOutline)
 | 
			
		||||
                this._items[this._highlighted].add_style_pseudo_class('outlined');
 | 
			
		||||
                this._items[this._highlighted].style_class = 'outlined-item-box';
 | 
			
		||||
            else
 | 
			
		||||
                this._items[this._highlighted].add_style_pseudo_class('selected');
 | 
			
		||||
                this._items[this._highlighted].style_class = 'selected-item-box';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let monitor = Main.layoutManager.primaryMonitor;
 | 
			
		||||
        let monitor = global.get_primary_monitor();
 | 
			
		||||
        let itemSize = this._items[index].allocation.x2 - this._items[index].allocation.x1;
 | 
			
		||||
        let [posX, posY] = this._items[index].get_transformed_position();
 | 
			
		||||
        posX += this.actor.x;
 | 
			
		||||
@@ -660,7 +649,7 @@ SwitcherList.prototype = {
 | 
			
		||||
 | 
			
		||||
    _scrollToRight : function() {
 | 
			
		||||
        this._scrollableLeft = true;
 | 
			
		||||
        let monitor = Main.layoutManager.primaryMonitor;
 | 
			
		||||
        let monitor = global.get_primary_monitor();
 | 
			
		||||
        let padding = this.actor.get_theme_node().get_horizontal_padding();
 | 
			
		||||
        let parentPadding = this.actor.get_parent().get_theme_node().get_horizontal_padding();
 | 
			
		||||
        let x = this._items[this._highlighted].allocation.x2 - monitor.width + padding + parentPadding;
 | 
			
		||||
@@ -757,7 +746,7 @@ SwitcherList.prototype = {
 | 
			
		||||
        let children = this._list.get_children();
 | 
			
		||||
        let childBox = new Clutter.ActorBox();
 | 
			
		||||
 | 
			
		||||
        let primary = Main.layoutManager.primaryMonitor;
 | 
			
		||||
        let primary = global.get_primary_monitor();
 | 
			
		||||
        let parentRightPadding = this.actor.get_parent().get_theme_node().get_padding(St.Side.RIGHT);
 | 
			
		||||
        if (this.actor.allocation.x2 == primary.x + primary.width - parentRightPadding) {
 | 
			
		||||
            if (this._squareItems)
 | 
			
		||||
@@ -815,7 +804,7 @@ AppIcon.prototype = {
 | 
			
		||||
        this.actor = new St.BoxLayout({ style_class: 'alt-tab-app',
 | 
			
		||||
                                         vertical: true });
 | 
			
		||||
        this.icon = null;
 | 
			
		||||
        this._iconBin = new St.Bin({ x_fill: true, y_fill: true });
 | 
			
		||||
        this._iconBin = new St.Bin();
 | 
			
		||||
 | 
			
		||||
        this.actor.add(this._iconBin, { x_fill: false, y_fill: false } );
 | 
			
		||||
        this.label = new St.Label({ text: this.app.get_name() });
 | 
			
		||||
@@ -829,14 +818,14 @@ AppIcon.prototype = {
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function AppSwitcher(apps, altTabPopup) {
 | 
			
		||||
    this._init(apps, altTabPopup);
 | 
			
		||||
function AppSwitcher(apps) {
 | 
			
		||||
    this._init(apps);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AppSwitcher.prototype = {
 | 
			
		||||
    __proto__ : SwitcherList.prototype,
 | 
			
		||||
 | 
			
		||||
    _init : function(apps, altTabPopup) {
 | 
			
		||||
    _init : function(apps) {
 | 
			
		||||
        SwitcherList.prototype._init.call(this, true);
 | 
			
		||||
 | 
			
		||||
        // Construct the AppIcons, sort by time, add to the popup
 | 
			
		||||
@@ -868,8 +857,6 @@ AppSwitcher.prototype = {
 | 
			
		||||
 | 
			
		||||
        this._curApp = -1;
 | 
			
		||||
        this._iconSize = 0;
 | 
			
		||||
        this._altTabPopup = altTabPopup;
 | 
			
		||||
        this._mouseTimeOutId = 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getPreferredHeight: function (actor, forWidth, alloc) {
 | 
			
		||||
@@ -877,19 +864,17 @@ AppSwitcher.prototype = {
 | 
			
		||||
        while(this._items.length > 1 && this._items[j].style_class != 'item-box') {
 | 
			
		||||
                j++;
 | 
			
		||||
        }
 | 
			
		||||
        let themeNode = this._items[j].get_theme_node();
 | 
			
		||||
        let iconPadding = themeNode.get_horizontal_padding();
 | 
			
		||||
        let iconBorder = themeNode.get_border_width(St.Side.LEFT) + themeNode.get_border_width(St.Side.RIGHT);
 | 
			
		||||
        let iconPadding = this._items[j].get_theme_node().get_horizontal_padding();
 | 
			
		||||
        let [iconMinHeight, iconNaturalHeight] = this.icons[j].label.get_preferred_height(-1);
 | 
			
		||||
        let iconSpacing = iconNaturalHeight + iconPadding + iconBorder;
 | 
			
		||||
        let iconSpacing = iconNaturalHeight + iconPadding;
 | 
			
		||||
        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;
 | 
			
		||||
        let focus = global.get_focus_monitor();
 | 
			
		||||
        let parentPadding = this.actor.get_parent().get_theme_node().get_horizontal_padding();
 | 
			
		||||
        let availWidth = primary.width - parentPadding - this.actor.get_theme_node().get_horizontal_padding();
 | 
			
		||||
        let availWidth = focus.width - parentPadding - this.actor.get_theme_node().get_horizontal_padding();
 | 
			
		||||
        let height = 0;
 | 
			
		||||
 | 
			
		||||
        for(let i =  0; i < iconSizes.length; i++) {
 | 
			
		||||
@@ -934,29 +919,6 @@ AppSwitcher.prototype = {
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // We override SwitcherList's _onItemEnter method to delay
 | 
			
		||||
    // activation when the thumbnail list is open
 | 
			
		||||
    _onItemEnter: function (index) {
 | 
			
		||||
        if (this._mouseTimeOutId != 0)
 | 
			
		||||
            Mainloop.source_remove(this._mouseTimeOutId);
 | 
			
		||||
        if (this._altTabPopup.thumbnailsVisible) {
 | 
			
		||||
            this._mouseTimeOutId = Mainloop.timeout_add(APP_ICON_HOVER_TIMEOUT,
 | 
			
		||||
                                                        Lang.bind(this, function () {
 | 
			
		||||
                                                                            this._enterItem(index);
 | 
			
		||||
                                                                            this._mouseTimeOutId = 0;
 | 
			
		||||
                                                                            return false;
 | 
			
		||||
                                                        }));
 | 
			
		||||
        } else
 | 
			
		||||
           this._itemEntered(index);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _enterItem: function(index) {
 | 
			
		||||
        let [x, y, mask] = global.get_pointer();
 | 
			
		||||
        let pickedActor = global.stage.get_actor_at_pos(Clutter.PickMode.ALL, x, y);
 | 
			
		||||
        if (this._items[index].contains(pickedActor))
 | 
			
		||||
            this._itemEntered(index);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // We override SwitcherList's highlight() method to also deal with
 | 
			
		||||
    // the AppSwitcher->ThumbnailList arrows. Apps with only 1 window
 | 
			
		||||
    // will hide their arrows by default, but show them when their
 | 
			
		||||
@@ -985,11 +947,14 @@ AppSwitcher.prototype = {
 | 
			
		||||
 | 
			
		||||
    _addIcon : function(appIcon) {
 | 
			
		||||
        this.icons.push(appIcon);
 | 
			
		||||
        this.addItem(appIcon.actor, appIcon.label);
 | 
			
		||||
        this.addItem(appIcon.actor);
 | 
			
		||||
 | 
			
		||||
        let n = this._arrows.length;
 | 
			
		||||
        let arrow = new St.DrawingArea({ style_class: 'switcher-arrow' });
 | 
			
		||||
        arrow.connect('repaint', function() { _drawArrow(arrow, St.Side.BOTTOM); });
 | 
			
		||||
        arrow.connect('repaint', Lang.bind(this,
 | 
			
		||||
            function (area) {
 | 
			
		||||
                Shell.draw_box_pointer(area, Shell.PointerDirection.DOWN);
 | 
			
		||||
            }));
 | 
			
		||||
        this._list.add_actor(arrow);
 | 
			
		||||
        this._arrows.push(arrow);
 | 
			
		||||
 | 
			
		||||
@@ -1055,12 +1020,9 @@ ThumbnailList.prototype = {
 | 
			
		||||
                this._labels.push(bin);
 | 
			
		||||
                bin.add_actor(name);
 | 
			
		||||
                box.add_actor(bin);
 | 
			
		||||
 | 
			
		||||
                this.addItem(box, name);
 | 
			
		||||
            } else {
 | 
			
		||||
                this.addItem(box, null);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.addItem(box);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -1078,9 +1040,6 @@ ThumbnailList.prototype = {
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < this._thumbnailBins.length; i++) {
 | 
			
		||||
            let mutterWindow = this._windows[i].get_compositor_private();
 | 
			
		||||
            if (!mutterWindow)
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            let windowTexture = mutterWindow.get_texture ();
 | 
			
		||||
            let [width, height] = windowTexture.get_size();
 | 
			
		||||
            let scale = Math.min(1.0, THUMBNAIL_DEFAULT_SIZE / width, availHeight / height);
 | 
			
		||||
@@ -1098,46 +1057,3 @@ ThumbnailList.prototype = {
 | 
			
		||||
        this._thumbnailBins = new Array();
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function _drawArrow(area, side) {
 | 
			
		||||
    let themeNode = area.get_theme_node();
 | 
			
		||||
    let borderColor = themeNode.get_border_color(side);
 | 
			
		||||
    let bodyColor = themeNode.get_foreground_color();
 | 
			
		||||
 | 
			
		||||
    let [width, height] = area.get_surface_size ();
 | 
			
		||||
    let cr = area.get_context();
 | 
			
		||||
 | 
			
		||||
    cr.setLineWidth(1.0);
 | 
			
		||||
    Clutter.cairo_set_source_color(cr, borderColor);
 | 
			
		||||
 | 
			
		||||
    switch (side) {
 | 
			
		||||
    case St.Side.TOP:
 | 
			
		||||
        cr.moveTo(0, height);
 | 
			
		||||
        cr.lineTo(Math.floor(width * 0.5), 0);
 | 
			
		||||
        cr.lineTo(width, height);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case St.Side.BOTTOM:
 | 
			
		||||
        cr.moveTo(width, 0);
 | 
			
		||||
        cr.lineTo(Math.floor(width * 0.5), height);
 | 
			
		||||
        cr.lineTo(0, 0);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case St.Side.LEFT:
 | 
			
		||||
        cr.moveTo(width, height);
 | 
			
		||||
        cr.lineTo(0, Math.floor(height * 0.5));
 | 
			
		||||
        cr.lineTo(width, 0);
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case St.Side.RIGHT:
 | 
			
		||||
        cr.moveTo(0, 0);
 | 
			
		||||
        cr.lineTo(width, Math.floor(height * 0.5));
 | 
			
		||||
        cr.lineTo(0, height);
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    cr.strokePreserve();
 | 
			
		||||
 | 
			
		||||
    Clutter.cairo_set_source_color(cr, bodyColor);
 | 
			
		||||
    cr.fill();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,14 +1,14 @@
 | 
			
		||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Gtk = imports.gi.Gtk;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const Meta = imports.gi.Meta;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Mainloop = imports.mainloop;
 | 
			
		||||
const Gettext = imports.gettext.domain('gnome-shell');
 | 
			
		||||
const _ = Gettext.gettext;
 | 
			
		||||
 | 
			
		||||
const AppFavorites = imports.ui.appFavorites;
 | 
			
		||||
const DND = imports.ui.dnd;
 | 
			
		||||
@@ -21,9 +21,7 @@ const Tweener = imports.ui.tweener;
 | 
			
		||||
const Workspace = imports.ui.workspace;
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
 | 
			
		||||
const MAX_APPLICATION_WORK_MILLIS = 75;
 | 
			
		||||
const MENU_POPUP_TIMEOUT = 600;
 | 
			
		||||
const SCROLL_TIME = 0.1;
 | 
			
		||||
 | 
			
		||||
function AlphabeticalView() {
 | 
			
		||||
    this._init();
 | 
			
		||||
@@ -31,34 +29,10 @@ function AlphabeticalView() {
 | 
			
		||||
 | 
			
		||||
AlphabeticalView.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._grid = new IconGrid.IconGrid({ xAlign: St.Align.START });
 | 
			
		||||
        this.actor = new St.BoxLayout({ vertical: true });
 | 
			
		||||
        this._grid = new IconGrid.IconGrid();
 | 
			
		||||
        this._appSystem = Shell.AppSystem.get_default();
 | 
			
		||||
 | 
			
		||||
        this._pendingAppLaterId = 0;
 | 
			
		||||
        this._apps = [];
 | 
			
		||||
        this._filterApp = null;
 | 
			
		||||
 | 
			
		||||
        let box = new St.BoxLayout({ vertical: 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,
 | 
			
		||||
                                         style_class: 'vfade' });
 | 
			
		||||
        this.actor.add_actor(box);
 | 
			
		||||
        this.actor.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
 | 
			
		||||
        this.actor.connect('notify::mapped', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                if (!this.actor.mapped)
 | 
			
		||||
                    return;
 | 
			
		||||
 | 
			
		||||
                let adjustment = this.actor.vscroll.adjustment;
 | 
			
		||||
                let direction = Overview.SwipeScrollDirection.VERTICAL;
 | 
			
		||||
                Main.overview.setScrollAdjustment(adjustment, direction);
 | 
			
		||||
 | 
			
		||||
                // Reset scroll on mapping
 | 
			
		||||
                adjustment.value = 0;
 | 
			
		||||
            }));
 | 
			
		||||
        this.actor.add(this._grid.actor, { y_align: St.Align.START, expand: true });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _removeAll: function() {
 | 
			
		||||
@@ -66,75 +40,20 @@ AlphabeticalView.prototype = {
 | 
			
		||||
        this._apps = [];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _addApp: function(appInfo) {
 | 
			
		||||
        let appIcon = new AppWellIcon(this._appSystem.get_app(appInfo.get_id()));
 | 
			
		||||
    _addApp: function(app) {
 | 
			
		||||
        let appIcon = new AppWellIcon(this._appSystem.get_app(app.get_id()));
 | 
			
		||||
        appIcon.connect('launching', Lang.bind(this, function() {
 | 
			
		||||
            this.emit('launching');
 | 
			
		||||
        }));
 | 
			
		||||
        appIcon._draggable.connect('drag-begin', Lang.bind(this, function() {
 | 
			
		||||
            this.emit('drag-begin');
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        this._grid.addItem(appIcon.actor);
 | 
			
		||||
        appIcon.actor.connect('key-focus-in', Lang.bind(this, this._ensureIconVisible));
 | 
			
		||||
 | 
			
		||||
        appIcon._appInfo = appInfo;
 | 
			
		||||
        if (this._filterApp && !this._filterApp(appInfo))
 | 
			
		||||
            appIcon.actor.hide();
 | 
			
		||||
 | 
			
		||||
        this._apps.push(appIcon);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _ensureIconVisible: function(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' });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setFilter: function(filter) {
 | 
			
		||||
        this._filterApp = filter;
 | 
			
		||||
        for (let i = 0; i < this._apps.length; i++)
 | 
			
		||||
            this._apps[i].actor.visible = filter(this._apps[i]._appInfo);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Create actors for the applications in an idle to avoid blocking
 | 
			
		||||
    // for too long; see bug 647778
 | 
			
		||||
    _addPendingApps: function() {
 | 
			
		||||
        let i;
 | 
			
		||||
        let startTimeMillis = new Date().getTime();
 | 
			
		||||
        for (i = 0; i < this._pendingAppIds.length; i++) {
 | 
			
		||||
            let id = this._pendingAppIds[i];
 | 
			
		||||
            this._addApp(this._pendingApps[id]);
 | 
			
		||||
 | 
			
		||||
            let currentTimeMillis = new Date().getTime();
 | 
			
		||||
            if (currentTimeMillis - startTimeMillis > MAX_APPLICATION_WORK_MILLIS)
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
        this._pendingAppIds.splice(0, i + 1);
 | 
			
		||||
        if (this._pendingAppIds.length > 0) {
 | 
			
		||||
            return true;
 | 
			
		||||
        } else {
 | 
			
		||||
            this._pendingAppLaterId = 0;
 | 
			
		||||
            this._pendingAppIds = null;
 | 
			
		||||
            this._pendingApps = null;
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    refresh: function(apps) {
 | 
			
		||||
        let ids = [];
 | 
			
		||||
        for (let i in apps)
 | 
			
		||||
@@ -145,15 +64,14 @@ AlphabeticalView.prototype = {
 | 
			
		||||
 | 
			
		||||
        this._removeAll();
 | 
			
		||||
 | 
			
		||||
        this._pendingAppIds = ids;
 | 
			
		||||
        this._pendingApps = apps;
 | 
			
		||||
        if (this._pendingAppLaterId)
 | 
			
		||||
            Meta.later_remove(this._pendingAppLaterId);
 | 
			
		||||
        this._pendingAppLaterId = Meta.later_add(Meta.LaterType.BEFORE_REDRAW,
 | 
			
		||||
                                                 Lang.bind(this, this._addPendingApps));
 | 
			
		||||
        for (let i = 0; i < ids.length; i++) {
 | 
			
		||||
            this._addApp(apps[ids[i]]);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Signals.addSignalMethods(AlphabeticalView.prototype);
 | 
			
		||||
 | 
			
		||||
function ViewByCategories() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
@@ -161,115 +79,59 @@ function ViewByCategories() {
 | 
			
		||||
ViewByCategories.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._appSystem = Shell.AppSystem.get_default();
 | 
			
		||||
        this.actor = new St.BoxLayout({ style_class: 'all-app' });
 | 
			
		||||
        this.actor = new St.BoxLayout({ vertical: true });
 | 
			
		||||
        this.actor._delegate = this;
 | 
			
		||||
 | 
			
		||||
        this._view = new AlphabeticalView();
 | 
			
		||||
 | 
			
		||||
        // 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._filters = new St.BoxLayout({ vertical: true, reactive: true });
 | 
			
		||||
        this._filtersBox = new St.ScrollView({ x_fill: false,
 | 
			
		||||
                                               y_fill: false,
 | 
			
		||||
                                               style_class: 'vfade' });
 | 
			
		||||
        this._filtersBox.add_actor(this._filters);
 | 
			
		||||
        this.actor.add(this._view.actor, { expand: true, x_fill: true, y_fill: true });
 | 
			
		||||
        this.actor.add(this._filtersBox, { expand: false, y_fill: false, y_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        // 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._allFilter)
 | 
			
		||||
                    this._selectCategory(-1);
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
        this._sections = [];
 | 
			
		||||
 | 
			
		||||
        // 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.actor.add(this._focusDummy);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _selectCategory: function(num) {
 | 
			
		||||
        if (this._currentCategory == num) // nothing to do
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._currentCategory = num;
 | 
			
		||||
 | 
			
		||||
        if (num != -1)
 | 
			
		||||
            this._allFilter.remove_style_pseudo_class('selected');
 | 
			
		||||
        else
 | 
			
		||||
            this._allFilter.add_style_pseudo_class('selected');
 | 
			
		||||
 | 
			
		||||
        this._view.setFilter(Lang.bind(this, function(app) {
 | 
			
		||||
            if (num == -1)
 | 
			
		||||
                return true;
 | 
			
		||||
            return this._sections[num].name == app.get_section();
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < this._sections.length; i++) {
 | 
			
		||||
            if (i == num)
 | 
			
		||||
                this._sections[i].filterActor.add_style_pseudo_class('selected');
 | 
			
		||||
            else
 | 
			
		||||
                this._sections[i].filterActor.remove_style_pseudo_class('selected');
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _addFilter: function(name, num) {
 | 
			
		||||
        let button = new St.Button({ label: GLib.markup_escape_text (name, -1),
 | 
			
		||||
                                     style_class: 'app-filter',
 | 
			
		||||
                                     x_align: St.Align.START,
 | 
			
		||||
                                     can_focus: true });
 | 
			
		||||
        this._filters.add(button, { expand: true, x_fill: true, y_fill: false });
 | 
			
		||||
        button.connect('clicked', Lang.bind(this, function() {
 | 
			
		||||
            this._selectCategory(num);
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        if (num != -1)
 | 
			
		||||
            this._sections[num] = { filterActor: button,
 | 
			
		||||
                                    name: name };
 | 
			
		||||
        else
 | 
			
		||||
            this._allFilter = button;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _removeAll: function() {
 | 
			
		||||
        this._sections = [];
 | 
			
		||||
        this._filters.destroy_children();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    refresh: function(apps) {
 | 
			
		||||
    _updateSections: function(apps) {
 | 
			
		||||
        this._removeAll();
 | 
			
		||||
 | 
			
		||||
        let sections = this._appSystem.get_sections();
 | 
			
		||||
        this._apps = apps;
 | 
			
		||||
 | 
			
		||||
        /* Translators: Filter to display all applications */
 | 
			
		||||
        this._addFilter(_("All"), -1);
 | 
			
		||||
 | 
			
		||||
        if (!sections)
 | 
			
		||||
            return;
 | 
			
		||||
        for (let i = 0; i < sections.length; i++) {
 | 
			
		||||
            if (i) {
 | 
			
		||||
                let actor = new St.Bin({ style_class: 'app-section-divider' });
 | 
			
		||||
                let divider = new St.Bin({ style_class: 'app-section-divider-container',
 | 
			
		||||
                                           child: actor,
 | 
			
		||||
                                           x_fill: true });
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < sections.length; i++)
 | 
			
		||||
            this._addFilter(sections[i], i);
 | 
			
		||||
                this.actor.add(divider, { y_fill: false, expand: true });
 | 
			
		||||
            }
 | 
			
		||||
            let _apps = apps.filter(function(app) {
 | 
			
		||||
                return app.get_section() == sections[i];
 | 
			
		||||
            });
 | 
			
		||||
            this._sections[i] = { view: new AlphabeticalView(),
 | 
			
		||||
                                  apps: _apps,
 | 
			
		||||
                                  name: sections[i] };
 | 
			
		||||
            this._sections[i].view.connect('launching', Lang.bind(this, function() {
 | 
			
		||||
                this.emit('launching');
 | 
			
		||||
            }));
 | 
			
		||||
            this._sections[i].view.connect('drag-begin', Lang.bind(this, function() {
 | 
			
		||||
                this.emit('drag-begin');
 | 
			
		||||
            }));
 | 
			
		||||
            this.actor.add(this._sections[i].view.actor, { y_align: St.Align.START, expand: true });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
        this._selectCategory(-1);
 | 
			
		||||
        this._view.refresh(apps);
 | 
			
		||||
    _removeAll: function() {
 | 
			
		||||
        this.actor.destroy_children();
 | 
			
		||||
        this._sections.forEach(function (section) { section.view.disconnectAll(); });
 | 
			
		||||
 | 
			
		||||
        if (this._focusDummy) {
 | 
			
		||||
            let focused = this._focusDummy.has_key_focus();
 | 
			
		||||
            this._focusDummy.destroy();
 | 
			
		||||
            this._focusDummy = null;
 | 
			
		||||
            if (focused)
 | 
			
		||||
                this.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
 | 
			
		||||
        this._sections = [];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    refresh: function(apps) {
 | 
			
		||||
        this._updateSections(apps);
 | 
			
		||||
        for (let i = 0; i < this._sections.length; i++) {
 | 
			
		||||
            this._sections[i].view.refresh(this._sections[i].apps);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Signals.addSignalMethods(ViewByCategories.prototype);
 | 
			
		||||
 | 
			
		||||
/* This class represents a display containing a collection of application items.
 | 
			
		||||
 * The applications are sorted based on their name.
 | 
			
		||||
 */
 | 
			
		||||
@@ -284,8 +146,17 @@ AllAppDisplay.prototype = {
 | 
			
		||||
            Main.queueDeferredWork(this._workId);
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        this._scrollView = new St.ScrollView({ x_fill: true,
 | 
			
		||||
                                               y_fill: false,
 | 
			
		||||
                                               vshadows: true });
 | 
			
		||||
        this.actor = new St.Bin({ style_class: 'all-app',
 | 
			
		||||
                                  y_align: St.Align.START,
 | 
			
		||||
                                  child: this._scrollView });
 | 
			
		||||
 | 
			
		||||
        this._appView = new ViewByCategories();
 | 
			
		||||
        this.actor = new St.Bin({ child: this._appView.actor, x_fill: true, y_fill: true });
 | 
			
		||||
        this._scrollView.add_actor(this._appView.actor);
 | 
			
		||||
 | 
			
		||||
        this._scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
 | 
			
		||||
 | 
			
		||||
        this._workId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplay));
 | 
			
		||||
    },
 | 
			
		||||
@@ -298,6 +169,8 @@ AllAppDisplay.prototype = {
 | 
			
		||||
        this._appView.refresh(apps);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
Signals.addSignalMethods(AllAppDisplay.prototype);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
function BaseAppSearchProvider() {
 | 
			
		||||
    this._init();
 | 
			
		||||
@@ -317,34 +190,17 @@ BaseAppSearchProvider.prototype = {
 | 
			
		||||
            return null;
 | 
			
		||||
        return { 'id': resultId,
 | 
			
		||||
                 'name': app.get_name(),
 | 
			
		||||
                 'createIcon': function(size) {
 | 
			
		||||
                                   return app.create_icon_texture(size);
 | 
			
		||||
                               }
 | 
			
		||||
               };
 | 
			
		||||
                 'icon': app.create_icon_texture(Search.RESULT_ICON_SIZE)};
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    activateResult: function(id, params) {
 | 
			
		||||
        params = Params.parse(params, { workspace: null,
 | 
			
		||||
                                        timestamp: null });
 | 
			
		||||
 | 
			
		||||
        let workspace = params.workspace ? params.workspace.index() : -1;
 | 
			
		||||
        let event = Clutter.get_current_event();
 | 
			
		||||
        let modifiers = event ? Shell.get_event_state(event) : 0;
 | 
			
		||||
        let openNewWindow = modifiers & Clutter.ModifierType.CONTROL_MASK;
 | 
			
		||||
 | 
			
		||||
    activateResult: function(id) {
 | 
			
		||||
        let app = this._appSys.get_app(id);
 | 
			
		||||
        if (openNewWindow)
 | 
			
		||||
            app.open_new_window(workspace);
 | 
			
		||||
        else
 | 
			
		||||
            app.activate(workspace);
 | 
			
		||||
        app.activate();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    dragActivateResult: function(id, params) {
 | 
			
		||||
        params = Params.parse(params, { workspace: null,
 | 
			
		||||
                                        timestamp: null });
 | 
			
		||||
 | 
			
		||||
    dragActivateResult: function(id) {
 | 
			
		||||
        let app = this._appSys.get_app(id);
 | 
			
		||||
        app.open_new_window(params.workspace ? params.workspace.index() : -1);
 | 
			
		||||
        app.open_new_window();
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -371,6 +227,10 @@ AppSearchProvider.prototype = {
 | 
			
		||||
        let app = this._appSys.get_app(resultMeta['id']);
 | 
			
		||||
        let icon = new AppWellIcon(app);
 | 
			
		||||
        return icon.actor;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    expandSearch: function(terms) {
 | 
			
		||||
        log('TODO expand search');
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -382,7 +242,7 @@ PrefsSearchProvider.prototype = {
 | 
			
		||||
    __proto__: BaseAppSearchProvider.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        BaseAppSearchProvider.prototype._init.call(this, _("SETTINGS"));
 | 
			
		||||
        BaseAppSearchProvider.prototype._init.call(this, _("PREFERENCES"));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getInitialResultSet: function(terms) {
 | 
			
		||||
@@ -391,24 +251,30 @@ PrefsSearchProvider.prototype = {
 | 
			
		||||
 | 
			
		||||
    getSubsearchResultSet: function(previousResults, terms) {
 | 
			
		||||
        return this._appSys.subsearch(true, previousResults, terms);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    expandSearch: function(terms) {
 | 
			
		||||
        let controlCenter = this._appSys.load_from_desktop_file('gnomecc.desktop');
 | 
			
		||||
        controlCenter.launch();
 | 
			
		||||
        Main.overview.hide();
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function AppIcon(app, params) {
 | 
			
		||||
    this._init(app, params);
 | 
			
		||||
function AppIcon(app) {
 | 
			
		||||
    this._init(app);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AppIcon.prototype = {
 | 
			
		||||
    __proto__:  IconGrid.BaseIcon.prototype,
 | 
			
		||||
 | 
			
		||||
    _init : function(app, params) {
 | 
			
		||||
    _init : function(app) {
 | 
			
		||||
        this.app = app;
 | 
			
		||||
 | 
			
		||||
        let label = this.app.get_name();
 | 
			
		||||
 | 
			
		||||
        IconGrid.BaseIcon.prototype._init.call(this,
 | 
			
		||||
                                               label,
 | 
			
		||||
                                               params);
 | 
			
		||||
                                               { setSizeManually: true });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    createIcon: function(iconSize) {
 | 
			
		||||
@@ -416,29 +282,23 @@ AppIcon.prototype = {
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function AppWellIcon(app, iconParams) {
 | 
			
		||||
    this._init(app, iconParams);
 | 
			
		||||
function AppWellIcon(app) {
 | 
			
		||||
    this._init(app);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AppWellIcon.prototype = {
 | 
			
		||||
    _init : function(app, iconParams) {
 | 
			
		||||
    _init : function(app) {
 | 
			
		||||
        this.app = app;
 | 
			
		||||
        this.actor = new St.Button({ style_class: 'app-well-app',
 | 
			
		||||
                                     reactive: true,
 | 
			
		||||
                                     button_mask: St.ButtonMask.ONE | St.ButtonMask.TWO,
 | 
			
		||||
                                     can_focus: true,
 | 
			
		||||
                                     x_fill: true,
 | 
			
		||||
                                     y_fill: true });
 | 
			
		||||
        this.actor = new St.Clickable({ style_class: 'app-well-app',
 | 
			
		||||
                                         reactive: true,
 | 
			
		||||
                                         x_fill: true,
 | 
			
		||||
                                         y_fill: true });
 | 
			
		||||
        this.actor._delegate = this;
 | 
			
		||||
 | 
			
		||||
        this.icon = new AppIcon(app, iconParams);
 | 
			
		||||
        this.icon = new AppIcon(app);
 | 
			
		||||
        this.actor.set_child(this.icon.actor);
 | 
			
		||||
 | 
			
		||||
        this.actor.label_actor = this.icon.label;
 | 
			
		||||
 | 
			
		||||
        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));
 | 
			
		||||
 | 
			
		||||
        this._menu = null;
 | 
			
		||||
        this._menuManager = new PopupMenu.PopupMenuManager(this);
 | 
			
		||||
@@ -449,15 +309,12 @@ AppWellIcon.prototype = {
 | 
			
		||||
                this._removeMenuTimeout();
 | 
			
		||||
                Main.overview.beginItemDrag(this);
 | 
			
		||||
            }));
 | 
			
		||||
        this._draggable.connect('drag-cancelled', Lang.bind(this,
 | 
			
		||||
            function () {
 | 
			
		||||
                Main.overview.cancelledItemDrag(this);
 | 
			
		||||
            }));
 | 
			
		||||
        this._draggable.connect('drag-end', Lang.bind(this,
 | 
			
		||||
            function () {
 | 
			
		||||
               Main.overview.endItemDrag(this);
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
        this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
 | 
			
		||||
        this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
 | 
			
		||||
 | 
			
		||||
        this._menuTimeoutId = 0;
 | 
			
		||||
@@ -496,34 +353,29 @@ AppWellIcon.prototype = {
 | 
			
		||||
                Lang.bind(this, function() {
 | 
			
		||||
                    this.popupMenu();
 | 
			
		||||
                }));
 | 
			
		||||
        } else if (button == 3) {
 | 
			
		||||
            this.popupMenu();
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onClicked: function(actor, button) {
 | 
			
		||||
    _onClicked: function(actor, event) {
 | 
			
		||||
        this._removeMenuTimeout();
 | 
			
		||||
 | 
			
		||||
        let button = event.get_button();
 | 
			
		||||
        if (button == 1) {
 | 
			
		||||
            this._onActivate(Clutter.get_current_event());
 | 
			
		||||
            this._onActivate(event);
 | 
			
		||||
        } else if (button == 2) {
 | 
			
		||||
            // Last workspace is always empty
 | 
			
		||||
            let launchWorkspace = global.screen.get_workspace_by_index(global.screen.n_workspaces - 1);
 | 
			
		||||
            launchWorkspace.activate(global.get_current_time());
 | 
			
		||||
            this.emit('launching');
 | 
			
		||||
            this.app.open_new_window(-1);
 | 
			
		||||
            Main.overview.hide();
 | 
			
		||||
            let newWorkspace = Main.overview.workspaces.addWorkspace();
 | 
			
		||||
            if (newWorkspace != null) {
 | 
			
		||||
                newWorkspace.activate(global.get_current_time());
 | 
			
		||||
                this.emit('launching');
 | 
			
		||||
                this.app.open_new_window();
 | 
			
		||||
                Main.overview.hide();
 | 
			
		||||
            }
 | 
			
		||||
        } else if (button == 3) {
 | 
			
		||||
            this.popupMenu();
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onKeyboardPopupMenu: function() {
 | 
			
		||||
        this.popupMenu();
 | 
			
		||||
        this._menu.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getId: function() {
 | 
			
		||||
        return this.app.get_id();
 | 
			
		||||
    },
 | 
			
		||||
@@ -534,35 +386,67 @@ AppWellIcon.prototype = {
 | 
			
		||||
 | 
			
		||||
        if (!this._menu) {
 | 
			
		||||
            this._menu = new AppIconMenu(this);
 | 
			
		||||
            this._menu.connect('highlight-window', Lang.bind(this, function (menu, window) {
 | 
			
		||||
                this.highlightWindow(window);
 | 
			
		||||
            }));
 | 
			
		||||
            this._menu.connect('activate-window', Lang.bind(this, function (menu, window) {
 | 
			
		||||
                this.activateWindow(window);
 | 
			
		||||
            }));
 | 
			
		||||
            this._menu.connect('popup', Lang.bind(this, function (menu, isPoppedUp) {
 | 
			
		||||
                if (!isPoppedUp)
 | 
			
		||||
                if (isPoppedUp) {
 | 
			
		||||
                    this._onMenuPoppedUp();
 | 
			
		||||
                } else {
 | 
			
		||||
                    this._onMenuPoppedDown();
 | 
			
		||||
                }
 | 
			
		||||
            }));
 | 
			
		||||
            Main.overview.connect('hiding', Lang.bind(this, function () { this._menu.close(); }));
 | 
			
		||||
 | 
			
		||||
            this._menuManager.addMenu(this._menu);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.actor.set_hover(true);
 | 
			
		||||
        this.actor.show_tooltip();
 | 
			
		||||
        this._menu.popup();
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    highlightWindow: function(metaWindow) {
 | 
			
		||||
        if (this._didActivateWindow)
 | 
			
		||||
            return;
 | 
			
		||||
        if (!this._getRunning())
 | 
			
		||||
            return;
 | 
			
		||||
        Main.overview.getWorkspacesForWindow(metaWindow).setHighlightWindow(metaWindow);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    activateWindow: function(metaWindow) {
 | 
			
		||||
        if (metaWindow) {
 | 
			
		||||
            this._didActivateWindow = true;
 | 
			
		||||
            Main.activateWindow(metaWindow);
 | 
			
		||||
        } else {
 | 
			
		||||
            Main.overview.hide();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onMenuPoppedUp: function() {
 | 
			
		||||
        if (this._getRunning()) {
 | 
			
		||||
            Main.overview.getWorkspacesForWindow(null).setApplicationWindowSelection(this.app.get_id());
 | 
			
		||||
            this._setWindowSelection = true;
 | 
			
		||||
            this._didActivateWindow = false;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onMenuPoppedDown: function() {
 | 
			
		||||
        this.actor.sync_hover();
 | 
			
		||||
 | 
			
		||||
        if (this._didActivateWindow)
 | 
			
		||||
            return;
 | 
			
		||||
        if (!this._setWindowSelection)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        Main.overview.getWorkspacesForWindow(null).setApplicationWindowSelection(null);
 | 
			
		||||
        this._setWindowSelection = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getRunning: function() {
 | 
			
		||||
        return this.app.state != Shell.AppState.STOPPED;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onActivate: function (event) {
 | 
			
		||||
@@ -571,22 +455,24 @@ AppWellIcon.prototype = {
 | 
			
		||||
 | 
			
		||||
        if (modifiers & Clutter.ModifierType.CONTROL_MASK
 | 
			
		||||
            && this.app.state == Shell.AppState.RUNNING) {
 | 
			
		||||
            this.app.open_new_window(-1);
 | 
			
		||||
            this.app.open_new_window();
 | 
			
		||||
        } else {
 | 
			
		||||
            this.app.activate(-1);
 | 
			
		||||
            this.app.activate();
 | 
			
		||||
        }
 | 
			
		||||
        Main.overview.hide();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    shellWorkspaceLaunch : function(params) {
 | 
			
		||||
        params = Params.parse(params, { workspace: null,
 | 
			
		||||
                                        timestamp: null });
 | 
			
		||||
    // called by this._menuManager when it has the grab
 | 
			
		||||
    menuEventFilter: function(event) {
 | 
			
		||||
        return this._menu.menuEventFilter(event);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
        this.app.open_new_window(params.workspace ? params.workspace.index() : -1);
 | 
			
		||||
    shellWorkspaceLaunch : function() {
 | 
			
		||||
        this.app.open_new_window();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getDragActor: function() {
 | 
			
		||||
        return this.app.create_icon_texture(Main.overview.dash.iconSize);
 | 
			
		||||
        return this.app.create_icon_texture(this.icon.iconSize);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Returns the original actor that should align with the actor
 | 
			
		||||
@@ -605,17 +491,11 @@ AppIconMenu.prototype = {
 | 
			
		||||
    __proto__: PopupMenu.PopupMenu.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function(source) {
 | 
			
		||||
        let side = St.Side.LEFT;
 | 
			
		||||
        if (St.Widget.get_default_direction() == St.TextDirection.RTL)
 | 
			
		||||
            side = St.Side.RIGHT;
 | 
			
		||||
 | 
			
		||||
        PopupMenu.PopupMenu.prototype._init.call(this, source.actor, 0.5, side, 0);
 | 
			
		||||
 | 
			
		||||
        // We want to keep the item hovered while the menu is up
 | 
			
		||||
        this.blockSourceEvents = true;
 | 
			
		||||
        PopupMenu.PopupMenu.prototype._init.call(this, source.actor, St.Align.MIDDLE, St.Side.LEFT, 0);
 | 
			
		||||
 | 
			
		||||
        this._source = source;
 | 
			
		||||
 | 
			
		||||
        this.connect('active-changed', Lang.bind(this, this._onActiveChanged));
 | 
			
		||||
        this.connect('activate', Lang.bind(this, this._onActivate));
 | 
			
		||||
        this.connect('open-state-changed', Lang.bind(this, this._onOpenStateChanged));
 | 
			
		||||
 | 
			
		||||
@@ -655,12 +535,14 @@ AppIconMenu.prototype = {
 | 
			
		||||
 | 
			
		||||
        let isFavorite = AppFavorites.getAppFavorites().isFavorite(this._source.app.get_id());
 | 
			
		||||
 | 
			
		||||
        this._newWindowMenuItem = this._appendMenuItem(_("New Window"));
 | 
			
		||||
        this._appendSeparator();
 | 
			
		||||
        this._newWindowMenuItem = windows.length > 0 ? this._appendMenuItem(_("New Window")) : null;
 | 
			
		||||
 | 
			
		||||
        if (windows.length > 0)
 | 
			
		||||
            this._appendSeparator();
 | 
			
		||||
        this._toggleFavoriteMenuItem = this._appendMenuItem(isFavorite ? _("Remove from Favorites")
 | 
			
		||||
                                                                    : _("Add to Favorites"));
 | 
			
		||||
 | 
			
		||||
        this._highlightedItem = null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _appendSeparator: function () {
 | 
			
		||||
@@ -684,16 +566,74 @@ AppIconMenu.prototype = {
 | 
			
		||||
        if (open) {
 | 
			
		||||
            this.emit('popup', true);
 | 
			
		||||
        } else {
 | 
			
		||||
            this._updateHighlight(null);
 | 
			
		||||
            this.emit('popup', false);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // called by this._menuManager when it has the grab
 | 
			
		||||
    menuEventFilter: function(event) {
 | 
			
		||||
        let eventType = event.type();
 | 
			
		||||
 | 
			
		||||
        // Check if the user is interacting with a window representation
 | 
			
		||||
        // rather than interacting with the menu
 | 
			
		||||
 | 
			
		||||
        if (eventType == Clutter.EventType.BUTTON_RELEASE) {
 | 
			
		||||
            let metaWindow = this._findMetaWindowForActor(event.get_source());
 | 
			
		||||
            if (metaWindow)
 | 
			
		||||
                this.emit('activate-window', metaWindow);
 | 
			
		||||
        } else if (eventType == Clutter.EventType.ENTER) {
 | 
			
		||||
            let metaWindow = this._findMetaWindowForActor(event.get_source());
 | 
			
		||||
            if (metaWindow)
 | 
			
		||||
                this._selectMenuItemForWindow(metaWindow, true);
 | 
			
		||||
        } else if (eventType == Clutter.EventType.LEAVE) {
 | 
			
		||||
            let metaWindow = this._findMetaWindowForActor(event.get_source());
 | 
			
		||||
            if (metaWindow)
 | 
			
		||||
                this._selectMenuItemForWindow(metaWindow, false);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _findMetaWindowForActor: function (actor) {
 | 
			
		||||
        if (actor._delegate instanceof Workspace.WindowClone)
 | 
			
		||||
            return actor._delegate.metaWindow;
 | 
			
		||||
        else if (actor.get_meta_window)
 | 
			
		||||
            return actor.get_meta_window();
 | 
			
		||||
        return null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateHighlight: function (item) {
 | 
			
		||||
        if (this._highlightedItem)
 | 
			
		||||
            this.emit('highlight-window', null);
 | 
			
		||||
        this._highlightedItem = item;
 | 
			
		||||
        if (this._highlightedItem) {
 | 
			
		||||
            let window = this._highlightedItem._window;
 | 
			
		||||
            if (window)
 | 
			
		||||
                this.emit('highlight-window', window);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _selectMenuItemForWindow: function (metaWindow, selected) {
 | 
			
		||||
        let items = this.getMenuItems();
 | 
			
		||||
        for (let i = 0; i < items.length; i++) {
 | 
			
		||||
            let item = items[i];
 | 
			
		||||
            let menuMetaWindow = item._window;
 | 
			
		||||
            if (menuMetaWindow == metaWindow)
 | 
			
		||||
                item.setActive(selected);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onActiveChanged: function (menu, child) {
 | 
			
		||||
        this._updateHighlight(child);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onActivate: function (actor, child) {
 | 
			
		||||
        if (child._window) {
 | 
			
		||||
            let metaWindow = child._window;
 | 
			
		||||
            this.emit('activate-window', metaWindow);
 | 
			
		||||
        } else if (child == this._newWindowMenuItem) {
 | 
			
		||||
            this._source.app.open_new_window(-1);
 | 
			
		||||
            this._source.app.open_new_window();
 | 
			
		||||
            this.emit('activate-window', null);
 | 
			
		||||
        } else if (child == this._toggleFavoriteMenuItem) {
 | 
			
		||||
            let favs = AppFavorites.getAppFavorites();
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,8 @@
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const Gettext = imports.gettext.domain('gnome-shell');
 | 
			
		||||
const _ = Gettext.gettext;
 | 
			
		||||
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,278 +0,0 @@
 | 
			
		||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
 | 
			
		||||
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const DBus = imports.dbus;
 | 
			
		||||
const Mainloop = imports.mainloop;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const ShellMountOperation = imports.ui.shellMountOperation;
 | 
			
		||||
const ScreenSaver = imports.misc.screenSaver;
 | 
			
		||||
 | 
			
		||||
// GSettings keys
 | 
			
		||||
const SETTINGS_SCHEMA = 'org.gnome.desktop.media-handling';
 | 
			
		||||
const SETTING_ENABLE_AUTOMOUNT = 'automount';
 | 
			
		||||
 | 
			
		||||
const AUTORUN_EXPIRE_TIMEOUT_SECS = 10;
 | 
			
		||||
 | 
			
		||||
const ConsoleKitSessionIface = {
 | 
			
		||||
    name: 'org.freedesktop.ConsoleKit.Session',
 | 
			
		||||
    methods: [{ name: 'IsActive',
 | 
			
		||||
                inSignature: '',
 | 
			
		||||
                outSignature: 'b' }],
 | 
			
		||||
    signals: [{ name: 'ActiveChanged',
 | 
			
		||||
                inSignature: 'b' }]
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const ConsoleKitSessionProxy = DBus.makeProxyClass(ConsoleKitSessionIface);
 | 
			
		||||
 | 
			
		||||
const ConsoleKitManagerIface = {
 | 
			
		||||
    name: 'org.freedesktop.ConsoleKit.Manager',
 | 
			
		||||
    methods: [{ name: 'GetCurrentSession',
 | 
			
		||||
                inSignature: '',
 | 
			
		||||
                outSignature: 'o' }]
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function ConsoleKitManager() {
 | 
			
		||||
    this._init();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
ConsoleKitManager.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.sessionActive = true;
 | 
			
		||||
 | 
			
		||||
        DBus.system.proxifyObject(this,
 | 
			
		||||
                                  'org.freedesktop.ConsoleKit',
 | 
			
		||||
                                  '/org/freedesktop/ConsoleKit/Manager');
 | 
			
		||||
 | 
			
		||||
        DBus.system.watch_name('org.freedesktop.ConsoleKit',
 | 
			
		||||
                               false, // do not launch a name-owner if none exists
 | 
			
		||||
                               Lang.bind(this, this._onManagerAppeared),
 | 
			
		||||
                               Lang.bind(this, this._onManagerVanished));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onManagerAppeared: function(owner) {
 | 
			
		||||
        this.GetCurrentSessionRemote(Lang.bind(this, this._onCurrentSession));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onManagerVanished: function(oldOwner) {
 | 
			
		||||
        this.sessionActive = true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onCurrentSession: function(session) {
 | 
			
		||||
        this._ckSession = new ConsoleKitSessionProxy(DBus.system, 'org.freedesktop.ConsoleKit', session);
 | 
			
		||||
 | 
			
		||||
        this._ckSession.connect
 | 
			
		||||
            ('ActiveChanged', Lang.bind(this, function(object, isActive) {
 | 
			
		||||
                this.sessionActive = isActive;            
 | 
			
		||||
            }));
 | 
			
		||||
        this._ckSession.IsActiveRemote(Lang.bind(this, function(isActive) {
 | 
			
		||||
            this.sessionActive = isActive;            
 | 
			
		||||
        }));
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
DBus.proxifyPrototype(ConsoleKitManager.prototype, ConsoleKitManagerIface);
 | 
			
		||||
 | 
			
		||||
function AutomountManager() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AutomountManager.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._settings = new Gio.Settings({ schema: SETTINGS_SCHEMA });
 | 
			
		||||
        this._volumeQueue = [];
 | 
			
		||||
 | 
			
		||||
        this.ckListener = new ConsoleKitManager();
 | 
			
		||||
 | 
			
		||||
        this._ssProxy = new ScreenSaver.ScreenSaverProxy();
 | 
			
		||||
        this._ssProxy.connect('ActiveChanged',
 | 
			
		||||
                              Lang.bind(this,
 | 
			
		||||
                                        this._screenSaverActiveChanged));
 | 
			
		||||
 | 
			
		||||
        this._volumeMonitor = Gio.VolumeMonitor.get();
 | 
			
		||||
 | 
			
		||||
        this._volumeMonitor.connect('volume-added',
 | 
			
		||||
                                    Lang.bind(this,
 | 
			
		||||
                                              this._onVolumeAdded));
 | 
			
		||||
        this._volumeMonitor.connect('volume-removed',
 | 
			
		||||
                                    Lang.bind(this,
 | 
			
		||||
                                              this._onVolumeRemoved));
 | 
			
		||||
        this._volumeMonitor.connect('drive-connected',
 | 
			
		||||
                                    Lang.bind(this,
 | 
			
		||||
                                              this._onDriveConnected));
 | 
			
		||||
        this._volumeMonitor.connect('drive-disconnected',
 | 
			
		||||
                                    Lang.bind(this,
 | 
			
		||||
                                              this._onDriveDisconnected));
 | 
			
		||||
        this._volumeMonitor.connect('drive-eject-button',
 | 
			
		||||
                                    Lang.bind(this,
 | 
			
		||||
                                              this._onDriveEjectButton));
 | 
			
		||||
 | 
			
		||||
        Mainloop.idle_add(Lang.bind(this, this._startupMountAll));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _screenSaverActiveChanged: function(object, isActive) {
 | 
			
		||||
        if (!isActive) {
 | 
			
		||||
            this._volumeQueue.forEach(Lang.bind(this, function(volume) {
 | 
			
		||||
                this._checkAndMountVolume(volume);
 | 
			
		||||
            }));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // clear the queue anyway
 | 
			
		||||
        this._volumeQueue = [];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _startupMountAll: function() {
 | 
			
		||||
        let volumes = this._volumeMonitor.get_volumes();
 | 
			
		||||
        volumes.forEach(Lang.bind(this, function(volume) {
 | 
			
		||||
            this._checkAndMountVolume(volume, { checkSession: false,
 | 
			
		||||
                                                useMountOp: false });
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDriveConnected: function() {
 | 
			
		||||
        // if we're not in the current ConsoleKit session,
 | 
			
		||||
        // or screensaver is active, don't play sounds
 | 
			
		||||
        if (!this.ckListener.sessionActive)
 | 
			
		||||
            return;        
 | 
			
		||||
 | 
			
		||||
        if (this._ssProxy.screenSaverActive)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        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.ckListener.sessionActive)
 | 
			
		||||
            return;        
 | 
			
		||||
 | 
			
		||||
        if (this._ssProxy.screenSaverActive)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        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.ckListener.sessionActive)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        // we force stop/eject in this case, so we don't have to pass a
 | 
			
		||||
        // mount operation object
 | 
			
		||||
        if (drive.can_stop()) {
 | 
			
		||||
            drive.stop
 | 
			
		||||
                (Gio.MountUnmountFlags.FORCE, null, null,
 | 
			
		||||
                 Lang.bind(this, function(drive, res) {
 | 
			
		||||
                     try {
 | 
			
		||||
                         drive.stop_finish(res);
 | 
			
		||||
                     } catch (e) {
 | 
			
		||||
                         log("Unable to stop the drive after drive-eject-button " + e.toString());
 | 
			
		||||
                     }
 | 
			
		||||
                 }));
 | 
			
		||||
        } else if (drive.can_eject()) {
 | 
			
		||||
            drive.eject_with_operation 
 | 
			
		||||
                (Gio.MountUnmountFlags.FORCE, null, null,
 | 
			
		||||
                 Lang.bind(this, function(drive, res) {
 | 
			
		||||
                     try {
 | 
			
		||||
                         drive.eject_with_operation_finish(res);
 | 
			
		||||
                     } catch (e) {
 | 
			
		||||
                         log("Unable to eject the drive after drive-eject-button " + e.toString());
 | 
			
		||||
                     }
 | 
			
		||||
                 }));
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onVolumeAdded: function(monitor, volume) {
 | 
			
		||||
        this._checkAndMountVolume(volume);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _checkAndMountVolume: function(volume, params) {
 | 
			
		||||
        params = Params.parse(params, { checkSession: true,
 | 
			
		||||
                                        useMountOp: true });
 | 
			
		||||
 | 
			
		||||
        if (params.checkSession) {
 | 
			
		||||
            // if we're not in the current ConsoleKit session,
 | 
			
		||||
            // don't attempt automount
 | 
			
		||||
            if (!this.ckListener.sessionActive)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            if (this._ssProxy.screenSaverActive) {
 | 
			
		||||
                if (this._volumeQueue.indexOf(volume) == -1)
 | 
			
		||||
                    this._volumeQueue.push(volume);
 | 
			
		||||
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!this._settings.get_boolean(SETTING_ENABLE_AUTOMOUNT) ||
 | 
			
		||||
            !volume.should_automount() ||
 | 
			
		||||
            !volume.can_mount()) {
 | 
			
		||||
	    // allow the autorun to run anyway; this can happen if the
 | 
			
		||||
            // mount gets added programmatically later, even if 
 | 
			
		||||
            // should_automount() or can_mount() are false, like for
 | 
			
		||||
            // blank optical media.
 | 
			
		||||
            this._allowAutorun(volume);
 | 
			
		||||
            this._allowAutorunExpire(volume);
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (params.useMountOp) {
 | 
			
		||||
            let operation = new ShellMountOperation.ShellMountOperation(volume);
 | 
			
		||||
            this._mountVolume(volume, operation.mountOp);
 | 
			
		||||
        } else {
 | 
			
		||||
            this._mountVolume(volume, null);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _mountVolume: function(volume, operation) {
 | 
			
		||||
        this._allowAutorun(volume);
 | 
			
		||||
        volume.mount(0, operation, null,
 | 
			
		||||
                     Lang.bind(this, this._onVolumeMounted));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onVolumeMounted: function(volume, res) {
 | 
			
		||||
        this._allowAutorunExpire(volume);
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            volume.mount_finish(res);
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
            let string = e.toString();
 | 
			
		||||
 | 
			
		||||
            // FIXME: needs proper error code handling instead of this
 | 
			
		||||
            // See https://bugzilla.gnome.org/show_bug.cgi?id=591480
 | 
			
		||||
            if (string.indexOf('No key available with this passphrase') != -1)
 | 
			
		||||
                this._reaskPassword(volume);
 | 
			
		||||
            else
 | 
			
		||||
                log('Unable to mount volume ' + volume.get_name() + ': ' + string);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onVolumeRemoved: function(monitor, volume) {
 | 
			
		||||
        this._volumeQueue = 
 | 
			
		||||
            this._volumeQueue.filter(function(element) {
 | 
			
		||||
                return (element != volume);
 | 
			
		||||
            });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _reaskPassword: function(volume) {
 | 
			
		||||
        let operation = new ShellMountOperation.ShellMountOperation(volume, { reaskPassword: true });
 | 
			
		||||
        this._mountVolume(volume, operation.mountOp);        
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _allowAutorun: function(volume) {
 | 
			
		||||
        volume.allowAutorun = true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _allowAutorunExpire: function(volume) {
 | 
			
		||||
        Mainloop.timeout_add_seconds(AUTORUN_EXPIRE_TIMEOUT_SECS, function() {
 | 
			
		||||
            volume.allowAutorun = false;
 | 
			
		||||
            return false;
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,634 +0,0 @@
 | 
			
		||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
 | 
			
		||||
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const DBus = imports.dbus;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const MessageTray = imports.ui.messageTray;
 | 
			
		||||
const ShellMountOperation = imports.ui.shellMountOperation;
 | 
			
		||||
 | 
			
		||||
// GSettings keys
 | 
			
		||||
const SETTINGS_SCHEMA = 'org.gnome.desktop.media-handling';
 | 
			
		||||
const SETTING_DISABLE_AUTORUN = 'autorun-never';
 | 
			
		||||
const SETTING_START_APP = 'autorun-x-content-start-app';
 | 
			
		||||
const SETTING_IGNORE = 'autorun-x-content-ignore';
 | 
			
		||||
const SETTING_OPEN_FOLDER = 'autorun-x-content-open-folder';
 | 
			
		||||
 | 
			
		||||
const AutorunSetting = {
 | 
			
		||||
    RUN: 0,
 | 
			
		||||
    IGNORE: 1,
 | 
			
		||||
    FILES: 2,
 | 
			
		||||
    ASK: 3
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const HOTPLUG_ICON_SIZE = 16;
 | 
			
		||||
 | 
			
		||||
// misc utils
 | 
			
		||||
function ignoreAutorunForMount(mount) {
 | 
			
		||||
    let root = mount.get_root();
 | 
			
		||||
    let volume = mount.get_volume();
 | 
			
		||||
 | 
			
		||||
    if ((root.is_native() && !isMountRootHidden(root)) ||
 | 
			
		||||
        (volume && volume.allowAutorun && volume.should_automount()))
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function isMountRootHidden(root) {
 | 
			
		||||
    let path = root.get_path();
 | 
			
		||||
 | 
			
		||||
    // skip any mounts in hidden directory hierarchies
 | 
			
		||||
    return (path.indexOf('/.') != -1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function startAppForMount(app, mount) {
 | 
			
		||||
    let files = [];
 | 
			
		||||
    let root = mount.get_root();
 | 
			
		||||
    let retval = false;
 | 
			
		||||
 | 
			
		||||
    files.push(root);
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        retval = app.launch(files, 
 | 
			
		||||
                            global.create_app_launch_context())
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
        log('Unable to launch the application ' + app.get_name()
 | 
			
		||||
            + ': ' + e.toString());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return retval;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/******************************************/
 | 
			
		||||
 | 
			
		||||
const HotplugSnifferIface = {
 | 
			
		||||
    name: 'org.gnome.Shell.HotplugSniffer',
 | 
			
		||||
    methods: [{ name: 'SniffURI',
 | 
			
		||||
                inSignature: 's',
 | 
			
		||||
                outSignature: 'as' }]
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const HotplugSniffer = function() {
 | 
			
		||||
    this._init();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
HotplugSniffer.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        DBus.session.proxifyObject(this,
 | 
			
		||||
                                   'org.gnome.Shell.HotplugSniffer',
 | 
			
		||||
                                   '/org/gnome/Shell/HotplugSniffer');
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
DBus.proxifyPrototype(HotplugSniffer.prototype, HotplugSnifferIface);
 | 
			
		||||
 | 
			
		||||
function ContentTypeDiscoverer(callback) {
 | 
			
		||||
    this._init(callback);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ContentTypeDiscoverer.prototype = {
 | 
			
		||||
    _init: function(callback) {
 | 
			
		||||
        this._callback = callback;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    guessContentTypes: function(mount) {
 | 
			
		||||
        // guess mount's content types using GIO
 | 
			
		||||
        mount.guess_content_type(false, null,
 | 
			
		||||
                                 Lang.bind(this,
 | 
			
		||||
                                           this._onContentTypeGuessed));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onContentTypeGuessed: function(mount, res) {
 | 
			
		||||
        let contentTypes = [];
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            contentTypes = mount.guess_content_type_finish(res);
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
            log('Unable to guess content types on added mount ' + mount.get_name()
 | 
			
		||||
                + ': ' + e.toString());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (contentTypes.length) {
 | 
			
		||||
            this._emitCallback(mount, contentTypes);
 | 
			
		||||
        } else {
 | 
			
		||||
            let root = mount.get_root();
 | 
			
		||||
 | 
			
		||||
            let hotplugSniffer = new HotplugSniffer();
 | 
			
		||||
            hotplugSniffer.SniffURIRemote
 | 
			
		||||
                (root.get_uri(), DBus.CALL_FLAG_START,
 | 
			
		||||
                 Lang.bind(this, function(contentTypes) {
 | 
			
		||||
                     this._emitCallback(mount, contentTypes);
 | 
			
		||||
                 }));
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _emitCallback: function(mount, contentTypes) {
 | 
			
		||||
        if (!contentTypes)
 | 
			
		||||
            contentTypes = [];
 | 
			
		||||
 | 
			
		||||
        // we're not interested in win32 software content types here
 | 
			
		||||
        contentTypes = contentTypes.filter(function(type) {
 | 
			
		||||
            return (type != 'x-content/win32-software');
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        let apps = [];
 | 
			
		||||
        contentTypes.forEach(function(type) {
 | 
			
		||||
            let app = Gio.app_info_get_default_for_type(type, false);
 | 
			
		||||
 | 
			
		||||
            if (app)
 | 
			
		||||
                apps.push(app);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        if (apps.length == 0)
 | 
			
		||||
            apps.push(Gio.app_info_get_default_for_type('inode/directory', false));
 | 
			
		||||
 | 
			
		||||
        this._callback(mount, apps, contentTypes);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function AutorunManager() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AutorunManager.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._volumeMonitor = Gio.VolumeMonitor.get();
 | 
			
		||||
 | 
			
		||||
        this._volumeMonitor.connect('mount-added',
 | 
			
		||||
                                    Lang.bind(this,
 | 
			
		||||
                                              this._onMountAdded));
 | 
			
		||||
        this._volumeMonitor.connect('mount-removed',
 | 
			
		||||
                                    Lang.bind(this,
 | 
			
		||||
                                              this._onMountRemoved));
 | 
			
		||||
 | 
			
		||||
        this._transDispatcher = new AutorunTransientDispatcher();
 | 
			
		||||
        this._createResidentSource();
 | 
			
		||||
 | 
			
		||||
        let mounts = this._volumeMonitor.get_mounts();
 | 
			
		||||
 | 
			
		||||
        mounts.forEach(Lang.bind(this, function (mount) {
 | 
			
		||||
            let discoverer = new ContentTypeDiscoverer(Lang.bind (this, 
 | 
			
		||||
                function (mount, apps) {
 | 
			
		||||
                    this._residentSource.addMount(mount, apps);
 | 
			
		||||
                }));
 | 
			
		||||
 | 
			
		||||
            discoverer.guessContentTypes(mount);
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createResidentSource: function() {
 | 
			
		||||
        this._residentSource = new AutorunResidentSource();
 | 
			
		||||
        this._residentSource.connect('destroy',
 | 
			
		||||
                                     Lang.bind(this,
 | 
			
		||||
                                               this._createResidentSource));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onMountAdded: function(monitor, mount) {
 | 
			
		||||
        // don't do anything if our session is not the currently
 | 
			
		||||
        // active one
 | 
			
		||||
        if (!Main.automountManager.ckListener.sessionActive)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let discoverer = new ContentTypeDiscoverer(Lang.bind (this,
 | 
			
		||||
            function (mount, apps, contentTypes) {
 | 
			
		||||
                this._transDispatcher.addMount(mount, apps, contentTypes);
 | 
			
		||||
                this._residentSource.addMount(mount, apps);
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
        discoverer.guessContentTypes(mount);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onMountRemoved: function(monitor, mount) {
 | 
			
		||||
        this._transDispatcher.removeMount(mount);
 | 
			
		||||
        this._residentSource.removeMount(mount);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    ejectMount: function(mount) {
 | 
			
		||||
        let mountOp = new ShellMountOperation.ShellMountOperation(mount);
 | 
			
		||||
 | 
			
		||||
        // first, see if we have a drive
 | 
			
		||||
        let drive = mount.get_drive();
 | 
			
		||||
        let volume = mount.get_volume();
 | 
			
		||||
 | 
			
		||||
        if (drive &&
 | 
			
		||||
            drive.get_start_stop_type() == Gio.DriveStartStopType.SHUTDOWN &&
 | 
			
		||||
            drive.can_stop()) {
 | 
			
		||||
            drive.stop(0, mountOp.mountOp, null,
 | 
			
		||||
                       Lang.bind(this, this._onStop));
 | 
			
		||||
        } else {
 | 
			
		||||
            if (mount.can_eject()) {
 | 
			
		||||
                mount.eject_with_operation(0, mountOp.mountOp, null,
 | 
			
		||||
                                           Lang.bind(this, this._onEject));
 | 
			
		||||
            } else if (volume && volume.can_eject()) {
 | 
			
		||||
                volume.eject_with_operation(0, mountOp.mountOp, null,
 | 
			
		||||
                                            Lang.bind(this, this._onEject));
 | 
			
		||||
            } else if (drive && drive.can_eject()) {
 | 
			
		||||
                drive.eject_with_operation(0, mountOp.mountOp, null,
 | 
			
		||||
                                           Lang.bind(this, this._onEject));
 | 
			
		||||
            } else if (mount.can_unmount()) {
 | 
			
		||||
                mount.unmount_with_operation(0, mountOp.mountOp, null,
 | 
			
		||||
                                             Lang.bind(this, this._onUnmount));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onUnmount: function(mount, res) {
 | 
			
		||||
        try {
 | 
			
		||||
            mount.unmount_with_operation_finish(res);
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
            // FIXME: we need to ignore G_IO_ERROR_FAILED_HANDLED errors here
 | 
			
		||||
            // but we can't access the error code from JS.
 | 
			
		||||
            // See https://bugzilla.gnome.org/show_bug.cgi?id=591480
 | 
			
		||||
            log('Unable to eject the mount ' + mount.get_name() 
 | 
			
		||||
                + ': ' + e.toString());
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onEject: function(source, res) {
 | 
			
		||||
        try {
 | 
			
		||||
            source.eject_with_operation_finish(res);
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
            // FIXME: we need to ignore G_IO_ERROR_FAILED_HANDLED errors here
 | 
			
		||||
            // but we can't access the error code from JS.
 | 
			
		||||
            // See https://bugzilla.gnome.org/show_bug.cgi?id=591480
 | 
			
		||||
            log('Unable to eject the drive ' + source.get_name() 
 | 
			
		||||
                + ': ' + e.toString());
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onStop: function(drive, res) {
 | 
			
		||||
        try {
 | 
			
		||||
            drive.stop_finish(res);
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
            // FIXME: we need to ignore G_IO_ERROR_FAILED_HANDLED errors here
 | 
			
		||||
            // but we can't access the error code from JS.
 | 
			
		||||
            // See https://bugzilla.gnome.org/show_bug.cgi?id=591480
 | 
			
		||||
            log('Unable to stop the drive ' + drive.get_name() 
 | 
			
		||||
                + ': ' + e.toString());
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function AutorunResidentSource() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AutorunResidentSource.prototype = {
 | 
			
		||||
    __proto__: MessageTray.Source.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        MessageTray.Source.prototype._init.call(this, _('Removable Devices'));
 | 
			
		||||
 | 
			
		||||
        this._mounts = [];
 | 
			
		||||
 | 
			
		||||
        this._notification = new AutorunResidentNotification(this);
 | 
			
		||||
        this._setSummaryIcon(this.createNotificationIcon(HOTPLUG_ICON_SIZE));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addMount: function(mount, apps) {
 | 
			
		||||
        if (ignoreAutorunForMount(mount))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let filtered = this._mounts.filter(function (element) {
 | 
			
		||||
            return (element.mount == mount);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        if (filtered.length != 0)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let element = { mount: mount, apps: apps };
 | 
			
		||||
        this._mounts.push(element);
 | 
			
		||||
        this._redisplay();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    removeMount: function(mount) {
 | 
			
		||||
        this._mounts =
 | 
			
		||||
            this._mounts.filter(function (element) {
 | 
			
		||||
                return (element.mount != mount);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        this._redisplay();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _redisplay: function() {
 | 
			
		||||
        if (this._mounts.length == 0) {
 | 
			
		||||
            this._notification.destroy();
 | 
			
		||||
            this.destroy();
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._notification.updateForMounts(this._mounts);
 | 
			
		||||
 | 
			
		||||
        // add ourselves as a source, and push the notification
 | 
			
		||||
        if (!Main.messageTray.contains(this)) {
 | 
			
		||||
            Main.messageTray.add(this);
 | 
			
		||||
            this.pushNotification(this._notification);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    createNotificationIcon: function(iconSize) {
 | 
			
		||||
        return new St.Icon ({ icon_name: 'drive-harddisk',
 | 
			
		||||
                              icon_size: iconSize ? iconSize : this.ICON_SIZE });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function AutorunResidentNotification(source) {
 | 
			
		||||
    this._init(source);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AutorunResidentNotification.prototype = {
 | 
			
		||||
    __proto__: MessageTray.Notification.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function(source) {
 | 
			
		||||
        MessageTray.Notification.prototype._init.call(this, source,
 | 
			
		||||
                                                      source.title, null,
 | 
			
		||||
                                                      { customContent: true });
 | 
			
		||||
 | 
			
		||||
        // set the notification as resident
 | 
			
		||||
        this.setResident(true);
 | 
			
		||||
 | 
			
		||||
        this._layout = new St.BoxLayout ({ style_class: 'hotplug-resident-box',
 | 
			
		||||
                                           vertical: true });
 | 
			
		||||
 | 
			
		||||
        this.addActor(this._layout,
 | 
			
		||||
                      { x_expand: true,
 | 
			
		||||
                        x_fill: true });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    updateForMounts: function(mounts) {
 | 
			
		||||
        // remove all the layout content
 | 
			
		||||
        this._layout.destroy_children();
 | 
			
		||||
 | 
			
		||||
        for (let idx = 0; idx < mounts.length; idx++) {
 | 
			
		||||
            let element = mounts[idx];
 | 
			
		||||
 | 
			
		||||
            let actor = this._itemForMount(element.mount, element.apps);
 | 
			
		||||
            this._layout.add(actor, { x_fill: true,
 | 
			
		||||
                                      expand: true });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _itemForMount: function(mount, apps) {
 | 
			
		||||
        let item = new St.BoxLayout();
 | 
			
		||||
 | 
			
		||||
        // prepare the mount button content
 | 
			
		||||
        let mountLayout = new St.BoxLayout();
 | 
			
		||||
 | 
			
		||||
        let mountIcon = new St.Icon({ gicon: mount.get_icon(),
 | 
			
		||||
                                      style_class: 'hotplug-resident-mount-icon' });
 | 
			
		||||
        mountLayout.add_actor(mountIcon);
 | 
			
		||||
 | 
			
		||||
        let labelBin = new St.Bin({ y_align: St.Align.MIDDLE });
 | 
			
		||||
        let mountLabel =
 | 
			
		||||
            new St.Label({ text: mount.get_name(),
 | 
			
		||||
                           style_class: 'hotplug-resident-mount-label',
 | 
			
		||||
                           track_hover: true,
 | 
			
		||||
                           reactive: true });
 | 
			
		||||
        labelBin.add_actor(mountLabel);
 | 
			
		||||
        mountLayout.add_actor(labelBin);
 | 
			
		||||
 | 
			
		||||
        let mountButton = new St.Button({ child: mountLayout,
 | 
			
		||||
                                          x_align: St.Align.START,
 | 
			
		||||
                                          x_fill: true,
 | 
			
		||||
                                          style_class: 'hotplug-resident-mount',
 | 
			
		||||
                                          button_mask: St.ButtonMask.ONE });
 | 
			
		||||
        item.add(mountButton, { x_align: St.Align.START,
 | 
			
		||||
                                expand: true });
 | 
			
		||||
 | 
			
		||||
        let ejectIcon = 
 | 
			
		||||
            new St.Icon({ icon_name: 'media-eject',
 | 
			
		||||
                          style_class: 'hotplug-resident-eject-icon' });
 | 
			
		||||
 | 
			
		||||
        let ejectButton =
 | 
			
		||||
            new St.Button({ style_class: 'hotplug-resident-eject-button',
 | 
			
		||||
                            button_mask: St.ButtonMask.ONE,
 | 
			
		||||
                            child: ejectIcon });
 | 
			
		||||
        item.add(ejectButton, { x_align: St.Align.END });
 | 
			
		||||
 | 
			
		||||
        // now connect signals
 | 
			
		||||
        mountButton.connect('clicked', Lang.bind(this, function(actor, event) {
 | 
			
		||||
            startAppForMount(apps[0], mount);
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        ejectButton.connect('clicked', Lang.bind(this, function() {
 | 
			
		||||
            Main.autorunManager.ejectMount(mount);
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        return item;
 | 
			
		||||
    },
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function AutorunTransientDispatcher() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AutorunTransientDispatcher.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._sources = [];
 | 
			
		||||
        this._settings = new Gio.Settings({ schema: SETTINGS_SCHEMA });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getAutorunSettingForType: function(contentType) {
 | 
			
		||||
        let runApp = this._settings.get_strv(SETTING_START_APP);
 | 
			
		||||
        if (runApp.indexOf(contentType) != -1)
 | 
			
		||||
            return AutorunSetting.RUN;
 | 
			
		||||
 | 
			
		||||
        let ignore = this._settings.get_strv(SETTING_IGNORE);
 | 
			
		||||
        if (ignore.indexOf(contentType) != -1)
 | 
			
		||||
            return AutorunSetting.IGNORE;
 | 
			
		||||
 | 
			
		||||
        let openFiles = this._settings.get_strv(SETTING_OPEN_FOLDER);
 | 
			
		||||
        if (openFiles.indexOf(contentType) != -1)
 | 
			
		||||
            return AutorunSetting.FILES;
 | 
			
		||||
 | 
			
		||||
        return AutorunSetting.ASK;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getSourceForMount: function(mount) {
 | 
			
		||||
        let filtered =
 | 
			
		||||
            this._sources.filter(function (source) {
 | 
			
		||||
                return (source.mount == mount);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        // we always make sure not to add two sources for the same
 | 
			
		||||
        // mount in addMount(), so it's safe to assume filtered.length
 | 
			
		||||
        // is always either 1 or 0.
 | 
			
		||||
        if (filtered.length == 1)
 | 
			
		||||
            return filtered[0];
 | 
			
		||||
 | 
			
		||||
        return null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _addSource: function(mount, apps) {
 | 
			
		||||
        // if we already have a source showing for this 
 | 
			
		||||
        // mount, return
 | 
			
		||||
        if (this._getSourceForMount(mount))
 | 
			
		||||
            return;
 | 
			
		||||
     
 | 
			
		||||
        // add a new source
 | 
			
		||||
        this._sources.push(new AutorunTransientSource(mount, apps));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addMount: function(mount, apps, contentTypes) {
 | 
			
		||||
        // if autorun is disabled globally, return
 | 
			
		||||
        if (this._settings.get_boolean(SETTING_DISABLE_AUTORUN))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        // if the mount doesn't want to be autorun, return
 | 
			
		||||
        if (ignoreAutorunForMount(mount))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let setting = this._getAutorunSettingForType(contentTypes[0]);
 | 
			
		||||
 | 
			
		||||
        // check at the settings for the first content type
 | 
			
		||||
        // to see whether we should ask
 | 
			
		||||
        if (setting == AutorunSetting.IGNORE)
 | 
			
		||||
            return; // return right away
 | 
			
		||||
 | 
			
		||||
        let success = false;
 | 
			
		||||
        let app = null;
 | 
			
		||||
 | 
			
		||||
        if (setting == AutorunSetting.RUN) {
 | 
			
		||||
            app = Gio.app_info_get_default_for_type(type, false);
 | 
			
		||||
        } else if (setting == AutorunSetting.FILES) {
 | 
			
		||||
            app = Gio.app_info_get_default_for_type('inode/directory', false);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (app)
 | 
			
		||||
            success = startAppForMount(app, mount);
 | 
			
		||||
 | 
			
		||||
        // we fallback here also in case the settings did not specify 'ask',
 | 
			
		||||
        // but we failed launching the default app or the default file manager
 | 
			
		||||
        if (!success)
 | 
			
		||||
            this._addSource(mount, apps);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    removeMount: function(mount) {
 | 
			
		||||
        let source = this._getSourceForMount(mount);
 | 
			
		||||
        
 | 
			
		||||
        // if we aren't tracking this mount, don't do anything
 | 
			
		||||
        if (!source)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        // destroy the notification source
 | 
			
		||||
        source.destroy();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function AutorunTransientSource(mount, apps) {
 | 
			
		||||
    this._init(mount, apps);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AutorunTransientSource.prototype = {
 | 
			
		||||
    __proto__: MessageTray.Source.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function(mount, apps) {
 | 
			
		||||
        MessageTray.Source.prototype._init.call(this, mount.get_name());
 | 
			
		||||
 | 
			
		||||
        this.mount = mount;
 | 
			
		||||
        this.apps = apps;
 | 
			
		||||
 | 
			
		||||
        this._notification = new AutorunTransientNotification(this);
 | 
			
		||||
        this._setSummaryIcon(this.createNotificationIcon(this.ICON_SIZE));
 | 
			
		||||
 | 
			
		||||
        // add ourselves as a source, and popup the notification
 | 
			
		||||
        Main.messageTray.add(this);
 | 
			
		||||
        this.notify(this._notification);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    createNotificationIcon: function(iconSize) {
 | 
			
		||||
        return new St.Icon({ gicon: this.mount.get_icon(),
 | 
			
		||||
                             icon_size: iconSize ? iconSize : this.ICON_SIZE });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function AutorunTransientNotification(source) {
 | 
			
		||||
    this._init(source);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AutorunTransientNotification.prototype = {
 | 
			
		||||
    __proto__: MessageTray.Notification.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function(source) {
 | 
			
		||||
        MessageTray.Notification.prototype._init.call(this, source,
 | 
			
		||||
                                                      source.title, null,
 | 
			
		||||
                                                      { customContent: true });
 | 
			
		||||
 | 
			
		||||
        this._box = new St.BoxLayout({ style_class: 'hotplug-transient-box',
 | 
			
		||||
                                       vertical: true });
 | 
			
		||||
        this.addActor(this._box);
 | 
			
		||||
 | 
			
		||||
        this._mount = source.mount;
 | 
			
		||||
 | 
			
		||||
        source.apps.forEach(Lang.bind(this, function (app) {
 | 
			
		||||
            let actor = this._buttonForApp(app);
 | 
			
		||||
 | 
			
		||||
            if (actor)
 | 
			
		||||
                this._box.add(actor, { x_fill: true,
 | 
			
		||||
                                       x_align: St.Align.START });
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        this._box.add(this._buttonForEject(), { x_fill: true,
 | 
			
		||||
                                                x_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        // set the notification to transient and urgent, so that it
 | 
			
		||||
        // expands out
 | 
			
		||||
        this.setTransient(true);
 | 
			
		||||
        this.setUrgency(MessageTray.Urgency.CRITICAL);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _buttonForApp: function(app) {
 | 
			
		||||
        let box = new St.BoxLayout();
 | 
			
		||||
        let icon = new St.Icon({ gicon: app.get_icon(),
 | 
			
		||||
                                 style_class: 'hotplug-notification-item-icon' });
 | 
			
		||||
        box.add(icon);
 | 
			
		||||
 | 
			
		||||
        let label = new St.Bin({ y_align: St.Align.MIDDLE,
 | 
			
		||||
                                 child: new St.Label
 | 
			
		||||
                                 ({ text: _("Open with %s").format(app.get_display_name()) })
 | 
			
		||||
                               });
 | 
			
		||||
        box.add(label);
 | 
			
		||||
 | 
			
		||||
        let button = new St.Button({ child: box,
 | 
			
		||||
                                     x_fill: true,
 | 
			
		||||
                                     x_align: St.Align.START,
 | 
			
		||||
                                     button_mask: St.ButtonMask.ONE,
 | 
			
		||||
                                     style_class: 'hotplug-notification-item' });
 | 
			
		||||
 | 
			
		||||
        button.connect('clicked', Lang.bind(this, function() {
 | 
			
		||||
            startAppForMount(app, this._mount);
 | 
			
		||||
            this.destroy();
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        return button;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _buttonForEject: function() {
 | 
			
		||||
        let box = new St.BoxLayout();
 | 
			
		||||
        let icon = new St.Icon({ icon_name: 'media-eject',
 | 
			
		||||
                                 style_class: 'hotplug-notification-item-icon' });
 | 
			
		||||
        box.add(icon);
 | 
			
		||||
 | 
			
		||||
        let label = new St.Bin({ y_align: St.Align.MIDDLE,
 | 
			
		||||
                                 child: new St.Label
 | 
			
		||||
                                 ({ text: _("Eject") })
 | 
			
		||||
                               });
 | 
			
		||||
        box.add(label);
 | 
			
		||||
 | 
			
		||||
        let button = new St.Button({ child: box,
 | 
			
		||||
                                     x_fill: true,
 | 
			
		||||
                                     x_align: St.Align.START,
 | 
			
		||||
                                     button_mask: St.ButtonMask.ONE,
 | 
			
		||||
                                     style_class: 'hotplug-notification-item' });
 | 
			
		||||
 | 
			
		||||
        button.connect('clicked', Lang.bind(this, function() {
 | 
			
		||||
            Main.autorunManager.ejectMount(this._mount);
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        return button;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -2,18 +2,16 @@
 | 
			
		||||
 | 
			
		||||
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 Tweener = imports.ui.tweener;
 | 
			
		||||
 | 
			
		||||
const POPUP_ANIMATION_TIME = 0.15;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * BoxPointer:
 | 
			
		||||
 * @side: side to draw the arrow on
 | 
			
		||||
 * @side: A St.Side type; currently only St.Side.TOP is implemented
 | 
			
		||||
 * @binProperties: Properties to set on contained bin
 | 
			
		||||
 *
 | 
			
		||||
 * An actor which displays a triangle "arrow" pointing to a given
 | 
			
		||||
@@ -42,80 +40,76 @@ BoxPointer.prototype = {
 | 
			
		||||
        this._border.connect('repaint', Lang.bind(this, this._drawBorder));
 | 
			
		||||
        this._container.add_actor(this._border);
 | 
			
		||||
        this.bin.raise(this._border);
 | 
			
		||||
        this._xOffset = 0;
 | 
			
		||||
        this._yOffset = 0;
 | 
			
		||||
        this._xPosition = 0;
 | 
			
		||||
        this._yPosition = 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    show: function(animate, onComplete) {
 | 
			
		||||
    animateAppear: function(onComplete) {
 | 
			
		||||
        let x = this.actor.x;
 | 
			
		||||
        let y = this.actor.y;
 | 
			
		||||
        let themeNode = this.actor.get_theme_node();
 | 
			
		||||
        let rise = themeNode.get_length('-arrow-rise');
 | 
			
		||||
 | 
			
		||||
        this.opacity = 0;
 | 
			
		||||
        this.actor.opacity = 0;
 | 
			
		||||
        this.actor.show();
 | 
			
		||||
 | 
			
		||||
        if (animate) {
 | 
			
		||||
            switch (this._arrowSide) {
 | 
			
		||||
                case St.Side.TOP:
 | 
			
		||||
                    this.yOffset = -rise;
 | 
			
		||||
                    break;
 | 
			
		||||
                case St.Side.BOTTOM:
 | 
			
		||||
                    this.yOffset = rise;
 | 
			
		||||
                    break;
 | 
			
		||||
                case St.Side.LEFT:
 | 
			
		||||
                    this.xOffset = -rise;
 | 
			
		||||
                    break;
 | 
			
		||||
                case St.Side.RIGHT:
 | 
			
		||||
                    this.xOffset = rise;
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
        switch (this._arrowSide) {
 | 
			
		||||
            case St.Side.TOP:
 | 
			
		||||
                this.actor.y -= rise;
 | 
			
		||||
                break;
 | 
			
		||||
            case St.Side.BOTTOM:
 | 
			
		||||
                this.actor.y += rise;
 | 
			
		||||
                break;
 | 
			
		||||
            case St.Side.LEFT:
 | 
			
		||||
                this.actor.x -= rise;
 | 
			
		||||
                break;
 | 
			
		||||
            case St.Side.RIGHT:
 | 
			
		||||
                this.actor.x += rise;
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Tweener.addTween(this, { opacity: 255,
 | 
			
		||||
                                 xOffset: 0,
 | 
			
		||||
                                 yOffset: 0,
 | 
			
		||||
                                 transition: "linear",
 | 
			
		||||
                                 onComplete: onComplete,
 | 
			
		||||
                                 time: POPUP_ANIMATION_TIME });
 | 
			
		||||
        Tweener.addTween(this.actor, { opacity: 255,
 | 
			
		||||
                                       x: x,
 | 
			
		||||
                                       y: y,
 | 
			
		||||
                                       transition: "linear",
 | 
			
		||||
                                       onComplete: onComplete,
 | 
			
		||||
                                       time: POPUP_ANIMATION_TIME });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    hide: function(animate, onComplete) {
 | 
			
		||||
        let xOffset = 0;
 | 
			
		||||
        let yOffset = 0;
 | 
			
		||||
    animateDisappear: function(onComplete) {
 | 
			
		||||
        let x = this.actor.x;
 | 
			
		||||
        let y = this.actor.y;
 | 
			
		||||
        let originalX = this.actor.x;
 | 
			
		||||
        let originalY = this.actor.y;
 | 
			
		||||
        let themeNode = this.actor.get_theme_node();
 | 
			
		||||
        let rise = themeNode.get_length('-arrow-rise');
 | 
			
		||||
 | 
			
		||||
        if (animate) {
 | 
			
		||||
            switch (this._arrowSide) {
 | 
			
		||||
                case St.Side.TOP:
 | 
			
		||||
                    yOffset = rise;
 | 
			
		||||
                    break;
 | 
			
		||||
                case St.Side.BOTTOM:
 | 
			
		||||
                    yOffset = -rise;
 | 
			
		||||
                    break;
 | 
			
		||||
                case St.Side.LEFT:
 | 
			
		||||
                    xOffset = rise;
 | 
			
		||||
                    break;
 | 
			
		||||
                case St.Side.RIGHT:
 | 
			
		||||
                    xOffset = -rise;
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
        switch (this._arrowSide) {
 | 
			
		||||
            case St.Side.TOP:
 | 
			
		||||
                y += rise;
 | 
			
		||||
                break;
 | 
			
		||||
            case St.Side.BOTTOM:
 | 
			
		||||
                y -= rise;
 | 
			
		||||
                break;
 | 
			
		||||
            case St.Side.LEFT:
 | 
			
		||||
                x += rise;
 | 
			
		||||
                break;
 | 
			
		||||
            case St.Side.RIGHT:
 | 
			
		||||
                x -= rise;
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Tweener.addTween(this, { opacity: 0,
 | 
			
		||||
                                 xOffset: xOffset,
 | 
			
		||||
                                 yOffset: yOffset,
 | 
			
		||||
                                 transition: "linear",
 | 
			
		||||
                                 time: POPUP_ANIMATION_TIME,
 | 
			
		||||
                                 onComplete: Lang.bind(this, function () {
 | 
			
		||||
                                     this.actor.hide();
 | 
			
		||||
                                     this.xOffset = 0;
 | 
			
		||||
                                     this.yOffset = 0;
 | 
			
		||||
                                     if (onComplete)
 | 
			
		||||
                                         onComplete();
 | 
			
		||||
                                 })
 | 
			
		||||
                               });
 | 
			
		||||
        Tweener.addTween(this.actor, { opacity: 0,
 | 
			
		||||
                                       x: x,
 | 
			
		||||
                                       y: y,
 | 
			
		||||
                                       transition: "linear",
 | 
			
		||||
                                       time: POPUP_ANIMATION_TIME,
 | 
			
		||||
                                       onComplete: Lang.bind(this, function () {
 | 
			
		||||
                                           this.actor.hide();
 | 
			
		||||
                                           this.actor.x = originalX;
 | 
			
		||||
                                           this.actor.y = originalY;
 | 
			
		||||
                                           if (onComplete)
 | 
			
		||||
                                               onComplete();
 | 
			
		||||
                                       })
 | 
			
		||||
                                     });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _adjustAllocationForArrow: function(isWidth, alloc) {
 | 
			
		||||
@@ -178,9 +172,6 @@ BoxPointer.prototype = {
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
        this.bin.allocate(childBox, flags);
 | 
			
		||||
 | 
			
		||||
        if (this._sourceActor && this._sourceActor.mapped)
 | 
			
		||||
            this._reposition(this._sourceActor, this._gap, this._alignment);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _drawBorder: function(area) {
 | 
			
		||||
@@ -194,8 +185,10 @@ BoxPointer.prototype = {
 | 
			
		||||
        let halfBorder = borderWidth / 2;
 | 
			
		||||
        let halfBase = Math.floor(base/2);
 | 
			
		||||
 | 
			
		||||
        let borderColor = themeNode.get_color('-arrow-border-color');
 | 
			
		||||
        let backgroundColor = themeNode.get_color('-arrow-background-color');
 | 
			
		||||
        let borderColor = new Clutter.Color();
 | 
			
		||||
        themeNode.get_color('-arrow-border-color', borderColor);
 | 
			
		||||
        let backgroundColor = new Clutter.Color();
 | 
			
		||||
        themeNode.get_color('-arrow-background-color', backgroundColor);
 | 
			
		||||
 | 
			
		||||
        let [width, height] = area.get_surface_size();
 | 
			
		||||
        let [boxWidth, boxHeight] = [width, height];
 | 
			
		||||
@@ -215,88 +208,46 @@ BoxPointer.prototype = {
 | 
			
		||||
            cr.translate(rise, 0);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let [x1, y1] = [halfBorder, halfBorder];
 | 
			
		||||
        let [x2, y2] = [boxWidth - halfBorder, boxHeight - halfBorder];
 | 
			
		||||
        cr.moveTo(borderRadius, halfBorder);
 | 
			
		||||
 | 
			
		||||
        cr.moveTo(x1 + borderRadius, y1);
 | 
			
		||||
        if (this._arrowSide == St.Side.TOP) {
 | 
			
		||||
            if (this._arrowOrigin < (x1 + (borderRadius + halfBase))) {
 | 
			
		||||
                cr.lineTo(this._arrowOrigin, y1 - rise);
 | 
			
		||||
                cr.lineTo(Math.max(x1 + borderRadius, this._arrowOrigin) + halfBase, y1);
 | 
			
		||||
            } else if (this._arrowOrigin > (x2 - (borderRadius + halfBase))) {
 | 
			
		||||
                cr.lineTo(Math.min(x2 - borderRadius, this._arrowOrigin) - halfBase, y1);
 | 
			
		||||
                cr.lineTo(this._arrowOrigin, y1 - rise);
 | 
			
		||||
            } else {
 | 
			
		||||
                cr.lineTo(this._arrowOrigin - halfBase, y1);
 | 
			
		||||
                cr.lineTo(this._arrowOrigin, y1 - rise);
 | 
			
		||||
                cr.lineTo(this._arrowOrigin + halfBase, y1);
 | 
			
		||||
            }
 | 
			
		||||
            cr.lineTo(this._arrowOrigin - halfBase, halfBorder);
 | 
			
		||||
            cr.lineTo(this._arrowOrigin, halfBorder - rise);
 | 
			
		||||
            cr.lineTo(this._arrowOrigin + halfBase, halfBorder);
 | 
			
		||||
        }
 | 
			
		||||
        cr.lineTo(boxWidth - borderRadius, halfBorder);
 | 
			
		||||
 | 
			
		||||
        cr.lineTo(x2 - borderRadius, y1);
 | 
			
		||||
 | 
			
		||||
        // top-right corner
 | 
			
		||||
        cr.arc(x2 - borderRadius, y1 + borderRadius, borderRadius,
 | 
			
		||||
        cr.arc(boxWidth - borderRadius - halfBorder, borderRadius + halfBorder, borderRadius,
 | 
			
		||||
               3*Math.PI/2, Math.PI*2);
 | 
			
		||||
 | 
			
		||||
        if (this._arrowSide == St.Side.RIGHT) {
 | 
			
		||||
            if (this._arrowOrigin < (y1 + (borderRadius + halfBase))) {
 | 
			
		||||
                cr.lineTo(x2 + rise, this._arrowOrigin);
 | 
			
		||||
                cr.lineTo(x2, Math.max(y1 + borderRadius, this._arrowOrigin) + halfBase);
 | 
			
		||||
            } else if (this._arrowOrigin > (y2 - (borderRadius + halfBase))) {
 | 
			
		||||
                cr.lineTo(x2, Math.min(y2 - borderRadius, this._arrowOrigin) - halfBase);
 | 
			
		||||
                cr.lineTo(x2 + rise, this._arrowOrigin);
 | 
			
		||||
            } else {
 | 
			
		||||
                cr.lineTo(x2, this._arrowOrigin - halfBase);
 | 
			
		||||
                cr.lineTo(x2 + rise, this._arrowOrigin);
 | 
			
		||||
                cr.lineTo(x2, this._arrowOrigin + halfBase);
 | 
			
		||||
            }
 | 
			
		||||
            cr.lineTo(boxWidth - halfBorder, this._arrowOrigin - halfBase);
 | 
			
		||||
            cr.lineTo(boxWidth - halfBorder + rise, this._arrowOrigin);
 | 
			
		||||
            cr.lineTo(boxWidth - halfBorder, this._arrowOrigin + halfBase);
 | 
			
		||||
        }
 | 
			
		||||
        cr.lineTo(boxWidth - halfBorder, boxHeight - borderRadius);
 | 
			
		||||
 | 
			
		||||
        cr.lineTo(x2, y2 - borderRadius);
 | 
			
		||||
 | 
			
		||||
        // bottom-right corner
 | 
			
		||||
        cr.arc(x2 - borderRadius, y2 - borderRadius, borderRadius,
 | 
			
		||||
        cr.arc(boxWidth - borderRadius - halfBorder, boxHeight - borderRadius - halfBorder, borderRadius,
 | 
			
		||||
               0, Math.PI/2);
 | 
			
		||||
 | 
			
		||||
        if (this._arrowSide == St.Side.BOTTOM) {
 | 
			
		||||
            if (this._arrowOrigin < (x1 + (borderRadius + halfBase))) {
 | 
			
		||||
                cr.lineTo(Math.max(x1 + borderRadius, this._arrowOrigin) + halfBase, y2);
 | 
			
		||||
                cr.lineTo(this._arrowOrigin, y2 + rise);
 | 
			
		||||
            } else if (this._arrowOrigin > (x2 - (borderRadius + halfBase))) {
 | 
			
		||||
                cr.lineTo(this._arrowOrigin, y2 + rise);
 | 
			
		||||
                cr.lineTo(Math.min(x2 - borderRadius, this._arrowOrigin) - halfBase, y2);
 | 
			
		||||
            } else {
 | 
			
		||||
                cr.lineTo(this._arrowOrigin + halfBase, y2);
 | 
			
		||||
                cr.lineTo(this._arrowOrigin, y2 + rise);
 | 
			
		||||
                cr.lineTo(this._arrowOrigin - halfBase, y2);
 | 
			
		||||
            }
 | 
			
		||||
            cr.lineTo(this._arrowOrigin + halfBase, boxHeight - halfBorder);
 | 
			
		||||
            cr.lineTo(this._arrowOrigin, boxHeight - halfBorder + rise);
 | 
			
		||||
            cr.lineTo(this._arrowOrigin - halfBase, boxHeight - halfBorder);
 | 
			
		||||
        }
 | 
			
		||||
        cr.lineTo(borderRadius, boxHeight - halfBorder);
 | 
			
		||||
 | 
			
		||||
        cr.lineTo(x1 + borderRadius, y2);
 | 
			
		||||
 | 
			
		||||
        // bottom-left corner
 | 
			
		||||
        cr.arc(x1 + borderRadius, y2 - borderRadius, borderRadius,
 | 
			
		||||
        cr.arc(borderRadius + halfBorder, boxHeight - borderRadius - halfBorder, borderRadius,
 | 
			
		||||
               Math.PI/2, Math.PI);
 | 
			
		||||
 | 
			
		||||
        if (this._arrowSide == St.Side.LEFT) {
 | 
			
		||||
            if (this._arrowOrigin < (y1 + (borderRadius + halfBase))) {
 | 
			
		||||
                cr.lineTo(x1, Math.max(y1 + borderRadius, this._arrowOrigin) + halfBase);
 | 
			
		||||
                cr.lineTo(x1 - rise, this._arrowOrigin);
 | 
			
		||||
            } else if (this._arrowOrigin > (y2 - (borderRadius + halfBase))) {
 | 
			
		||||
                cr.lineTo(x1 - rise, this._arrowOrigin);
 | 
			
		||||
                cr.lineTo(x1, Math.min(y2 - borderRadius, this._arrowOrigin) - halfBase);
 | 
			
		||||
            } else {
 | 
			
		||||
                cr.lineTo(x1, this._arrowOrigin + halfBase);
 | 
			
		||||
                cr.lineTo(x1 - rise, this._arrowOrigin);
 | 
			
		||||
                cr.lineTo(x1, this._arrowOrigin - halfBase);
 | 
			
		||||
            }
 | 
			
		||||
            cr.lineTo(halfBorder, this._arrowOrigin + halfBase);
 | 
			
		||||
            cr.lineTo(halfBorder - rise, this._arrowOrigin);
 | 
			
		||||
            cr.lineTo(halfBorder, this._arrowOrigin - halfBase);
 | 
			
		||||
        }
 | 
			
		||||
        cr.lineTo(halfBorder, borderRadius);
 | 
			
		||||
 | 
			
		||||
        cr.lineTo(x1, y1 + borderRadius);
 | 
			
		||||
 | 
			
		||||
        // top-left corner
 | 
			
		||||
        cr.arc(x1 + borderRadius, y1 + borderRadius, borderRadius,
 | 
			
		||||
        cr.arc(borderRadius + halfBorder, borderRadius + halfBorder, borderRadius,
 | 
			
		||||
               Math.PI, 3*Math.PI/2);
 | 
			
		||||
 | 
			
		||||
        Clutter.cairo_set_source_color(cr, backgroundColor);
 | 
			
		||||
@@ -311,47 +262,32 @@ BoxPointer.prototype = {
 | 
			
		||||
        // so that we can query the correct size.
 | 
			
		||||
        this.actor.show();
 | 
			
		||||
 | 
			
		||||
        this._sourceActor = sourceActor;
 | 
			
		||||
        this._gap = gap;
 | 
			
		||||
        this._alignment = alignment;
 | 
			
		||||
 | 
			
		||||
        this._reposition(sourceActor, gap, alignment);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _reposition: function(sourceActor, gap, alignment) {
 | 
			
		||||
        // Position correctly relative to the sourceActor
 | 
			
		||||
        let sourceNode = sourceActor.get_theme_node();
 | 
			
		||||
        let sourceContentBox = sourceNode.get_content_box(sourceActor.get_allocation_box());
 | 
			
		||||
        let sourceAllocation = Shell.util_get_transformed_allocation(sourceActor);
 | 
			
		||||
        let sourceCenterX = sourceAllocation.x1 + sourceContentBox.x1 + (sourceContentBox.x2 - sourceContentBox.x1) / 2;
 | 
			
		||||
        let sourceCenterY = sourceAllocation.y1 + sourceContentBox.y1 + (sourceContentBox.y2 - sourceContentBox.y1) / 2;
 | 
			
		||||
        let [sourceX, sourceY] = sourceActor.get_transformed_position();
 | 
			
		||||
        let [sourceWidth, sourceHeight] = sourceActor.get_transformed_size();
 | 
			
		||||
 | 
			
		||||
        let [minWidth, minHeight, natWidth, natHeight] = this.actor.get_preferred_size();
 | 
			
		||||
 | 
			
		||||
        // We also want to keep it onscreen, and separated from the
 | 
			
		||||
        // edge by the same distance as the main part of the box is
 | 
			
		||||
        // separated from its sourceActor
 | 
			
		||||
        let primary = Main.layoutManager.primaryMonitor;
 | 
			
		||||
        let themeNode = this.actor.get_theme_node();
 | 
			
		||||
        let borderWidth = themeNode.get_length('-arrow-border-width');
 | 
			
		||||
        let arrowBase = themeNode.get_length('-arrow-base');
 | 
			
		||||
        let borderRadius = themeNode.get_length('-arrow-border-radius');
 | 
			
		||||
        let margin = (4 * borderRadius + borderWidth + arrowBase);
 | 
			
		||||
        let halfMargin = margin / 2;
 | 
			
		||||
        let primary = global.get_primary_monitor();
 | 
			
		||||
        let arrowRise = this.actor.get_theme_node().get_length('-arrow-rise');
 | 
			
		||||
 | 
			
		||||
        let resX, resY;
 | 
			
		||||
 | 
			
		||||
        switch (this._arrowSide) {
 | 
			
		||||
        case St.Side.TOP:
 | 
			
		||||
            resY = sourceAllocation.y2 + gap;
 | 
			
		||||
            resY = sourceY + sourceHeight + gap;
 | 
			
		||||
            break;
 | 
			
		||||
        case St.Side.BOTTOM:
 | 
			
		||||
            resY = sourceAllocation.y1 - natHeight - gap;
 | 
			
		||||
            resY = sourceY - natHeight - gap;
 | 
			
		||||
            break;
 | 
			
		||||
        case St.Side.LEFT:
 | 
			
		||||
            resX = sourceAllocation.x2 + gap;
 | 
			
		||||
            resX = sourceX + sourceWidth + gap;
 | 
			
		||||
            break;
 | 
			
		||||
        case St.Side.RIGHT:
 | 
			
		||||
            resX = sourceAllocation.x1 - natWidth - gap;
 | 
			
		||||
            resX = sourceX - natWidth - gap;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -360,21 +296,42 @@ BoxPointer.prototype = {
 | 
			
		||||
        switch (this._arrowSide) {
 | 
			
		||||
        case St.Side.TOP:
 | 
			
		||||
        case St.Side.BOTTOM:
 | 
			
		||||
            resX = sourceCenterX - (halfMargin + (natWidth - margin) * alignment);
 | 
			
		||||
            switch (alignment) {
 | 
			
		||||
            case St.Align.START:
 | 
			
		||||
                resX = sourceX;
 | 
			
		||||
                break;
 | 
			
		||||
            case St.Align.MIDDLE:
 | 
			
		||||
                resX = sourceX - Math.floor((natWidth - sourceWidth) / 2);
 | 
			
		||||
                break;
 | 
			
		||||
            case St.Align.END:
 | 
			
		||||
                resX = sourceX - (natWidth - sourceWidth);
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            resX = Math.max(resX, primary.x + 10);
 | 
			
		||||
            resX = Math.min(resX, primary.x + primary.width - (10 + natWidth));
 | 
			
		||||
            this.setArrowOrigin(sourceCenterX - resX);
 | 
			
		||||
            resX = Math.min(resX, primary.x + primary.width - natWidth - arrowRise - gap);
 | 
			
		||||
            resX = Math.max(resX, primary.x);
 | 
			
		||||
 | 
			
		||||
            this.setArrowOrigin((sourceX - resX) + Math.floor(sourceWidth / 2));
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case St.Side.LEFT:
 | 
			
		||||
        case St.Side.RIGHT:
 | 
			
		||||
            resY = sourceCenterY - (halfMargin + (natHeight - margin) * alignment);
 | 
			
		||||
            switch (alignment) {
 | 
			
		||||
            case St.Align.START:
 | 
			
		||||
                resY = sourceY;
 | 
			
		||||
                break;
 | 
			
		||||
            case St.Align.MIDDLE:
 | 
			
		||||
                resY = sourceY - Math.floor((natHeight - sourceHeight) / 2);
 | 
			
		||||
                break;
 | 
			
		||||
            case St.Align.END:
 | 
			
		||||
                resY = sourceY - (natHeight - sourceHeight);
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            resY = Math.max(resY, primary.y + 10);
 | 
			
		||||
            resY = Math.min(resY, primary.y + primary.height - (10 + natHeight));
 | 
			
		||||
            resY = Math.min(resY, primary.y + primary.height - natHeight - arrowRise - gap);
 | 
			
		||||
            resY = Math.max(resY, primary.y);
 | 
			
		||||
 | 
			
		||||
            this.setArrowOrigin(sourceCenterY - resY);
 | 
			
		||||
            this.setArrowOrigin((sourceY - resY) + Math.floor(sourceHeight / 2));
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -385,9 +342,9 @@ BoxPointer.prototype = {
 | 
			
		||||
            parent = parent.get_parent();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._xPosition = Math.floor(x);
 | 
			
		||||
        this._yPosition = Math.floor(y);
 | 
			
		||||
        this._shiftActor();
 | 
			
		||||
        // Actually set the position
 | 
			
		||||
        this.actor.x = Math.floor(x);
 | 
			
		||||
        this.actor.y = Math.floor(y);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // @origin: Coordinate specifying middle of the arrow, along
 | 
			
		||||
@@ -398,42 +355,5 @@ BoxPointer.prototype = {
 | 
			
		||||
            this._arrowOrigin = origin;
 | 
			
		||||
            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 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.
 | 
			
		||||
        this.actor.set_anchor_point(-(this._xPosition + this._xOffset),
 | 
			
		||||
                                    -(this._yPosition + this._yOffset));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    set xOffset(offset) {
 | 
			
		||||
        this._xOffset = offset;
 | 
			
		||||
        this._shiftActor();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get xOffset() {
 | 
			
		||||
        return this._xOffset;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    set yOffset(offset) {
 | 
			
		||||
        this._yOffset = offset;
 | 
			
		||||
        this._shiftActor();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get yOffset() {
 | 
			
		||||
        return this._yOffset;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    set opacity(opacity) {
 | 
			
		||||
        this.actor.opacity = opacity;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get opacity() {
 | 
			
		||||
        return this.actor.opacity;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,84 +1,22 @@
 | 
			
		||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
 | 
			
		||||
 | 
			
		||||
const DBus = imports.dbus;
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const Pango = imports.gi.Pango;
 | 
			
		||||
const Gettext_gtk30 = imports.gettext.domain('gtk30');
 | 
			
		||||
const Mainloop = imports.mainloop;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Gettext_gtk20 = imports.gettext.domain('gtk20');
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
const CLOCK_FORMAT_KEY        = 'clock-format';
 | 
			
		||||
 | 
			
		||||
function _sameDay(dateA, dateB) {
 | 
			
		||||
    return (dateA.getDate() == dateB.getDate() &&
 | 
			
		||||
            dateA.getMonth() == dateB.getMonth() &&
 | 
			
		||||
            dateA.getYear() == dateB.getYear());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _sameYear(dateA, dateB) {
 | 
			
		||||
    return (dateA.getYear() == dateB.getYear());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* TODO: maybe needs config - right now we assume that Saturday and
 | 
			
		||||
 * Sunday are non-work days (not true in e.g. Israel, it's Sunday and
 | 
			
		||||
 * Monday there)
 | 
			
		||||
 */
 | 
			
		||||
function _isWorkDay(date) {
 | 
			
		||||
    return date.getDay() != 0 && date.getDay() != 6;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _getBeginningOfDay(date) {
 | 
			
		||||
    let ret = new Date(date.getTime());
 | 
			
		||||
    ret.setHours(0);
 | 
			
		||||
    ret.setMinutes(0);
 | 
			
		||||
    ret.setSeconds(0);
 | 
			
		||||
    ret.setMilliseconds(0);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _getEndOfDay(date) {
 | 
			
		||||
    let ret = new Date(date.getTime());
 | 
			
		||||
    ret.setHours(23);
 | 
			
		||||
    ret.setMinutes(59);
 | 
			
		||||
    ret.setSeconds(59);
 | 
			
		||||
    ret.setMilliseconds(999);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _formatEventTime(event, clockFormat) {
 | 
			
		||||
    let ret;
 | 
			
		||||
    if (event.allDay) {
 | 
			
		||||
        /* Translators: Shown in calendar event list for all day events
 | 
			
		||||
         * Keep it short, best if you can use less then 10 characters
 | 
			
		||||
         */
 | 
			
		||||
        ret = C_("event list time", "All Day");
 | 
			
		||||
    } else {
 | 
			
		||||
        switch (clockFormat) {
 | 
			
		||||
        case '24h':
 | 
			
		||||
            /* 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':
 | 
			
		||||
            /* Transators: Shown in calendar event list, if 12h format */
 | 
			
		||||
            ret = event.date.toLocaleFormat(C_("event list time", "%l:%M %p"));
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _getCalendarWeekForDate(date) {
 | 
			
		||||
    // Based on the algorithms found here:
 | 
			
		||||
    // http://en.wikipedia.org/wiki/Talk:ISO_week_date
 | 
			
		||||
@@ -105,259 +43,12 @@ function _getDigitWidth(actor){
 | 
			
		||||
    return width;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _getCalendarDayAbbreviation(dayNumber) {
 | 
			
		||||
    let abbreviations = [
 | 
			
		||||
        /* Translators: Calendar grid abbreviation for Sunday.
 | 
			
		||||
         *
 | 
			
		||||
         * NOTE: These grid abbreviations are always shown together
 | 
			
		||||
         * and in order, e.g. "S M T W T F S".
 | 
			
		||||
         */
 | 
			
		||||
        C_("grid sunday", "S"),
 | 
			
		||||
        /* Translators: Calendar grid abbreviation for Monday */
 | 
			
		||||
        C_("grid monday", "M"),
 | 
			
		||||
        /* Translators: Calendar grid abbreviation for Tuesday */
 | 
			
		||||
        C_("grid tuesday", "T"),
 | 
			
		||||
        /* Translators: Calendar grid abbreviation for Wednesday */
 | 
			
		||||
        C_("grid wednesday", "W"),
 | 
			
		||||
        /* Translators: Calendar grid abbreviation for Thursday */
 | 
			
		||||
        C_("grid thursday", "T"),
 | 
			
		||||
        /* Translators: Calendar grid abbreviation for Friday */
 | 
			
		||||
        C_("grid friday", "F"),
 | 
			
		||||
        /* Translators: Calendar grid abbreviation for Saturday */
 | 
			
		||||
        C_("grid saturday", "S")
 | 
			
		||||
    ];
 | 
			
		||||
    return abbreviations[dayNumber];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _getEventDayAbbreviation(dayNumber) {
 | 
			
		||||
    let abbreviations = [
 | 
			
		||||
        /* Translators: Event list abbreviation for Sunday.
 | 
			
		||||
         *
 | 
			
		||||
         * NOTE: These list abbreviations are normally not shown together
 | 
			
		||||
         * so they need to be unique (e.g. Tuesday and Thursday cannot
 | 
			
		||||
         * both be 'T').
 | 
			
		||||
         */
 | 
			
		||||
        C_("list sunday", "Su"),
 | 
			
		||||
        /* Translators: Event list abbreviation for Monday */
 | 
			
		||||
        C_("list monday", "M"),
 | 
			
		||||
        /* Translators: Event list abbreviation for Tuesday */
 | 
			
		||||
        C_("list tuesday", "T"),
 | 
			
		||||
        /* Translators: Event list abbreviation for Wednesday */
 | 
			
		||||
        C_("list wednesday", "W"),
 | 
			
		||||
        /* Translators: Event list abbreviation for Thursday */
 | 
			
		||||
        C_("list thursday", "Th"),
 | 
			
		||||
        /* Translators: Event list abbreviation for Friday */
 | 
			
		||||
        C_("list friday", "F"),
 | 
			
		||||
        /* Translators: Event list abbreviation for Saturday */
 | 
			
		||||
        C_("list saturday", "S")
 | 
			
		||||
    ];
 | 
			
		||||
    return abbreviations[dayNumber];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Abstraction for an appointment/event in a calendar
 | 
			
		||||
 | 
			
		||||
function CalendarEvent(date, end, summary, allDay) {
 | 
			
		||||
    this._init(date, end, summary, allDay);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CalendarEvent.prototype = {
 | 
			
		||||
    _init: function(date, end, summary, allDay) {
 | 
			
		||||
        this.date = date;
 | 
			
		||||
        this.end = end;
 | 
			
		||||
        this.summary = summary;
 | 
			
		||||
        this.allDay = allDay;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Interface for appointments/events - e.g. the contents of a calendar
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
// First, an implementation with no events
 | 
			
		||||
function EmptyEventSource() {
 | 
			
		||||
function Calendar() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EmptyEventSource.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    requestRange: function(begin, end) {
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getEvents: function(begin, end) {
 | 
			
		||||
        let result = [];
 | 
			
		||||
        return result;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    hasEvents: function(day) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
Signals.addSignalMethods(EmptyEventSource.prototype);
 | 
			
		||||
 | 
			
		||||
const CalendarServerIface = {
 | 
			
		||||
    name: 'org.gnome.Shell.CalendarServer',
 | 
			
		||||
    methods: [{ name: 'GetEvents',
 | 
			
		||||
                inSignature: 'xxb',
 | 
			
		||||
                outSignature: 'a(sssbxxa{sv})' }],
 | 
			
		||||
    signals: [{ name: 'Changed',
 | 
			
		||||
                inSignature: '' }]
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const CalendarServer = function () {
 | 
			
		||||
    this._init();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
CalendarServer.prototype = {
 | 
			
		||||
     _init: function() {
 | 
			
		||||
         DBus.session.proxifyObject(this, 'org.gnome.Shell.CalendarServer', '/org/gnome/Shell/CalendarServer');
 | 
			
		||||
     }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
DBus.proxifyPrototype(CalendarServer.prototype, CalendarServerIface);
 | 
			
		||||
 | 
			
		||||
// an implementation that reads data from a session bus service
 | 
			
		||||
function DBusEventSource(owner) {
 | 
			
		||||
    this._init(owner);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _datesEqual(a, b) {
 | 
			
		||||
    if (a < b)
 | 
			
		||||
        return false;
 | 
			
		||||
    else if (a > b)
 | 
			
		||||
        return false;
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _dateIntervalsOverlap(a0, a1, b0, b1)
 | 
			
		||||
{
 | 
			
		||||
    if (a1 <= b0)
 | 
			
		||||
        return false;
 | 
			
		||||
    else if (b1 <= a0)
 | 
			
		||||
        return false;
 | 
			
		||||
    else
 | 
			
		||||
        return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
DBusEventSource.prototype = {
 | 
			
		||||
    _init: function(owner) {
 | 
			
		||||
        this._resetCache();
 | 
			
		||||
 | 
			
		||||
        this._dbusProxy = new CalendarServer(owner);
 | 
			
		||||
        this._dbusProxy.connect('Changed', Lang.bind(this, this._onChanged));
 | 
			
		||||
 | 
			
		||||
        DBus.session.watch_name('org.gnome.Shell.CalendarServer',
 | 
			
		||||
                                false, // do not launch a name-owner if none exists
 | 
			
		||||
                                Lang.bind(this, this._onNameAppeared),
 | 
			
		||||
                                Lang.bind(this, this._onNameVanished));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _resetCache: function() {
 | 
			
		||||
        this._events = [];
 | 
			
		||||
        this._lastRequestBegin = null;
 | 
			
		||||
        this._lastRequestEnd = null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onNameAppeared: function(owner) {
 | 
			
		||||
        this._resetCache();
 | 
			
		||||
        this._loadEvents(true);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onNameVanished: function(oldOwner) {
 | 
			
		||||
        this._resetCache();
 | 
			
		||||
        this.emit('changed');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onChanged: function() {
 | 
			
		||||
        this._loadEvents(false);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onEventsReceived: function(appointments) {
 | 
			
		||||
        let newEvents = [];
 | 
			
		||||
        if (appointments != null) {
 | 
			
		||||
            for (let n = 0; n < appointments.length; n++) {
 | 
			
		||||
                let a = appointments[n];
 | 
			
		||||
                let date = new Date(a[4] * 1000);
 | 
			
		||||
                let end = new Date(a[5] * 1000);
 | 
			
		||||
                let summary = a[1];
 | 
			
		||||
                let allDay = a[3];
 | 
			
		||||
                let event = new CalendarEvent(date, end, summary, allDay);
 | 
			
		||||
                newEvents.push(event);
 | 
			
		||||
            }
 | 
			
		||||
            newEvents.sort(function(event1, event2) {
 | 
			
		||||
                return event1.date.getTime() - event2.date.getTime();
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._events = newEvents;
 | 
			
		||||
        this.emit('changed');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _loadEvents: function(forceReload) {
 | 
			
		||||
        if (this._curRequestBegin && this._curRequestEnd){
 | 
			
		||||
            let callFlags = 0;
 | 
			
		||||
            if (forceReload)
 | 
			
		||||
                callFlags |= DBus.CALL_FLAG_START;
 | 
			
		||||
            this._dbusProxy.GetEventsRemote(this._curRequestBegin.getTime() / 1000,
 | 
			
		||||
                                            this._curRequestEnd.getTime() / 1000,
 | 
			
		||||
                                            forceReload,
 | 
			
		||||
                                            Lang.bind(this, this._onEventsReceived),
 | 
			
		||||
                                            callFlags);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    requestRange: function(begin, end, forceReload) {
 | 
			
		||||
        if (forceReload || !(_datesEqual(begin, this._lastRequestBegin) && _datesEqual(end, this._lastRequestEnd))) {
 | 
			
		||||
            this._lastRequestBegin = begin;
 | 
			
		||||
            this._lastRequestEnd = end;
 | 
			
		||||
            this._curRequestBegin = begin;
 | 
			
		||||
            this._curRequestEnd = end;
 | 
			
		||||
            this._loadEvents(forceReload);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getEvents: function(begin, end) {
 | 
			
		||||
        let result = [];
 | 
			
		||||
        for(let n = 0; n < this._events.length; n++) {
 | 
			
		||||
            let event = this._events[n];
 | 
			
		||||
            if (_dateIntervalsOverlap (event.date, event.end, begin, end)) {
 | 
			
		||||
                result.push(event);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return result;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    hasEvents: function(day) {
 | 
			
		||||
        let dayBegin = _getBeginningOfDay(day);
 | 
			
		||||
        let dayEnd = _getEndOfDay(day);
 | 
			
		||||
 | 
			
		||||
        let events = this.getEvents(dayBegin, dayEnd);
 | 
			
		||||
 | 
			
		||||
        if (events.length == 0)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
Signals.addSignalMethods(DBusEventSource.prototype);
 | 
			
		||||
 | 
			
		||||
// Calendar:
 | 
			
		||||
// @eventSource: is an object implementing the EventSource API, e.g. the
 | 
			
		||||
// requestRange(), getEvents(), hasEvents() methods and the ::changed signal.
 | 
			
		||||
function Calendar(eventSource) {
 | 
			
		||||
    this._init(eventSource);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Calendar.prototype = {
 | 
			
		||||
    _init: function(eventSource) {
 | 
			
		||||
        this._eventSource = eventSource;
 | 
			
		||||
 | 
			
		||||
        this._eventSource.connect('changed', Lang.bind(this,
 | 
			
		||||
                                                       function() {
 | 
			
		||||
                                                           this._update(false);
 | 
			
		||||
                                                       }));
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        // FIXME: This is actually the fallback method for GTK+ for the week start;
 | 
			
		||||
        // GTK+ by preference uses nl_langinfo (NL_TIME_FIRST_WEEKDAY). We probably
 | 
			
		||||
        // should add a C function so we can do the full handling.
 | 
			
		||||
@@ -369,7 +60,7 @@ Calendar.prototype = {
 | 
			
		||||
        this._settings.connect('changed::' + SHOW_WEEKDATE_KEY, Lang.bind(this, this._onSettingsChange));
 | 
			
		||||
        this._useWeekdate = this._settings.get_boolean(SHOW_WEEKDATE_KEY);
 | 
			
		||||
 | 
			
		||||
        let weekStartString = Gettext_gtk30.gettext('calendar:week_start:0');
 | 
			
		||||
        let weekStartString = Gettext_gtk20.gettext('calendar:week_start:0');
 | 
			
		||||
        if (weekStartString.indexOf('calendar:week_start:') == 0) {
 | 
			
		||||
            this._weekStart = parseInt(weekStartString.substring(20));
 | 
			
		||||
        }
 | 
			
		||||
@@ -380,8 +71,7 @@ Calendar.prototype = {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Find the ordering for month/year in the calendar heading
 | 
			
		||||
        this._headerFormatWithoutYear = '%B';
 | 
			
		||||
        switch (Gettext_gtk30.gettext('calendar:MY')) {
 | 
			
		||||
        switch (Gettext_gtk20.gettext('calendar:MY')) {
 | 
			
		||||
        case 'calendar:MY':
 | 
			
		||||
            this._headerFormat = '%B %Y';
 | 
			
		||||
            break;
 | 
			
		||||
@@ -395,7 +85,7 @@ Calendar.prototype = {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Start off with the current date
 | 
			
		||||
        this._selectedDate = new Date();
 | 
			
		||||
        this.date = new Date();
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.Table({ homogeneous: false,
 | 
			
		||||
                                    style_class: 'calendar',
 | 
			
		||||
@@ -405,17 +95,14 @@ Calendar.prototype = {
 | 
			
		||||
                           Lang.bind(this, this._onScroll));
 | 
			
		||||
 | 
			
		||||
        this._buildHeader ();
 | 
			
		||||
        this._update();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Sets the calendar to show a specific date
 | 
			
		||||
    setDate: function(date, forceReload) {
 | 
			
		||||
        if (!_sameDay(date, this._selectedDate)) {
 | 
			
		||||
            this._selectedDate = date;
 | 
			
		||||
            this._update(forceReload);
 | 
			
		||||
            this.emit('selected-date-changed', new Date(this._selectedDate));
 | 
			
		||||
        } else {
 | 
			
		||||
            if (forceReload)
 | 
			
		||||
                this._update(forceReload);
 | 
			
		||||
    setDate: function(date) {
 | 
			
		||||
        if (!_sameDay(date, this.date)) {
 | 
			
		||||
            this.date = date;
 | 
			
		||||
            this._update();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -429,36 +116,45 @@ Calendar.prototype = {
 | 
			
		||||
                       { row: 0, col: 0, col_span: offsetCols + 7 });
 | 
			
		||||
 | 
			
		||||
        this.actor.connect('style-changed', Lang.bind(this, this._onStyleChange));
 | 
			
		||||
        let [backlabel, forwardlabel] = ['<', '>'];
 | 
			
		||||
        if (St.Widget.get_default_direction () == St.TextDirection.RTL) {
 | 
			
		||||
            [backlabel, forwardlabel] = [forwardlabel, backlabel];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let back = new St.Button({ style_class: 'calendar-change-month-back' });
 | 
			
		||||
        let back = new St.Button({ label: backlabel, style_class: 'calendar-change-month'  });
 | 
			
		||||
        this._topBox.add(back);
 | 
			
		||||
        back.connect('clicked', Lang.bind(this, this._onPrevMonthButtonClicked));
 | 
			
		||||
        back.connect('clicked', Lang.bind(this, this._prevMonth));
 | 
			
		||||
 | 
			
		||||
        this._monthLabel = new St.Label({style_class: 'calendar-month-label'});
 | 
			
		||||
        this._topBox.add(this._monthLabel, { expand: true, x_fill: false, x_align: St.Align.MIDDLE });
 | 
			
		||||
        this._dateLabel = new St.Label();
 | 
			
		||||
        this._topBox.add(this._dateLabel, { expand: true, x_fill: false, x_align: St.Align.MIDDLE });
 | 
			
		||||
 | 
			
		||||
        let forward = new St.Button({ style_class: 'calendar-change-month-forward' });
 | 
			
		||||
        let forward = new St.Button({ label: forwardlabel, style_class: 'calendar-change-month' });
 | 
			
		||||
        this._topBox.add(forward);
 | 
			
		||||
        forward.connect('clicked', Lang.bind(this, this._onNextMonthButtonClicked));
 | 
			
		||||
        forward.connect('clicked', Lang.bind(this, this._nextMonth));
 | 
			
		||||
 | 
			
		||||
        // Add weekday labels...
 | 
			
		||||
        //
 | 
			
		||||
        // We need to figure out the abbreviated localized names for the days of the week;
 | 
			
		||||
        // we do this by just getting the next 7 days starting from right now and then putting
 | 
			
		||||
        // them in the right cell in the table. It doesn't matter if we add them in order
 | 
			
		||||
        let iter = new Date(this._selectedDate);
 | 
			
		||||
        let iter = new Date(this.date);
 | 
			
		||||
        iter.setSeconds(0); // Leap second protection. Hah!
 | 
			
		||||
        iter.setHours(12);
 | 
			
		||||
 | 
			
		||||
        if (this._useWeekdate) {
 | 
			
		||||
            this._weekdateHeader = new St.Label();
 | 
			
		||||
            this.actor.add(this._weekdateHeader,
 | 
			
		||||
                              { row: 1,
 | 
			
		||||
                                col: 0,
 | 
			
		||||
                                x_fill: false, x_align: St.Align.MIDDLE });
 | 
			
		||||
            this._setWeekdateHeaderWidth();
 | 
			
		||||
        } else {
 | 
			
		||||
            this._weekdateHeader = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < 7; i++) {
 | 
			
		||||
            // Could use iter.toLocaleFormat('%a') but that normally gives three characters
 | 
			
		||||
            // and we want, ideally, a single character for e.g. S M T W T F S
 | 
			
		||||
            let customDayAbbrev = _getCalendarDayAbbreviation(iter.getDay());
 | 
			
		||||
            let label = new St.Label({ style_class: 'calendar-day-base calendar-day-heading',
 | 
			
		||||
                                       text: customDayAbbrev });
 | 
			
		||||
            this.actor.add(label,
 | 
			
		||||
            this.actor.add(new St.Label({ text: iter.toLocaleFormat('%a') }),
 | 
			
		||||
                           { row: 1,
 | 
			
		||||
                             col: offsetCols + (7 + iter.getDay() - this._weekStart) % 7,
 | 
			
		||||
                             x_fill: false, x_align: St.Align.MIDDLE });
 | 
			
		||||
                             x_fill: false, x_align: St.Align.END });
 | 
			
		||||
            iter.setTime(iter.getTime() + MSECS_IN_DAY);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -482,72 +178,43 @@ Calendar.prototype = {
 | 
			
		||||
        switch (event.get_scroll_direction()) {
 | 
			
		||||
        case Clutter.ScrollDirection.UP:
 | 
			
		||||
        case Clutter.ScrollDirection.LEFT:
 | 
			
		||||
            this._onPrevMonthButtonClicked();
 | 
			
		||||
            this._prevMonth();
 | 
			
		||||
            break;
 | 
			
		||||
        case Clutter.ScrollDirection.DOWN:
 | 
			
		||||
        case Clutter.ScrollDirection.RIGHT:
 | 
			
		||||
            this._onNextMonthButtonClicked();
 | 
			
		||||
            this._nextMonth();
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onPrevMonthButtonClicked: function() {
 | 
			
		||||
        let newDate = new Date(this._selectedDate);
 | 
			
		||||
        let oldMonth = newDate.getMonth();
 | 
			
		||||
        if (oldMonth == 0) {
 | 
			
		||||
            newDate.setMonth(11);
 | 
			
		||||
            newDate.setFullYear(newDate.getFullYear() - 1);
 | 
			
		||||
            if (newDate.getMonth() != 11) {
 | 
			
		||||
                let day = 32 - new Date(newDate.getFullYear() - 1, 11, 32).getDate();
 | 
			
		||||
                newDate = new Date(newDate.getFullYear() - 1, 11, day);
 | 
			
		||||
            }
 | 
			
		||||
    _prevMonth: function() {
 | 
			
		||||
        if (this.date.getMonth() == 0) {
 | 
			
		||||
            this.date.setMonth(11);
 | 
			
		||||
            this.date.setFullYear(this.date.getFullYear() - 1);
 | 
			
		||||
        } else {
 | 
			
		||||
            this.date.setMonth(this.date.getMonth() - 1);
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            newDate.setMonth(oldMonth - 1);
 | 
			
		||||
            if (newDate.getMonth() != oldMonth - 1) {
 | 
			
		||||
                let day = 32 - new Date(newDate.getFullYear(), oldMonth - 1, 32).getDate();
 | 
			
		||||
                newDate = new Date(newDate.getFullYear(), oldMonth - 1, day);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.setDate(newDate, false);
 | 
			
		||||
        this._update();
 | 
			
		||||
   },
 | 
			
		||||
 | 
			
		||||
   _onNextMonthButtonClicked: function() {
 | 
			
		||||
        let newDate = new Date(this._selectedDate);
 | 
			
		||||
        let oldMonth = newDate.getMonth();
 | 
			
		||||
        if (oldMonth == 11) {
 | 
			
		||||
            newDate.setMonth(0);
 | 
			
		||||
            newDate.setFullYear(newDate.getFullYear() + 1);
 | 
			
		||||
            if (newDate.getMonth() != 0) {
 | 
			
		||||
                let day = 32 - new Date(newDate.getFullYear() + 1, 0, 32).getDate();
 | 
			
		||||
                newDate = new Date(newDate.getFullYear() + 1, 0, day);
 | 
			
		||||
            }
 | 
			
		||||
    _nextMonth: function() {
 | 
			
		||||
        if (this.date.getMonth() == 11) {
 | 
			
		||||
            this.date.setMonth(0);
 | 
			
		||||
            this.date.setFullYear(this.date.getFullYear() + 1);
 | 
			
		||||
        } else {
 | 
			
		||||
            this.date.setMonth(this.date.getMonth() + 1);
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            newDate.setMonth(oldMonth + 1);
 | 
			
		||||
            if (newDate.getMonth() != oldMonth + 1) {
 | 
			
		||||
                let day = 32 - new Date(newDate.getFullYear(), oldMonth + 1, 32).getDate();
 | 
			
		||||
                newDate = new Date(newDate.getFullYear(), oldMonth + 1, day);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
       this.setDate(newDate, false);
 | 
			
		||||
        this._update();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onSettingsChange: function() {
 | 
			
		||||
        this._useWeekdate = this._settings.get_boolean(SHOW_WEEKDATE_KEY);
 | 
			
		||||
        this._buildHeader();
 | 
			
		||||
        this._update(false);
 | 
			
		||||
        this._update();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _update: function(forceReload) {
 | 
			
		||||
        let now = new Date();
 | 
			
		||||
 | 
			
		||||
        if (_sameYear(this._selectedDate, now))
 | 
			
		||||
            this._monthLabel.text = this._selectedDate.toLocaleFormat(this._headerFormatWithoutYear);
 | 
			
		||||
        else
 | 
			
		||||
            this._monthLabel.text = this._selectedDate.toLocaleFormat(this._headerFormat);
 | 
			
		||||
    _update: function() {
 | 
			
		||||
        this._dateLabel.text = this.date.toLocaleFormat(this._headerFormat);
 | 
			
		||||
 | 
			
		||||
        // Remove everything but the topBox and the weekday labels
 | 
			
		||||
        let children = this.actor.get_children();
 | 
			
		||||
@@ -555,217 +222,45 @@ Calendar.prototype = {
 | 
			
		||||
            children[i].destroy();
 | 
			
		||||
 | 
			
		||||
        // Start at the beginning of the week before the start of the month
 | 
			
		||||
        let beginDate = new Date(this._selectedDate);
 | 
			
		||||
        beginDate.setDate(1);
 | 
			
		||||
        beginDate.setSeconds(0);
 | 
			
		||||
        beginDate.setHours(12);
 | 
			
		||||
        let daysToWeekStart = (7 + beginDate.getDay() - this._weekStart) % 7;
 | 
			
		||||
        beginDate.setTime(beginDate.getTime() - daysToWeekStart * MSECS_IN_DAY);
 | 
			
		||||
        let iter = new Date(this.date);
 | 
			
		||||
        iter.setDate(1);
 | 
			
		||||
        iter.setSeconds(0);
 | 
			
		||||
        iter.setHours(12);
 | 
			
		||||
        let daysToWeekStart = (7 + iter.getDay() - this._weekStart) % 7;
 | 
			
		||||
        iter.setTime(iter.getTime() - daysToWeekStart * MSECS_IN_DAY);
 | 
			
		||||
 | 
			
		||||
        let now = new Date();
 | 
			
		||||
 | 
			
		||||
        let iter = new Date(beginDate);
 | 
			
		||||
        let row = 2;
 | 
			
		||||
        while (true) {
 | 
			
		||||
            let button = new St.Button({ label: iter.getDate().toString() });
 | 
			
		||||
 | 
			
		||||
            let iterStr = iter.toUTCString();
 | 
			
		||||
            button.connect('clicked', Lang.bind(this, function() {
 | 
			
		||||
                let newlySelectedDate = new Date(iterStr);
 | 
			
		||||
                this.setDate(newlySelectedDate, false);
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
            let hasEvents = this._eventSource.hasEvents(iter);
 | 
			
		||||
            let styleClass = 'calendar-day-base calendar-day';
 | 
			
		||||
            if (_isWorkDay(iter))
 | 
			
		||||
                styleClass += ' calendar-work-day'
 | 
			
		||||
            else
 | 
			
		||||
                styleClass += ' calendar-nonwork-day'
 | 
			
		||||
 | 
			
		||||
            // Hack used in lieu of border-collapse - see gnome-shell.css
 | 
			
		||||
            if (row == 2)
 | 
			
		||||
                styleClass = 'calendar-day-top ' + styleClass;
 | 
			
		||||
            if (iter.getDay() == this._weekStart)
 | 
			
		||||
                styleClass = 'calendar-day-left ' + styleClass;
 | 
			
		||||
 | 
			
		||||
            let label = new St.Label({ text: iter.getDate().toString() });
 | 
			
		||||
            if (_sameDay(now, iter))
 | 
			
		||||
                styleClass += ' calendar-today';
 | 
			
		||||
            else if (iter.getMonth() != this._selectedDate.getMonth())
 | 
			
		||||
                styleClass += ' calendar-other-month-day';
 | 
			
		||||
 | 
			
		||||
            if (_sameDay(this._selectedDate, iter))
 | 
			
		||||
                button.add_style_pseudo_class('active');
 | 
			
		||||
 | 
			
		||||
            if (hasEvents)
 | 
			
		||||
                styleClass += ' calendar-day-with-events'
 | 
			
		||||
 | 
			
		||||
            button.style_class = styleClass;
 | 
			
		||||
                label.style_class = 'calendar-day calendar-today';
 | 
			
		||||
            else if (iter.getMonth() != this.date.getMonth())
 | 
			
		||||
                label.style_class = 'calendar-day calendar-other-month-day';
 | 
			
		||||
            else
 | 
			
		||||
                label.style_class = 'calendar-day';
 | 
			
		||||
 | 
			
		||||
            let offsetCols = this._useWeekdate ? 1 : 0;
 | 
			
		||||
            this.actor.add(button,
 | 
			
		||||
                           { row: row, col: offsetCols + (7 + iter.getDay() - this._weekStart) % 7 });
 | 
			
		||||
            this.actor.add(label,
 | 
			
		||||
                           { row: row, col: offsetCols + (7 + iter.getDay() - this._weekStart) % 7,
 | 
			
		||||
                             x_fill: false, x_align: St.Align.END });
 | 
			
		||||
 | 
			
		||||
            if (this._useWeekdate && iter.getDay() == 4) {
 | 
			
		||||
                let label = new St.Label({ text: _getCalendarWeekForDate(iter).toString(),
 | 
			
		||||
                                           style_class: 'calendar-day-base calendar-week-number'});
 | 
			
		||||
                                           style_class: 'calendar-day calendar-calendarweek'});
 | 
			
		||||
                this.actor.add(label,
 | 
			
		||||
                               { row: row, col: 0, y_align: St.Align.MIDDLE });
 | 
			
		||||
                              { row: row, col: 0,
 | 
			
		||||
		                        x_fill: false, x_align: St.Align.MIDDLE });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            iter.setTime(iter.getTime() + MSECS_IN_DAY);
 | 
			
		||||
            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())
 | 
			
		||||
                if (iter.getMonth() > this.date.getMonth() || iter.getYear() > this.date.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);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Signals.addSignalMethods(Calendar.prototype);
 | 
			
		||||
 | 
			
		||||
function EventsList(eventSource) {
 | 
			
		||||
    this._init(eventSource);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EventsList.prototype = {
 | 
			
		||||
    _init: function(eventSource) {
 | 
			
		||||
        this.actor = new St.BoxLayout({ vertical: true, style_class: 'events-header-vbox'});
 | 
			
		||||
        this._date = new Date();
 | 
			
		||||
        this._eventSource = eventSource;
 | 
			
		||||
        this._eventSource.connect('changed', Lang.bind(this, this._update));
 | 
			
		||||
        this._desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
 | 
			
		||||
        this._desktopSettings.connect('changed', Lang.bind(this, this._update));
 | 
			
		||||
        let weekStartString = Gettext_gtk30.gettext('calendar:week_start:0');
 | 
			
		||||
        if (weekStartString.indexOf('calendar:week_start:') == 0) {
 | 
			
		||||
            this._weekStart = parseInt(weekStartString.substring(20));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (isNaN(this._weekStart) ||
 | 
			
		||||
                  this._weekStart < 0 ||
 | 
			
		||||
                  this._weekStart > 6) {
 | 
			
		||||
            log('Translation of "calendar:week_start:0" in GTK+ is not correct');
 | 
			
		||||
            this._weekStart = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._update();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _addEvent: function(dayNameBox, timeBox, eventTitleBox, includeDayName, day, time, desc) {
 | 
			
		||||
        if (includeDayName) {
 | 
			
		||||
            dayNameBox.add(new St.Label( { style_class: 'events-day-dayname',
 | 
			
		||||
                                           text: day } ),
 | 
			
		||||
                           { x_fill: true } );
 | 
			
		||||
        }
 | 
			
		||||
        timeBox.add(new St.Label( { style_class: 'events-day-time',
 | 
			
		||||
                                    text: time} ),
 | 
			
		||||
                    { x_fill: true } );
 | 
			
		||||
        eventTitleBox.add(new St.Label( { style_class: 'events-day-task',
 | 
			
		||||
                                          text: desc} ));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _addPeriod: function(header, begin, end, includeDayName, showNothingScheduled) {
 | 
			
		||||
        let events = this._eventSource.getEvents(begin, end);
 | 
			
		||||
 | 
			
		||||
        let clockFormat = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);;
 | 
			
		||||
 | 
			
		||||
        if (events.length == 0 && !showNothingScheduled)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let vbox = new St.BoxLayout( {vertical: true} );
 | 
			
		||||
        this.actor.add(vbox);
 | 
			
		||||
 | 
			
		||||
        vbox.add(new St.Label({ style_class: 'events-day-header', text: header }));
 | 
			
		||||
        let box = new St.BoxLayout({style_class: 'events-header-hbox'});
 | 
			
		||||
        let dayNameBox = new St.BoxLayout({ vertical: true, style_class: 'events-day-name-box' });
 | 
			
		||||
        let timeBox = new St.BoxLayout({ vertical: true, style_class: 'events-time-box' });
 | 
			
		||||
        let eventTitleBox = new St.BoxLayout({ vertical: true, style_class: 'events-event-box' });
 | 
			
		||||
        box.add(dayNameBox, {x_fill: false});
 | 
			
		||||
        box.add(timeBox, {x_fill: false});
 | 
			
		||||
        box.add(eventTitleBox, {expand: true});
 | 
			
		||||
        vbox.add(box);
 | 
			
		||||
 | 
			
		||||
        for (let n = 0; n < events.length; n++) {
 | 
			
		||||
            let event = events[n];
 | 
			
		||||
            let dayString = _getEventDayAbbreviation(event.date.getDay());
 | 
			
		||||
            let timeString = _formatEventTime(event, clockFormat);
 | 
			
		||||
            let summaryString = event.summary;
 | 
			
		||||
            this._addEvent(dayNameBox, timeBox, eventTitleBox, includeDayName, dayString, timeString, summaryString);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (events.length == 0 && showNothingScheduled) {
 | 
			
		||||
            let now = new Date();
 | 
			
		||||
            /* Translators: Text to show if there are no events */
 | 
			
		||||
            let nothingEvent = new CalendarEvent(now, now, _("Nothing Scheduled"), true);
 | 
			
		||||
            let timeString = _formatEventTime(nothingEvent, clockFormat);
 | 
			
		||||
            this._addEvent(dayNameBox, timeBox, eventTitleBox, false, "", timeString, nothingEvent.summary);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _showOtherDay: function(day) {
 | 
			
		||||
        this.actor.destroy_children();
 | 
			
		||||
 | 
			
		||||
        let dayBegin = _getBeginningOfDay(day);
 | 
			
		||||
        let dayEnd = _getEndOfDay(day);
 | 
			
		||||
 | 
			
		||||
        let dayString;
 | 
			
		||||
        let now = new Date();
 | 
			
		||||
        if (_sameYear(day, now))
 | 
			
		||||
            /* Translators: Shown on calendar heading when selected day occurs on current year */
 | 
			
		||||
            dayString = day.toLocaleFormat(C_("calendar heading", "%A, %B %d"));
 | 
			
		||||
        else
 | 
			
		||||
            /* Translators: Shown on calendar heading when selected day occurs on different year */
 | 
			
		||||
            dayString = day.toLocaleFormat(C_("calendar heading", "%A, %B %d, %Y"));
 | 
			
		||||
        this._addPeriod(dayString, dayBegin, dayEnd, false, true);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _showToday: function() {
 | 
			
		||||
        this.actor.destroy_children();
 | 
			
		||||
 | 
			
		||||
        let now = new Date();
 | 
			
		||||
        let dayBegin = _getBeginningOfDay(now);
 | 
			
		||||
        let dayEnd = _getEndOfDay(now);
 | 
			
		||||
        this._addPeriod(_("Today"), dayBegin, dayEnd, false, true);
 | 
			
		||||
 | 
			
		||||
        let tomorrowBegin = new Date(dayBegin.getTime() + 86400 * 1000);
 | 
			
		||||
        let tomorrowEnd = new Date(dayEnd.getTime() + 86400 * 1000);
 | 
			
		||||
        this._addPeriod(_("Tomorrow"), tomorrowBegin, tomorrowEnd, false, true);
 | 
			
		||||
 | 
			
		||||
        if (dayEnd.getDay() <= 4 + this._weekStart) {
 | 
			
		||||
            /* If now is within the first 5 days we show "This week" and
 | 
			
		||||
             * include events up until and including Saturday/Sunday
 | 
			
		||||
             * (depending on whether a week starts on Sunday/Monday).
 | 
			
		||||
             */
 | 
			
		||||
            let thisWeekBegin = new Date(dayBegin.getTime() + 2 * 86400 * 1000);
 | 
			
		||||
            let thisWeekEnd = new Date(dayEnd.getTime() + (6 + this._weekStart - dayEnd.getDay()) * 86400 * 1000);
 | 
			
		||||
            this._addPeriod(_("This week"), thisWeekBegin, thisWeekEnd, true, false);
 | 
			
		||||
        } else {
 | 
			
		||||
            /* otherwise it's one of the two last days of the week ... show
 | 
			
		||||
             * "Next week" and include events up until and including *next*
 | 
			
		||||
             * Saturday/Sunday
 | 
			
		||||
             */
 | 
			
		||||
            let nextWeekBegin = new Date(dayBegin.getTime() + 2 * 86400 * 1000);
 | 
			
		||||
            let nextWeekEnd = new Date(dayEnd.getTime() + (13 + this._weekStart - dayEnd.getDay()) * 86400 * 1000);
 | 
			
		||||
            this._addPeriod(_("Next week"), nextWeekBegin, nextWeekEnd, true, false);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Sets the event list to show events from a specific date
 | 
			
		||||
    setDate: function(date) {
 | 
			
		||||
        if (!_sameDay(date, this._date)) {
 | 
			
		||||
            this._date = date;
 | 
			
		||||
            this._update();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _update: function() {
 | 
			
		||||
        let today = new Date();
 | 
			
		||||
        if (_sameDay (this._date, today)) {
 | 
			
		||||
            this._showToday();
 | 
			
		||||
        } else {
 | 
			
		||||
            this._showOtherDay(this._date);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										224
									
								
								js/ui/chrome.js
									
									
									
									
									
								
							
							
						
						@@ -8,13 +8,19 @@ const Signals = imports.signals;
 | 
			
		||||
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
const ScreenSaver = imports.misc.screenSaver;
 | 
			
		||||
 | 
			
		||||
// This manages the shell "chrome"; the UI that's visible in the
 | 
			
		||||
// normal mode (ie, outside the Overview), that surrounds the main
 | 
			
		||||
// workspace content.
 | 
			
		||||
 | 
			
		||||
const Visibility = {
 | 
			
		||||
    FULL:       1,
 | 
			
		||||
    FULLSCREEN: 2,
 | 
			
		||||
    OVERVIEW:   3
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const defaultParams = {
 | 
			
		||||
    visibleInOverview: false,
 | 
			
		||||
    visibleInFullscreen: false,
 | 
			
		||||
    affectsStruts: true,
 | 
			
		||||
    affectsInputRegion: true
 | 
			
		||||
@@ -31,13 +37,12 @@ Chrome.prototype = {
 | 
			
		||||
        Main.uiGroup.add_actor(this.actor);
 | 
			
		||||
        this.actor.connect('allocate', Lang.bind(this, this._allocated));
 | 
			
		||||
 | 
			
		||||
        this._monitors = [];
 | 
			
		||||
        this._inFullscreen = false;
 | 
			
		||||
        this._inOverview = false;
 | 
			
		||||
        this.visibility = Visibility.FULL;
 | 
			
		||||
 | 
			
		||||
        this._trackedActors = [];
 | 
			
		||||
 | 
			
		||||
        Main.layoutManager.connect('monitors-changed',
 | 
			
		||||
                                   Lang.bind(this, this._relayout));
 | 
			
		||||
        global.screen.connect('restacked',
 | 
			
		||||
                              Lang.bind(this, this._windowsRestacked));
 | 
			
		||||
 | 
			
		||||
@@ -50,15 +55,7 @@ Chrome.prototype = {
 | 
			
		||||
        Main.overview.connect('hidden',
 | 
			
		||||
                             Lang.bind(this, this._overviewHidden));
 | 
			
		||||
 | 
			
		||||
        this._screenSaverProxy = new ScreenSaver.ScreenSaverProxy();
 | 
			
		||||
        this._screenSaverProxy.connect('ActiveChanged', Lang.bind(this, this._onScreenSaverActiveChanged));
 | 
			
		||||
        this._screenSaverProxy.GetActiveRemote(Lang.bind(this,
 | 
			
		||||
            function(result, err) {
 | 
			
		||||
                if (!err)
 | 
			
		||||
                    this._onScreenSaverActiveChanged(this._screenSaverProxy, result);
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
        this._relayout();
 | 
			
		||||
        this._queueUpdateRegions();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _allocated: function(actor, box, flags) {
 | 
			
		||||
@@ -79,8 +76,11 @@ Chrome.prototype = {
 | 
			
		||||
    // in its visibility will affect the input region, but NOT the
 | 
			
		||||
    // struts.
 | 
			
		||||
    //
 | 
			
		||||
    // If %visibleInFullscreen is %true, the actor will be visible
 | 
			
		||||
    // even when a fullscreen window should be covering it.
 | 
			
		||||
    // If %visibleInOverview is %true in @params, @actor will remain
 | 
			
		||||
    // visible when the overview is brought up. Otherwise it will
 | 
			
		||||
    // automatically be hidden. Likewise, if %visibleInFullscreen is
 | 
			
		||||
    // %true, the actor will be visible even when a fullscreen window
 | 
			
		||||
    // should be covering it.
 | 
			
		||||
    //
 | 
			
		||||
    // If %affectsStruts or %affectsInputRegion is %false, the actor
 | 
			
		||||
    // will not have the indicated effect.
 | 
			
		||||
@@ -99,7 +99,7 @@ Chrome.prototype = {
 | 
			
		||||
    //
 | 
			
		||||
    // @params can have any of the same values as in addActor(), though
 | 
			
		||||
    // some possibilities don't make sense (eg, trying to have a
 | 
			
		||||
    // %visibleInFullscreen child of a non-%visibleInFullscreen parent).
 | 
			
		||||
    // %visibleInOverview child of a non-%visibleInOverview parent).
 | 
			
		||||
    // By default, @actor has the same params as its chrome ancestor.
 | 
			
		||||
    trackActor: function(actor, params) {
 | 
			
		||||
        let ancestor = actor.get_parent();
 | 
			
		||||
@@ -192,12 +192,26 @@ Chrome.prototype = {
 | 
			
		||||
    _updateVisibility: function() {
 | 
			
		||||
        for (let i = 0; i < this._trackedActors.length; i++) {
 | 
			
		||||
            let actorData = this._trackedActors[i];
 | 
			
		||||
            if (!this._inOverview && !actorData.visibleInFullscreen &&
 | 
			
		||||
                this._findMonitorForActor(actorData.actor).inFullscreen)
 | 
			
		||||
            if (this._inOverview && !actorData.visibleInOverview)
 | 
			
		||||
                this.actor.set_skip_paint(actorData.actor, true);
 | 
			
		||||
            else if (!this._inOverview && this._inFullscreen && !actorData.visibleInFullscreen)
 | 
			
		||||
                this.actor.set_skip_paint(actorData.actor, true);
 | 
			
		||||
            else
 | 
			
		||||
                this.actor.set_skip_paint(actorData.actor, false);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let newVisibility;
 | 
			
		||||
        if (this._inOverview)
 | 
			
		||||
            newVisibility = Visibility.OVERVIEW;
 | 
			
		||||
        else if (this._inFullscreen)
 | 
			
		||||
            newVisibility = Visibility.FULLSCREEN;
 | 
			
		||||
        else
 | 
			
		||||
            newVisibility = Visibility.FULL;
 | 
			
		||||
 | 
			
		||||
        if (newVisibility != this.visibility) {
 | 
			
		||||
            this.visibility = newVisibility;
 | 
			
		||||
            this.emit('visibility-changed', this.visibility);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _overviewShowing: function() {
 | 
			
		||||
@@ -212,68 +226,15 @@ Chrome.prototype = {
 | 
			
		||||
        this._queueUpdateRegions();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _relayout: function() {
 | 
			
		||||
        this._monitors = Main.layoutManager.monitors;
 | 
			
		||||
        this._primaryMonitor = Main.layoutManager.primaryMonitor;
 | 
			
		||||
 | 
			
		||||
        this._updateFullscreen();
 | 
			
		||||
        this._updateVisibility();
 | 
			
		||||
        this._queueUpdateRegions();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onScreenSaverActiveChanged: function(proxy, screenSaverActive) {
 | 
			
		||||
        this.actor.visible = !screenSaverActive;
 | 
			
		||||
        this._queueUpdateRegions();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _findMonitorForRect: function(x, y, w, h) {
 | 
			
		||||
        // First look at what monitor the center of the rectangle is at
 | 
			
		||||
        let cx = x + w/2;
 | 
			
		||||
        let cy = y + h/2;
 | 
			
		||||
        for (let i = 0; i < this._monitors.length; i++) {
 | 
			
		||||
            let monitor = this._monitors[i];
 | 
			
		||||
            if (cx >= monitor.x && cx < monitor.x + monitor.width &&
 | 
			
		||||
                cy >= monitor.y && cy < monitor.y + monitor.height)
 | 
			
		||||
                return monitor;
 | 
			
		||||
        }
 | 
			
		||||
        // If the center is not on a monitor, return the first overlapping monitor
 | 
			
		||||
        for (let i = 0; i < this._monitors.length; i++) {
 | 
			
		||||
            let monitor = this._monitors[i];
 | 
			
		||||
            if (x + w > monitor.x && x < monitor.x + monitor.width &&
 | 
			
		||||
                y + h > monitor.y && y < monitor.y + monitor.height)
 | 
			
		||||
                return monitor;
 | 
			
		||||
        }
 | 
			
		||||
        // otherwise on no monitor
 | 
			
		||||
        return null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _findMonitorForWindow: function(window) {
 | 
			
		||||
        return this._findMonitorForRect(window.x, window.y, window.width, window.height);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // This call guarantees that we return some monitor to simplify usage of it
 | 
			
		||||
    // In practice all tracked actors should be visible on some monitor anyway
 | 
			
		||||
    _findMonitorForActor: function(actor) {
 | 
			
		||||
        let [x, y] = actor.get_transformed_position();
 | 
			
		||||
        let [w, h] = actor.get_transformed_size();
 | 
			
		||||
        let monitor = this._findMonitorForRect(x, y, w, h);
 | 
			
		||||
        if (monitor)
 | 
			
		||||
            return monitor;
 | 
			
		||||
        return this._primaryMonitor; // Not on any monitor, pretend its on the primary
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _queueUpdateRegions: function() {
 | 
			
		||||
        if (!this._updateRegionIdle)
 | 
			
		||||
            this._updateRegionIdle = Mainloop.idle_add(Lang.bind(this, this._updateRegions),
 | 
			
		||||
                                                       Meta.PRIORITY_BEFORE_REDRAW);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateFullscreen: function() {
 | 
			
		||||
        let windows = Main.getWindowActorsForWorkspace(global.screen.get_active_workspace_index());
 | 
			
		||||
 | 
			
		||||
        // Reset all monitors to not fullscreen
 | 
			
		||||
        for (let i = 0; i < this._monitors.length; i++)
 | 
			
		||||
            this._monitors[i].inFullscreen = false;
 | 
			
		||||
    _windowsRestacked: function() {
 | 
			
		||||
        let windows = global.get_window_actors();
 | 
			
		||||
        let primary = global.get_primary_monitor();
 | 
			
		||||
 | 
			
		||||
        // The chrome layer should be visible unless there is a window
 | 
			
		||||
        // with layer FULLSCREEN, or a window with layer
 | 
			
		||||
@@ -287,48 +248,39 @@ Chrome.prototype = {
 | 
			
		||||
 | 
			
		||||
        // @windows is sorted bottom to top.
 | 
			
		||||
 | 
			
		||||
        let wasInFullscreen = this._inFullscreen;
 | 
			
		||||
        this._inFullscreen = false;
 | 
			
		||||
        for (let i = windows.length - 1; i > -1; i--) {
 | 
			
		||||
            let window = windows[i];
 | 
			
		||||
            let layer = windows[i].get_meta_window().get_layer();
 | 
			
		||||
 | 
			
		||||
            // Skip minimized windows
 | 
			
		||||
            if (!window.showing_on_its_workspace())
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            let layer = window.get_meta_window().get_layer();
 | 
			
		||||
            // There are 3 cases we check here for:
 | 
			
		||||
            // 1.) Monitor sized window
 | 
			
		||||
            // 2.) Window with a position somewhere on the primary screen having the _NET_WM_FULLSCREEN flag set
 | 
			
		||||
            // 3.) Window that is partly off screen (tries to hide its decorations) which might have negative coords
 | 
			
		||||
            // We check for 1.) and 2.) by checking if the upper right corner is on the primary monitor, but avoid the case
 | 
			
		||||
            // where it overlaps with the secondary screen (like window.x + window.width == primary.x + primary.width)
 | 
			
		||||
            // For 3.) we just ignore negative values as they don't really make sense
 | 
			
		||||
 | 
			
		||||
            if (layer == Meta.StackLayer.FULLSCREEN) {
 | 
			
		||||
                let monitor = this._findMonitorForWindow(window);
 | 
			
		||||
                if (monitor)
 | 
			
		||||
                    monitor.inFullscreen = true;
 | 
			
		||||
                if (Math.max(windows[i].x, 0) >= primary.x && Math.max(windows[i].x, 0) < primary.x + primary.width &&
 | 
			
		||||
                    Math.max(windows[i].y, 0) >= primary.y && Math.max(windows[i].y, 0) < primary.y + primary.height) {
 | 
			
		||||
                        this._inFullscreen = true;
 | 
			
		||||
                        break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (layer == Meta.StackLayer.OVERRIDE_REDIRECT) {
 | 
			
		||||
                let monitor = this._findMonitorForWindow(window);
 | 
			
		||||
                if (monitor &&
 | 
			
		||||
                    window.x <= monitor.x &&
 | 
			
		||||
                    window.x + window.width >= monitor.x + monitor.width &&
 | 
			
		||||
                    window.y <= monitor.y &&
 | 
			
		||||
                    window.y + window.height >= monitor.y + monitor.height)
 | 
			
		||||
                    monitor.inFullscreen = true;
 | 
			
		||||
                if (windows[i].x <= primary.x &&
 | 
			
		||||
                    windows[i].x + windows[i].width >= primary.x + primary.width &&
 | 
			
		||||
                    windows[i].y <= primary.y &&
 | 
			
		||||
                    windows[i].y + windows[i].height >= primary.y + primary.height) {
 | 
			
		||||
                    this._inFullscreen = true;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            } else
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _windowsRestacked: function() {
 | 
			
		||||
        let wasInFullscreen = [];
 | 
			
		||||
        for (let i = 0; i < this._monitors.length; i++)
 | 
			
		||||
            wasInFullscreen[i] = this._monitors[i].inFullscreen;
 | 
			
		||||
 | 
			
		||||
        this._updateFullscreen();
 | 
			
		||||
 | 
			
		||||
        let changed = false;
 | 
			
		||||
        for (let i = 0; i < wasInFullscreen.length; i++) {
 | 
			
		||||
            if (wasInFullscreen[i] != this._monitors[i].inFullscreen) {
 | 
			
		||||
                changed = true;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (changed) {
 | 
			
		||||
        if (this._inFullscreen != wasInFullscreen) {
 | 
			
		||||
            this._updateVisibility();
 | 
			
		||||
            this._queueUpdateRegions();
 | 
			
		||||
        }
 | 
			
		||||
@@ -360,75 +312,41 @@ Chrome.prototype = {
 | 
			
		||||
            if (!actorData.affectsStruts)
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            // Limit struts to the size of the screen
 | 
			
		||||
            let x1 = Math.max(x, 0);
 | 
			
		||||
            let x2 = Math.min(x + w, global.screen_width);
 | 
			
		||||
            let y1 = Math.max(y, 0);
 | 
			
		||||
            let y2 = Math.min(y + h, global.screen_height);
 | 
			
		||||
 | 
			
		||||
            // NetWM struts are not really powerful enought to handle
 | 
			
		||||
            // a multi-monitor scenario, they only describe what happens
 | 
			
		||||
            // around the outer sides of the full display region. However
 | 
			
		||||
            // it can describe a partial region along each side, so
 | 
			
		||||
            // we can support having the struts only affect the
 | 
			
		||||
            // primary monitor. This should be enough as we only have
 | 
			
		||||
            // chrome affecting the struts on the primary monitor so
 | 
			
		||||
            // far.
 | 
			
		||||
            //
 | 
			
		||||
            // Metacity wants to know what side of the screen the
 | 
			
		||||
            // strut is considered to be attached to. If the actor is
 | 
			
		||||
            // only touching one edge, or is touching the entire
 | 
			
		||||
            // border of the primary monitor, then it's obvious which
 | 
			
		||||
            // side to call it. If it's in a corner, we pick a side
 | 
			
		||||
            // width/height of one edge, then it's obvious which side
 | 
			
		||||
            // to call it. If it's in a corner, we pick a side
 | 
			
		||||
            // arbitrarily. If it doesn't touch any edges, or it spans
 | 
			
		||||
            // the width/height across the middle of the screen, then
 | 
			
		||||
            // we don't create a strut for it at all.
 | 
			
		||||
            let side;
 | 
			
		||||
            let primary = this._primaryMonitor;
 | 
			
		||||
            if (x1 <= primary.x && x2 >= primary.x + primary.width) {
 | 
			
		||||
                if (y1 <= primary.y)
 | 
			
		||||
            if (w >= global.screen_width) {
 | 
			
		||||
                if (y <= 0)
 | 
			
		||||
                    side = Meta.Side.TOP;
 | 
			
		||||
                else if (y2 >= primary.y + primary.height)
 | 
			
		||||
                else if (y + h >= global.screen_height)
 | 
			
		||||
                    side = Meta.Side.BOTTOM;
 | 
			
		||||
                else
 | 
			
		||||
                    continue;
 | 
			
		||||
            } else if (y1 <= primary.y && y2 >= primary.y + primary.height) {
 | 
			
		||||
                if (x1 <= 0)
 | 
			
		||||
            } else if (h >= global.screen_height) {
 | 
			
		||||
                if (x <= 0)
 | 
			
		||||
                    side = Meta.Side.LEFT;
 | 
			
		||||
                else if (x2 >= global.screen_width)
 | 
			
		||||
                else if (x + w >= global.screen_width)
 | 
			
		||||
                    side = Meta.Side.RIGHT;
 | 
			
		||||
                else
 | 
			
		||||
                    continue;
 | 
			
		||||
            } else if (x1 <= 0)
 | 
			
		||||
            } else if (x <= 0)
 | 
			
		||||
                side = Meta.Side.LEFT;
 | 
			
		||||
            else if (y1 <= 0)
 | 
			
		||||
            else if (y <= 0)
 | 
			
		||||
                side = Meta.Side.TOP;
 | 
			
		||||
            else if (x2 >= global.screen_width)
 | 
			
		||||
            else if (x + w >= global.screen_width)
 | 
			
		||||
                side = Meta.Side.RIGHT;
 | 
			
		||||
            else if (y2 >= global.screen_height)
 | 
			
		||||
            else if (y + h >= global.screen_height)
 | 
			
		||||
                side = Meta.Side.BOTTOM;
 | 
			
		||||
            else
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            // Ensure that the strut rects goes all the way to the screen edge,
 | 
			
		||||
            // as this really what mutter expects.
 | 
			
		||||
            switch (side) {
 | 
			
		||||
            case Meta.Side.TOP:
 | 
			
		||||
                y1 = 0;
 | 
			
		||||
                break;
 | 
			
		||||
            case Meta.Side.BOTTOM:
 | 
			
		||||
                y2 = global.screen_height;
 | 
			
		||||
                break;
 | 
			
		||||
            case Meta.Side.LEFT:
 | 
			
		||||
                x1 = 0;
 | 
			
		||||
                break;
 | 
			
		||||
            case Meta.Side.RIGHT:
 | 
			
		||||
                x2 = global.screen_width;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let strutRect = new Meta.Rectangle({ x: x1, y: y1, width: x2 - x1, height: y2 - y1});
 | 
			
		||||
            let strut = new Meta.Strut({ rect: strutRect, side: side });
 | 
			
		||||
            let strut = new Meta.Strut({ rect: rect, side: side });
 | 
			
		||||
            struts.push(strut);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,328 +0,0 @@
 | 
			
		||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Gdk = imports.gi.Gdk;
 | 
			
		||||
const Gtk = imports.gi.Gtk;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Meta = imports.gi.Meta;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
const AltTab = imports.ui.altTab;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
const Tweener = imports.ui.tweener;
 | 
			
		||||
 | 
			
		||||
const POPUP_APPICON_SIZE = 96;
 | 
			
		||||
const POPUP_FADE_TIME = 0.1; // seconds
 | 
			
		||||
 | 
			
		||||
const SortGroup = {
 | 
			
		||||
    TOP:    0,
 | 
			
		||||
    MIDDLE: 1,
 | 
			
		||||
    BOTTOM: 2
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function CtrlAltTabManager() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CtrlAltTabManager.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._items = [];
 | 
			
		||||
        this._focusManager = St.FocusManager.get_for_stage(global.stage);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addGroup: function(root, name, icon, params) {
 | 
			
		||||
        let item = Params.parse(params, { sortGroup: SortGroup.MIDDLE,
 | 
			
		||||
                                          proxy: root,
 | 
			
		||||
                                          focusCallback: null });
 | 
			
		||||
 | 
			
		||||
        item.root = root;
 | 
			
		||||
        item.name = name;
 | 
			
		||||
        item.iconName = icon;
 | 
			
		||||
 | 
			
		||||
        this._items.push(item);
 | 
			
		||||
        root.connect('destroy', Lang.bind(this, function() { this.removeGroup(root); }));
 | 
			
		||||
        this._focusManager.add_group(root);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    removeGroup: function(root) {
 | 
			
		||||
        this._focusManager.remove_group(root);
 | 
			
		||||
        for (let i = 0; i < this._items.length; i++) {
 | 
			
		||||
            if (this._items[i].root == root) {
 | 
			
		||||
                this._items.splice(i, 1);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    focusGroup: function(item) {
 | 
			
		||||
        if (global.stage_input_mode == Shell.StageInputMode.NONREACTIVE ||
 | 
			
		||||
            global.stage_input_mode == Shell.StageInputMode.NORMAL)
 | 
			
		||||
            global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
 | 
			
		||||
 | 
			
		||||
        if (item.window)
 | 
			
		||||
            Main.activateWindow(item.window);
 | 
			
		||||
        else if (item.focusCallback)
 | 
			
		||||
            item.focusCallback();
 | 
			
		||||
        else
 | 
			
		||||
            item.root.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Sort the items into a consistent order; panel first, tray last,
 | 
			
		||||
    // and everything else in between, sorted by X coordinate, so that
 | 
			
		||||
    // they will have the same left-to-right ordering in the
 | 
			
		||||
    // Ctrl-Alt-Tab dialog as they do onscreen.
 | 
			
		||||
    _sortItems: function(a, b) {
 | 
			
		||||
        if (a.sortGroup != b.sortGroup)
 | 
			
		||||
            return a.sortGroup - b.sortGroup;
 | 
			
		||||
 | 
			
		||||
        let y;
 | 
			
		||||
        if (a.x == undefined) {
 | 
			
		||||
            if (a.window)
 | 
			
		||||
                a.x = a.window.get_compositor_private().x;
 | 
			
		||||
            else
 | 
			
		||||
                [a.x, y] = a.proxy.get_transformed_position();
 | 
			
		||||
        }
 | 
			
		||||
        if (b.x == undefined) {
 | 
			
		||||
            if (b.window)
 | 
			
		||||
                b.x = b.window.get_compositor_private().x;
 | 
			
		||||
            else
 | 
			
		||||
                [b.x, y] = b.proxy.get_transformed_position();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return a.x - b.x;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    popup: function(backwards) {
 | 
			
		||||
        // Start with the set of focus groups that are currently mapped
 | 
			
		||||
        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.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;
 | 
			
		||||
                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,
 | 
			
		||||
                             sortGroup: SortGroup.MIDDLE });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!items.length)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        items.sort(Lang.bind(this, this._sortItems));
 | 
			
		||||
        new CtrlAltTabPopup().show(items, backwards);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function mod(a, b) {
 | 
			
		||||
    return (a + b) % b;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function CtrlAltTabPopup() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CtrlAltTabPopup.prototype = {
 | 
			
		||||
    _init : function() {
 | 
			
		||||
        this.actor = new Shell.GenericContainer({ name: 'ctrlAltTabPopup',
 | 
			
		||||
                                                  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));
 | 
			
		||||
 | 
			
		||||
        this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
 | 
			
		||||
 | 
			
		||||
        this._haveModal = false;
 | 
			
		||||
        this._selection = 0;
 | 
			
		||||
 | 
			
		||||
        Main.uiGroup.add_actor(this.actor);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getPreferredWidth: function (actor, forHeight, alloc) {
 | 
			
		||||
        let primary = Main.layoutManager.primaryMonitor;
 | 
			
		||||
 | 
			
		||||
        alloc.min_size = primary.width;
 | 
			
		||||
        alloc.natural_size = primary.width;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getPreferredHeight: function (actor, forWidth, alloc) {
 | 
			
		||||
        let primary = Main.layoutManager.primaryMonitor;
 | 
			
		||||
 | 
			
		||||
        alloc.min_size = primary.height;
 | 
			
		||||
        alloc.natural_size = primary.height;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _allocate: function (actor, box, flags) {
 | 
			
		||||
        let childBox = new Clutter.ActorBox();
 | 
			
		||||
        let primary = Main.layoutManager.primaryMonitor;
 | 
			
		||||
 | 
			
		||||
        let leftPadding = this.actor.get_theme_node().get_padding(St.Side.LEFT);
 | 
			
		||||
        let vPadding = this.actor.get_theme_node().get_vertical_padding();
 | 
			
		||||
        let hPadding = this.actor.get_theme_node().get_horizontal_padding();
 | 
			
		||||
 | 
			
		||||
        let [childMinHeight, childNaturalHeight] = this._switcher.actor.get_preferred_height(primary.width - hPadding);
 | 
			
		||||
        let [childMinWidth, childNaturalWidth] = this._switcher.actor.get_preferred_width(childNaturalHeight);
 | 
			
		||||
        childBox.x1 = Math.max(primary.x + leftPadding, primary.x + Math.floor((primary.width - childNaturalWidth) / 2));
 | 
			
		||||
        childBox.x2 = Math.min(primary.width - hPadding, childBox.x1 + childNaturalWidth);
 | 
			
		||||
        childBox.y1 = primary.y + Math.floor((primary.height - childNaturalHeight) / 2);
 | 
			
		||||
        childBox.y2 = childBox.y1 + childNaturalHeight;
 | 
			
		||||
        this._switcher.actor.allocate(childBox, flags);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    show : function(items, startBackwards) {
 | 
			
		||||
        if (!Main.pushModal(this.actor))
 | 
			
		||||
            return false;
 | 
			
		||||
        this._haveModal = true;
 | 
			
		||||
 | 
			
		||||
        this._keyPressEventId = this.actor.connect('key-press-event', Lang.bind(this, this._keyPressEvent));
 | 
			
		||||
        this._keyReleaseEventId = this.actor.connect('key-release-event', Lang.bind(this, this._keyReleaseEvent));
 | 
			
		||||
 | 
			
		||||
        this._items = items;
 | 
			
		||||
        this._switcher = new CtrlAltTabSwitcher(items);
 | 
			
		||||
        this.actor.add_actor(this._switcher.actor);
 | 
			
		||||
 | 
			
		||||
        if (startBackwards)
 | 
			
		||||
            this._selection = this._items.length - 1;
 | 
			
		||||
        this._select(this._selection);
 | 
			
		||||
 | 
			
		||||
        let [x, y, mods] = global.get_pointer();
 | 
			
		||||
        if (!(mods & Gdk.ModifierType.MOD1_MASK)) {
 | 
			
		||||
            this._finish();
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.actor.opacity = 0;
 | 
			
		||||
        this.actor.show();
 | 
			
		||||
        Tweener.addTween(this.actor,
 | 
			
		||||
                         { opacity: 255,
 | 
			
		||||
                           time: POPUP_FADE_TIME,
 | 
			
		||||
                           transition: 'easeOutQuad'
 | 
			
		||||
                         });
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _next : function() {
 | 
			
		||||
        return mod(this._selection + 1, this._items.length);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _previous : function() {
 | 
			
		||||
        return mod(this._selection - 1, this._items.length);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _keyPressEvent : function(actor, event) {
 | 
			
		||||
        let keysym = event.get_key_symbol();
 | 
			
		||||
        let shift = (Shell.get_event_state(event) & Clutter.ModifierType.SHIFT_MASK);
 | 
			
		||||
        if (shift && keysym == Clutter.KEY_Tab)
 | 
			
		||||
            keysym = Clutter.ISO_Left_Tab;
 | 
			
		||||
 | 
			
		||||
        if (keysym == Clutter.KEY_Escape)
 | 
			
		||||
            this.destroy();
 | 
			
		||||
        else if (keysym == Clutter.KEY_Tab)
 | 
			
		||||
            this._select(this._next());
 | 
			
		||||
        else if (keysym == Clutter.KEY_ISO_Left_Tab)
 | 
			
		||||
            this._select(this._previous());
 | 
			
		||||
        else if (keysym == Clutter.KEY_Left)
 | 
			
		||||
            this._select(this._previous());
 | 
			
		||||
        else if (keysym == Clutter.KEY_Right)
 | 
			
		||||
            this._select(this._next());
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _keyReleaseEvent : function(actor, event) {
 | 
			
		||||
        let [x, y, mods] = global.get_pointer();
 | 
			
		||||
        let state = mods & Clutter.ModifierType.MOD1_MASK;
 | 
			
		||||
 | 
			
		||||
        if (state == 0)
 | 
			
		||||
            this._finish();
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _finish : function() {
 | 
			
		||||
        this.destroy();
 | 
			
		||||
 | 
			
		||||
        Main.ctrlAltTabManager.focusGroup(this._items[this._selection]);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _popModal: function() {
 | 
			
		||||
        if (this._haveModal) {
 | 
			
		||||
            Main.popModal(this.actor);
 | 
			
		||||
            this._haveModal = false;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroy : function() {
 | 
			
		||||
        this._popModal();
 | 
			
		||||
        Tweener.addTween(this.actor,
 | 
			
		||||
                         { opacity: 0,
 | 
			
		||||
                           time: POPUP_FADE_TIME,
 | 
			
		||||
                           transition: 'easeOutQuad',
 | 
			
		||||
                           onComplete: Lang.bind(this,
 | 
			
		||||
                               function() {
 | 
			
		||||
                                   this.actor.destroy();
 | 
			
		||||
                               })
 | 
			
		||||
                         });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDestroy : function() {
 | 
			
		||||
        this._popModal();
 | 
			
		||||
        if (this._keyPressEventId)
 | 
			
		||||
            this.actor.disconnect(this._keyPressEventId);
 | 
			
		||||
        if (this._keyReleaseEventId)
 | 
			
		||||
            this.actor.disconnect(this._keyReleaseEventId);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _select : function(num) {
 | 
			
		||||
        this._selection = num;
 | 
			
		||||
        this._switcher.highlight(num);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function CtrlAltTabSwitcher(items) {
 | 
			
		||||
    this._init(items);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CtrlAltTabSwitcher.prototype = {
 | 
			
		||||
    __proto__ : AltTab.SwitcherList.prototype,
 | 
			
		||||
 | 
			
		||||
    _init : function(items) {
 | 
			
		||||
        AltTab.SwitcherList.prototype._init.call(this, true);
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < items.length; i++)
 | 
			
		||||
            this._addIcon(items[i]);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _addIcon : function(item) {
 | 
			
		||||
        let box = new St.BoxLayout({ style_class: 'alt-tab-app',
 | 
			
		||||
                                     vertical: true });
 | 
			
		||||
 | 
			
		||||
        let icon = item.iconActor;
 | 
			
		||||
        if (!icon) {
 | 
			
		||||
            icon = new St.Icon({ icon_name: item.iconName,
 | 
			
		||||
                                 icon_type: St.IconType.SYMBOLIC,
 | 
			
		||||
                                 icon_size: POPUP_APPICON_SIZE });
 | 
			
		||||
        }
 | 
			
		||||
        box.add(icon, { x_fill: false, y_fill: false } );
 | 
			
		||||
 | 
			
		||||
        let text = new St.Label({ text: item.name });
 | 
			
		||||
        box.add(text, { x_fill: false });
 | 
			
		||||
 | 
			
		||||
        this.addItem(box, text);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										562
									
								
								js/ui/dash.js
									
									
									
									
									
								
							
							
						
						@@ -1,188 +1,34 @@
 | 
			
		||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Meta = imports.gi.Meta;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Gettext = imports.gettext.domain('gnome-shell');
 | 
			
		||||
const _ = Gettext.gettext;
 | 
			
		||||
 | 
			
		||||
const AppDisplay = imports.ui.appDisplay;
 | 
			
		||||
const AppFavorites = imports.ui.appFavorites;
 | 
			
		||||
const DND = imports.ui.dnd;
 | 
			
		||||
const IconGrid = imports.ui.iconGrid;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const Tweener = imports.ui.tweener;
 | 
			
		||||
const Workspace = imports.ui.workspace;
 | 
			
		||||
 | 
			
		||||
const DASH_ANIMATION_TIME = 0.2;
 | 
			
		||||
 | 
			
		||||
// A container like StBin, but taking the child's scale into account
 | 
			
		||||
// when requesting a size
 | 
			
		||||
function DashItemContainer() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DashItemContainer.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        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.child = null;
 | 
			
		||||
        this._childScale = 1;
 | 
			
		||||
        this._childOpacity = 255;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _allocate: function(actor, box, flags) {
 | 
			
		||||
        if (this.child == null)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let availWidth = box.x2 - box.x1;
 | 
			
		||||
        let availHeight = box.y2 - box.y1;
 | 
			
		||||
        let [minChildWidth, minChildHeight, natChildWidth, natChildHeight] =
 | 
			
		||||
            this.child.get_preferred_size();
 | 
			
		||||
        let [childScaleX, childScaleY] = this.child.get_scale();
 | 
			
		||||
 | 
			
		||||
        let childWidth = Math.min(natChildWidth * childScaleX, availWidth);
 | 
			
		||||
        let childHeight = Math.min(natChildHeight * childScaleY, availHeight);
 | 
			
		||||
 | 
			
		||||
        let childBox = new Clutter.ActorBox();
 | 
			
		||||
        childBox.x1 = (availWidth - childWidth) / 2;
 | 
			
		||||
        childBox.y1 = (availHeight - childHeight) / 2;
 | 
			
		||||
        childBox.x2 = childBox.x1 + childWidth;
 | 
			
		||||
        childBox.y2 = childBox.y1 + childHeight;
 | 
			
		||||
 | 
			
		||||
        this.child.allocate(childBox, flags);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getPreferredHeight: function(actor, forWidth, alloc) {
 | 
			
		||||
        alloc.min_size = 0;
 | 
			
		||||
        alloc.natural_size = 0;
 | 
			
		||||
 | 
			
		||||
        if (this.child == null)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let [minHeight, natHeight] = this.child.get_preferred_height(forWidth);
 | 
			
		||||
        alloc.min_size += minHeight * this.child.scale_y;
 | 
			
		||||
        alloc.natural_size += natHeight * this.child.scale_y;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getPreferredWidth: function(actor, forHeight, alloc) {
 | 
			
		||||
        alloc.min_size = 0;
 | 
			
		||||
        alloc.natural_size = 0;
 | 
			
		||||
 | 
			
		||||
        if (this.child == null)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let [minWidth, natWidth] = this.child.get_preferred_width(forHeight);
 | 
			
		||||
        alloc.min_size = minWidth * this.child.scale_y;
 | 
			
		||||
        alloc.natural_size = natWidth * this.child.scale_y;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setChild: function(actor) {
 | 
			
		||||
        if (this.child == actor)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this.actor.destroy_children();
 | 
			
		||||
 | 
			
		||||
        this.child = actor;
 | 
			
		||||
        this.actor.add_actor(this.child);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    animateIn: function() {
 | 
			
		||||
        if (this.child == null)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this.childScale = 0;
 | 
			
		||||
        this.childOpacity = 0;
 | 
			
		||||
        Tweener.addTween(this,
 | 
			
		||||
                         { childScale: 1.0,
 | 
			
		||||
                           childOpacity: 255,
 | 
			
		||||
                           time: DASH_ANIMATION_TIME,
 | 
			
		||||
                           transition: 'easeOutQuad'
 | 
			
		||||
                         });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    animateOutAndDestroy: function() {
 | 
			
		||||
        if (this.child == null) {
 | 
			
		||||
            this.actor.destroy();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.childScale = 1.0;
 | 
			
		||||
        Tweener.addTween(this,
 | 
			
		||||
                         { childScale: 0.0,
 | 
			
		||||
                           childOpacity: 0,
 | 
			
		||||
                           time: DASH_ANIMATION_TIME,
 | 
			
		||||
                           transition: 'easeOutQuad',
 | 
			
		||||
                           onComplete: Lang.bind(this, function() {
 | 
			
		||||
                               this.actor.destroy();
 | 
			
		||||
                           })
 | 
			
		||||
                         });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    set childScale(scale) {
 | 
			
		||||
        this._childScale = scale;
 | 
			
		||||
 | 
			
		||||
        if (this.child == null)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this.child.set_scale_with_gravity(scale, scale,
 | 
			
		||||
                                          Clutter.Gravity.CENTER);
 | 
			
		||||
        this.actor.queue_relayout();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get childScale() {
 | 
			
		||||
        return this._childScale;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    set childOpacity(opacity) {
 | 
			
		||||
        this._childOpacity = opacity;
 | 
			
		||||
 | 
			
		||||
        if (this.child == null)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this.child.set_opacity(opacity);
 | 
			
		||||
        this.actor.queue_redraw();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get childOpacity() {
 | 
			
		||||
        return this._childOpacity;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function RemoveFavoriteIcon() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
RemoveFavoriteIcon.prototype = {
 | 
			
		||||
    __proto__: DashItemContainer.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        DashItemContainer.prototype._init.call(this);
 | 
			
		||||
 | 
			
		||||
        this._iconBin = new St.Bin({ style_class: 'remove-favorite' });
 | 
			
		||||
        this.actor = new St.Bin({ style_class: 'remove-favorite' });
 | 
			
		||||
        this._iconActor = null;
 | 
			
		||||
        this.icon = new IconGrid.BaseIcon(_("Remove"),
 | 
			
		||||
                                           { setSizeManually: true,
 | 
			
		||||
                                             showLabel: false,
 | 
			
		||||
                                             createIcon: Lang.bind(this, this._createIcon) });
 | 
			
		||||
        this._iconBin.set_child(this.icon.actor);
 | 
			
		||||
        this._iconBin._delegate = this;
 | 
			
		||||
 | 
			
		||||
        this.setChild(this._iconBin);
 | 
			
		||||
        this.hiding = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    animateOutAndDestroy: function() {
 | 
			
		||||
        DashItemContainer.prototype.animateOutAndDestroy.call(this);
 | 
			
		||||
        this.hiding = true;
 | 
			
		||||
        this.actor.set_child(this.icon.actor);
 | 
			
		||||
        this.actor._delegate = this;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createIcon: function(size) {
 | 
			
		||||
@@ -193,7 +39,7 @@ RemoveFavoriteIcon.prototype = {
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setHover: function(hovered) {
 | 
			
		||||
        this._iconBin.set_hover(hovered);
 | 
			
		||||
        this.actor.set_hover(hovered);
 | 
			
		||||
        if (this._iconActor)
 | 
			
		||||
            this._iconActor.set_hover(hovered);
 | 
			
		||||
    },
 | 
			
		||||
@@ -208,7 +54,7 @@ RemoveFavoriteIcon.prototype = {
 | 
			
		||||
        if (source instanceof AppDisplay.AppWellIcon) {
 | 
			
		||||
            let appSystem = Shell.AppSystem.get_default();
 | 
			
		||||
            app = appSystem.get_app(source.getId());
 | 
			
		||||
        } else if (source.metaWindow) {
 | 
			
		||||
        } else if (source instanceof Workspace.WindowClone) {
 | 
			
		||||
            let tracker = Shell.WindowTracker.get_default();
 | 
			
		||||
            app = tracker.get_window_app(source.metaWindow);
 | 
			
		||||
        }
 | 
			
		||||
@@ -226,35 +72,24 @@ RemoveFavoriteIcon.prototype = {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
function DragPlaceholderItem() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DragPlaceholderItem.prototype = {
 | 
			
		||||
    __proto__: DashItemContainer.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        DashItemContainer.prototype._init.call(this);
 | 
			
		||||
        this.setChild(new St.Bin({ style_class: 'dash-placeholder' }));
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
function Dash() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Dash.prototype = {
 | 
			
		||||
    _init : function() {
 | 
			
		||||
        this._placeholderText = null;
 | 
			
		||||
        this._menus = [];
 | 
			
		||||
        this._menuDisplays = [];
 | 
			
		||||
        this._maxHeight = -1;
 | 
			
		||||
        this.iconSize = 64;
 | 
			
		||||
        this._shownInitially = false;
 | 
			
		||||
        this._iconSize = 48;
 | 
			
		||||
 | 
			
		||||
        this._dragPlaceholder = null;
 | 
			
		||||
        this._dragPlaceholderPos = -1;
 | 
			
		||||
        this._animatingPlaceholdersCount = 0;
 | 
			
		||||
        this._favRemoveTarget = null;
 | 
			
		||||
 | 
			
		||||
        this._favorites = [];
 | 
			
		||||
 | 
			
		||||
        this._box = new St.BoxLayout({ name: 'dash',
 | 
			
		||||
                                       vertical: true,
 | 
			
		||||
                                       clip_to_allocation: true });
 | 
			
		||||
@@ -276,62 +111,47 @@ Dash.prototype = {
 | 
			
		||||
        this._appSystem.connect('installed-changed', Lang.bind(this, this._queueRedisplay));
 | 
			
		||||
        AppFavorites.getAppFavorites().connect('changed', Lang.bind(this, this._queueRedisplay));
 | 
			
		||||
        this._tracker.connect('app-state-changed', Lang.bind(this, this._queueRedisplay));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
        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._onDragCancelled));
 | 
			
		||||
        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));
 | 
			
		||||
    show: function() {
 | 
			
		||||
        this._itemDragBeginId = Main.overview.connect('item-drag-begin',
 | 
			
		||||
            Lang.bind(this, this._onDragBegin));
 | 
			
		||||
        this._itemDragEndId = Main.overview.connect('item-drag-end',
 | 
			
		||||
            Lang.bind(this, this._onDragEnd));
 | 
			
		||||
        this._windowDragBeginId = Main.overview.connect('window-drag-begin',
 | 
			
		||||
            Lang.bind(this, this._onDragBegin));
 | 
			
		||||
        this._windowDragEndId = Main.overview.connect('window-drag-end',
 | 
			
		||||
            Lang.bind(this, this._onDragEnd));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    hide: function() {
 | 
			
		||||
        Main.overview.disconnect(this._itemDragBeginId);
 | 
			
		||||
        Main.overview.disconnect(this._itemDragEndId);
 | 
			
		||||
        Main.overview.disconnect(this._windowDragBeginId);
 | 
			
		||||
        Main.overview.disconnect(this._windowDragEndId);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDragBegin: function() {
 | 
			
		||||
        this._dragCancelled = false;
 | 
			
		||||
        this._dragMonitor = {
 | 
			
		||||
            dragMotion: Lang.bind(this, this._onDragMotion)
 | 
			
		||||
        };
 | 
			
		||||
        DND.addDragMonitor(this._dragMonitor);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDragCancelled: function() {
 | 
			
		||||
        this._dragCancelled = true;
 | 
			
		||||
        this._endDrag();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDragEnd: function() {
 | 
			
		||||
        if (this._dragCancelled)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._endDrag();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _endDrag: function() {
 | 
			
		||||
        this._clearDragPlaceholder();
 | 
			
		||||
        if (this._favRemoveTarget) {
 | 
			
		||||
            this._favRemoveTarget.actor.hide();
 | 
			
		||||
            this._adjustIconSize();
 | 
			
		||||
            this._favRemoveTarget.actor.show();
 | 
			
		||||
 | 
			
		||||
            this._favRemoveTarget.animateOutAndDestroy();
 | 
			
		||||
            this._favRemoveTarget.actor.connect('destroy', Lang.bind(this,
 | 
			
		||||
                function() {
 | 
			
		||||
                    this._favRemoveTarget = null;
 | 
			
		||||
                }));
 | 
			
		||||
            this._favRemoveTarget.actor.destroy();
 | 
			
		||||
            this._favRemoveTarget = null;
 | 
			
		||||
        }
 | 
			
		||||
        DND.removeDragMonitor(this._dragMonitor);
 | 
			
		||||
        DND.removeMonitor(this._dragMonitor);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDragMotion: function(dragEvent) {
 | 
			
		||||
        let app = null;
 | 
			
		||||
        if (dragEvent.source instanceof AppDisplay.AppWellIcon)
 | 
			
		||||
            app = this._appSystem.get_app(dragEvent.source.getId());
 | 
			
		||||
        else if (dragEvent.source.metaWindow)
 | 
			
		||||
        else if (dragEvent.source instanceof Workspace.WindowClone)
 | 
			
		||||
            app = this._tracker.get_window_app(dragEvent.source.metaWindow);
 | 
			
		||||
        else
 | 
			
		||||
            return DND.DragMotionResult.CONTINUE;
 | 
			
		||||
@@ -342,15 +162,10 @@ Dash.prototype = {
 | 
			
		||||
 | 
			
		||||
        let srcIsFavorite = (id in favorites);
 | 
			
		||||
 | 
			
		||||
        if (srcIsFavorite &&
 | 
			
		||||
            dragEvent.source.actor &&
 | 
			
		||||
            this.actor.contains (dragEvent.source.actor) &&
 | 
			
		||||
            this._favRemoveTarget == null) {
 | 
			
		||||
        if (srcIsFavorite && this._favRemoveTarget == null) {
 | 
			
		||||
                this._favRemoveTarget = new RemoveFavoriteIcon();
 | 
			
		||||
                this._favRemoveTarget.icon.setIconSize(this.iconSize);
 | 
			
		||||
                this._favRemoveTarget.icon.setIconSize(this._iconSize);
 | 
			
		||||
                this._box.add(this._favRemoveTarget.actor);
 | 
			
		||||
                this._adjustIconSize();
 | 
			
		||||
                this._favRemoveTarget.animateIn();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let favRemoveHovered = false;
 | 
			
		||||
@@ -378,10 +193,8 @@ Dash.prototype = {
 | 
			
		||||
        Main.queueDeferredWork(this._workId);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createAppItem: function(app) {
 | 
			
		||||
        let display = new AppDisplay.AppWellIcon(app,
 | 
			
		||||
                                                 { setSizeManually: true,
 | 
			
		||||
                                                   showLabel: false });
 | 
			
		||||
    _addApp: function(app) {
 | 
			
		||||
        let display = new AppDisplay.AppWellIcon(app);
 | 
			
		||||
        display._draggable.connect('drag-begin',
 | 
			
		||||
                                   Lang.bind(this, function() {
 | 
			
		||||
                                       display.actor.opacity = 50;
 | 
			
		||||
@@ -390,227 +203,63 @@ Dash.prototype = {
 | 
			
		||||
                                   Lang.bind(this, function() {
 | 
			
		||||
                                       display.actor.opacity = 255;
 | 
			
		||||
                                   }));
 | 
			
		||||
        display.actor.set_tooltip_text(app.get_name());
 | 
			
		||||
 | 
			
		||||
        let item = new DashItemContainer();
 | 
			
		||||
        item.setChild(display.actor);
 | 
			
		||||
 | 
			
		||||
        display.icon.setIconSize(this.iconSize);
 | 
			
		||||
 | 
			
		||||
        return item;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _adjustIconSize: function() {
 | 
			
		||||
        let children = this._box.get_children();
 | 
			
		||||
        if (children.length == 0) {
 | 
			
		||||
            this._box.add_style_pseudo_class('empty');
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._box.remove_style_pseudo_class('empty');
 | 
			
		||||
 | 
			
		||||
        if (this._maxHeight == -1)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let iconChildren = children.filter(function(actor) {
 | 
			
		||||
            return actor.visible &&
 | 
			
		||||
                   actor._delegate.child &&
 | 
			
		||||
                   actor._delegate.child._delegate &&
 | 
			
		||||
                   actor._delegate.child._delegate.icon;
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        // Compute the amount of extra space (or missing space) we have
 | 
			
		||||
        // per icon with the current icon size
 | 
			
		||||
        let [minHeight, natHeight] = this.actor.get_preferred_height(-1);
 | 
			
		||||
        let diff = (this._maxHeight - natHeight) / iconChildren.length;
 | 
			
		||||
 | 
			
		||||
        let iconSizes = [ 16, 22, 24, 32, 48, 64 ];
 | 
			
		||||
 | 
			
		||||
        let newIconSize = 16;
 | 
			
		||||
        for (let i = 0; i < iconSizes.length; i++) {
 | 
			
		||||
            if (iconSizes[i] < this.iconSize + diff)
 | 
			
		||||
                newIconSize = iconSizes[i];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (newIconSize == this.iconSize)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let oldIconSize = this.iconSize;
 | 
			
		||||
        this.iconSize = newIconSize;
 | 
			
		||||
 | 
			
		||||
        let scale = oldIconSize / newIconSize;
 | 
			
		||||
        for (let i = 0; i < iconChildren.length; i++) {
 | 
			
		||||
            let icon = iconChildren[i]._delegate.child._delegate.icon;
 | 
			
		||||
 | 
			
		||||
            // Set the new size immediately, to keep the icons' sizes
 | 
			
		||||
            // in sync with this.iconSize
 | 
			
		||||
            icon.setIconSize(this.iconSize);
 | 
			
		||||
 | 
			
		||||
            // Don't animate the icon size change when the overview
 | 
			
		||||
            // is not visible or when initially filling the dash
 | 
			
		||||
            if (!Main.overview.visible || !this._shownInitially)
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            let [targetWidth, targetHeight] = icon.icon.get_size();
 | 
			
		||||
 | 
			
		||||
            // Scale the icon's texture to the previous size and
 | 
			
		||||
            // tween to the new size
 | 
			
		||||
            icon.icon.set_size(icon.icon.width * scale,
 | 
			
		||||
                               icon.icon.height * scale);
 | 
			
		||||
 | 
			
		||||
            Tweener.addTween(icon.icon,
 | 
			
		||||
                             { width: targetWidth,
 | 
			
		||||
                               height: targetHeight,
 | 
			
		||||
                               time: DASH_ANIMATION_TIME,
 | 
			
		||||
                               transition: 'easeOutQuad'
 | 
			
		||||
                             });
 | 
			
		||||
        }
 | 
			
		||||
        this._box.add(display.actor);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _redisplay: function () {
 | 
			
		||||
        this._box.hide();
 | 
			
		||||
        this._box.remove_all();
 | 
			
		||||
 | 
			
		||||
        let favorites = AppFavorites.getAppFavorites().getFavoriteMap();
 | 
			
		||||
 | 
			
		||||
        /* hardcode here pending some design about how exactly desktop contexts behave */
 | 
			
		||||
        let contextId = '';
 | 
			
		||||
 | 
			
		||||
        let running = this._tracker.get_running_apps(contextId);
 | 
			
		||||
        let runningIds = this._appIdListToHash(running);
 | 
			
		||||
 | 
			
		||||
        let children = this._box.get_children().filter(function(actor) {
 | 
			
		||||
                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._delegate.child._delegate.app;
 | 
			
		||||
            });
 | 
			
		||||
        // Apps supposed to be in the dash
 | 
			
		||||
        let newApps = [];
 | 
			
		||||
 | 
			
		||||
        for (let id in favorites)
 | 
			
		||||
            newApps.push(favorites[id]);
 | 
			
		||||
        for (let id in favorites) {
 | 
			
		||||
            let app = favorites[id];
 | 
			
		||||
            this._addApp(app);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < running.length; i++) {
 | 
			
		||||
            let app = running[i];
 | 
			
		||||
            if (app.get_id() in favorites)
 | 
			
		||||
                continue;
 | 
			
		||||
            newApps.push(app);
 | 
			
		||||
            this._addApp(app);
 | 
			
		||||
        }
 | 
			
		||||
        if (this._placeholderText) {
 | 
			
		||||
            this._placeholderText.destroy();
 | 
			
		||||
            this._placeholderText = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Figure out the actual changes to the list of items; we iterate
 | 
			
		||||
        // over both the list of items currently in the dash and the list
 | 
			
		||||
        // of items expected there, and collect additions and removals.
 | 
			
		||||
        // Moves are both an addition and a removal, where the order of
 | 
			
		||||
        // the operations depends on whether we encounter the position
 | 
			
		||||
        // where the item has been added first or the one from where it
 | 
			
		||||
        // was removed.
 | 
			
		||||
        // There is an assumption that only one item is moved at a given
 | 
			
		||||
        // time; when moving several items at once, everything will still
 | 
			
		||||
        // end up at the right position, but there might be additional
 | 
			
		||||
        // additions/removals (e.g. it might remove all the launchers
 | 
			
		||||
        // and add them back in the new order even if a smaller set of
 | 
			
		||||
        // additions and removals is possible).
 | 
			
		||||
        // If above assumptions turns out to be a problem, we might need
 | 
			
		||||
        // to use a more sophisticated algorithm, e.g. Longest Common
 | 
			
		||||
        // Subsequence as used by diff.
 | 
			
		||||
        let addedItems = [];
 | 
			
		||||
        let removedActors = [];
 | 
			
		||||
        let children = this._box.get_children();
 | 
			
		||||
        if (children.length == 0) {
 | 
			
		||||
            this._placeholderText = new St.Label({ text: _("Drag here to add favorites") });
 | 
			
		||||
            this._box.add_actor(this._placeholderText);
 | 
			
		||||
        } else if (this._maxHeight > -1) {
 | 
			
		||||
            let iconSizes = [ 48, 32, 24, 22, 16 ];
 | 
			
		||||
 | 
			
		||||
        let newIndex = 0;
 | 
			
		||||
        let oldIndex = 0;
 | 
			
		||||
        while (newIndex < newApps.length || oldIndex < oldApps.length) {
 | 
			
		||||
            // No change at oldIndex/newIndex
 | 
			
		||||
            if (oldApps[oldIndex] == newApps[newIndex]) {
 | 
			
		||||
                oldIndex++;
 | 
			
		||||
                newIndex++;
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            for (let i = 0; i < iconSizes.length; i++) {
 | 
			
		||||
                let minHeight, natHeight;
 | 
			
		||||
 | 
			
		||||
            // App removed at oldIndex
 | 
			
		||||
            if (oldApps[oldIndex] &&
 | 
			
		||||
                newApps.indexOf(oldApps[oldIndex]) == -1) {
 | 
			
		||||
                removedActors.push(children[oldIndex]);
 | 
			
		||||
                oldIndex++;
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
                this._iconSize = iconSizes[i];
 | 
			
		||||
                for (let j = 0; j < children.length; j++)
 | 
			
		||||
                    children[j]._delegate.icon.setIconSize(this._iconSize);
 | 
			
		||||
 | 
			
		||||
            // App added at newIndex
 | 
			
		||||
            if (newApps[newIndex] &&
 | 
			
		||||
                oldApps.indexOf(newApps[newIndex]) == -1) {
 | 
			
		||||
                addedItems.push({ app: newApps[newIndex],
 | 
			
		||||
                                  item: this._createAppItem(newApps[newIndex]),
 | 
			
		||||
                                  pos: newIndex });
 | 
			
		||||
                newIndex++;
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
                [minHeight, natHeight] = this.actor.get_preferred_height(-1);
 | 
			
		||||
 | 
			
		||||
            // App moved
 | 
			
		||||
            let insertHere = newApps[newIndex + 1] &&
 | 
			
		||||
                             newApps[newIndex + 1] == oldApps[oldIndex];
 | 
			
		||||
            let alreadyRemoved = removedActors.reduce(function(result, actor) {
 | 
			
		||||
                let removedApp = actor._delegate.child._delegate.app;
 | 
			
		||||
                return result || removedApp == newApps[newIndex];
 | 
			
		||||
            }, false);
 | 
			
		||||
 | 
			
		||||
            if (insertHere || alreadyRemoved) {
 | 
			
		||||
                let newItem = this._createAppItem(newApps[newIndex]);
 | 
			
		||||
                addedItems.push({ app: newApps[newIndex],
 | 
			
		||||
                                  item: newItem,
 | 
			
		||||
                                  pos: newIndex + removedActors.length });
 | 
			
		||||
                newIndex++;
 | 
			
		||||
            } else {
 | 
			
		||||
                removedActors.push(children[oldIndex]);
 | 
			
		||||
                oldIndex++;
 | 
			
		||||
                if (natHeight <= this._maxHeight)
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < addedItems.length; i++)
 | 
			
		||||
            this._box.insert_actor(addedItems[i].item.actor,
 | 
			
		||||
                                   addedItems[i].pos);
 | 
			
		||||
 | 
			
		||||
        // Hide removed actors to not take them into account
 | 
			
		||||
        // when adjusting the icon size ...
 | 
			
		||||
        for (let i = 0; i < removedActors.length; i++)
 | 
			
		||||
            removedActors[i].hide();
 | 
			
		||||
 | 
			
		||||
        // ... and do the same for the remove target if necessary
 | 
			
		||||
        if (this._favRemoveTarget && this._favRemoveTarget.hiding)
 | 
			
		||||
            this._favRemoveTarget.actor.hide();
 | 
			
		||||
 | 
			
		||||
        this._adjustIconSize();
 | 
			
		||||
 | 
			
		||||
        if (this._favRemoveTarget && this._favRemoveTarget.hiding)
 | 
			
		||||
            this._favRemoveTarget.actor.show();
 | 
			
		||||
 | 
			
		||||
        // Skip animations on first run when adding the initial set
 | 
			
		||||
        // of items, to avoid all items zooming in at once
 | 
			
		||||
        if (!this._shownInitially) {
 | 
			
		||||
            this._shownInitially = true;
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < removedActors.length; i++) {
 | 
			
		||||
            removedActors[i].show();
 | 
			
		||||
            let item = removedActors[i]._delegate;
 | 
			
		||||
 | 
			
		||||
            // Don't animate item removal when the overview is hidden
 | 
			
		||||
            if (Main.overview.visible)
 | 
			
		||||
                item.animateOutAndDestroy();
 | 
			
		||||
            else
 | 
			
		||||
                item.actor.destroy();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Don't animate item addition when the overview is hidden
 | 
			
		||||
        if (!Main.overview.visible)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < addedItems.length; i++)
 | 
			
		||||
            addedItems[i].item.animateIn();
 | 
			
		||||
        this._box.show();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _clearDragPlaceholder: function() {
 | 
			
		||||
        if (this._dragPlaceholder) {
 | 
			
		||||
            this._dragPlaceholder.animateOutAndDestroy();
 | 
			
		||||
            this._dragPlaceholder.destroy();
 | 
			
		||||
            this._dragPlaceholder = null;
 | 
			
		||||
            this._dragPlaceholderPos = -1;
 | 
			
		||||
        }
 | 
			
		||||
@@ -620,79 +269,40 @@ Dash.prototype = {
 | 
			
		||||
        let app = null;
 | 
			
		||||
        if (source instanceof AppDisplay.AppWellIcon)
 | 
			
		||||
            app = this._appSystem.get_app(source.getId());
 | 
			
		||||
        else if (source.metaWindow)
 | 
			
		||||
        else if (source instanceof Workspace.WindowClone)
 | 
			
		||||
            app = this._tracker.get_window_app(source.metaWindow);
 | 
			
		||||
 | 
			
		||||
        // Don't allow favoriting of transient apps
 | 
			
		||||
        if (app == null || app.is_transient())
 | 
			
		||||
            return DND.DragMotionResult.NO_DROP;
 | 
			
		||||
 | 
			
		||||
        let favorites = AppFavorites.getAppFavorites().getFavorites();
 | 
			
		||||
        let numFavorites = favorites.length;
 | 
			
		||||
 | 
			
		||||
        let favPos = favorites.indexOf(app);
 | 
			
		||||
 | 
			
		||||
        let children = this._box.get_children();
 | 
			
		||||
        let numChildren = children.length;
 | 
			
		||||
        let numFavorites = AppFavorites.getAppFavorites().getFavorites().length;
 | 
			
		||||
        let numChildren = this._box.get_children().length;
 | 
			
		||||
        let boxHeight = this._box.height;
 | 
			
		||||
 | 
			
		||||
        // Keep the placeholder out of the index calculation; assuming that
 | 
			
		||||
        // 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.actor.height;
 | 
			
		||||
            boxHeight -= this._dragPlaceholder.height;
 | 
			
		||||
            numChildren--;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let pos = Math.round(y * numChildren / boxHeight);
 | 
			
		||||
 | 
			
		||||
        if (pos != this._dragPlaceholderPos && pos <= numFavorites) {
 | 
			
		||||
            if (this._animatingPlaceholdersCount > 0) {
 | 
			
		||||
                let appChildren = children.filter(function(actor) {
 | 
			
		||||
                    return actor._delegate &&
 | 
			
		||||
                           actor._delegate.child &&
 | 
			
		||||
                           actor._delegate.child._delegate &&
 | 
			
		||||
                           actor._delegate.child._delegate.app;
 | 
			
		||||
                });
 | 
			
		||||
                this._dragPlaceholderPos = children.indexOf(appChildren[pos]);
 | 
			
		||||
            } else {
 | 
			
		||||
                this._dragPlaceholderPos = pos;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Don't allow positioning before or after self
 | 
			
		||||
            if (favPos != -1 && (pos == favPos || pos == favPos + 1)) {
 | 
			
		||||
                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;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // If the placeholder already exists, we just move
 | 
			
		||||
            // it, but if we are adding it, expand its size in
 | 
			
		||||
            // an animation
 | 
			
		||||
            let fadeIn;
 | 
			
		||||
            if (this._dragPlaceholder) {
 | 
			
		||||
                this._dragPlaceholder.actor.destroy();
 | 
			
		||||
                fadeIn = false;
 | 
			
		||||
            } else {
 | 
			
		||||
                fadeIn = true;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this._dragPlaceholder = new DragPlaceholderItem();
 | 
			
		||||
            this._box.insert_actor(this._dragPlaceholder.actor,
 | 
			
		||||
                                   this._dragPlaceholderPos);
 | 
			
		||||
            if (fadeIn)
 | 
			
		||||
                this._dragPlaceholder.animateIn();
 | 
			
		||||
            this._dragPlaceholderPos = pos;
 | 
			
		||||
            if (this._dragPlaceholder)
 | 
			
		||||
                this._dragPlaceholder.destroy();
 | 
			
		||||
            this._dragPlaceholder = new St.Bin({ style_class: 'dash-placeholder' });
 | 
			
		||||
            this._box.insert_actor(this._dragPlaceholder, pos);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let srcIsFavorite = (favPos != -1);
 | 
			
		||||
        let id = app.get_id();
 | 
			
		||||
 | 
			
		||||
        let favorites = AppFavorites.getAppFavorites().getFavoriteMap();
 | 
			
		||||
 | 
			
		||||
        let srcIsFavorite = (id in favorites);
 | 
			
		||||
 | 
			
		||||
        if (srcIsFavorite)
 | 
			
		||||
            return DND.DragMotionResult.MOVE_DROP;
 | 
			
		||||
@@ -705,7 +315,7 @@ Dash.prototype = {
 | 
			
		||||
        let app = null;
 | 
			
		||||
        if (source instanceof AppDisplay.AppWellIcon) {
 | 
			
		||||
            app = this._appSystem.get_app(source.getId());
 | 
			
		||||
        } else if (source.metaWindow) {
 | 
			
		||||
        } else if (source instanceof Workspace.WindowClone) {
 | 
			
		||||
            app = this._tracker.get_window_app(source.metaWindow);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -723,11 +333,7 @@ Dash.prototype = {
 | 
			
		||||
        let favPos = 0;
 | 
			
		||||
        let children = this._box.get_children();
 | 
			
		||||
        for (let i = 0; i < this._dragPlaceholderPos; i++) {
 | 
			
		||||
            if (this._dragPlaceholder &&
 | 
			
		||||
                children[i] == this._dragPlaceholder.actor)
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            let childId = children[i]._delegate.child._delegate.app.get_id();
 | 
			
		||||
            let childId = children[i]._delegate.app.get_id();
 | 
			
		||||
            if (childId == id)
 | 
			
		||||
                continue;
 | 
			
		||||
            if (childId in favorites)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,211 +0,0 @@
 | 
			
		||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
 | 
			
		||||
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Mainloop = imports.mainloop;
 | 
			
		||||
const Cairo = imports.cairo;
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
const Util = imports.misc.util;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const PanelMenu = imports.ui.panelMenu;
 | 
			
		||||
const PopupMenu = imports.ui.popupMenu;
 | 
			
		||||
const Calendar = imports.ui.calendar;
 | 
			
		||||
 | 
			
		||||
// in org.gnome.desktop.interface
 | 
			
		||||
const CLOCK_FORMAT_KEY        = 'clock-format';
 | 
			
		||||
 | 
			
		||||
// in org.gnome.shell.clock
 | 
			
		||||
const CLOCK_SHOW_DATE_KEY     = 'show-date';
 | 
			
		||||
const CLOCK_SHOW_SECONDS_KEY  = 'show-seconds';
 | 
			
		||||
 | 
			
		||||
function _onVertSepRepaint (area)
 | 
			
		||||
{
 | 
			
		||||
    let cr = area.get_context();
 | 
			
		||||
    let themeNode = area.get_theme_node();
 | 
			
		||||
    let [width, height] = area.get_surface_size();
 | 
			
		||||
    let stippleColor = themeNode.get_color('-stipple-color');
 | 
			
		||||
    let stippleWidth = themeNode.get_length('-stipple-width');
 | 
			
		||||
    let x = Math.floor(width/2) + 0.5;
 | 
			
		||||
    cr.moveTo(x, 0);
 | 
			
		||||
    cr.lineTo(x, height);
 | 
			
		||||
    Clutter.cairo_set_source_color(cr, stippleColor);
 | 
			
		||||
    cr.setDash([1, 3], 1); // Hard-code for now
 | 
			
		||||
    cr.setLineWidth(stippleWidth);
 | 
			
		||||
    cr.stroke();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function DateMenuButton() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DateMenuButton.prototype = {
 | 
			
		||||
    __proto__: PanelMenu.Button.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        let item;
 | 
			
		||||
        let hbox;
 | 
			
		||||
        let vbox;
 | 
			
		||||
 | 
			
		||||
        this._eventSource = new Calendar.DBusEventSource();
 | 
			
		||||
 | 
			
		||||
        let menuAlignment = 0.25;
 | 
			
		||||
        if (St.Widget.get_default_direction() == St.TextDirection.RTL)
 | 
			
		||||
            menuAlignment = 1.0 - menuAlignment;
 | 
			
		||||
        PanelMenu.Button.prototype._init.call(this, menuAlignment);
 | 
			
		||||
 | 
			
		||||
        this._clock = new St.Label();
 | 
			
		||||
        this.actor.set_child(this._clock);
 | 
			
		||||
 | 
			
		||||
        hbox = new St.BoxLayout({name: 'calendarArea'});
 | 
			
		||||
        this.menu.addActor(hbox);
 | 
			
		||||
 | 
			
		||||
        // Fill up the first column
 | 
			
		||||
 | 
			
		||||
        vbox = new St.BoxLayout({vertical: true});
 | 
			
		||||
        hbox.add(vbox);
 | 
			
		||||
 | 
			
		||||
        // Date
 | 
			
		||||
        this._date = new St.Label();
 | 
			
		||||
        this._date.style_class = 'datemenu-date-label';
 | 
			
		||||
        vbox.add(this._date);
 | 
			
		||||
 | 
			
		||||
        this._eventList = new Calendar.EventsList(this._eventSource);
 | 
			
		||||
 | 
			
		||||
        // Calendar
 | 
			
		||||
        this._calendar = new Calendar.Calendar(this._eventSource);
 | 
			
		||||
        this._calendar.connect('selected-date-changed',
 | 
			
		||||
                               Lang.bind(this, function(calendar, date) {
 | 
			
		||||
                                   this._eventList.setDate(date);
 | 
			
		||||
                               }));
 | 
			
		||||
        vbox.add(this._calendar.actor);
 | 
			
		||||
 | 
			
		||||
        item = new PopupMenu.PopupSeparatorMenuItem();
 | 
			
		||||
        item.setColumnWidths(1);
 | 
			
		||||
        vbox.add(item.actor, {y_align: St.Align.END, expand: true, y_fill: false});
 | 
			
		||||
        item = new PopupMenu.PopupMenuItem(_("Date and Time Settings"));
 | 
			
		||||
        item.connect('activate', Lang.bind(this, this._onPreferencesActivate));
 | 
			
		||||
        item.actor.can_focus = false;
 | 
			
		||||
        vbox.add(item.actor);
 | 
			
		||||
 | 
			
		||||
        // Add vertical separator
 | 
			
		||||
 | 
			
		||||
        item = new St.DrawingArea({ style_class: 'calendar-vertical-separator',
 | 
			
		||||
                                    pseudo_class: 'highlighted' });
 | 
			
		||||
        item.connect('repaint', Lang.bind(this, _onVertSepRepaint));
 | 
			
		||||
        hbox.add(item);
 | 
			
		||||
 | 
			
		||||
        // Fill up the second column
 | 
			
		||||
 | 
			
		||||
        vbox = new St.BoxLayout({vertical: true});
 | 
			
		||||
        hbox.add(vbox, { expand: true });
 | 
			
		||||
 | 
			
		||||
        // Event list
 | 
			
		||||
        vbox.add(this._eventList.actor, { expand: true });
 | 
			
		||||
 | 
			
		||||
        item = new PopupMenu.PopupMenuItem(_("Open Calendar"));
 | 
			
		||||
        item.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
 | 
			
		||||
        item.actor.can_focus = false;
 | 
			
		||||
        vbox.add(item.actor, {y_align: St.Align.END, expand: true, y_fill: false});
 | 
			
		||||
 | 
			
		||||
        // Whenever the menu is opened, select today
 | 
			
		||||
        this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) {
 | 
			
		||||
            if (isOpen) {
 | 
			
		||||
                let now = new Date();
 | 
			
		||||
                /* Passing true to setDate() forces events to be reloaded. We
 | 
			
		||||
                 * want this behavior, because
 | 
			
		||||
                 *
 | 
			
		||||
                 *   o It will cause activation of the calendar server which is
 | 
			
		||||
                 *     useful if it has crashed
 | 
			
		||||
                 *
 | 
			
		||||
                 *   o It will cause the calendar server to reload events which
 | 
			
		||||
                 *     is useful if dynamic updates are not supported or not
 | 
			
		||||
                 *     properly working
 | 
			
		||||
                 *
 | 
			
		||||
                 * Since this only happens when the menu is opened, the cost
 | 
			
		||||
                 * isn't very big.
 | 
			
		||||
                 */
 | 
			
		||||
                this._calendar.setDate(now, true);
 | 
			
		||||
                // No need to update this._eventList as ::selected-date-changed
 | 
			
		||||
                // signal will fire
 | 
			
		||||
            }
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        // Done with hbox for calendar and event list
 | 
			
		||||
 | 
			
		||||
        // Track changes to clock settings
 | 
			
		||||
        this._desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
 | 
			
		||||
        this._clockSettings = new Gio.Settings({ schema: 'org.gnome.shell.clock' });
 | 
			
		||||
        this._desktopSettings.connect('changed', Lang.bind(this, this._updateClockAndDate));
 | 
			
		||||
        this._clockSettings.connect('changed', Lang.bind(this, this._updateClockAndDate));
 | 
			
		||||
 | 
			
		||||
        // Start the clock
 | 
			
		||||
        this._updateClockAndDate();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateClockAndDate: function() {
 | 
			
		||||
        let format = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);
 | 
			
		||||
        let showDate = this._clockSettings.get_boolean(CLOCK_SHOW_DATE_KEY);
 | 
			
		||||
        let showSeconds = this._clockSettings.get_boolean(CLOCK_SHOW_SECONDS_KEY);
 | 
			
		||||
 | 
			
		||||
        let clockFormat;
 | 
			
		||||
        let dateFormat;
 | 
			
		||||
 | 
			
		||||
        switch (format) {
 | 
			
		||||
            case '24h':
 | 
			
		||||
                if (showDate)
 | 
			
		||||
	            /* Translators: This is the time format with date used
 | 
			
		||||
                       in 24-hour mode. */
 | 
			
		||||
                    clockFormat = showSeconds ? _("%a %b %e, %R:%S")
 | 
			
		||||
                                              : _("%a %b %e, %R");
 | 
			
		||||
                else
 | 
			
		||||
	            /* Translators: This is the time format without date used
 | 
			
		||||
                       in 24-hour mode. */
 | 
			
		||||
                    clockFormat = showSeconds ? _("%a %R:%S")
 | 
			
		||||
                                              : _("%a %R");
 | 
			
		||||
                break;
 | 
			
		||||
            case '12h':
 | 
			
		||||
            default:
 | 
			
		||||
                if (showDate)
 | 
			
		||||
	            /* Translators: This is a time format with date used
 | 
			
		||||
                       for AM/PM. */
 | 
			
		||||
                    clockFormat = showSeconds ? _("%a %b %e, %l:%M:%S %p")
 | 
			
		||||
                                              : _("%a %b %e, %l:%M %p");
 | 
			
		||||
                else
 | 
			
		||||
	            /* Translators: This is a time format without date used
 | 
			
		||||
                       for AM/PM. */
 | 
			
		||||
                    clockFormat = showSeconds ? _("%a %l:%M:%S %p")
 | 
			
		||||
                                              : _("%a %l:%M %p");
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let displayDate = new Date();
 | 
			
		||||
 | 
			
		||||
        this._clock.set_text(displayDate.toLocaleFormat(clockFormat));
 | 
			
		||||
 | 
			
		||||
        /* Translators: This is the date format to use when the calendar popup is
 | 
			
		||||
         * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
 | 
			
		||||
         */
 | 
			
		||||
        dateFormat = _("%A %B %e, %Y");
 | 
			
		||||
        this._date.set_text(displayDate.toLocaleFormat(dateFormat));
 | 
			
		||||
 | 
			
		||||
        Mainloop.timeout_add_seconds(1, Lang.bind(this, this._updateClockAndDate));
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onPreferencesActivate: function() {
 | 
			
		||||
        this.menu.close();
 | 
			
		||||
        Main.overview.hide();
 | 
			
		||||
        let app = Shell.AppSystem.get_default().get_app('gnome-datetime-panel.desktop');
 | 
			
		||||
        app.activate(-1);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onOpenCalendarActivate: function() {
 | 
			
		||||
        this.menu.close();
 | 
			
		||||
        // TODO: pass the selected day
 | 
			
		||||
        Util.spawn(['evolution', '-c', 'calendar']);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										141
									
								
								js/ui/dnd.js
									
									
									
									
									
								
							
							
						
						@@ -26,9 +26,9 @@ const DragMotionResult = {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const DRAG_CURSOR_MAP = {
 | 
			
		||||
    0: Shell.Cursor.DND_UNSUPPORTED_TARGET,
 | 
			
		||||
    1: Shell.Cursor.DND_COPY,
 | 
			
		||||
    2: Shell.Cursor.DND_MOVE
 | 
			
		||||
    0: Shell.Cursor.UNSUPPORTED_TARGET,
 | 
			
		||||
    1: Shell.Cursor.COPY,
 | 
			
		||||
    2: Shell.Cursor.MOVE
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const DragDropResult = {
 | 
			
		||||
@@ -61,7 +61,7 @@ function addDragMonitor(monitor) {
 | 
			
		||||
    dragMonitors.push(monitor);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function removeDragMonitor(monitor) {
 | 
			
		||||
function removeMonitor(monitor) {
 | 
			
		||||
    for (let i = 0; i < dragMonitors.length; i++)
 | 
			
		||||
        if (dragMonitors[i] == monitor) {
 | 
			
		||||
            dragMonitors.splice(i, 1);
 | 
			
		||||
@@ -87,10 +87,6 @@ _Draggable.prototype = {
 | 
			
		||||
 | 
			
		||||
        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._cancelDrag(global.get_current_time());
 | 
			
		||||
            this.disconnectAll();
 | 
			
		||||
@@ -104,14 +100,6 @@ _Draggable.prototype = {
 | 
			
		||||
        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).
 | 
			
		||||
 | 
			
		||||
        // During the drag, we eat enter/leave events so that actors don't prelight or show
 | 
			
		||||
        // tooltips. But we remember the actors that we first left/last entered so we can
 | 
			
		||||
        // fix up the hover state after the drag ends.
 | 
			
		||||
        this._firstLeaveActor = null;
 | 
			
		||||
        this._lastEnterActor = null;
 | 
			
		||||
 | 
			
		||||
        this._eventsGrabbed = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onButtonPress : function (actor, event) {
 | 
			
		||||
@@ -122,11 +110,11 @@ _Draggable.prototype = {
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        this._buttonDown = true;
 | 
			
		||||
        // special case St.Button: grabbing the pointer would mess up the
 | 
			
		||||
        // special case St.Clickable: grabbing the pointer would mess up the
 | 
			
		||||
        // internal state, so we start the drag manually on hover change
 | 
			
		||||
        if (this.actor instanceof St.Button)
 | 
			
		||||
        if (this.actor instanceof St.Clickable)
 | 
			
		||||
            this.actor.connect('notify::hover',
 | 
			
		||||
                               Lang.bind(this, this._onButtonHoverChanged));
 | 
			
		||||
                               Lang.bind(this, this._onClickableHoverChanged));
 | 
			
		||||
        else
 | 
			
		||||
            this._grabActor();
 | 
			
		||||
 | 
			
		||||
@@ -137,8 +125,8 @@ _Draggable.prototype = {
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onButtonHoverChanged: function(button) {
 | 
			
		||||
        if (button.hover || !button.pressed)
 | 
			
		||||
    _onClickableHoverChanged: function(button) {
 | 
			
		||||
        if (button.hover || !button.held)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        button.fake_release();
 | 
			
		||||
@@ -154,26 +142,18 @@ _Draggable.prototype = {
 | 
			
		||||
 | 
			
		||||
    _ungrabActor: function() {
 | 
			
		||||
        Clutter.ungrab_pointer();
 | 
			
		||||
        if (!this._onEventId)
 | 
			
		||||
            return;
 | 
			
		||||
        this.actor.disconnect(this._onEventId);
 | 
			
		||||
        this._onEventId = null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _grabEvents: function() {
 | 
			
		||||
        if (!this._eventsGrabbed) {
 | 
			
		||||
            Clutter.grab_pointer(_getEventHandlerActor());
 | 
			
		||||
            Clutter.grab_keyboard(_getEventHandlerActor());
 | 
			
		||||
            this._eventsGrabbed = true;
 | 
			
		||||
        }
 | 
			
		||||
        Clutter.grab_pointer(_getEventHandlerActor());
 | 
			
		||||
        Clutter.grab_keyboard(_getEventHandlerActor());
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _ungrabEvents: function() {
 | 
			
		||||
        if (this._eventsGrabbed) {
 | 
			
		||||
            Clutter.ungrab_pointer();
 | 
			
		||||
            Clutter.ungrab_keyboard();
 | 
			
		||||
            this._eventsGrabbed = false;
 | 
			
		||||
        }
 | 
			
		||||
        Clutter.ungrab_pointer();
 | 
			
		||||
        Clutter.ungrab_keyboard();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onEvent: function(actor, event) {
 | 
			
		||||
@@ -210,11 +190,6 @@ _Draggable.prototype = {
 | 
			
		||||
                this._cancelDrag(event.get_time());
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
        } else if (event.type() == Clutter.EventType.LEAVE) {
 | 
			
		||||
            if (this._firstLeaveActor == null)
 | 
			
		||||
                this._firstLeaveActor = event.get_source();
 | 
			
		||||
        } else if (event.type() == Clutter.EventType.ENTER) {
 | 
			
		||||
            this._lastEnterActor = event.get_source();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
@@ -238,7 +213,7 @@ _Draggable.prototype = {
 | 
			
		||||
        if (this._onEventId)
 | 
			
		||||
            this._ungrabActor();
 | 
			
		||||
        this._grabEvents();
 | 
			
		||||
        global.set_cursor(Shell.Cursor.DND_IN_DRAG);
 | 
			
		||||
        global.set_cursor(Shell.Cursor.IN_DRAG);
 | 
			
		||||
 | 
			
		||||
        this._dragX = this._dragStartX = stageX;
 | 
			
		||||
        this._dragY = this._dragStartY = stageY;
 | 
			
		||||
@@ -284,13 +259,13 @@ _Draggable.prototype = {
 | 
			
		||||
            this._dragOffsetY = actorStageY - this._dragStartY;
 | 
			
		||||
 | 
			
		||||
            // Set the actor's scale such that it will keep the same
 | 
			
		||||
            // transformed size when it's reparented to the uiGroup
 | 
			
		||||
            // transformed size when it's reparented to the stage
 | 
			
		||||
            let [scaledWidth, scaledHeight] = this.actor.get_transformed_size();
 | 
			
		||||
            this.actor.set_scale(scaledWidth / this.actor.width,
 | 
			
		||||
                                 scaledHeight / this.actor.height);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._dragActor.reparent(Main.uiGroup);
 | 
			
		||||
        this._dragActor.reparent(this.actor.get_stage());
 | 
			
		||||
        this._dragActor.raise_top();
 | 
			
		||||
        Shell.util_set_hidden_from_pick(this._dragActor, true);
 | 
			
		||||
 | 
			
		||||
@@ -399,7 +374,7 @@ _Draggable.prototype = {
 | 
			
		||||
                }
 | 
			
		||||
                target = target.get_parent();
 | 
			
		||||
            }
 | 
			
		||||
            global.set_cursor(Shell.Cursor.DND_IN_DRAG);
 | 
			
		||||
            global.set_cursor(Shell.Cursor.IN_DRAG);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
@@ -442,7 +417,7 @@ _Draggable.prototype = {
 | 
			
		||||
                        return true;
 | 
			
		||||
                    // If it accepted the drop without taking the actor,
 | 
			
		||||
                    // handle it ourselves.
 | 
			
		||||
                    if (this._dragActor.get_parent() == Main.uiGroup) {
 | 
			
		||||
                    if (this._dragActor.get_parent() == this._dragActor.get_stage()) {
 | 
			
		||||
                        if (this._restoreOnSuccess) {
 | 
			
		||||
                            this._restoreDragActor(event.get_time());
 | 
			
		||||
                            return true;
 | 
			
		||||
@@ -465,51 +440,25 @@ _Draggable.prototype = {
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Get position of the drag actor's source if the source is still around,
 | 
			
		||||
    // or return the original location if the actor itself was being dragged
 | 
			
		||||
    // or the source is no longer around.
 | 
			
		||||
    _getRestoreLocation: function() {
 | 
			
		||||
        let x, y, scale;
 | 
			
		||||
        let locX = this._snapBackX;
 | 
			
		||||
        let locY = this._snapBackY;
 | 
			
		||||
 | 
			
		||||
        if (this._dragActorSource && this._dragActorSource.visible) {
 | 
			
		||||
            // Snap the clone back to its source
 | 
			
		||||
            [x, y] = this._dragActorSource.get_transformed_position();
 | 
			
		||||
            let [sourceScaledWidth, sourceScaledHeight] = this._dragActorSource.get_transformed_size();
 | 
			
		||||
            scale = this._dragActor.width / sourceScaledWidth;
 | 
			
		||||
        } else if (this._dragOrigParent) {
 | 
			
		||||
            // Snap the actor back to its original position within
 | 
			
		||||
            // its parent, adjusting for the fact that the parent
 | 
			
		||||
            // may have been moved or scaled
 | 
			
		||||
            let [parentX, parentY] = this._dragOrigParent.get_transformed_position();
 | 
			
		||||
            let [parentWidth, parentHeight] = this._dragOrigParent.get_size();
 | 
			
		||||
            let [parentScaledWidth, parentScaledHeight] = this._dragOrigParent.get_transformed_size();
 | 
			
		||||
            let parentScale = 1.0;
 | 
			
		||||
            if (parentWidth != 0)
 | 
			
		||||
                parentScale = parentScaledWidth / parentWidth;
 | 
			
		||||
 | 
			
		||||
            x = parentX + parentScale * this._dragOrigX;
 | 
			
		||||
            y = parentY + parentScale * this._dragOrigY;
 | 
			
		||||
            scale = this._dragOrigScale * parentScale;
 | 
			
		||||
        } else {
 | 
			
		||||
            // Snap back actor to its original stage position
 | 
			
		||||
            x = this._snapBackX;
 | 
			
		||||
            y = this._snapBackY;
 | 
			
		||||
            scale = this._snapBackScale;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return [x, y, scale];
 | 
			
		||||
        if (this._dragActorSource && this._dragActorSource.visible)
 | 
			
		||||
            [locX, locY] = this._dragActorSource.get_transformed_position();
 | 
			
		||||
        return [locX, locY];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _cancelDrag: function(eventTime) {
 | 
			
		||||
        this.emit('drag-cancelled', eventTime);
 | 
			
		||||
        this._dragInProgress = false;
 | 
			
		||||
        let [snapBackX, snapBackY, snapBackScale] = this._getRestoreLocation();
 | 
			
		||||
        let [snapBackX, snapBackY] = this._getRestoreLocation();
 | 
			
		||||
 | 
			
		||||
        if (this._actorDestroyed) {
 | 
			
		||||
            global.unset_cursor();
 | 
			
		||||
            if (!this._buttonDown)
 | 
			
		||||
                this._dragComplete();
 | 
			
		||||
            this.emit('drag-end', eventTime, false);
 | 
			
		||||
            if (!this._dragOrigParent)
 | 
			
		||||
                this._dragActor.destroy();
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -518,8 +467,8 @@ _Draggable.prototype = {
 | 
			
		||||
        Tweener.addTween(this._dragActor,
 | 
			
		||||
                         { x: snapBackX,
 | 
			
		||||
                           y: snapBackY,
 | 
			
		||||
                           scale_x: snapBackScale,
 | 
			
		||||
                           scale_y: snapBackScale,
 | 
			
		||||
                           scale_x: this._snapBackScale,
 | 
			
		||||
                           scale_y: this._snapBackScale,
 | 
			
		||||
                           opacity: this._dragOrigOpacity,
 | 
			
		||||
                           time: SNAP_BACK_ANIMATION_TIME,
 | 
			
		||||
                           transition: 'easeOutQuad',
 | 
			
		||||
@@ -531,11 +480,11 @@ _Draggable.prototype = {
 | 
			
		||||
 | 
			
		||||
    _restoreDragActor: function(eventTime) {
 | 
			
		||||
        this._dragInProgress = false;
 | 
			
		||||
        [restoreX, restoreY, restoreScale] = this._getRestoreLocation();
 | 
			
		||||
        [restoreX, restoreY] = this._getRestoreLocation();
 | 
			
		||||
 | 
			
		||||
        // fade the actor back in at its original location
 | 
			
		||||
        this._dragActor.set_position(restoreX, restoreY);
 | 
			
		||||
        this._dragActor.set_scale(restoreScale, restoreScale);
 | 
			
		||||
        this._dragActor.set_scale(this._snapBackScale, this._snapBackScale);
 | 
			
		||||
        this._dragActor.opacity = 0;
 | 
			
		||||
 | 
			
		||||
        this._animationInProgress = true;
 | 
			
		||||
@@ -565,36 +514,12 @@ _Draggable.prototype = {
 | 
			
		||||
            this._dragComplete();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Actor is an actor we have entered or left during the drag; call
 | 
			
		||||
    // st_widget_sync_hover on all StWidget ancestors
 | 
			
		||||
    _syncHover: function(actor) {
 | 
			
		||||
        while (actor) {
 | 
			
		||||
            let parent = actor.get_parent();
 | 
			
		||||
            if (actor instanceof St.Widget)
 | 
			
		||||
                actor.sync_hover();
 | 
			
		||||
 | 
			
		||||
            actor = parent;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _dragComplete: function() {
 | 
			
		||||
        if (!this._actorDestroyed)
 | 
			
		||||
            Shell.util_set_hidden_from_pick(this._dragActor, false);
 | 
			
		||||
 | 
			
		||||
        this._ungrabEvents();
 | 
			
		||||
 | 
			
		||||
        if (this._firstLeaveActor) {
 | 
			
		||||
            this._syncHover(this._firstLeaveActor);
 | 
			
		||||
            this._firstLeaveActor = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this._lastEnterActor) {
 | 
			
		||||
            this._syncHover(this._lastEnterActor);
 | 
			
		||||
            this._lastEnterActor = null;
 | 
			
		||||
        }
 | 
			
		||||
        Shell.util_set_hidden_from_pick(this._dragActor, false);
 | 
			
		||||
 | 
			
		||||
        this._dragActor = undefined;
 | 
			
		||||
        currentDraggable = null;
 | 
			
		||||
        this._ungrabEvents();
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,487 @@
 | 
			
		||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Mainloop = imports.mainloop;
 | 
			
		||||
const Gettext = imports.gettext.domain('gnome-shell');
 | 
			
		||||
const _ = Gettext.gettext;
 | 
			
		||||
 | 
			
		||||
const DocInfo = imports.misc.docInfo;
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
const DND = imports.ui.dnd;
 | 
			
		||||
const GenericDisplay = imports.ui.genericDisplay;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const Search = imports.ui.search;
 | 
			
		||||
 | 
			
		||||
const MAX_DASH_DOCS = 50;
 | 
			
		||||
const DASH_DOCS_ICON_SIZE = 16;
 | 
			
		||||
 | 
			
		||||
const DEFAULT_SPACING = 4;
 | 
			
		||||
 | 
			
		||||
/* This class represents a single display item containing information about a document.
 | 
			
		||||
 * We take the current number of seconds in the constructor to avoid looking up the current
 | 
			
		||||
 * time for every item when they are created in a batch.
 | 
			
		||||
 *
 | 
			
		||||
 * docInfo - DocInfo object containing information about the document
 | 
			
		||||
 * currentSeconds - current number of seconds since the epoch
 | 
			
		||||
 */
 | 
			
		||||
function DocDisplayItem(docInfo, currentSecs) {
 | 
			
		||||
    this._init(docInfo, currentSecs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DocDisplayItem.prototype = {
 | 
			
		||||
    __proto__:  GenericDisplay.GenericDisplayItem.prototype,
 | 
			
		||||
 | 
			
		||||
    _init : function(docInfo, currentSecs) {
 | 
			
		||||
        GenericDisplay.GenericDisplayItem.prototype._init.call(this);
 | 
			
		||||
        this._docInfo = docInfo;
 | 
			
		||||
 | 
			
		||||
        this._setItemInfo(docInfo.name, '');
 | 
			
		||||
 | 
			
		||||
        this._timeoutTime = -1;
 | 
			
		||||
        this._resetTimeDisplay(currentSecs);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    //// Public methods ////
 | 
			
		||||
 | 
			
		||||
    getUpdateTimeoutTime: function() {
 | 
			
		||||
        return this._timeoutTime;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Update any relative-time based displays for this item.
 | 
			
		||||
    redisplay: function(currentSecs) {
 | 
			
		||||
        this._resetTimeDisplay(currentSecs);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    //// Public method overrides ////
 | 
			
		||||
 | 
			
		||||
    // Opens a document represented by this display item.
 | 
			
		||||
    launch : function() {
 | 
			
		||||
        this._docInfo.launch();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    //// Protected method overrides ////
 | 
			
		||||
 | 
			
		||||
    // Returns an icon for the item.
 | 
			
		||||
    _createIcon : function() {
 | 
			
		||||
        return this._docInfo.createIcon(GenericDisplay.ITEM_DISPLAY_ICON_SIZE);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Returns a preview icon for the item.
 | 
			
		||||
    _createPreviewIcon : function() {
 | 
			
		||||
        return this._docInfo.createIcon(GenericDisplay.PREVIEW_ICON_SIZE);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Creates and returns a large preview icon, but only if this._docInfo is an image file
 | 
			
		||||
    // and we were able to generate a pixbuf from it successfully.
 | 
			
		||||
    _createLargePreviewIcon : function() {
 | 
			
		||||
        if (this._docInfo.mimeType == null || this._docInfo.mimeType.indexOf('image/') != 0)
 | 
			
		||||
            return null;
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            return St.TextureCache.get_default().load_uri_sync(St.TextureCachePolicy.NONE,
 | 
			
		||||
                                                               this._docInfo.uri, -1, -1);
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
            // An exception will be raised when the image format isn't know
 | 
			
		||||
            /* FIXME: http://bugzilla.gnome.org/show_bug.cgi?id=591480: should
 | 
			
		||||
             *        only ignore GDK_PIXBUF_ERROR_UNKNOWN_TYPE. */
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    //// Drag and Drop ////
 | 
			
		||||
 | 
			
		||||
    shellWorkspaceLaunch: function() {
 | 
			
		||||
        this.launch();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    //// Private Methods ////
 | 
			
		||||
 | 
			
		||||
    // Updates the last visited time displayed in the description text for the item.
 | 
			
		||||
    _resetTimeDisplay: function(currentSecs) {
 | 
			
		||||
        let lastSecs = this._docInfo.timestamp;
 | 
			
		||||
        let timeDelta = currentSecs - lastSecs;
 | 
			
		||||
        let [text, nextUpdate] = global.format_time_relative_pretty(timeDelta);
 | 
			
		||||
        this._timeoutTime = currentSecs + nextUpdate;
 | 
			
		||||
        this._setDescriptionText(text);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* This class represents a display containing a collection of document items.
 | 
			
		||||
 * The documents are sorted by how recently they were last visited.
 | 
			
		||||
 */
 | 
			
		||||
function DocDisplay() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DocDisplay.prototype = {
 | 
			
		||||
    __proto__:  GenericDisplay.GenericDisplay.prototype,
 | 
			
		||||
 | 
			
		||||
    _init : function() {
 | 
			
		||||
        GenericDisplay.GenericDisplay.prototype._init.call(this);
 | 
			
		||||
        // We keep a single timeout callback for updating last visited times
 | 
			
		||||
        // for all the items in the display. This avoids creating individual
 | 
			
		||||
        // callbacks for each item in the display. So proper time updates
 | 
			
		||||
        // for individual items and item details depend on the item being
 | 
			
		||||
        // associated with one of the displays.
 | 
			
		||||
        this._updateTimeoutTargetTime = -1;
 | 
			
		||||
        this._updateTimeoutId = 0;
 | 
			
		||||
 | 
			
		||||
        this._docManager = DocInfo.getDocManager();
 | 
			
		||||
        this._docsStale = true;
 | 
			
		||||
        this._docManager.connect('changed', Lang.bind(this, function(mgr, userData) {
 | 
			
		||||
            this._docsStale = true;
 | 
			
		||||
            // Changes in local recent files should not happen when we are in the Overview mode,
 | 
			
		||||
            // but redisplaying right away is cool when we use Zephyr.
 | 
			
		||||
            // Also, we might be displaying remote documents, like Google Docs, in the future
 | 
			
		||||
            // which might be edited by someone else.
 | 
			
		||||
            this._redisplay(GenericDisplay.RedisplayFlags.NONE);
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        this.connect('destroy', Lang.bind(this, function (o) {
 | 
			
		||||
            if (this._updateTimeoutId > 0)
 | 
			
		||||
                Mainloop.source_remove(this._updateTimeoutId);
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    //// Protected method overrides ////
 | 
			
		||||
 | 
			
		||||
    // Gets the list of recent items from the recent items manager.
 | 
			
		||||
    _refreshCache : function() {
 | 
			
		||||
        if (!this._docsStale)
 | 
			
		||||
            return true;
 | 
			
		||||
        this._allItems = {};
 | 
			
		||||
        Lang.copyProperties(this._docManager.getInfosByUri(), this._allItems);
 | 
			
		||||
        this._docsStale = false;
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Sets the list of the displayed items based on how recently they were last visited.
 | 
			
		||||
    _setDefaultList : function() {
 | 
			
		||||
        // It seems to be an implementation detail of the Mozilla JavaScript that object
 | 
			
		||||
        // properties are returned during the iteration in the same order in which they were
 | 
			
		||||
        // defined, but it is not a guarantee according to this 
 | 
			
		||||
        // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Statements/for...in
 | 
			
		||||
        // While this._allItems associative array seems to always be ordered by last added,
 | 
			
		||||
        // as the results of this._recentManager.get_items() based on which it is constructed are,
 | 
			
		||||
        // we should do the sorting manually because we want the order to be based on last visited.
 | 
			
		||||
        //
 | 
			
		||||
        // This function is called each time the search string is set back to '' or we display
 | 
			
		||||
        // the Overview, so we are doing the sorting over the same items multiple times if the list
 | 
			
		||||
        // of recent items didn't change. We could store an additional array of doc ids and sort
 | 
			
		||||
        // them once when they are returned by this._recentManager.get_items() to avoid having to do 
 | 
			
		||||
        // this sorting each time, but the sorting seems to be very fast anyway, so there is no need
 | 
			
		||||
        // to introduce an additional class variable.
 | 
			
		||||
        this._matchedItems = {};
 | 
			
		||||
        this._matchedItemKeys = [];
 | 
			
		||||
        let docIdsToRemove = [];
 | 
			
		||||
        for (docId in this._allItems) {
 | 
			
		||||
            this._matchedItems[docId] = 1;
 | 
			
		||||
            this._matchedItemKeys.push(docId);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (docId in docIdsToRemove) {
 | 
			
		||||
            delete this._allItems[docId];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._matchedItemKeys.sort(Lang.bind(this, this._compareItems));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Compares items associated with the item ids based on how recently the items
 | 
			
		||||
    // were last visited.
 | 
			
		||||
    // Returns an integer value indicating the result of the comparison.
 | 
			
		||||
   _compareItems : function(itemIdA, itemIdB) {
 | 
			
		||||
        let docA = this._allItems[itemIdA];
 | 
			
		||||
        let docB = this._allItems[itemIdB];
 | 
			
		||||
 | 
			
		||||
        return docB.timestamp - docA.timestamp;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Checks if the item info can be a match for the search string by checking
 | 
			
		||||
    // the name of the document. Item info is expected to be GtkRecentInfo.
 | 
			
		||||
    // Returns a boolean flag indicating if itemInfo is a match.
 | 
			
		||||
    _isInfoMatching : function(itemInfo, search) {
 | 
			
		||||
        if (!itemInfo.exists())
 | 
			
		||||
            return false;
 | 
			
		||||
 
 | 
			
		||||
        if (search == null || search == '')
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        let name = itemInfo.name.toLowerCase();
 | 
			
		||||
        if (name.indexOf(search) >= 0)
 | 
			
		||||
            return true;
 | 
			
		||||
        // TODO: we can also check doc URIs, so that
 | 
			
		||||
        // if you search for a directory name, we display recent files from it
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Creates a DocDisplayItem based on itemInfo, which is expected to be a DocInfo object.
 | 
			
		||||
    _createDisplayItem: function(itemInfo) {
 | 
			
		||||
        let currentSecs = new Date().getTime() / 1000;
 | 
			
		||||
        let docDisplayItem = new DocDisplayItem(itemInfo, currentSecs);
 | 
			
		||||
        this._updateTimeoutCallback(docDisplayItem, currentSecs);
 | 
			
		||||
        return docDisplayItem;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    //// Private Methods ////
 | 
			
		||||
 | 
			
		||||
    // A callback function that redisplays the items, updating their descriptions,
 | 
			
		||||
    // and sets up a new timeout callback.
 | 
			
		||||
    _docTimeout: function () {
 | 
			
		||||
        let currentSecs = new Date().getTime() / 1000;
 | 
			
		||||
        this._updateTimeoutId = 0;
 | 
			
		||||
        this._updateTimeoutTargetTime = -1;
 | 
			
		||||
        for (let docId in this._displayedItems) {
 | 
			
		||||
            let docDisplayItem = this._displayedItems[docId];
 | 
			
		||||
            docDisplayItem.redisplay(currentSecs);          
 | 
			
		||||
            this._updateTimeoutCallback(docDisplayItem, currentSecs);
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Updates the timeout callback if the timeout time for the docDisplayItem 
 | 
			
		||||
    // is earlier than the target time for the current timeout callback.
 | 
			
		||||
    _updateTimeoutCallback: function (docDisplayItem, currentSecs) {
 | 
			
		||||
        let timeoutTime = docDisplayItem.getUpdateTimeoutTime();
 | 
			
		||||
        if (this._updateTimeoutTargetTime < 0 || timeoutTime < this._updateTimeoutTargetTime) {
 | 
			
		||||
            if (this._updateTimeoutId > 0)
 | 
			
		||||
                Mainloop.source_remove(this._updateTimeoutId);
 | 
			
		||||
            this._updateTimeoutId = Mainloop.timeout_add_seconds(timeoutTime - currentSecs, Lang.bind(this, this._docTimeout));
 | 
			
		||||
            this._updateTimeoutTargetTime = timeoutTime;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Signals.addSignalMethods(DocDisplay.prototype);
 | 
			
		||||
 | 
			
		||||
function DashDocDisplayItem(docInfo) {
 | 
			
		||||
    this._init(docInfo);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DashDocDisplayItem.prototype = {
 | 
			
		||||
    _init: function(docInfo) {
 | 
			
		||||
        this._info = docInfo;
 | 
			
		||||
        this._icon = docInfo.createIcon(DASH_DOCS_ICON_SIZE);
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.Clickable({ style_class: 'recent-docs-item',
 | 
			
		||||
                                        reactive: true,
 | 
			
		||||
                                        x_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        let box = new St.BoxLayout({ style_class: 'recent-docs-item-box' });
 | 
			
		||||
        this.actor.set_child(box);
 | 
			
		||||
 | 
			
		||||
        box.add(this._icon);
 | 
			
		||||
 | 
			
		||||
        let text = new St.Label({ text: docInfo.name });
 | 
			
		||||
        box.add(text);
 | 
			
		||||
 | 
			
		||||
        this.actor.connect('clicked', Lang.bind(this, function () {
 | 
			
		||||
            docInfo.launch();
 | 
			
		||||
            Main.overview.hide();
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        this.actor._delegate = this;
 | 
			
		||||
        let draggable = DND.makeDraggable(this.actor);
 | 
			
		||||
        draggable.connect('drag-begin',
 | 
			
		||||
                          Lang.bind(this, function() {
 | 
			
		||||
                              Main.overview.beginItemDrag(this);
 | 
			
		||||
                          }));
 | 
			
		||||
        draggable.connect('drag-end',
 | 
			
		||||
                          Lang.bind(this, function() {
 | 
			
		||||
                              Main.overview.endItemDrag(this);
 | 
			
		||||
                          }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getUri: function() {
 | 
			
		||||
        return this._info.uri;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getDragActorSource: function() {
 | 
			
		||||
        return this._icon;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getDragActor: function(stageX, stageY) {
 | 
			
		||||
        this.dragActor = this._info.createIcon(DASH_DOCS_ICON_SIZE);
 | 
			
		||||
        return this.dragActor;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    //// Drag and drop functions ////
 | 
			
		||||
 | 
			
		||||
    shellWorkspaceLaunch: function () {
 | 
			
		||||
        this._info.launch();
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Class used to display two column recent documents in the dash
 | 
			
		||||
 */
 | 
			
		||||
function DashDocDisplay() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DashDocDisplay.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.actor = new Shell.GenericContainer();
 | 
			
		||||
        this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
 | 
			
		||||
        this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
 | 
			
		||||
        this.actor.connect('allocate', Lang.bind(this, this._allocate));
 | 
			
		||||
        this._workId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplay));
 | 
			
		||||
 | 
			
		||||
        this._actorsByUri = {};
 | 
			
		||||
 | 
			
		||||
        this._docManager = DocInfo.getDocManager();
 | 
			
		||||
        this._docManager.connect('changed', Lang.bind(this, this._onDocsChanged));
 | 
			
		||||
        this._pendingDocsChange = true;
 | 
			
		||||
        this._checkDocExistence = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getPreferredWidth: function(actor, forHeight, alloc) {
 | 
			
		||||
        let children = actor.get_children();
 | 
			
		||||
 | 
			
		||||
        // We use two columns maximum.  Just take the min and natural size of the
 | 
			
		||||
        // first two items, even though strictly speaking it's not correct; we'd
 | 
			
		||||
        // need to calculate how many items we could fit for the height, then
 | 
			
		||||
        // take the biggest preferred width for each column.
 | 
			
		||||
        // In practice the dash gets a fixed width anyways.
 | 
			
		||||
 | 
			
		||||
        // If we have one child, add its minimum and natural size
 | 
			
		||||
        if (children.length > 0) {
 | 
			
		||||
            let [minSize, naturalSize] = children[0].get_preferred_width(forHeight);
 | 
			
		||||
            alloc.min_size += minSize;
 | 
			
		||||
            alloc.natural_size += naturalSize;
 | 
			
		||||
        }
 | 
			
		||||
        // If we have two, add its size, plus DEFAULT_SPACING
 | 
			
		||||
        if (children.length > 1) {
 | 
			
		||||
            let [minSize, naturalSize] = children[1].get_preferred_width(forHeight);
 | 
			
		||||
            alloc.min_size += DEFAULT_SPACING + minSize;
 | 
			
		||||
            alloc.natural_size += DEFAULT_SPACING + naturalSize;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getPreferredHeight: function(actor, forWidth, alloc) {
 | 
			
		||||
        let children = actor.get_children();
 | 
			
		||||
 | 
			
		||||
        // The width of an item is our allocated width, minus spacing, divided in half.
 | 
			
		||||
        this._itemWidth = Math.floor((forWidth - DEFAULT_SPACING) / 2);
 | 
			
		||||
 | 
			
		||||
        let maxNatural = 0;
 | 
			
		||||
        for (let i = 0; i < children.length; i++) {
 | 
			
		||||
            let child = children[i];
 | 
			
		||||
            let [minSize, naturalSize] = child.get_preferred_height(this._itemWidth);
 | 
			
		||||
            maxNatural = Math.max(maxNatural, naturalSize);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._itemHeight = maxNatural;
 | 
			
		||||
 | 
			
		||||
        let firstColumnChildren = Math.ceil(children.length / 2);
 | 
			
		||||
        alloc.natural_size = (firstColumnChildren * maxNatural +
 | 
			
		||||
                              (firstColumnChildren - 1) * DEFAULT_SPACING);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _allocate: function(actor, box, flags) {
 | 
			
		||||
        let width = box.x2 - box.x1;
 | 
			
		||||
        let height = box.y2 - box.y1;
 | 
			
		||||
 | 
			
		||||
        // Make sure this._itemWidth/Height have been computed, even
 | 
			
		||||
        // if the parent actor didn't check our size before allocating.
 | 
			
		||||
        // (Not clear if that is required or not as a Clutter
 | 
			
		||||
        // invariant; this is safe and cheap because of caching.)
 | 
			
		||||
        actor.get_preferred_height(width);
 | 
			
		||||
 | 
			
		||||
        let children = actor.get_children();
 | 
			
		||||
 | 
			
		||||
        let x = 0;
 | 
			
		||||
        let y = 0;
 | 
			
		||||
        let columnIndex = 0;
 | 
			
		||||
        let i = 0;
 | 
			
		||||
        // Loop over the children, going vertically down first.  When we run
 | 
			
		||||
        // out of vertical space (our y variable is bigger than box.y2), switch
 | 
			
		||||
        // to the second column.
 | 
			
		||||
        while (i < children.length) {
 | 
			
		||||
            let child = children[i];
 | 
			
		||||
 | 
			
		||||
            if (y + this._itemHeight > box.y2) {
 | 
			
		||||
                // Is this the second column, or we're in
 | 
			
		||||
                // the first column and can't even fit one
 | 
			
		||||
                // item?  In that case, break.
 | 
			
		||||
                if (columnIndex == 1 || i == 0) {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                // Set x to the halfway point.
 | 
			
		||||
                columnIndex += 1;
 | 
			
		||||
                x = x + this._itemWidth + DEFAULT_SPACING;
 | 
			
		||||
                // And y is back to the top.
 | 
			
		||||
                y = 0;
 | 
			
		||||
                // Retry this same item, now that we're in the second column.
 | 
			
		||||
                // By looping back to the top here, we re-test the size
 | 
			
		||||
                // again for the second column.
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let childBox = new Clutter.ActorBox();
 | 
			
		||||
            childBox.x1 = x;
 | 
			
		||||
            childBox.y1 = y;
 | 
			
		||||
            childBox.x2 = childBox.x1 + this._itemWidth;
 | 
			
		||||
            childBox.y2 = y + this._itemHeight;
 | 
			
		||||
 | 
			
		||||
            y = childBox.y2 + DEFAULT_SPACING;
 | 
			
		||||
 | 
			
		||||
            child.allocate(childBox, flags);
 | 
			
		||||
            this.actor.set_skip_paint(child, false);
 | 
			
		||||
 | 
			
		||||
            i++;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this._checkDocExistence) {
 | 
			
		||||
            // Now we know how many docs we are displaying, queue a check to see if any of them
 | 
			
		||||
            // have been deleted. If they are deleted, then we'll get a 'changed' signal; since
 | 
			
		||||
            // we'll now be displaying items we weren't previously, we'll check again to see
 | 
			
		||||
            // if they were deleted, and so forth and so on.
 | 
			
		||||
            // TODO: We should change this to ask for as many as we can fit in the given space:
 | 
			
		||||
            // https://bugzilla.gnome.org/show_bug.cgi?id=603522#c23
 | 
			
		||||
            this._docManager.queueExistenceCheck(i);
 | 
			
		||||
            this._checkDocExistence = false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (; i < children.length; i++)
 | 
			
		||||
            this.actor.set_skip_paint(children[i], true);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDocsChanged: function() {
 | 
			
		||||
        this._checkDocExistence = true;
 | 
			
		||||
        Main.queueDeferredWork(this._workId);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _redisplay: function() {
 | 
			
		||||
        // Should be kept alive by the _actorsByUri
 | 
			
		||||
        this.actor.remove_all();
 | 
			
		||||
        let docs = this._docManager.getTimestampOrderedInfos();
 | 
			
		||||
        for (let i = 0; i < docs.length && i < MAX_DASH_DOCS; i++) {
 | 
			
		||||
            let doc = docs[i];
 | 
			
		||||
            let display = this._actorsByUri[doc.uri];
 | 
			
		||||
            if (display) {
 | 
			
		||||
                this.actor.add_actor(display.actor);
 | 
			
		||||
            } else {
 | 
			
		||||
                let display = new DashDocDisplayItem(doc);
 | 
			
		||||
                this.actor.add_actor(display.actor);
 | 
			
		||||
                this._actorsByUri[doc.uri] = display;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        // Any unparented actors must have been deleted
 | 
			
		||||
        for (let uri in this._actorsByUri) {
 | 
			
		||||
            let display = this._actorsByUri[uri];
 | 
			
		||||
            if (display.actor.get_parent() == null) {
 | 
			
		||||
                display.actor.destroy();
 | 
			
		||||
                delete this._actorsByUri[uri];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        this.emit('changed');
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Signals.addSignalMethods(DashDocDisplay.prototype);
 | 
			
		||||
 | 
			
		||||
function DocSearchProvider() {
 | 
			
		||||
    this._init();
 | 
			
		||||
@@ -23,18 +501,12 @@ DocSearchProvider.prototype = {
 | 
			
		||||
            return null;
 | 
			
		||||
        return { 'id': resultId,
 | 
			
		||||
                 'name': docInfo.name,
 | 
			
		||||
                 'createIcon': function(size) {
 | 
			
		||||
                                   return docInfo.createIcon(size);
 | 
			
		||||
                               }
 | 
			
		||||
               };
 | 
			
		||||
                 'icon': docInfo.createIcon(Search.RESULT_ICON_SIZE)};
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    activateResult: function(id, params) {
 | 
			
		||||
        params = Params.parse(params, { workspace: null,
 | 
			
		||||
                                        timestamp: null });
 | 
			
		||||
 | 
			
		||||
    activateResult: function(id) {
 | 
			
		||||
        let docInfo = this._docManager.lookupByUri(id);
 | 
			
		||||
        docInfo.launch(params.workspace ? params.workspace.index() : -1);
 | 
			
		||||
        docInfo.launch();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getInitialResultSet: function(terms) {
 | 
			
		||||
@@ -43,5 +515,9 @@ DocSearchProvider.prototype = {
 | 
			
		||||
 | 
			
		||||
    getSubsearchResultSet: function(previousResults, terms) {
 | 
			
		||||
        return this._docManager.subsearch(previousResults, terms);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    expandSearch: function(terms) {
 | 
			
		||||
        log('TODO expand docs search');
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,532 +0,0 @@
 | 
			
		||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright 2010 Red Hat, Inc
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 2, or (at your option)
 | 
			
		||||
 * any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 | 
			
		||||
 * 02111-1307, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
const DBus = imports.dbus;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Gdm = imports.gi.Gdm;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Gtk = imports.gi.Gtk;
 | 
			
		||||
const Pango = imports.gi.Pango;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
 | 
			
		||||
const GnomeSession = imports.misc.gnomeSession
 | 
			
		||||
const Lightbox = imports.ui.lightbox;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const ModalDialog = imports.ui.modalDialog;
 | 
			
		||||
const Tweener = imports.ui.tweener;
 | 
			
		||||
 | 
			
		||||
let _endSessionDialog = null;
 | 
			
		||||
 | 
			
		||||
const _ITEM_ICON_SIZE = 48;
 | 
			
		||||
const _DIALOG_ICON_SIZE = 32;
 | 
			
		||||
 | 
			
		||||
const GSM_SESSION_MANAGER_LOGOUT_FORCE = 2;
 | 
			
		||||
 | 
			
		||||
const EndSessionDialogIface = {
 | 
			
		||||
    name: 'org.gnome.SessionManager.EndSessionDialog',
 | 
			
		||||
    methods: [{ name: 'Open',
 | 
			
		||||
                inSignature: 'uuuao',
 | 
			
		||||
                outSignature: ''
 | 
			
		||||
              }
 | 
			
		||||
             ],
 | 
			
		||||
    signals: [{ name: 'Canceled',
 | 
			
		||||
                inSignature: '',
 | 
			
		||||
              }],
 | 
			
		||||
    properties: []
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const logoutDialogContent = {
 | 
			
		||||
    subjectWithUser: _("Log Out %s"),
 | 
			
		||||
    subject: _("Log Out"),
 | 
			
		||||
    inhibitedDescription: _("Click Log Out to quit these applications and log out of the system."),
 | 
			
		||||
    uninhibitedDescriptionWithUser: _("%s will be logged out automatically in %d seconds."),
 | 
			
		||||
    uninhibitedDescription: _("You will be logged out automatically in %d seconds."),
 | 
			
		||||
    endDescription: _("Logging out of the system."),
 | 
			
		||||
    confirmButtons: [{ signal: 'ConfirmedLogout',
 | 
			
		||||
                       label:  _("Log Out") }],
 | 
			
		||||
    iconStyleClass: 'end-session-dialog-logout-icon'
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const shutdownDialogContent = {
 | 
			
		||||
    subject: _("Power Off"),
 | 
			
		||||
    inhibitedDescription: _("Click Power Off to quit these applications and power off the system."),
 | 
			
		||||
    uninhibitedDescription: _("The system will power off automatically in %d seconds."),
 | 
			
		||||
    endDescription: _("Powering off the system."),
 | 
			
		||||
    confirmButtons: [{ signal: 'ConfirmedReboot',
 | 
			
		||||
                       label:  _("Restart") },
 | 
			
		||||
                     { signal: 'ConfirmedShutdown',
 | 
			
		||||
                       label:  _("Power Off") }],
 | 
			
		||||
    iconName: 'system-shutdown',
 | 
			
		||||
    iconStyleClass: 'end-session-dialog-shutdown-icon'
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const restartDialogContent = {
 | 
			
		||||
    subject: _("Restart"),
 | 
			
		||||
    inhibitedDescription: _("Click Restart to quit these applications and restart the system."),
 | 
			
		||||
    uninhibitedDescription: _("The system will restart automatically in %d seconds."),
 | 
			
		||||
    endDescription: _("Restarting the system."),
 | 
			
		||||
    confirmButtons: [{ signal: 'ConfirmedReboot',
 | 
			
		||||
                       label:  _("Restart") }],
 | 
			
		||||
    iconName: 'system-shutdown',
 | 
			
		||||
    iconStyleClass: 'end-session-dialog-shutdown-icon'
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const DialogContent = {
 | 
			
		||||
    0 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_LOGOUT */: logoutDialogContent,
 | 
			
		||||
    1 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_SHUTDOWN */: shutdownDialogContent,
 | 
			
		||||
    2 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_RESTART */: restartDialogContent
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function findAppFromInhibitor(inhibitor) {
 | 
			
		||||
    let desktopFile = inhibitor.app_id;
 | 
			
		||||
 | 
			
		||||
    if (!GLib.str_has_suffix(desktopFile, '.desktop'))
 | 
			
		||||
      desktopFile += '.desktop';
 | 
			
		||||
 | 
			
		||||
    let candidateDesktopFiles = [];
 | 
			
		||||
 | 
			
		||||
    candidateDesktopFiles.push(desktopFile);
 | 
			
		||||
    candidateDesktopFiles.push('gnome-' + desktopFile);
 | 
			
		||||
 | 
			
		||||
    let appSystem = Shell.AppSystem.get_default();
 | 
			
		||||
    let app = null;
 | 
			
		||||
    for (let i = 0; i < candidateDesktopFiles.length; i++) {
 | 
			
		||||
        try {
 | 
			
		||||
            app = appSystem.get_app(candidateDesktopFiles[i]);
 | 
			
		||||
 | 
			
		||||
            if (app)
 | 
			
		||||
                break;
 | 
			
		||||
        } catch(e) {
 | 
			
		||||
            // ignore errors
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return app;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function ListItem(app, reason) {
 | 
			
		||||
    this._init(app, reason);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ListItem.prototype = {
 | 
			
		||||
    _init: function(app, reason) {
 | 
			
		||||
        this._app = app;
 | 
			
		||||
        this._reason = reason;
 | 
			
		||||
 | 
			
		||||
        if (this._reason == null)
 | 
			
		||||
          this._reason = '';
 | 
			
		||||
 | 
			
		||||
        let layout = new St.BoxLayout({ vertical: false});
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.Button({ style_class: 'end-session-dialog-app-list-item',
 | 
			
		||||
                                     can_focus:   true,
 | 
			
		||||
                                     child:       layout,
 | 
			
		||||
                                     reactive:    true,
 | 
			
		||||
                                     x_align:     St.Align.START,
 | 
			
		||||
                                     x_fill:      true });
 | 
			
		||||
 | 
			
		||||
        this._icon = this._app.create_icon_texture(_ITEM_ICON_SIZE);
 | 
			
		||||
 | 
			
		||||
        let iconBin = new St.Bin({ style_class: 'end-session-dialog-app-list-item-icon',
 | 
			
		||||
                                   child:       this._icon });
 | 
			
		||||
        layout.add(iconBin);
 | 
			
		||||
 | 
			
		||||
        let textLayout = new St.BoxLayout({ style_class: 'end-session-dialog-app-list-item-text-box',
 | 
			
		||||
                                            vertical:    true });
 | 
			
		||||
        layout.add(textLayout);
 | 
			
		||||
 | 
			
		||||
        this._nameLabel = new St.Label({ text:        this._app.get_name(),
 | 
			
		||||
                                         style_class: 'end-session-dialog-app-list-item-name' });
 | 
			
		||||
        textLayout.add(this._nameLabel,
 | 
			
		||||
                       { expand: false,
 | 
			
		||||
                         x_fill: true });
 | 
			
		||||
 | 
			
		||||
        this._descriptionLabel = new St.Label({ text:        this._reason,
 | 
			
		||||
                                                style_class: 'end-session-dialog-app-list-item-description' });
 | 
			
		||||
        textLayout.add(this._descriptionLabel,
 | 
			
		||||
                       { expand: true,
 | 
			
		||||
                         x_fill: true });
 | 
			
		||||
 | 
			
		||||
        this.actor.connect('clicked', Lang.bind(this, this._onClicked));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onClicked: function() {
 | 
			
		||||
        this.emit('activate');
 | 
			
		||||
        this._app.activate(-1);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
Signals.addSignalMethods(ListItem.prototype);
 | 
			
		||||
 | 
			
		||||
// The logout timer only shows updates every 10 seconds
 | 
			
		||||
// until the last 10 seconds, then it shows updates every
 | 
			
		||||
// second.  This function takes a given time and returns
 | 
			
		||||
// what we should show to the user for that time.
 | 
			
		||||
function _roundSecondsToInterval(totalSeconds, secondsLeft, interval) {
 | 
			
		||||
    let time;
 | 
			
		||||
 | 
			
		||||
    time = Math.ceil(secondsLeft);
 | 
			
		||||
 | 
			
		||||
    // Final count down is in decrements of 1
 | 
			
		||||
    if (time <= interval)
 | 
			
		||||
        return time;
 | 
			
		||||
 | 
			
		||||
    // Round up higher than last displayable time interval
 | 
			
		||||
    time += interval - 1;
 | 
			
		||||
 | 
			
		||||
    // Then round down to that time interval
 | 
			
		||||
    if (time > totalSeconds)
 | 
			
		||||
        time = Math.ceil(totalSeconds);
 | 
			
		||||
    else
 | 
			
		||||
        time -= time % interval;
 | 
			
		||||
 | 
			
		||||
    return time;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _setLabelText(label, text) {
 | 
			
		||||
    if (text) {
 | 
			
		||||
        label.set_text(text);
 | 
			
		||||
        label.show();
 | 
			
		||||
    } else {
 | 
			
		||||
        label.set_text('');
 | 
			
		||||
        label.hide();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function EndSessionDialog() {
 | 
			
		||||
    if (_endSessionDialog == null) {
 | 
			
		||||
        this._init();
 | 
			
		||||
        DBus.session.exportObject('/org/gnome/SessionManager/EndSessionDialog',
 | 
			
		||||
                                  this);
 | 
			
		||||
        _endSessionDialog = this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return _endSessionDialog;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function init() {
 | 
			
		||||
    // This always returns the same singleton object
 | 
			
		||||
    // By instantiating it initially, we register the
 | 
			
		||||
    // bus object, etc.
 | 
			
		||||
    let dialog = new EndSessionDialog();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EndSessionDialog.prototype = {
 | 
			
		||||
    __proto__: ModalDialog.ModalDialog.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        ModalDialog.ModalDialog.prototype._init.call(this, { styleClass: 'end-session-dialog' });
 | 
			
		||||
 | 
			
		||||
        this._user = Gdm.UserManager.ref_default().get_user(GLib.get_user_name());
 | 
			
		||||
 | 
			
		||||
        this._secondsLeft = 0;
 | 
			
		||||
        this._totalSecondsToStayOpen = 0;
 | 
			
		||||
        this._inhibitors = [];
 | 
			
		||||
 | 
			
		||||
        this.connect('destroy',
 | 
			
		||||
                     Lang.bind(this, this._onDestroy));
 | 
			
		||||
        this.connect('opened',
 | 
			
		||||
                     Lang.bind(this, this._onOpened));
 | 
			
		||||
 | 
			
		||||
        this._userLoadedId = this._user.connect('notify::is_loaded',
 | 
			
		||||
                                                Lang.bind(this, this._updateContent));
 | 
			
		||||
 | 
			
		||||
        this._userChangedId = this._user.connect('changed',
 | 
			
		||||
                                                 Lang.bind(this, this._updateContent));
 | 
			
		||||
 | 
			
		||||
        let mainContentLayout = new St.BoxLayout({ vertical: false });
 | 
			
		||||
        this.contentLayout.add(mainContentLayout,
 | 
			
		||||
                               { x_fill: true,
 | 
			
		||||
                                 y_fill: false });
 | 
			
		||||
 | 
			
		||||
        this._iconBin = new St.Bin();
 | 
			
		||||
        mainContentLayout.add(this._iconBin,
 | 
			
		||||
                              { x_fill:  true,
 | 
			
		||||
                                y_fill:  false,
 | 
			
		||||
                                x_align: St.Align.END,
 | 
			
		||||
                                y_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        let messageLayout = new St.BoxLayout({ vertical: true });
 | 
			
		||||
        mainContentLayout.add(messageLayout,
 | 
			
		||||
                              { y_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        this._subjectLabel = new St.Label({ style_class: 'end-session-dialog-subject' });
 | 
			
		||||
 | 
			
		||||
        messageLayout.add(this._subjectLabel,
 | 
			
		||||
                          { y_fill:  false,
 | 
			
		||||
                            y_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        this._descriptionLabel = new St.Label({ style_class: 'end-session-dialog-description' });
 | 
			
		||||
        this._descriptionLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
 | 
			
		||||
        this._descriptionLabel.clutter_text.line_wrap = true;
 | 
			
		||||
 | 
			
		||||
        messageLayout.add(this._descriptionLabel,
 | 
			
		||||
                          { y_fill:  true,
 | 
			
		||||
                            y_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        let scrollView = new St.ScrollView({ style_class: 'end-session-dialog-app-list'});
 | 
			
		||||
        scrollView.set_policy(Gtk.PolicyType.NEVER,
 | 
			
		||||
                              Gtk.PolicyType.AUTOMATIC);
 | 
			
		||||
        this.contentLayout.add(scrollView,
 | 
			
		||||
                               { x_fill: true,
 | 
			
		||||
                                 y_fill: true });
 | 
			
		||||
        scrollView.hide();
 | 
			
		||||
 | 
			
		||||
        this._applicationList = new St.BoxLayout({ vertical: true });
 | 
			
		||||
        scrollView.add_actor(this._applicationList,
 | 
			
		||||
                             { x_fill:  true,
 | 
			
		||||
                               y_fill:  true,
 | 
			
		||||
                               x_align: St.Align.START,
 | 
			
		||||
                               y_align: St.Align.MIDDLE });
 | 
			
		||||
 | 
			
		||||
        this._applicationList.connect('actor-added',
 | 
			
		||||
                                      Lang.bind(this, function() {
 | 
			
		||||
                                          if (this._applicationList.get_children().length == 1)
 | 
			
		||||
                                              scrollView.show();
 | 
			
		||||
                                      }));
 | 
			
		||||
 | 
			
		||||
        this._applicationList.connect('actor-removed',
 | 
			
		||||
                                      Lang.bind(this, function() {
 | 
			
		||||
                                          if (this._applicationList.get_children().length == 0)
 | 
			
		||||
                                              scrollView.hide();
 | 
			
		||||
                                      }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDestroy: function() {
 | 
			
		||||
        this._user.disconnect(this._userLoadedId);
 | 
			
		||||
        this._user.disconnect(this._userChangedId);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _setIconFromFile: function(iconFile, styleClass) {
 | 
			
		||||
        if (styleClass)
 | 
			
		||||
            this._iconBin.set_style_class_name(styleClass);
 | 
			
		||||
        this._iconBin.set_style(null);
 | 
			
		||||
 | 
			
		||||
        this._iconBin.child = null;
 | 
			
		||||
        if (iconFile) {
 | 
			
		||||
            this._iconBin.show();
 | 
			
		||||
            this._iconBin.set_style('background-image: url("' + iconFile + '");');
 | 
			
		||||
        } else {
 | 
			
		||||
            this._iconBin.hide();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _setIconFromName: function(iconName, styleClass) {
 | 
			
		||||
        if (styleClass)
 | 
			
		||||
            this._iconBin.set_style_class_name(styleClass);
 | 
			
		||||
        this._iconBin.set_style(null);
 | 
			
		||||
 | 
			
		||||
        if (iconName != null) {
 | 
			
		||||
            let textureCache = St.TextureCache.get_default();
 | 
			
		||||
            let icon = textureCache.load_icon_name(this._iconBin.get_theme_node(),
 | 
			
		||||
                                                   iconName,
 | 
			
		||||
                                                   St.IconType.SYMBOLIC,
 | 
			
		||||
                                                   _DIALOG_ICON_SIZE);
 | 
			
		||||
 | 
			
		||||
            this._iconBin.child = icon;
 | 
			
		||||
            this._iconBin.show();
 | 
			
		||||
        } else {
 | 
			
		||||
            this._iconBin.child = null;
 | 
			
		||||
            this._iconBin.hide();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateContent: function() {
 | 
			
		||||
        if (this.state != ModalDialog.State.OPENING &&
 | 
			
		||||
            this.state != ModalDialog.State.OPENED)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let dialogContent = DialogContent[this._type];
 | 
			
		||||
 | 
			
		||||
        let subject = dialogContent.subject;
 | 
			
		||||
        let description;
 | 
			
		||||
 | 
			
		||||
        if (this._user.is_loaded && !dialogContent.iconName) {
 | 
			
		||||
            let iconFile = this._user.get_icon_file();
 | 
			
		||||
            if (GLib.file_test(iconFile, GLib.FileTest.EXISTS))
 | 
			
		||||
                this._setIconFromFile(iconFile, dialogContent.iconStyleClass);
 | 
			
		||||
            else
 | 
			
		||||
                this._setIconFromName('avatar-default', dialogContent.iconStyleClass);
 | 
			
		||||
        } else if (dialogContent.iconName) {
 | 
			
		||||
            this._setIconFromName(dialogContent.iconName,
 | 
			
		||||
                                  dialogContent.iconStyleClass);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this._inhibitors.length > 0) {
 | 
			
		||||
            this._stopTimer();
 | 
			
		||||
            description = dialogContent.inhibitedDescription;
 | 
			
		||||
        } else if (this._secondsLeft > 0 && this._inhibitors.length == 0) {
 | 
			
		||||
            let displayTime = _roundSecondsToInterval(this._totalSecondsToStayOpen,
 | 
			
		||||
                                                      this._secondsLeft,
 | 
			
		||||
                                                      10);
 | 
			
		||||
 | 
			
		||||
            if (this._user.is_loaded) {
 | 
			
		||||
                let realName = this._user.get_real_name();
 | 
			
		||||
 | 
			
		||||
                if (realName != null) {
 | 
			
		||||
                    if (dialogContent.subjectWithUser)
 | 
			
		||||
                        subject = dialogContent.subjectWithUser.format(realName);
 | 
			
		||||
 | 
			
		||||
                    if (dialogContent.uninhibitedDescriptionWithUser)
 | 
			
		||||
                        description = dialogContent.uninhibitedDescriptionWithUser.format(realName, displayTime);
 | 
			
		||||
                    else
 | 
			
		||||
                        description = dialogContent.uninhibitedDescription.format(displayTime);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!description)
 | 
			
		||||
                description = dialogContent.uninhibitedDescription.format(displayTime);
 | 
			
		||||
        } else {
 | 
			
		||||
            description = dialogContent.endDescription;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        _setLabelText(this._subjectLabel, subject);
 | 
			
		||||
        _setLabelText(this._descriptionLabel, description);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateButtons: function() {
 | 
			
		||||
        let dialogContent = DialogContent[this._type];
 | 
			
		||||
        let buttons = [{ action: Lang.bind(this, this.cancel),
 | 
			
		||||
                         label:  _("Cancel"),
 | 
			
		||||
                         key:    Clutter.Escape }];
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < dialogContent.confirmButtons.length; i++) {
 | 
			
		||||
            let signal = dialogContent.confirmButtons[i].signal;
 | 
			
		||||
            let label = dialogContent.confirmButtons[i].label;
 | 
			
		||||
            buttons.push({ action: Lang.bind(this, function() {
 | 
			
		||||
                                       this._confirm(signal);
 | 
			
		||||
                                   }),
 | 
			
		||||
                           label: label });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.setButtons(buttons);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    close: function() {
 | 
			
		||||
        ModalDialog.ModalDialog.prototype.close.call(this);
 | 
			
		||||
        DBus.session.emit_signal('/org/gnome/SessionManager/EndSessionDialog',
 | 
			
		||||
                                 'org.gnome.SessionManager.EndSessionDialog',
 | 
			
		||||
                                 'Closed', '', []);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    cancel: function() {
 | 
			
		||||
        this._stopTimer();
 | 
			
		||||
        DBus.session.emit_signal('/org/gnome/SessionManager/EndSessionDialog',
 | 
			
		||||
                                 'org.gnome.SessionManager.EndSessionDialog',
 | 
			
		||||
                                 'Canceled', '', []);
 | 
			
		||||
        this.close(global.get_current_time());
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _confirm: function(signal) {
 | 
			
		||||
        this._fadeOutDialog();
 | 
			
		||||
        this._stopTimer();
 | 
			
		||||
        DBus.session.emit_signal('/org/gnome/SessionManager/EndSessionDialog',
 | 
			
		||||
                                 'org.gnome.SessionManager.EndSessionDialog',
 | 
			
		||||
                                 signal, '', []);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onOpened: function() {
 | 
			
		||||
        if (this._inhibitors.length == 0)
 | 
			
		||||
            this._startTimer();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _startTimer: function() {
 | 
			
		||||
        this._secondsLeft = this._totalSecondsToStayOpen;
 | 
			
		||||
        Tweener.addTween(this,
 | 
			
		||||
                         { _secondsLeft: 0,
 | 
			
		||||
                           time: this._secondsLeft,
 | 
			
		||||
                           transition: 'linear',
 | 
			
		||||
                           onUpdate: Lang.bind(this, this._updateContent),
 | 
			
		||||
                           onComplete: Lang.bind(this, function() {
 | 
			
		||||
                                           let dialogContent = DialogContent[this._type];
 | 
			
		||||
                                           let button = dialogContent.confirmButtons[dialogContent.confirmButtons.length - 1];
 | 
			
		||||
                                           this._confirm(button.signal);
 | 
			
		||||
                                       }),
 | 
			
		||||
                         });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _stopTimer: function() {
 | 
			
		||||
        Tweener.removeTweens(this);
 | 
			
		||||
        this._secondsLeft = 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onInhibitorLoaded: function(inhibitor) {
 | 
			
		||||
        if (this._inhibitors.indexOf(inhibitor) < 0) {
 | 
			
		||||
            // Stale inhibitor
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let app = findAppFromInhibitor(inhibitor);
 | 
			
		||||
 | 
			
		||||
        if (app) {
 | 
			
		||||
            let item = new ListItem(app, inhibitor.reason);
 | 
			
		||||
            item.connect('activate',
 | 
			
		||||
                         Lang.bind(this, function() {
 | 
			
		||||
                             this.close(global.get_current_time());
 | 
			
		||||
                         }));
 | 
			
		||||
            this._applicationList.add(item.actor, { x_fill: true });
 | 
			
		||||
            this._stopTimer();
 | 
			
		||||
        } else {
 | 
			
		||||
            // inhibiting app is a service, not an application
 | 
			
		||||
            this._inhibitors.splice(this._inhibitors.indexOf(inhibitor), 1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._updateContent();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    OpenAsync: function(type, timestamp, totalSecondsToStayOpen, inhibitorObjectPaths, callback) {
 | 
			
		||||
        this._totalSecondsToStayOpen = totalSecondsToStayOpen;
 | 
			
		||||
        this._inhibitors = [];
 | 
			
		||||
        this._applicationList.destroy_children();
 | 
			
		||||
        this._type = type;
 | 
			
		||||
 | 
			
		||||
        if (!(this._type in DialogContent))
 | 
			
		||||
            throw new DBus.DBusError('org.gnome.Shell.ModalDialog.TypeError',
 | 
			
		||||
                                     "Unknown dialog type requested");
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < inhibitorObjectPaths.length; i++) {
 | 
			
		||||
            let inhibitor = new GnomeSession.Inhibitor(inhibitorObjectPaths[i]);
 | 
			
		||||
 | 
			
		||||
            inhibitor.connect('is-loaded',
 | 
			
		||||
                              Lang.bind(this, function() {
 | 
			
		||||
                                  this._onInhibitorLoaded(inhibitor);
 | 
			
		||||
                              }));
 | 
			
		||||
            this._inhibitors.push(inhibitor);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._updateButtons();
 | 
			
		||||
 | 
			
		||||
        if (!this.open(timestamp))
 | 
			
		||||
            throw new DBus.DBusError('org.gnome.Shell.ModalDialog.GrabError',
 | 
			
		||||
                                     "Cannot grab pointer and keyboard");
 | 
			
		||||
 | 
			
		||||
        this._updateContent();
 | 
			
		||||
 | 
			
		||||
        let signalId = this.connect('opened',
 | 
			
		||||
                                    Lang.bind(this, function() {
 | 
			
		||||
                                        callback();
 | 
			
		||||
                                        this.disconnect(signalId);
 | 
			
		||||
                                    }));
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
DBus.conformExport(EndSessionDialog.prototype, EndSessionDialogIface);
 | 
			
		||||
@@ -1,22 +1,14 @@
 | 
			
		||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
 | 
			
		||||
 | 
			
		||||
imports.gi.versions.Clutter = '1.0';
 | 
			
		||||
imports.gi.versions.Gio = '2.0';
 | 
			
		||||
imports.gi.versions.Gdk = '3.0';
 | 
			
		||||
imports.gi.versions.GdkPixbuf = '2.0';
 | 
			
		||||
imports.gi.versions.Gtk = '3.0';
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;;
 | 
			
		||||
const Gettext = imports.gettext;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Gtk = imports.gi.Gtk;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Gettext_gtk20 = imports.gettext.domain('gtk20');
 | 
			
		||||
 | 
			
		||||
// We can't import shell JS modules yet, because they may have
 | 
			
		||||
// variable initializations, etc, that depend on init() already having
 | 
			
		||||
// been run.
 | 
			
		||||
const Tweener = imports.ui.tweener;
 | 
			
		||||
 | 
			
		||||
const Format = imports.misc.format;
 | 
			
		||||
 | 
			
		||||
// "monkey patch" in some varargs ClutterContainer methods; we need
 | 
			
		||||
// to do this per-container class since there is no representation
 | 
			
		||||
@@ -25,7 +17,7 @@ function _patchContainerClass(containerClass) {
 | 
			
		||||
    // This one is a straightforward mapping of the C method
 | 
			
		||||
    containerClass.prototype.child_set = function(actor, props) {
 | 
			
		||||
        let meta = this.get_child_meta(actor);
 | 
			
		||||
        for (let prop in props)
 | 
			
		||||
        for (prop in props)
 | 
			
		||||
            meta[prop] = props[prop];
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
@@ -39,42 +31,44 @@ function _patchContainerClass(containerClass) {
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function init() {
 | 
			
		||||
    // Add some bindings to the global JS namespace; (gjs keeps the web
 | 
			
		||||
    // browser convention of having that namespace be called 'window'.)
 | 
			
		||||
    window.global = Shell.Global.get();
 | 
			
		||||
// Replace @method with something that throws an error instead
 | 
			
		||||
function _blockMethod(method, replacement, reason) {
 | 
			
		||||
    let match = method.match(/^(.+)\.([^.]+)$/);
 | 
			
		||||
    if (!match)
 | 
			
		||||
        throw new Error('Bad method name "' + method + '"');
 | 
			
		||||
    let proto = 'imports.gi.' + match[1] + '.prototype';
 | 
			
		||||
    let property = match[2];
 | 
			
		||||
 | 
			
		||||
    window._ = Gettext.gettext;
 | 
			
		||||
    window.C_ = Gettext.pgettext;
 | 
			
		||||
    window.ngettext = Gettext.ngettext;
 | 
			
		||||
    if (!global.set_property_mutable(proto, property, true))
 | 
			
		||||
        throw new Error('Bad method name "' + method + '"');
 | 
			
		||||
 | 
			
		||||
    // eval() is evil in general, but we know it's safe here since
 | 
			
		||||
    // set_property_mutable() would have failed if proto was
 | 
			
		||||
    // malformed.
 | 
			
		||||
    let node = eval(proto);
 | 
			
		||||
 | 
			
		||||
    let msg = 'Do not use "' + method + '".';
 | 
			
		||||
    if (replacement)
 | 
			
		||||
        msg += ' Use "' + replacement + '" instead.';
 | 
			
		||||
    if (reason)
 | 
			
		||||
        msg += ' (' + reason + ')';
 | 
			
		||||
 | 
			
		||||
    node[property] = function() {
 | 
			
		||||
        throw new Error(msg);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    global.set_property_mutable(proto, property, false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function init() {
 | 
			
		||||
    Tweener.init();
 | 
			
		||||
    String.prototype.format = Format.format;
 | 
			
		||||
 | 
			
		||||
    // Set the default direction for St widgets (this needs to be done before any use of St)
 | 
			
		||||
    if (Gtk.Widget.get_default_direction() == Gtk.TextDirection.RTL) {
 | 
			
		||||
    if (Gettext_gtk20.gettext('default:LTR') == 'default:RTL') {
 | 
			
		||||
        St.Widget.set_default_direction(St.TextDirection.RTL);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Miscellaneous monkeypatching
 | 
			
		||||
    _patchContainerClass(St.BoxLayout);
 | 
			
		||||
    _patchContainerClass(St.Table);
 | 
			
		||||
 | 
			
		||||
    Clutter.Actor.prototype.toString = function() {
 | 
			
		||||
        return St.describe_actor(this);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let origToString = Object.prototype.toString;
 | 
			
		||||
    Object.prototype.toString = function() {
 | 
			
		||||
        let base = origToString.call(this);
 | 
			
		||||
        if ('actor' in this && this.actor instanceof Clutter.Actor)
 | 
			
		||||
            return base.replace(/\]$/, ' delegate for ' + this.actor.toString().substring(1));
 | 
			
		||||
        else
 | 
			
		||||
            return base;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // Work around https://bugzilla.mozilla.org/show_bug.cgi?id=508783
 | 
			
		||||
    Date.prototype.toLocaleFormat = function(format) {
 | 
			
		||||
        return Shell.util_format_date(format, this.getTime());
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let slowdownEnv = GLib.getenv('GNOME_SHELL_SLOWDOWN_FACTOR');
 | 
			
		||||
    if (slowdownEnv) {
 | 
			
		||||
        let factor = parseFloat(slowdownEnv);
 | 
			
		||||
@@ -82,10 +76,26 @@ function init() {
 | 
			
		||||
            St.set_slow_down_factor(factor);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // OK, now things are initialized enough that we can import shell JS
 | 
			
		||||
    const Format = imports.misc.format;
 | 
			
		||||
    const Tweener = imports.ui.tweener;
 | 
			
		||||
    _patchContainerClass(St.BoxLayout);
 | 
			
		||||
    _patchContainerClass(St.Table);
 | 
			
		||||
 | 
			
		||||
    Tweener.init();
 | 
			
		||||
    String.prototype.format = Format.format;
 | 
			
		||||
    Clutter.Actor.prototype.toString = function() {
 | 
			
		||||
        return St.describe_actor(this);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    if (window.global === undefined) // test environment
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    _blockMethod('Clutter.Event.get_state', 'Shell.get_event_state',
 | 
			
		||||
                 'gjs\'s handling of Clutter.ModifierType is broken. See bug 597292.');
 | 
			
		||||
    _blockMethod('Gdk.Display.get_device_state', 'global.get_pointer',
 | 
			
		||||
                 'gjs\'s handling of Gdk.ModifierType is broken. See bug 597292.');
 | 
			
		||||
    _blockMethod('Gdk.Window.get_device_position', 'global.get_pointer',
 | 
			
		||||
                 'gjs\'s handling of Gdk.ModifierType is broken. See bug 597292.');
 | 
			
		||||
 | 
			
		||||
    // Now close the back door to prevent extensions from trying to
 | 
			
		||||
    // abuse it. We can't actually delete it since
 | 
			
		||||
    // Shell.Global.prototype itself is read-only.
 | 
			
		||||
    global.set_property_mutable('imports.gi.Shell.Global.prototype', 'set_property_mutable', true);
 | 
			
		||||
    Shell.Global.prototype.set_property_mutable = undefined;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -3,9 +3,6 @@
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
 | 
			
		||||
const Config = imports.misc.config;
 | 
			
		||||
 | 
			
		||||
const ExtensionState = {
 | 
			
		||||
    ENABLED: 1,
 | 
			
		||||
@@ -23,42 +20,11 @@ const ExtensionType = {
 | 
			
		||||
const extensionMeta = {};
 | 
			
		||||
// Maps uuid -> importer object (extension directory tree)
 | 
			
		||||
const extensions = {};
 | 
			
		||||
// Arrays of uuids
 | 
			
		||||
// Array of uuids
 | 
			
		||||
var disabledExtensions;
 | 
			
		||||
var enabledExtensions;
 | 
			
		||||
// GFile for user extensions
 | 
			
		||||
var userExtensionsDir = null;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * versionCheck:
 | 
			
		||||
 * @required: an array of versions we're compatible with
 | 
			
		||||
 * @current: the version we have
 | 
			
		||||
 *
 | 
			
		||||
 * Check if a component is compatible for an extension.
 | 
			
		||||
 * @required is an array, and at least one version must match.
 | 
			
		||||
 * @current must be in the format <major>.<minor>.<point>.<micro>
 | 
			
		||||
 * <micro> is always ignored
 | 
			
		||||
 * <point> is ignored if <minor> is even (so you can target the
 | 
			
		||||
 * whole stable release)
 | 
			
		||||
 * <minor> and <major> must match
 | 
			
		||||
 * Each target version must be at least <major> and <minor>
 | 
			
		||||
 */
 | 
			
		||||
function versionCheck(required, current) {
 | 
			
		||||
    let currentArray = current.split('.');
 | 
			
		||||
    let major = currentArray[0];
 | 
			
		||||
    let minor = currentArray[1];
 | 
			
		||||
    let point = currentArray[2];
 | 
			
		||||
    for (let i = 0; i < required.length; i++) {
 | 
			
		||||
        let requiredArray = required[i].split('.');
 | 
			
		||||
        if (requiredArray[0] == major &&
 | 
			
		||||
            requiredArray[1] == minor &&
 | 
			
		||||
            (requiredArray[2] == point ||
 | 
			
		||||
             (requiredArray[2] == undefined && parseInt(minor) % 2 == 0)))
 | 
			
		||||
            return true;
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function loadExtension(dir, enabled, type) {
 | 
			
		||||
    let info;
 | 
			
		||||
    let baseErrorString = 'While loading extension from "' + dir.get_parse_name() + '": ';
 | 
			
		||||
@@ -69,13 +35,7 @@ function loadExtension(dir, enabled, type) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let metadataContents;
 | 
			
		||||
    try {
 | 
			
		||||
        metadataContents = Shell.get_file_contents_utf8_sync(metadataFile.get_path());
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
        global.logError(baseErrorString + 'Failed to load metadata.json: ' + e);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    let [success, metadataContents, len, etag] = metadataFile.load_contents(null);
 | 
			
		||||
    let meta;
 | 
			
		||||
    try {
 | 
			
		||||
        meta = JSON.parse(metadataContents);
 | 
			
		||||
@@ -83,8 +43,8 @@ function loadExtension(dir, enabled, type) {
 | 
			
		||||
        global.logError(baseErrorString + 'Failed to parse metadata.json: ' + e);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    let requiredProperties = ['uuid', 'name', 'description', 'shell-version'];
 | 
			
		||||
    for (let i = 0; i < requiredProperties.length; i++) {
 | 
			
		||||
    let requiredProperties = ['uuid', 'name', 'description'];
 | 
			
		||||
    for (let i = 0; i < requiredProperties; i++) {
 | 
			
		||||
        let prop = requiredProperties[i];
 | 
			
		||||
        if (!meta[prop]) {
 | 
			
		||||
            global.logError(baseErrorString + 'missing "' + prop + '" property in metadata.json');
 | 
			
		||||
@@ -108,12 +68,6 @@ function loadExtension(dir, enabled, type) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!versionCheck(meta['shell-version'], Config.PACKAGE_VERSION) ||
 | 
			
		||||
        (meta['js-version'] && !versionCheck(meta['js-version'], Config.GJS_VERSION))) {
 | 
			
		||||
        global.logError(baseErrorString + 'extension is not compatible with current GNOME Shell and/or GJS version');
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    extensionMeta[meta.uuid] = meta;
 | 
			
		||||
    extensionMeta[meta.uuid].type = type;
 | 
			
		||||
    extensionMeta[meta.uuid].path = dir.get_path();
 | 
			
		||||
@@ -158,7 +112,7 @@ function loadExtension(dir, enabled, type) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    try {
 | 
			
		||||
        extensionModule.main(meta);
 | 
			
		||||
        extensionModule.main();
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
        if (stylesheetPath != null)
 | 
			
		||||
            theme.unload_stylesheet(stylesheetPath);
 | 
			
		||||
@@ -179,28 +133,17 @@ function init() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    disabledExtensions = global.settings.get_strv('disabled-extensions', -1);
 | 
			
		||||
    enabledExtensions = global.settings.get_strv('enabled-extensions', -1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _loadExtensionsIn(dir, type) {
 | 
			
		||||
    let fileEnum;
 | 
			
		||||
    let fileEnum = dir.enumerate_children('standard::*', Gio.FileQueryInfoFlags.NONE, null);
 | 
			
		||||
    let file, info;
 | 
			
		||||
    try {
 | 
			
		||||
        fileEnum = dir.enumerate_children('standard::*', Gio.FileQueryInfoFlags.NONE, null);
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
        global.logError('' + e);
 | 
			
		||||
       return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    while ((info = fileEnum.next_file(null)) != null) {
 | 
			
		||||
        let fileType = info.get_file_type();
 | 
			
		||||
        if (fileType != Gio.FileType.DIRECTORY)
 | 
			
		||||
            continue;
 | 
			
		||||
        let name = info.get_name();
 | 
			
		||||
	// Enable all but disabled extensions if enabledExtensions is not set.
 | 
			
		||||
	// If it is set, enable one those, except they are disabled as well.
 | 
			
		||||
        let enabled = (enabledExtensions.length == 0 || enabledExtensions.indexOf(name) >= 0)
 | 
			
		||||
	    && disabledExtensions.indexOf(name) < 0;
 | 
			
		||||
        let enabled = disabledExtensions.indexOf(name) < 0;
 | 
			
		||||
        let child = dir.get_child(name);
 | 
			
		||||
        loadExtension(child, enabled, type);
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										693
									
								
								js/ui/genericDisplay.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,693 @@
 | 
			
		||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
 | 
			
		||||
 | 
			
		||||
const Gtk = imports.gi.Gtk;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
const DND = imports.ui.dnd;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
 | 
			
		||||
const RedisplayFlags = { NONE: 0,
 | 
			
		||||
                         FULL: 1 << 1,
 | 
			
		||||
                         SUBSEARCH: 1 << 2,
 | 
			
		||||
                         IMMEDIATE: 1 << 3 };
 | 
			
		||||
 | 
			
		||||
// Used by subclasses
 | 
			
		||||
const ITEM_DISPLAY_ICON_SIZE = 48;
 | 
			
		||||
const PREVIEW_ICON_SIZE = 96;
 | 
			
		||||
 | 
			
		||||
/* This is a virtual class that represents a single display item containing
 | 
			
		||||
 * a name, a description, and an icon. It allows selecting an item and represents
 | 
			
		||||
 * it by highlighting it with a different background color than the default.
 | 
			
		||||
 */
 | 
			
		||||
function GenericDisplayItem() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GenericDisplayItem.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.actor = new St.BoxLayout({ style_class: 'generic-display-item',
 | 
			
		||||
                                         reactive: true });
 | 
			
		||||
 | 
			
		||||
        this.actor._delegate = this;
 | 
			
		||||
        this.actor.connect('button-release-event',
 | 
			
		||||
                           Lang.bind(this,
 | 
			
		||||
                                     function() {
 | 
			
		||||
                                         // Activates the item by launching it
 | 
			
		||||
                                         this.emit('activate');
 | 
			
		||||
                                         return true;  
 | 
			
		||||
                                     }));
 | 
			
		||||
 | 
			
		||||
        let draggable = DND.makeDraggable(this.actor);
 | 
			
		||||
        draggable.connect('drag-begin',
 | 
			
		||||
                          Lang.bind(this, function() {
 | 
			
		||||
                              Main.overview.beginItemDrag(this);
 | 
			
		||||
                          }));
 | 
			
		||||
        draggable.connect('drag-end',
 | 
			
		||||
                          Lang.bind(this, function() {
 | 
			
		||||
                              Main.overview.endItemDrag(this);
 | 
			
		||||
                          }));
 | 
			
		||||
 | 
			
		||||
        this._iconBin = new St.Bin();
 | 
			
		||||
        this.actor.add(this._iconBin);
 | 
			
		||||
 | 
			
		||||
        this._infoText = new St.BoxLayout({ style_class: 'generic-display-item-text',
 | 
			
		||||
                                             vertical: true });
 | 
			
		||||
        this.actor.add(this._infoText, { expand: true, y_fill: false });
 | 
			
		||||
 | 
			
		||||
        this._name = null;
 | 
			
		||||
        this._description = null;
 | 
			
		||||
        this._icon = null;
 | 
			
		||||
 | 
			
		||||
        this._initialLoadComplete = false;
 | 
			
		||||
 | 
			
		||||
        // An array of details description actors that we create over time for the item.
 | 
			
		||||
        // It is used for updating the description text inside the details actor when
 | 
			
		||||
        // the description text for the item is updated.
 | 
			
		||||
        this._detailsDescriptions = [];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    //// Draggable object interface ////
 | 
			
		||||
 | 
			
		||||
    // Returns a cloned texture of the item's icon to represent the item as it 
 | 
			
		||||
    // is being dragged. 
 | 
			
		||||
    getDragActor: function(stageX, stageY) {
 | 
			
		||||
        return this._createIcon();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Returns the item icon, a separate copy of which is used to
 | 
			
		||||
    // represent the item as it is being dragged. This is used to
 | 
			
		||||
    // determine a snap-back location for the drag icon if it does
 | 
			
		||||
    // not get accepted by any drop target.
 | 
			
		||||
    getDragActorSource: function() {
 | 
			
		||||
        return this._icon;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    //// Public methods ////
 | 
			
		||||
 | 
			
		||||
    // Highlights the item by setting a different background color than the default 
 | 
			
		||||
    // if isSelected is true, removes the highlighting otherwise.
 | 
			
		||||
    markSelected: function(isSelected) {
 | 
			
		||||
        if (isSelected)
 | 
			
		||||
            this.actor.add_style_pseudo_class('selected');
 | 
			
		||||
        else
 | 
			
		||||
            this.actor.remove_style_pseudo_class('selected');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * Returns an actor containing item details. In the future details can have more information than what
 | 
			
		||||
     * the preview pop-up has and be item-type specific.
 | 
			
		||||
     */
 | 
			
		||||
    createDetailsActor: function() {
 | 
			
		||||
 | 
			
		||||
        let details = new St.BoxLayout({ style_class: 'generic-display-container',
 | 
			
		||||
                                         vertical: true });
 | 
			
		||||
 | 
			
		||||
        let mainDetails = new St.BoxLayout({ style_class: 'generic-display-container' });
 | 
			
		||||
 | 
			
		||||
        // Inner box with name and description
 | 
			
		||||
        let textDetails = new St.BoxLayout({ style_class: 'generic-display-details',
 | 
			
		||||
                                             vertical: true });
 | 
			
		||||
        let detailsName = new St.Label({ style_class: 'generic-display-details-name',
 | 
			
		||||
                                          text: this._name.text });
 | 
			
		||||
        textDetails.add(detailsName);
 | 
			
		||||
 | 
			
		||||
        let detailsDescription = new St.Label({ text: this._description.text });
 | 
			
		||||
        textDetails.add(detailsDescription);
 | 
			
		||||
 | 
			
		||||
        this._detailsDescriptions.push(detailsDescription);
 | 
			
		||||
 | 
			
		||||
        mainDetails.add(textDetails, { expand: true });
 | 
			
		||||
 | 
			
		||||
        let previewIcon = this._createPreviewIcon();
 | 
			
		||||
        let largePreviewIcon = this._createLargePreviewIcon();
 | 
			
		||||
 | 
			
		||||
        if (previewIcon != null && largePreviewIcon == null) {
 | 
			
		||||
            mainDetails.insert_actor(previewIcon, 0);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        details.add(mainDetails);
 | 
			
		||||
 | 
			
		||||
        if (largePreviewIcon != null) {
 | 
			
		||||
            details.add(largePreviewIcon);
 | 
			
		||||
        }
 | 
			
		||||
   
 | 
			
		||||
        return details;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Destroys the item.
 | 
			
		||||
    destroy: function() {
 | 
			
		||||
        this.actor.destroy();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    //// Pure virtual public methods ////
 | 
			
		||||
 | 
			
		||||
    // Performes an action associated with launching this item, such as opening a file or an application.
 | 
			
		||||
    launch: function() {
 | 
			
		||||
        throw new Error('Not implemented');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    //// Protected methods ////
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * Creates the graphical elements for the item based on the item information.
 | 
			
		||||
     *
 | 
			
		||||
     * nameText - name of the item
 | 
			
		||||
     * descriptionText - short description of the item
 | 
			
		||||
     */
 | 
			
		||||
    _setItemInfo: function(nameText, descriptionText) {
 | 
			
		||||
        if (this._name != null) {
 | 
			
		||||
            // this also removes this._name from the parent container,
 | 
			
		||||
            // so we don't need to call this.actor.remove_actor(this._name) directly
 | 
			
		||||
            this._name.destroy();
 | 
			
		||||
            this._name = null;
 | 
			
		||||
        } 
 | 
			
		||||
        if (this._description != null) {
 | 
			
		||||
            this._description.destroy();
 | 
			
		||||
            this._description = null;
 | 
			
		||||
        } 
 | 
			
		||||
        if (this._icon != null) {
 | 
			
		||||
            // though we get the icon from elsewhere, we assume its ownership here,
 | 
			
		||||
            // and therefore should be responsible for distroying it
 | 
			
		||||
            this._icon.destroy();
 | 
			
		||||
            this._icon = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._icon = this._createIcon();
 | 
			
		||||
        this._iconBin.set_child(this._icon);
 | 
			
		||||
 | 
			
		||||
        this._name = new St.Label({ style_class: 'generic-display-item-name',
 | 
			
		||||
                                     text: nameText });
 | 
			
		||||
        this._infoText.add(this._name);
 | 
			
		||||
 | 
			
		||||
        this._description = new St.Label({ style_class: 'generic-display-item-description',
 | 
			
		||||
                                            text: descriptionText ? descriptionText : '' });
 | 
			
		||||
        this._infoText.add(this._description);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Sets the description text for the item, including the description text
 | 
			
		||||
    // in the details actors that have been created for the item.
 | 
			
		||||
    _setDescriptionText: function(text) {
 | 
			
		||||
        this._description.text = text;
 | 
			
		||||
        for (let i = 0; i < this._detailsDescriptions.length; i++) {
 | 
			
		||||
            let detailsDescription = this._detailsDescriptions[i];
 | 
			
		||||
            if (detailsDescription != null) {
 | 
			
		||||
                detailsDescription.text = text;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    //// Virtual protected methods ////
 | 
			
		||||
 | 
			
		||||
    // Creates and returns a large preview icon, but only if we have a detailed image.
 | 
			
		||||
    _createLargePreviewIcon : function() {
 | 
			
		||||
        return null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    //// Pure virtual protected methods ////
 | 
			
		||||
 | 
			
		||||
    // Returns an icon for the item.
 | 
			
		||||
    _createIcon: function() {
 | 
			
		||||
        throw new Error('Not implemented');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Returns a preview icon for the item.
 | 
			
		||||
    _createPreviewIcon: function() {
 | 
			
		||||
        throw new Error('Not implemented');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //// Private methods ////
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Signals.addSignalMethods(GenericDisplayItem.prototype);
 | 
			
		||||
 | 
			
		||||
/* This is a virtual class that represents a display containing a collection of items
 | 
			
		||||
 * that can be filtered with a search string.
 | 
			
		||||
 */
 | 
			
		||||
function GenericDisplay() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GenericDisplay.prototype = {
 | 
			
		||||
    _init : function() {
 | 
			
		||||
        this._search = '';
 | 
			
		||||
        this._expanded = false;
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.ScrollView({ x_fill: true,
 | 
			
		||||
                                         y_fill: false,
 | 
			
		||||
                                         vshadows: true });
 | 
			
		||||
        this._list = new St.BoxLayout({ style_class: 'generic-display-container',
 | 
			
		||||
                                         vertical: true });
 | 
			
		||||
        this.actor.add_actor(this._list);
 | 
			
		||||
        this.actor.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
 | 
			
		||||
 | 
			
		||||
        this._pendingRedisplay = RedisplayFlags.NONE;
 | 
			
		||||
        this.actor.connect('notify::mapped', Lang.bind(this, this._onMappedNotify));
 | 
			
		||||
 | 
			
		||||
        // map<itemId, Object> where Object represents the item info
 | 
			
		||||
        this._allItems = {};
 | 
			
		||||
        // set<itemId>
 | 
			
		||||
        this._matchedItems = {};
 | 
			
		||||
        // sorted array of items matched by search
 | 
			
		||||
        this._matchedItemKeys = [];
 | 
			
		||||
        // map<itemId, GenericDisplayItem>
 | 
			
		||||
        this._displayedItems = {};
 | 
			
		||||
        this._openDetailIndex = -1;
 | 
			
		||||
        this._selectedIndex = -1;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    //// Public methods ////
 | 
			
		||||
 | 
			
		||||
    // Sets the search string and displays the matching items.
 | 
			
		||||
    setSearch: function(text) {
 | 
			
		||||
        let lowertext = text.toLowerCase();
 | 
			
		||||
        if (lowertext == this._search) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        let flags = RedisplayFlags.IMMEDIATE;
 | 
			
		||||
        if (this._search != '') {
 | 
			
		||||
            // Because we combine search terms with OR, we have to be sure that no new term
 | 
			
		||||
            // was introduced before deciding that the new search results will be a subset of
 | 
			
		||||
            // the existing search results.
 | 
			
		||||
            if (lowertext.indexOf(this._search) == 0 &&
 | 
			
		||||
                lowertext.split(/\s+/).length == this._search.split(/\s+/).length) {
 | 
			
		||||
                flags |= RedisplayFlags.SUBSEARCH;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        this._search = lowertext;
 | 
			
		||||
        this._redisplay(flags);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Launches the item that is currently selected, closing the Overview
 | 
			
		||||
    activateSelected: function() {
 | 
			
		||||
        if (this._selectedIndex != -1) {
 | 
			
		||||
            let selected = this._findDisplayedByIndex(this._selectedIndex);
 | 
			
		||||
            selected.launch();
 | 
			
		||||
            this.unsetSelected();
 | 
			
		||||
            Main.overview.hide();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Moves the selection one up. If the selection was already on the top item, it's moved
 | 
			
		||||
    // to the bottom one. Returns true if the selection actually moved up, false if it wrapped 
 | 
			
		||||
    // around to the bottom.
 | 
			
		||||
    selectUp: function() {
 | 
			
		||||
        let count = this._getVisibleCount();
 | 
			
		||||
        let selectedUp = true;
 | 
			
		||||
        let prev = this._selectedIndex - 1;
 | 
			
		||||
        if (this._selectedIndex <= 0) {
 | 
			
		||||
            prev = count - 1;
 | 
			
		||||
            selectedUp = false; 
 | 
			
		||||
        }
 | 
			
		||||
        this._selectIndex(prev);
 | 
			
		||||
        return selectedUp;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Moves the selection one down. If the selection was already on the bottom item, it's moved
 | 
			
		||||
    // to the top one. Returns true if the selection actually moved down, false if it wrapped 
 | 
			
		||||
    // around to the top.
 | 
			
		||||
    selectDown: function() {
 | 
			
		||||
        let count = this._getVisibleCount();
 | 
			
		||||
        let selectedDown = true;
 | 
			
		||||
        let next = this._selectedIndex + 1;
 | 
			
		||||
        if (this._selectedIndex == count - 1) {
 | 
			
		||||
            next = 0;
 | 
			
		||||
            selectedDown = false;
 | 
			
		||||
        }
 | 
			
		||||
        this._selectIndex(next);
 | 
			
		||||
        return selectedDown;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Selects the first item among the displayed items.
 | 
			
		||||
    selectFirstItem: function() {
 | 
			
		||||
        if (this.hasItems())
 | 
			
		||||
            this._selectIndex(0);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Selects the last item among the displayed items.
 | 
			
		||||
    selectLastItem: function() {
 | 
			
		||||
        let count = this._getVisibleCount();
 | 
			
		||||
        if (this.hasItems())
 | 
			
		||||
            this._selectIndex(count - 1);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Returns true if the display has some item selected.
 | 
			
		||||
    hasSelected: function() {
 | 
			
		||||
        return this._selectedIndex != -1;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Removes selection if some display item is selected.
 | 
			
		||||
    unsetSelected: function() {
 | 
			
		||||
        this._selectIndex(-1);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Returns true if the display has any displayed items.
 | 
			
		||||
    hasItems: function() {
 | 
			
		||||
        // TODO: figure out why this._list.displayedCount is returning a
 | 
			
		||||
        // positive number when this._mathedItems.length is 0
 | 
			
		||||
        // This can be triggered if a search string is entered for which there are no matches.
 | 
			
		||||
        // log('this._mathedItems.length: ' + this._matchedItems.length + ' this._list.displayedCount ' + this._list.displayedCount);
 | 
			
		||||
        return this._matchedItemKeys.length > 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getMatchedItemsCount: function() {
 | 
			
		||||
        return this._matchedItemKeys.length;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Load the initial state
 | 
			
		||||
    load: function() {
 | 
			
		||||
        this._redisplay(RedisplayFlags.FULL);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Should be called when the display is closed
 | 
			
		||||
    resetState: function() {
 | 
			
		||||
        this._filterReset();
 | 
			
		||||
        this._openDetailIndex = -1;
 | 
			
		||||
        this.actor.get_vscroll_bar().get_adjustment().value = 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Returns an actor which acts as a sidebar; this is used for
 | 
			
		||||
    // the applications category view
 | 
			
		||||
    getNavigationArea: function () {
 | 
			
		||||
        return null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    createDetailsForIndex: function(index) {
 | 
			
		||||
        let item = this._findDisplayedByIndex(index);
 | 
			
		||||
        return item.createDetailsActor();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    //// Protected methods ////
 | 
			
		||||
 | 
			
		||||
    _recreateDisplayItems: function() {
 | 
			
		||||
        this._removeAllDisplayItems();
 | 
			
		||||
        this._setDefaultList();
 | 
			
		||||
        for (let itemId in this._allItems) {
 | 
			
		||||
            this._addDisplayItem(itemId);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Creates a display item based on the information associated with itemId
 | 
			
		||||
    // and adds it to the list of displayed items, but does not yet display it.
 | 
			
		||||
    _addDisplayItem : function(itemId) {
 | 
			
		||||
        if (this._displayedItems.hasOwnProperty(itemId)) {
 | 
			
		||||
            log('Tried adding a display item for ' + itemId + ', but an item with this item id is already among displayed items.');
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let itemInfo = this._allItems[itemId];
 | 
			
		||||
        let displayItem = this._createDisplayItem(itemInfo);
 | 
			
		||||
 | 
			
		||||
        displayItem.connect('activate',
 | 
			
		||||
                            Lang.bind(this,
 | 
			
		||||
                                      function() {
 | 
			
		||||
                                          // update the selection
 | 
			
		||||
                                          this._selectIndex(this._list.get_children().indexOf(displayItem.actor));
 | 
			
		||||
                                          this.activateSelected();
 | 
			
		||||
                                      }));
 | 
			
		||||
 | 
			
		||||
        displayItem.connect('show-details',
 | 
			
		||||
                            Lang.bind(this,
 | 
			
		||||
                                      function() {
 | 
			
		||||
                                          let index = this._list.get_children().indexOf(displayItem.actor);
 | 
			
		||||
                                          /* Close the details pane if already open */
 | 
			
		||||
                                          if (index == this._openDetailIndex) {
 | 
			
		||||
                                              this._openDetailIndex = -1;
 | 
			
		||||
                                              this.emit('show-details', -1);
 | 
			
		||||
                                          } else {
 | 
			
		||||
                                              this._openDetailIndex = index;
 | 
			
		||||
                                              this.emit('show-details', index);
 | 
			
		||||
                                          }
 | 
			
		||||
                                      }));
 | 
			
		||||
        this._displayedItems[itemId] = displayItem;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Removes an item identifed by the itemId from the displayed items.
 | 
			
		||||
    _removeDisplayItem: function(itemId) {
 | 
			
		||||
        let children = this._list.get_children();
 | 
			
		||||
        let count = children.length;
 | 
			
		||||
        let displayItem = this._displayedItems[itemId];
 | 
			
		||||
        let displayItemIndex = children.indexOf(displayItem.actor);
 | 
			
		||||
 | 
			
		||||
        if (this.hasSelected() && count == 1) {
 | 
			
		||||
            this.unsetSelected();
 | 
			
		||||
        } else if (this.hasSelected() && displayItemIndex < this._selectedIndex) {
 | 
			
		||||
            this.selectUp();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        displayItem.destroy();
 | 
			
		||||
 | 
			
		||||
        delete this._displayedItems[itemId];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Removes all displayed items.
 | 
			
		||||
    _removeAllDisplayItems: function() {
 | 
			
		||||
        this.unsetSelected();
 | 
			
		||||
        for (itemId in this._displayedItems)
 | 
			
		||||
            this._removeDisplayItem(itemId);
 | 
			
		||||
     },
 | 
			
		||||
 | 
			
		||||
    // Return true if there's an active search or other constraint
 | 
			
		||||
    // on the list
 | 
			
		||||
    _filterActive: function() {
 | 
			
		||||
        return this._search != '';
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Called when we are resetting state
 | 
			
		||||
    _filterReset: function() {
 | 
			
		||||
        this.unsetSelected();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _compareSearchMatch: function(a, b) {
 | 
			
		||||
        let countA = this._matchedItems[a];
 | 
			
		||||
        let countB = this._matchedItems[b];
 | 
			
		||||
        if (countA > countB)
 | 
			
		||||
            return -1;
 | 
			
		||||
        else if (countA < countB)
 | 
			
		||||
            return 1;
 | 
			
		||||
        else
 | 
			
		||||
            return this._compareItems(a, b);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _setMatches: function(matches) {
 | 
			
		||||
        this._matchedItems = matches;
 | 
			
		||||
        this._matchedItemKeys = [];
 | 
			
		||||
        for (let itemId in this._matchedItems) {
 | 
			
		||||
            this._matchedItemKeys.push(itemId);
 | 
			
		||||
        }
 | 
			
		||||
        this._matchedItemKeys.sort(Lang.bind(this, this._compareSearchMatch));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * _redisplaySubSearch:
 | 
			
		||||
     * A somewhat more optimized function called when we know
 | 
			
		||||
     * that we're going to be displaying a subset of the items
 | 
			
		||||
     * we already had, in the same order.  In that case, we can
 | 
			
		||||
     * just hide the actors that shouldn't be shown.
 | 
			
		||||
     */
 | 
			
		||||
    _redisplaySubSearch: function() {
 | 
			
		||||
        let matches = this._getSearchMatchedItems(true);
 | 
			
		||||
 | 
			
		||||
        // Just hide all from the old set,
 | 
			
		||||
        // we'll show the ones we want below
 | 
			
		||||
        for (let itemId in this._displayedItems) {
 | 
			
		||||
            let item = this._displayedItems[itemId];
 | 
			
		||||
            item.actor.hide();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._setMatches(matches);
 | 
			
		||||
 | 
			
		||||
        for (let itemId in matches) {
 | 
			
		||||
            let item = this._displayedItems[itemId];
 | 
			
		||||
            item.actor.show();
 | 
			
		||||
        }
 | 
			
		||||
        this._list.queue_relayout();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _redisplayReordering: function() {
 | 
			
		||||
        if (!this._filterActive()) {
 | 
			
		||||
            this._setDefaultList();
 | 
			
		||||
        } else {
 | 
			
		||||
            this._setMatches(this._getSearchMatchedItems(false));
 | 
			
		||||
        }
 | 
			
		||||
        this._list.remove_all();
 | 
			
		||||
        for (let i = 0; i < this._matchedItemKeys.length; i++) {
 | 
			
		||||
            let itemId = this._matchedItemKeys[i];
 | 
			
		||||
            let item = this._displayedItems[itemId];
 | 
			
		||||
            item.actor.show();
 | 
			
		||||
            this._list.add_actor(item.actor);
 | 
			
		||||
       }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * Updates the displayed items, applying the search string if one exists.
 | 
			
		||||
     * @flags: Flags controlling redisplay behavior as follows:
 | 
			
		||||
     *  SUBSEARCH - Indicates that the current _search is a superstring of the previous
 | 
			
		||||
     *  one, which implies we only need to re-search through previous results.
 | 
			
		||||
     *  FULL - Indicates that we need recreate all displayed items.
 | 
			
		||||
     *  IMMEDIATE - Do the full redisplay even if we're not mapped.  This is useful
 | 
			
		||||
     *  if you want to get the number of matched items and show/hide a section based on
 | 
			
		||||
     *  that number.
 | 
			
		||||
     */
 | 
			
		||||
    _redisplay: function(flags) {
 | 
			
		||||
        let immediate = (flags & RedisplayFlags.IMMEDIATE) != 0;
 | 
			
		||||
        if (!immediate && !this.actor.mapped) {
 | 
			
		||||
            this._pendingRedisplay |= flags;
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let isSubSearch = (flags & RedisplayFlags.SUBSEARCH) != 0;
 | 
			
		||||
        let fullReload = (flags & RedisplayFlags.FULL) != 0;
 | 
			
		||||
 | 
			
		||||
        let hadSelected = this.hasSelected();
 | 
			
		||||
        this.unsetSelected();
 | 
			
		||||
 | 
			
		||||
        if (!this._initialLoadComplete)
 | 
			
		||||
            fullReload = true;
 | 
			
		||||
 | 
			
		||||
        if (!this._refreshCache())
 | 
			
		||||
            fullReload = true;
 | 
			
		||||
 | 
			
		||||
        if (fullReload) {
 | 
			
		||||
            this._recreateDisplayItems();
 | 
			
		||||
            this._initialLoadComplete = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (isSubSearch) {
 | 
			
		||||
            this._redisplaySubSearch();
 | 
			
		||||
        } else {
 | 
			
		||||
            this._redisplayReordering();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (hadSelected) {
 | 
			
		||||
            this._selectedIndex = -1;
 | 
			
		||||
            this.selectFirstItem();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.emit('redisplayed');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    //// Pure virtual protected methods ////
 | 
			
		||||
 | 
			
		||||
    // Performs the steps needed to have the latest information about the items.
 | 
			
		||||
    // Implementation should return %true if we are up to date, and %false
 | 
			
		||||
    // if a full reload occurred.
 | 
			
		||||
    _refreshCache: function() {
 | 
			
		||||
        throw new Error('Not implemented');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Sets the list of the displayed items based on the default sorting order.
 | 
			
		||||
    // The default sorting order is specific to each implementing class.
 | 
			
		||||
    _setDefaultList: function() {
 | 
			
		||||
        throw new Error('Not implemented');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Compares items associated with the item ids based on the order in which the
 | 
			
		||||
    // items should be displayed.
 | 
			
		||||
    // Intended to be used as a compareFunction for array.sort().
 | 
			
		||||
    // Returns an integer value indicating the result of the comparison.
 | 
			
		||||
    _compareItems: function(itemIdA, itemIdB) {
 | 
			
		||||
        throw new Error('Not implemented');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Checks if the item info can be a match for the search string. 
 | 
			
		||||
    // Returns a boolean flag indicating if that's the case.
 | 
			
		||||
    _isInfoMatching: function(itemInfo, search) {
 | 
			
		||||
        throw new Error('Not implemented');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Creates a display item based on itemInfo.
 | 
			
		||||
    _createDisplayItem: function(itemInfo) {
 | 
			
		||||
        throw new Error('Not implemented');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    //// Private methods ////
 | 
			
		||||
 | 
			
		||||
    _getItemSearchScore: function(itemId, terms) {
 | 
			
		||||
        let item = this._allItems[itemId];
 | 
			
		||||
        let score = 0;
 | 
			
		||||
        for (let i = 0; i < terms.length; i++) {
 | 
			
		||||
            let term = terms[i];
 | 
			
		||||
            if (this._isInfoMatching(item, term)) {
 | 
			
		||||
                score++;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return score;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getSearchMatchedItems: function(isSubSearch) {
 | 
			
		||||
        // Break the search up into terms, and search for each
 | 
			
		||||
        // individual term.  Keep track of the number of terms
 | 
			
		||||
        // each item matched.
 | 
			
		||||
        let terms = this._search.split(/\s+/);
 | 
			
		||||
        let matchScores = {};
 | 
			
		||||
 | 
			
		||||
        if (isSubSearch) {
 | 
			
		||||
            for (let i = 0; i < this._matchedItemKeys.length; i++) {
 | 
			
		||||
                let itemId = this._matchedItemKeys[i];
 | 
			
		||||
                let score = this._getItemSearchScore(itemId, terms);
 | 
			
		||||
                if (score > 0)
 | 
			
		||||
                    matchScores[itemId] = score;
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            for (let itemId in this._displayedItems) {
 | 
			
		||||
                let score = this._getItemSearchScore(itemId, terms);
 | 
			
		||||
                if (score > 0)
 | 
			
		||||
                    matchScores[itemId] = score;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return matchScores;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Returns a display item based on its index in the ordering of the
 | 
			
		||||
    // display children.
 | 
			
		||||
    _findDisplayedByIndex: function(index) {
 | 
			
		||||
        let actor = this._list.get_children()[index];
 | 
			
		||||
        return this._findDisplayedByActor(actor);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Returns a display item based on the actor that represents it in 
 | 
			
		||||
    // the display.
 | 
			
		||||
    _findDisplayedByActor: function(actor) {
 | 
			
		||||
        for (itemId in this._displayedItems) {
 | 
			
		||||
            let item = this._displayedItems[itemId];
 | 
			
		||||
            if (item.actor == actor) {
 | 
			
		||||
                return item;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Selects (e.g. highlights) a display item at the provided index,
 | 
			
		||||
    // updates this.selectedItemDetails actor, and emits 'selected' signal.
 | 
			
		||||
    _selectIndex: function(index) {
 | 
			
		||||
        // Cleanup from the previous item
 | 
			
		||||
        if (this.hasSelected()) {
 | 
			
		||||
            this._findDisplayedByIndex(this._selectedIndex).markSelected(false);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._selectedIndex = index;
 | 
			
		||||
        if (index < 0)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        // Mark the new item as selected and create its details pane
 | 
			
		||||
        let item = this._findDisplayedByIndex(index);
 | 
			
		||||
        item.markSelected(true);
 | 
			
		||||
        this.emit('selected');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getVisibleCount: function() {
 | 
			
		||||
        return this._list.get_n_children();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onMappedNotify: function () {
 | 
			
		||||
        let mapped = this.actor.mapped;
 | 
			
		||||
        if (mapped && this._pendingRedisplay > RedisplayFlags.NONE)
 | 
			
		||||
            this._redisplay(this._pendingRedisplay);
 | 
			
		||||
 | 
			
		||||
        this._pendingRedisplay = RedisplayFlags.NONE;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Signals.addSignalMethods(GenericDisplay.prototype);
 | 
			
		||||
@@ -17,8 +17,7 @@ function BaseIcon(label, createIcon) {
 | 
			
		||||
BaseIcon.prototype = {
 | 
			
		||||
    _init : function(label, params) {
 | 
			
		||||
        params = Params.parse(params, { createIcon: null,
 | 
			
		||||
                                        setSizeManually: false,
 | 
			
		||||
                                        showLabel: true });
 | 
			
		||||
                                        setSizeManually: false });
 | 
			
		||||
        this.actor = new St.Bin({ style_class: 'overview-icon',
 | 
			
		||||
                                  x_fill: true,
 | 
			
		||||
                                  y_fill: true });
 | 
			
		||||
@@ -41,52 +40,42 @@ BaseIcon.prototype = {
 | 
			
		||||
 | 
			
		||||
        box.add_actor(this._iconBin);
 | 
			
		||||
 | 
			
		||||
        if (params.showLabel) {
 | 
			
		||||
            this.label = new St.Label({ text: label });
 | 
			
		||||
            box.add_actor(this.label);
 | 
			
		||||
        } else {
 | 
			
		||||
            this.label = null;
 | 
			
		||||
        }
 | 
			
		||||
        this._name = new St.Label({ text: label });
 | 
			
		||||
        box.add_actor(this._name);
 | 
			
		||||
 | 
			
		||||
        if (params.createIcon)
 | 
			
		||||
            this.createIcon = params.createIcon;
 | 
			
		||||
        this._setSizeManually = params.setSizeManually;
 | 
			
		||||
 | 
			
		||||
        this.icon = null;
 | 
			
		||||
        this.icon = this.createIcon(this.iconSize);
 | 
			
		||||
        this._iconBin.set_child(this.icon);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _allocate: function(actor, box, flags) {
 | 
			
		||||
        let availWidth = box.x2 - box.x1;
 | 
			
		||||
        let availHeight = box.y2 - box.y1;
 | 
			
		||||
 | 
			
		||||
        let iconSize = availHeight;
 | 
			
		||||
 | 
			
		||||
        let [labelMinHeight, labelNatHeight] = this._name.get_preferred_height(-1);
 | 
			
		||||
        let [iconMinHeight, iconNatHeight] = this._iconBin.get_preferred_height(-1);
 | 
			
		||||
        let [iconMinWidth, iconNatWidth] = this._iconBin.get_preferred_width(-1);
 | 
			
		||||
        let preferredHeight = iconNatHeight;
 | 
			
		||||
        let preferredHeight = labelNatHeight + this._spacing + iconNatHeight;
 | 
			
		||||
        let labelHeight = availHeight >= preferredHeight ? labelNatHeight
 | 
			
		||||
                                                         : labelMinHeight;
 | 
			
		||||
        let iconSize = availHeight - this._spacing - labelHeight;
 | 
			
		||||
        let iconPadding = (availWidth - iconSize) / 2;
 | 
			
		||||
 | 
			
		||||
        let childBox = new Clutter.ActorBox();
 | 
			
		||||
 | 
			
		||||
        if (this.label) {
 | 
			
		||||
            let [labelMinHeight, labelNatHeight] = this.label.get_preferred_height(-1);
 | 
			
		||||
            preferredHeight += this._spacing + labelNatHeight;
 | 
			
		||||
 | 
			
		||||
            let labelHeight = availHeight >= preferredHeight ? labelNatHeight
 | 
			
		||||
                                                             : labelMinHeight;
 | 
			
		||||
            iconSize -= this._spacing + labelHeight;
 | 
			
		||||
 | 
			
		||||
            childBox.x1 = 0;
 | 
			
		||||
            childBox.x2 = availWidth;
 | 
			
		||||
            childBox.y1 = iconSize + this._spacing;
 | 
			
		||||
            childBox.y2 = childBox.y1 + labelHeight;
 | 
			
		||||
            this.label.allocate(childBox, flags);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        childBox.x1 = Math.floor((availWidth - iconNatWidth) / 2);
 | 
			
		||||
        childBox.y1 = Math.floor((iconSize - iconNatHeight) / 2);
 | 
			
		||||
        childBox.x2 = childBox.x1 + iconNatWidth;
 | 
			
		||||
        childBox.y2 = childBox.y1 + iconNatHeight;
 | 
			
		||||
        childBox.x1 = iconPadding;
 | 
			
		||||
        childBox.y1 = 0;
 | 
			
		||||
        childBox.x2 = availWidth - iconPadding;
 | 
			
		||||
        childBox.y2 = iconSize;
 | 
			
		||||
        this._iconBin.allocate(childBox, flags);
 | 
			
		||||
 | 
			
		||||
        childBox.x1 = 0;
 | 
			
		||||
        childBox.x2 = availWidth;
 | 
			
		||||
        childBox.y1 = iconSize + this._spacing;
 | 
			
		||||
        childBox.y2 = childBox.y1 + labelHeight;
 | 
			
		||||
        this._name.allocate(childBox, flags);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getPreferredWidth: function(actor, forHeight, alloc) {
 | 
			
		||||
@@ -95,14 +84,9 @@ BaseIcon.prototype = {
 | 
			
		||||
 | 
			
		||||
    _getPreferredHeight: function(actor, forWidth, alloc) {
 | 
			
		||||
        let [iconMinHeight, iconNatHeight] = this._iconBin.get_preferred_height(forWidth);
 | 
			
		||||
        alloc.min_size = iconMinHeight;
 | 
			
		||||
        alloc.natural_size = iconNatHeight;
 | 
			
		||||
 | 
			
		||||
        if (this.label) {
 | 
			
		||||
            let [labelMinHeight, labelNatHeight] = this.label.get_preferred_height(forWidth);
 | 
			
		||||
            alloc.min_size += this._spacing + labelMinHeight;
 | 
			
		||||
            alloc.natural_size += this._spacing + labelNatHeight;
 | 
			
		||||
        }
 | 
			
		||||
        let [labelMinHeight, labelNatHeight] = this._name.get_preferred_height(forWidth);
 | 
			
		||||
        alloc.min_size = iconMinHeight + this._spacing + labelMinHeight;
 | 
			
		||||
        alloc.natural_size = iconNatHeight + this._spacing + labelNatHeight;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // This can be overridden by a subclass, or by the createIcon
 | 
			
		||||
@@ -115,23 +99,16 @@ BaseIcon.prototype = {
 | 
			
		||||
        if (!this._setSizeManually)
 | 
			
		||||
            throw new Error('setSizeManually has to be set to use setIconsize');
 | 
			
		||||
 | 
			
		||||
        this._setIconSize(size);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _setIconSize: function(size) {
 | 
			
		||||
        if (size == this.iconSize)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._createIconTexture(size);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createIconTexture: function(size) {
 | 
			
		||||
        if (this.icon)
 | 
			
		||||
            this.icon.destroy();
 | 
			
		||||
        this.icon.destroy();
 | 
			
		||||
        this.iconSize = size;
 | 
			
		||||
        this.icon = this.createIcon(this.iconSize);
 | 
			
		||||
 | 
			
		||||
        // The icon returned by createIcon() might actually be smaller than
 | 
			
		||||
        // the requested icon size (for instance StTextureCache does this
 | 
			
		||||
        // for fallback icons), so set the size explicitly.
 | 
			
		||||
        this.icon.set_size(this.iconSize, this.iconSize);
 | 
			
		||||
 | 
			
		||||
        this._iconBin.child = this.icon;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -139,15 +116,12 @@ BaseIcon.prototype = {
 | 
			
		||||
        let node = this.actor.get_theme_node();
 | 
			
		||||
        this._spacing = node.get_length('spacing');
 | 
			
		||||
 | 
			
		||||
        let size;
 | 
			
		||||
        if (this._setSizeManually) {
 | 
			
		||||
            size = this.iconSize;
 | 
			
		||||
        } else {
 | 
			
		||||
            let [found, len] = node.lookup_length('icon-size', false);
 | 
			
		||||
            size = found ? len : ICON_SIZE;
 | 
			
		||||
        }
 | 
			
		||||
        if (this._setSizeManually)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._createIconTexture(size);
 | 
			
		||||
        let len = node.get_length('icon-size');
 | 
			
		||||
        if (len > 0)
 | 
			
		||||
            this._setIconSize(len);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -191,16 +165,8 @@ IconGrid.prototype = {
 | 
			
		||||
        alloc.natural_size = nColumns * this._item_size + totalSpacing;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getVisibleChildren: function() {
 | 
			
		||||
        let children = this._grid.get_children();
 | 
			
		||||
        children = children.filter(function(actor) {
 | 
			
		||||
            return actor.visible;
 | 
			
		||||
        });
 | 
			
		||||
        return children;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getPreferredHeight: function (grid, forWidth, alloc) {
 | 
			
		||||
        let children = this._getVisibleChildren();
 | 
			
		||||
        let children = this._grid.get_children();
 | 
			
		||||
        let [nColumns, usedWidth] = this._computeLayout(forWidth);
 | 
			
		||||
        let nRows;
 | 
			
		||||
        if (nColumns > 0)
 | 
			
		||||
@@ -216,7 +182,7 @@ IconGrid.prototype = {
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _allocate: function (grid, box, flags) {
 | 
			
		||||
        let children = this._getVisibleChildren();
 | 
			
		||||
        let children = this._grid.get_children();
 | 
			
		||||
        let availWidth = box.x2 - box.x1;
 | 
			
		||||
        let availHeight = box.y2 - box.y1;
 | 
			
		||||
 | 
			
		||||
@@ -281,11 +247,8 @@ IconGrid.prototype = {
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    childrenInRow: function(rowWidth) {
 | 
			
		||||
        return this._computeLayout(rowWidth)[0];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _computeLayout: function (forWidth) {
 | 
			
		||||
        let children = this._grid.get_children();
 | 
			
		||||
        let nColumns = 0;
 | 
			
		||||
        let usedWidth = 0;
 | 
			
		||||
        while ((this._colLimit == null || nColumns < this._colLimit) &&
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										323
									
								
								js/ui/layout.js
									
									
									
									
									
								
							
							
						
						@@ -1,323 +0,0 @@
 | 
			
		||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const Tweener = imports.ui.tweener;
 | 
			
		||||
 | 
			
		||||
const HOT_CORNER_ACTIVATION_TIMEOUT = 0.5;
 | 
			
		||||
 | 
			
		||||
function LayoutManager() {
 | 
			
		||||
    this._init.apply(this, arguments);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
LayoutManager.prototype = {
 | 
			
		||||
    _init: function () {
 | 
			
		||||
        this._rtl = (St.Widget.get_default_direction() == St.TextDirection.RTL);
 | 
			
		||||
        this.monitors = [];
 | 
			
		||||
        this.primaryMonitor = null;
 | 
			
		||||
        this.primaryIndex = -1;
 | 
			
		||||
        this._hotCorners = [];
 | 
			
		||||
 | 
			
		||||
        this._updateMonitors();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // This is called by Main after everything else is constructed;
 | 
			
		||||
    // _updateHotCorners needs access to Main.panel, which didn't exist
 | 
			
		||||
    // yet when the LayoutManager was constructed.
 | 
			
		||||
    init: function() {
 | 
			
		||||
        global.screen.connect('monitors-changed', Lang.bind(this, this._monitorsChanged));
 | 
			
		||||
        this._updateHotCorners();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateMonitors: function() {
 | 
			
		||||
        let screen = global.screen;
 | 
			
		||||
 | 
			
		||||
        this.monitors = [];
 | 
			
		||||
        let nMonitors = screen.get_n_monitors();
 | 
			
		||||
        for (let i = 0; i < nMonitors; i++)
 | 
			
		||||
            this.monitors.push(screen.get_monitor_geometry(i));
 | 
			
		||||
 | 
			
		||||
        if (nMonitors == 1) {
 | 
			
		||||
            this.primaryIndex = this.bottomIndex = 0;
 | 
			
		||||
        } else {
 | 
			
		||||
            // If there are monitors below the primary, then we need
 | 
			
		||||
            // to split primary from bottom.
 | 
			
		||||
            this.primaryIndex = this.bottomIndex = screen.get_primary_monitor();
 | 
			
		||||
            for (let i = 0; i < this.monitors.length; i++) {
 | 
			
		||||
                let monitor = this.monitors[i];
 | 
			
		||||
                if (this._isAboveOrBelowPrimary(monitor)) {
 | 
			
		||||
                    if (monitor.y > this.monitors[this.bottomIndex].y)
 | 
			
		||||
                        this.bottomIndex = i;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        this.primaryMonitor = this.monitors[this.primaryIndex];
 | 
			
		||||
        this.bottomMonitor = this.monitors[this.bottomIndex];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateHotCorners: function() {
 | 
			
		||||
        // destroy old hot corners
 | 
			
		||||
        for (let i = 0; i < this._hotCorners.length; i++)
 | 
			
		||||
            this._hotCorners[i].destroy();
 | 
			
		||||
        this._hotCorners = [];
 | 
			
		||||
 | 
			
		||||
        // build new hot corners
 | 
			
		||||
        for (let i = 0; i < this.monitors.length; i++) {
 | 
			
		||||
            if (i == this.primaryIndex)
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            let monitor = this.monitors[i];
 | 
			
		||||
            let cornerX = this._rtl ? monitor.x + monitor.width : monitor.x;
 | 
			
		||||
            let cornerY = monitor.y;
 | 
			
		||||
 | 
			
		||||
            let haveTopLeftCorner = true;
 | 
			
		||||
 | 
			
		||||
            // Check if we have a top left (right for RTL) corner.
 | 
			
		||||
            // I.e. if there is no monitor directly above or to the left(right)
 | 
			
		||||
            let besideX = this._rtl ? monitor.x + 1 : cornerX - 1;
 | 
			
		||||
            let besideY = cornerY;
 | 
			
		||||
            let aboveX = cornerX;
 | 
			
		||||
            let aboveY = cornerY - 1;
 | 
			
		||||
 | 
			
		||||
            for (let j = 0; j < this.monitors.length; j++) {
 | 
			
		||||
                if (i == j)
 | 
			
		||||
                    continue;
 | 
			
		||||
                let otherMonitor = this.monitors[j];
 | 
			
		||||
                if (besideX >= otherMonitor.x &&
 | 
			
		||||
                    besideX < otherMonitor.x + otherMonitor.width &&
 | 
			
		||||
                    besideY >= otherMonitor.y &&
 | 
			
		||||
                    besideY < otherMonitor.y + otherMonitor.height) {
 | 
			
		||||
                    haveTopLeftCorner = false;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                if (aboveX >= otherMonitor.x &&
 | 
			
		||||
                    aboveX < otherMonitor.x + otherMonitor.width &&
 | 
			
		||||
                    aboveY >= otherMonitor.y &&
 | 
			
		||||
                    aboveY < otherMonitor.y + otherMonitor.height) {
 | 
			
		||||
                    haveTopLeftCorner = false;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!haveTopLeftCorner)
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            let corner = new HotCorner();
 | 
			
		||||
            this._hotCorners.push(corner);
 | 
			
		||||
            corner.actor.set_position(cornerX, cornerY);
 | 
			
		||||
            Main.chrome.addActor(corner.actor, { affectsStruts: false });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _monitorsChanged: function() {
 | 
			
		||||
        this._updateMonitors();
 | 
			
		||||
        this._updateHotCorners();
 | 
			
		||||
 | 
			
		||||
        this.emit('monitors-changed');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _isAboveOrBelowPrimary: function(monitor) {
 | 
			
		||||
        let primary = this.monitors[this.primaryIndex];
 | 
			
		||||
        let monitorLeft = monitor.x, monitorRight = monitor.x + monitor.width;
 | 
			
		||||
        let primaryLeft = primary.x, primaryRight = primary.x + primary.width;
 | 
			
		||||
 | 
			
		||||
        if ((monitorLeft >= primaryLeft && monitorLeft <= primaryRight) ||
 | 
			
		||||
            (monitorRight >= primaryLeft && monitorRight <= primaryRight) ||
 | 
			
		||||
            (primaryLeft >= monitorLeft && primaryLeft <= monitorRight) ||
 | 
			
		||||
            (primaryRight >= monitorLeft && primaryRight <= monitorRight))
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get focusIndex() {
 | 
			
		||||
        let screen = global.screen;
 | 
			
		||||
        let display = screen.get_display();
 | 
			
		||||
        let focusWindow = display.focus_window;
 | 
			
		||||
 | 
			
		||||
        if (focusWindow) {
 | 
			
		||||
            let wrect = focusWindow.get_outer_rect();
 | 
			
		||||
            for (let i = 0; i < this.monitors.length; i++) {
 | 
			
		||||
                let monitor = this.monitors[i];
 | 
			
		||||
 | 
			
		||||
                if (monitor.x <= wrect.x && monitor.y <= wrect.y &&
 | 
			
		||||
                    monitor.x + monitor.width > wrect.x &&
 | 
			
		||||
                    monitor.y + monitor.height > wrect.y)
 | 
			
		||||
                    return i;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return this.primaryIndex;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get focusMonitor() {
 | 
			
		||||
        return this.monitors[this.focusIndex];
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
Signals.addSignalMethods(LayoutManager.prototype);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// HotCorner:
 | 
			
		||||
//
 | 
			
		||||
// This class manages a "hot corner" that can toggle switching to
 | 
			
		||||
// overview.
 | 
			
		||||
function HotCorner() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
HotCorner.prototype = {
 | 
			
		||||
    _init : function() {
 | 
			
		||||
        // We use this flag to mark the case where the user has entered the
 | 
			
		||||
        // hot corner and has not left both the hot corner and a surrounding
 | 
			
		||||
        // guard area (the "environs"). This avoids triggering the hot corner
 | 
			
		||||
        // multiple times due to an accidental jitter.
 | 
			
		||||
        this._entered = false;
 | 
			
		||||
 | 
			
		||||
        this.actor = new Clutter.Group({ name: 'hot-corner-environs',
 | 
			
		||||
                                         width: 3,
 | 
			
		||||
                                         height: 3,
 | 
			
		||||
                                         reactive: true });
 | 
			
		||||
 | 
			
		||||
        this._corner = new Clutter.Rectangle({ name: 'hot-corner',
 | 
			
		||||
                                               width: 1,
 | 
			
		||||
                                               height: 1,
 | 
			
		||||
                                               opacity: 0,
 | 
			
		||||
                                               reactive: true });
 | 
			
		||||
        this._corner._delegate = this;
 | 
			
		||||
 | 
			
		||||
        this.actor.add_actor(this._corner);
 | 
			
		||||
 | 
			
		||||
        if (St.Widget.get_default_direction() == St.TextDirection.RTL) {
 | 
			
		||||
            this._corner.set_position(this.actor.width - this._corner.width, 0);
 | 
			
		||||
            this.actor.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
 | 
			
		||||
        } else {
 | 
			
		||||
            this._corner.set_position(0, 0);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._activationTime = 0;
 | 
			
		||||
 | 
			
		||||
        this.actor.connect('leave-event',
 | 
			
		||||
                           Lang.bind(this, this._onEnvironsLeft));
 | 
			
		||||
 | 
			
		||||
        // Clicking on the hot corner environs should result in the
 | 
			
		||||
        // same behavior as clicking on the hot corner.
 | 
			
		||||
        this.actor.connect('button-release-event',
 | 
			
		||||
                           Lang.bind(this, this._onCornerClicked));
 | 
			
		||||
 | 
			
		||||
        // In addition to being triggered by the mouse enter event,
 | 
			
		||||
        // the hot corner can be triggered by clicking on it. This is
 | 
			
		||||
        // useful if the user wants to undo the effect of triggering
 | 
			
		||||
        // the hot corner once in the hot corner.
 | 
			
		||||
        this._corner.connect('enter-event',
 | 
			
		||||
                             Lang.bind(this, this._onCornerEntered));
 | 
			
		||||
        this._corner.connect('button-release-event',
 | 
			
		||||
                             Lang.bind(this, this._onCornerClicked));
 | 
			
		||||
        this._corner.connect('leave-event',
 | 
			
		||||
                             Lang.bind(this, this._onCornerLeft));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroy: function() {
 | 
			
		||||
        this.actor.destroy();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _addRipple : function(delay, time, startScale, startOpacity, finalScale, finalOpacity) {
 | 
			
		||||
        // We draw a ripple by using a source image and animating it scaling
 | 
			
		||||
        // outwards and fading away. We want the ripples to move linearly
 | 
			
		||||
        // or it looks unrealistic, but if the opacity of the ripple goes
 | 
			
		||||
        // linearly to zero it fades away too quickly, so we use Tweener's
 | 
			
		||||
        // 'onUpdate' to give a non-linear curve to the fade-away and make
 | 
			
		||||
        // it more visible in the middle section.
 | 
			
		||||
 | 
			
		||||
        let [x, y] = this._corner.get_transformed_position();
 | 
			
		||||
        let ripple = new St.BoxLayout({ style_class: 'ripple-box',
 | 
			
		||||
                                        opacity: 255 * Math.sqrt(startOpacity),
 | 
			
		||||
                                        scale_x: startScale,
 | 
			
		||||
                                        scale_y: startScale,
 | 
			
		||||
                                        x: x,
 | 
			
		||||
                                        y: y });
 | 
			
		||||
        ripple._opacity =  startOpacity;
 | 
			
		||||
        if (ripple.get_direction() == St.TextDirection.RTL)
 | 
			
		||||
            ripple.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
 | 
			
		||||
        Tweener.addTween(ripple, { _opacity: finalOpacity,
 | 
			
		||||
                                   scale_x: finalScale,
 | 
			
		||||
                                   scale_y: finalScale,
 | 
			
		||||
                                   delay: delay,
 | 
			
		||||
                                   time: time,
 | 
			
		||||
                                   transition: 'linear',
 | 
			
		||||
                                   onUpdate: function() { ripple.opacity = 255 * Math.sqrt(ripple._opacity); },
 | 
			
		||||
                                   onComplete: function() { ripple.destroy(); } });
 | 
			
		||||
        Main.uiGroup.add_actor(ripple);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    rippleAnimation: function() {
 | 
			
		||||
        // Show three concentric ripples expanding outwards; the exact
 | 
			
		||||
        // parameters were found by trial and error, so don't look
 | 
			
		||||
        // for them to make perfect sense mathematically
 | 
			
		||||
 | 
			
		||||
        //              delay  time  scale opacity => scale opacity
 | 
			
		||||
        this._addRipple(0.0,   0.83,  0.25,  1.0,    1.5,  0.0);
 | 
			
		||||
        this._addRipple(0.05,  1.0,   0.0,   0.7,    1.25, 0.0);
 | 
			
		||||
        this._addRipple(0.35,  1.0,   0.0,   0.3,    1,    0.0);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    handleDragOver: function(source, actor, x, y, time) {
 | 
			
		||||
        if (source != Main.xdndHandler)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (!Main.overview.visible && !Main.overview.animationInProgress) {
 | 
			
		||||
            this.rippleAnimation();
 | 
			
		||||
            Main.overview.showTemporarily();
 | 
			
		||||
            Main.overview.beginItemDrag(actor);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onCornerEntered : function() {
 | 
			
		||||
        if (!this._entered) {
 | 
			
		||||
            this._entered = true;
 | 
			
		||||
            if (!Main.overview.animationInProgress) {
 | 
			
		||||
                this._activationTime = Date.now() / 1000;
 | 
			
		||||
 | 
			
		||||
                this.rippleAnimation();
 | 
			
		||||
                Main.overview.toggle();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onCornerClicked : function() {
 | 
			
		||||
        if (this.shouldToggleOverviewOnClick())
 | 
			
		||||
            Main.overview.toggle();
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onCornerLeft : function(actor, event) {
 | 
			
		||||
        if (event.get_related() != this.actor)
 | 
			
		||||
            this._entered = false;
 | 
			
		||||
        // Consume event, otherwise this will confuse onEnvironsLeft
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onEnvironsLeft : function(actor, event) {
 | 
			
		||||
        if (event.get_related() != this._corner)
 | 
			
		||||
            this._entered = false;
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Checks if the Activities button is currently sensitive to
 | 
			
		||||
    // clicks. The first call to this function within the
 | 
			
		||||
    // HOT_CORNER_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.
 | 
			
		||||
    shouldToggleOverviewOnClick: function() {
 | 
			
		||||
        if (Main.overview.animationInProgress)
 | 
			
		||||
            return false;
 | 
			
		||||
        if (this._activationTime == 0 || Date.now() / 1000 - this._activationTime > HOT_CORNER_ACTIVATION_TIMEOUT)
 | 
			
		||||
            return true;
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
@@ -1,19 +1,18 @@
 | 
			
		||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Cogl = imports.gi.Cogl;
 | 
			
		||||
const GConf = imports.gi.GConf;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const Gtk = imports.gi.Gtk;
 | 
			
		||||
const Pango = imports.gi.Pango;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Mainloop = imports.mainloop;
 | 
			
		||||
const Gettext = imports.gettext.domain('gnome-shell');
 | 
			
		||||
const _ = Gettext.gettext;
 | 
			
		||||
 | 
			
		||||
const History = imports.misc.history;
 | 
			
		||||
const ExtensionSystem = imports.ui.extensionSystem;
 | 
			
		||||
const Link = imports.ui.link;
 | 
			
		||||
const Tweener = imports.ui.tweener;
 | 
			
		||||
@@ -26,7 +25,6 @@ var commandHeader = 'const Clutter = imports.gi.Clutter; ' +
 | 
			
		||||
                    'const Mainloop = imports.mainloop; ' +
 | 
			
		||||
                    'const Meta = imports.gi.Meta; ' +
 | 
			
		||||
                    'const Shell = imports.gi.Shell; ' +
 | 
			
		||||
                    'const Tp = imports.gi.TelepathyGLib; ' +
 | 
			
		||||
                    'const Main = imports.ui.main; ' +
 | 
			
		||||
                    'const Lang = imports.lang; ' +
 | 
			
		||||
                    'const Tweener = imports.ui.tweener; ' +
 | 
			
		||||
@@ -38,8 +36,6 @@ var commandHeader = 'const Clutter = imports.gi.Clutter; ' +
 | 
			
		||||
                       'const it = Main.lookingGlass.getIt(); ' +
 | 
			
		||||
                    'const r = Lang.bind(Main.lookingGlass, Main.lookingGlass.getResult); ';
 | 
			
		||||
 | 
			
		||||
const HISTORY_KEY = 'looking-glass-history';
 | 
			
		||||
 | 
			
		||||
function Notebook() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
@@ -99,19 +95,12 @@ Notebook.prototype = {
 | 
			
		||||
    selectIndex: function(index) {
 | 
			
		||||
        if (index == this._selectedIndex)
 | 
			
		||||
            return;
 | 
			
		||||
        this._unselect();
 | 
			
		||||
        if (index < 0) {
 | 
			
		||||
            this._unselect();
 | 
			
		||||
            this.emit('selection', null);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Focus the new tab before unmapping the old one
 | 
			
		||||
        let tabData = this._tabs[index];
 | 
			
		||||
        if (!tabData.scrollView.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false))
 | 
			
		||||
            this.actor.grab_key_focus();
 | 
			
		||||
 | 
			
		||||
        this._unselect();
 | 
			
		||||
 | 
			
		||||
        tabData.labelBox.add_style_pseudo_class('selected');
 | 
			
		||||
        tabData.scrollView.show();
 | 
			
		||||
        this._selectedIndex = index;
 | 
			
		||||
@@ -370,30 +359,6 @@ ObjInspector.prototype = {
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function addBorderPaintHook(actor) {
 | 
			
		||||
    let signalId = actor.connect_after('paint',
 | 
			
		||||
        function () {
 | 
			
		||||
            let color = new Cogl.Color();
 | 
			
		||||
            color.init_from_4ub(0xff, 0, 0, 0xc4);
 | 
			
		||||
            Cogl.set_source_color(color);
 | 
			
		||||
 | 
			
		||||
            let geom = actor.get_allocation_geometry();
 | 
			
		||||
            let width = 2;
 | 
			
		||||
 | 
			
		||||
            // clockwise order
 | 
			
		||||
            Cogl.rectangle(0, 0, geom.width, width);
 | 
			
		||||
            Cogl.rectangle(geom.width - width, width,
 | 
			
		||||
                           geom.width, geom.height);
 | 
			
		||||
            Cogl.rectangle(0, geom.height,
 | 
			
		||||
                           geom.width - width, geom.height - width);
 | 
			
		||||
            Cogl.rectangle(0, geom.height - width,
 | 
			
		||||
                           width, width);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
    actor.queue_redraw();
 | 
			
		||||
    return signalId;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function Inspector() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
@@ -433,10 +398,7 @@ Inspector.prototype = {
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _allocate: function(actor, box, flags) {
 | 
			
		||||
        if (!this._eventHandler)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let primary = Main.layoutManager.primaryMonitor;
 | 
			
		||||
        let primary = global.get_primary_monitor();
 | 
			
		||||
 | 
			
		||||
        let [minWidth, minHeight, natWidth, natHeight] =
 | 
			
		||||
            this._eventHandler.get_preferred_size();
 | 
			
		||||
@@ -453,7 +415,6 @@ Inspector.prototype = {
 | 
			
		||||
        Clutter.ungrab_pointer(this._eventHandler);
 | 
			
		||||
        Clutter.ungrab_keyboard(this._eventHandler);
 | 
			
		||||
        this._eventHandler.destroy();
 | 
			
		||||
        this._eventHandler = null;
 | 
			
		||||
        this.emit('closed');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -529,13 +490,10 @@ Inspector.prototype = {
 | 
			
		||||
        let position = '[inspect x: ' + stageX + ' y: ' + stageY + ']';
 | 
			
		||||
        this._displayText.text = '';
 | 
			
		||||
        this._displayText.text = position + ' ' + this._target;
 | 
			
		||||
 | 
			
		||||
        if (this._borderPaintTarget != this._target) {
 | 
			
		||||
            if (this._borderPaintTarget != null)
 | 
			
		||||
                this._borderPaintTarget.disconnect(this._borderPaintId);
 | 
			
		||||
            this._borderPaintTarget = this._target;
 | 
			
		||||
            this._borderPaintId = addBorderPaintHook(this._target);
 | 
			
		||||
        }
 | 
			
		||||
        if (this._borderPaintTarget != null)
 | 
			
		||||
            this._borderPaintTarget.disconnect(this._borderPaintId);
 | 
			
		||||
        this._borderPaintTarget = this._target;
 | 
			
		||||
        this._borderPaintId = Shell.add_hook_paint_red_border(this._target);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -582,53 +540,6 @@ ErrorLog.prototype = {
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function Memory() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Memory.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.actor = new St.BoxLayout({ vertical: true });
 | 
			
		||||
        this._glibc_uordblks = new St.Label();
 | 
			
		||||
        this.actor.add(this._glibc_uordblks);
 | 
			
		||||
 | 
			
		||||
        this._js_bytes = new St.Label();
 | 
			
		||||
        this.actor.add(this._js_bytes);
 | 
			
		||||
 | 
			
		||||
        this._gjs_boxed = new St.Label();
 | 
			
		||||
        this.actor.add(this._gjs_boxed);
 | 
			
		||||
 | 
			
		||||
        this._gjs_gobject = new St.Label();
 | 
			
		||||
        this.actor.add(this._gjs_gobject);
 | 
			
		||||
 | 
			
		||||
        this._gjs_function = new St.Label();
 | 
			
		||||
        this.actor.add(this._gjs_function);
 | 
			
		||||
 | 
			
		||||
        this._gjs_closure = new St.Label();
 | 
			
		||||
        this.actor.add(this._gjs_closure);
 | 
			
		||||
 | 
			
		||||
        this._gcbutton = new St.Button({ label: 'Full GC',
 | 
			
		||||
                                         style_class: 'lg-obj-inspector-button' });
 | 
			
		||||
        this._gcbutton.connect('clicked', Lang.bind(this, function () { global.gc(); this._renderText(); }));
 | 
			
		||||
        this.actor.add(this._gcbutton, { x_align: St.Align.START,
 | 
			
		||||
                                         x_fill: false });
 | 
			
		||||
 | 
			
		||||
        this.actor.connect('notify::mapped', Lang.bind(this, this._renderText));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _renderText: function() {
 | 
			
		||||
        if (!this.actor.mapped)
 | 
			
		||||
            return;
 | 
			
		||||
        let memInfo = global.get_memory_info();
 | 
			
		||||
        this._glibc_uordblks.text = 'glibc_uordblks: ' + memInfo.glibc_uordblks;
 | 
			
		||||
        this._js_bytes.text = 'js bytes: ' + memInfo.js_bytes;
 | 
			
		||||
        this._gjs_boxed.text = 'gjs_boxed: ' + memInfo.gjs_boxed;
 | 
			
		||||
        this._gjs_gobject.text = 'gjs_gobject: ' + memInfo.gjs_gobject;
 | 
			
		||||
        this._gjs_function.text = 'gjs_function: ' + memInfo.gjs_function;
 | 
			
		||||
        this._gjs_closure.text = 'gjs_closure: ' + memInfo.gjs_closure;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function Extensions() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
@@ -728,10 +639,18 @@ function LookingGlass() {
 | 
			
		||||
 | 
			
		||||
LookingGlass.prototype = {
 | 
			
		||||
    _init : function() {
 | 
			
		||||
        this._idleHistorySaveId = 0;
 | 
			
		||||
        let historyPath = global.userdatadir + '/lookingglass-history.txt';
 | 
			
		||||
        this._historyFile = Gio.file_new_for_path(historyPath);
 | 
			
		||||
        this._savedText = null;
 | 
			
		||||
        this._historyNavIndex = -1;
 | 
			
		||||
        this._history = [];
 | 
			
		||||
        this._borderPaintTarget = null;
 | 
			
		||||
        this._borderPaintId = 0;
 | 
			
		||||
        this._borderDestroyId = 0;
 | 
			
		||||
 | 
			
		||||
        this._readHistory();
 | 
			
		||||
 | 
			
		||||
        this._open = false;
 | 
			
		||||
 | 
			
		||||
        this._offset = 0;
 | 
			
		||||
@@ -744,11 +663,11 @@ LookingGlass.prototype = {
 | 
			
		||||
                                        style_class: 'lg-dialog',
 | 
			
		||||
                                        vertical: true,
 | 
			
		||||
                                        visible: false });
 | 
			
		||||
        this.actor.connect('key-press-event', Lang.bind(this, this._globalKeyPressEvent));
 | 
			
		||||
 | 
			
		||||
        this._interfaceSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
 | 
			
		||||
        this._interfaceSettings.connect('changed::monospace-font-name',
 | 
			
		||||
                                        Lang.bind(this, this._updateFont));
 | 
			
		||||
        let gconf = GConf.Client.get_default();
 | 
			
		||||
        gconf.add_dir('/desktop/gnome/interface', GConf.ClientPreloadType.PRELOAD_NONE);
 | 
			
		||||
        gconf.notify_add('/desktop/gnome/interface/monospace_font_name',
 | 
			
		||||
                         Lang.bind(this, this._updateFont));
 | 
			
		||||
        this._updateFont();
 | 
			
		||||
 | 
			
		||||
        Main.uiGroup.add_actor(this.actor);
 | 
			
		||||
@@ -760,7 +679,6 @@ LookingGlass.prototype = {
 | 
			
		||||
        let toolbar = new St.BoxLayout({ name: 'Toolbar' });
 | 
			
		||||
        this.actor.add_actor(toolbar);
 | 
			
		||||
        let inspectIcon = new St.Icon({ icon_name: 'gtk-color-picker',
 | 
			
		||||
                                        icon_type: St.IconType.FULLCOLOR,
 | 
			
		||||
                                        icon_size: 24 });
 | 
			
		||||
        toolbar.add_actor(inspectIcon);
 | 
			
		||||
        inspectIcon.reactive = true;
 | 
			
		||||
@@ -798,7 +716,12 @@ LookingGlass.prototype = {
 | 
			
		||||
        let label = new St.Label({ text: 'js>>> ' });
 | 
			
		||||
        entryArea.add(label);
 | 
			
		||||
 | 
			
		||||
        this._entry = new St.Entry({ can_focus: true });
 | 
			
		||||
        this._entry = new St.Entry();
 | 
			
		||||
        /* unmapping the edit box will un-focus it, undo that */
 | 
			
		||||
        notebook.connect('selection', Lang.bind(this, function (nb, child) {
 | 
			
		||||
            if (child == this._evalBox)
 | 
			
		||||
                global.stage.set_key_focus(this._entry);
 | 
			
		||||
        }));
 | 
			
		||||
        entryArea.add(this._entry, { expand: true });
 | 
			
		||||
 | 
			
		||||
        this._windowList = new WindowList();
 | 
			
		||||
@@ -811,9 +734,6 @@ LookingGlass.prototype = {
 | 
			
		||||
        this._errorLog = new ErrorLog();
 | 
			
		||||
        notebook.appendPage('Errors', this._errorLog.actor);
 | 
			
		||||
 | 
			
		||||
        this._memory = new Memory();
 | 
			
		||||
        notebook.appendPage('Memory', this._memory.actor);
 | 
			
		||||
 | 
			
		||||
        this._extensions = new Extensions();
 | 
			
		||||
        notebook.appendPage('Extensions', this._extensions.actor);
 | 
			
		||||
 | 
			
		||||
@@ -827,15 +747,39 @@ LookingGlass.prototype = {
 | 
			
		||||
            if (text == '')
 | 
			
		||||
                return true;
 | 
			
		||||
            this._evaluate(text);
 | 
			
		||||
            this._historyNavIndex = -1;
 | 
			
		||||
            return true;
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        this._history = new History.HistoryManager({ gsettingsKey: HISTORY_KEY, 
 | 
			
		||||
                                                     entry: this._entry.clutter_text });
 | 
			
		||||
        this._entry.clutter_text.connect('key-press-event', Lang.bind(this, function(o, e) {
 | 
			
		||||
            let symbol = e.get_key_symbol();
 | 
			
		||||
            if (symbol == Clutter.Up) {
 | 
			
		||||
                if (this._historyNavIndex >= this._history.length - 1)
 | 
			
		||||
                    return true;
 | 
			
		||||
                this._historyNavIndex++;
 | 
			
		||||
                if (this._historyNavIndex == 0)
 | 
			
		||||
                    this._savedText = this._entry.text;
 | 
			
		||||
                this._entry.text = this._history[this._history.length - this._historyNavIndex - 1];
 | 
			
		||||
                return true;
 | 
			
		||||
            } else if (symbol == Clutter.Down) {
 | 
			
		||||
                if (this._historyNavIndex <= 0)
 | 
			
		||||
                    return true;
 | 
			
		||||
                this._historyNavIndex--;
 | 
			
		||||
                if (this._historyNavIndex < 0)
 | 
			
		||||
                    this._entry.text = this._savedText;
 | 
			
		||||
                else
 | 
			
		||||
                    this._entry.text = this._history[this._history.length - this._historyNavIndex - 1];
 | 
			
		||||
                return true;
 | 
			
		||||
            } else {
 | 
			
		||||
                this._historyNavIndex = -1;
 | 
			
		||||
                this._savedText = null;
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateFont: function() {
 | 
			
		||||
        let fontName = this._interfaceSettings.get_string('monospace-font-name');
 | 
			
		||||
        let gconf = GConf.Client.get_default();
 | 
			
		||||
        let fontName = gconf.get_string('/desktop/gnome/interface/monospace_font_name');
 | 
			
		||||
        // 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);
 | 
			
		||||
@@ -846,6 +790,29 @@ LookingGlass.prototype = {
 | 
			
		||||
            + 'font-family: "' + fontDesc.get_family() + '";';
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _readHistory: function () {
 | 
			
		||||
        if (!this._historyFile.query_exists(null))
 | 
			
		||||
            return;
 | 
			
		||||
        let [result, contents, length, etag] = this._historyFile.load_contents(null);
 | 
			
		||||
        this._history = contents.split('\n').filter(function (e) { return e != ''; });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _queueHistorySave: function() {
 | 
			
		||||
        if (this._idleHistorySaveId > 0)
 | 
			
		||||
            return;
 | 
			
		||||
        this._idleHistorySaveId = Mainloop.timeout_add_seconds(5, Lang.bind(this, this._doSaveHistory));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _doSaveHistory: function () {
 | 
			
		||||
        this._idleHistorySaveId = false;
 | 
			
		||||
        let output = this._historyFile.replace(null, true, Gio.FileCreateFlags.NONE, null);
 | 
			
		||||
        let dataOut = new Gio.DataOutputStream({ base_stream: output });
 | 
			
		||||
        dataOut.put_string(this._history.join('\n'), null);
 | 
			
		||||
        dataOut.put_string('\n', null);
 | 
			
		||||
        dataOut.close(null);
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _pushResult: function(command, obj) {
 | 
			
		||||
        let index = this._results.length + this._offset;
 | 
			
		||||
        let result = new Result('>>> ' + command, obj, index);
 | 
			
		||||
@@ -857,7 +824,7 @@ LookingGlass.prototype = {
 | 
			
		||||
        }
 | 
			
		||||
        if (obj instanceof Clutter.Actor) {
 | 
			
		||||
            this._borderPaintTarget = obj;
 | 
			
		||||
            this._borderPaintId = addBorderPaintHook(obj);
 | 
			
		||||
            this._borderPaintId = Shell.add_hook_paint_red_border(obj);
 | 
			
		||||
            this._borderDestroyId = obj.connect('destroy', Lang.bind(this, function () {
 | 
			
		||||
                this._borderDestroyId = 0;
 | 
			
		||||
                this._borderPaintTarget = null;
 | 
			
		||||
@@ -876,7 +843,8 @@ LookingGlass.prototype = {
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _evaluate : function(command) {
 | 
			
		||||
        this._history.addItem(command);
 | 
			
		||||
        this._history.push(command);
 | 
			
		||||
        this._queueHistorySave();
 | 
			
		||||
 | 
			
		||||
        let fullCmd = commandHeader + command;
 | 
			
		||||
 | 
			
		||||
@@ -907,7 +875,7 @@ LookingGlass.prototype = {
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _resizeTo: function(actor) {
 | 
			
		||||
        let primary = Main.layoutManager.primaryMonitor;
 | 
			
		||||
        let primary = global.get_primary_monitor();
 | 
			
		||||
        let myWidth = primary.width * 0.7;
 | 
			
		||||
        let myHeight = primary.height * 0.7;
 | 
			
		||||
        let [srcX, srcY] = actor.get_transformed_position();
 | 
			
		||||
@@ -957,17 +925,20 @@ LookingGlass.prototype = {
 | 
			
		||||
        if (this._open)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (!Main.pushModal(this._entry))
 | 
			
		||||
        if (!Main.pushModal(this.actor))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._notebook.selectIndex(0);
 | 
			
		||||
        this._keyPressEventId = global.stage.connect('key-press-event',
 | 
			
		||||
            Lang.bind(this, this._globalKeyPressEvent));
 | 
			
		||||
 | 
			
		||||
        this.actor.show();
 | 
			
		||||
        this.actor.lower(Main.chrome.actor);
 | 
			
		||||
        this._open = true;
 | 
			
		||||
        this._history.lastItem();
 | 
			
		||||
 | 
			
		||||
        Tweener.removeTweens(this.actor);
 | 
			
		||||
 | 
			
		||||
        global.stage.set_key_focus(this._entry);
 | 
			
		||||
 | 
			
		||||
        // We inverse compensate for the slow-down so you can change the factor
 | 
			
		||||
        // through LookingGlass without long waits.
 | 
			
		||||
        Tweener.addTween(this.actor, { time: 0.5 / St.get_slow_down_factor(),
 | 
			
		||||
@@ -980,8 +951,12 @@ LookingGlass.prototype = {
 | 
			
		||||
        if (!this._open)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (this._keyPressEventId)
 | 
			
		||||
            global.stage.disconnect(this._keyPressEventId);
 | 
			
		||||
 | 
			
		||||
        this._objInspector.actor.hide();
 | 
			
		||||
 | 
			
		||||
        this._historyNavIndex = -1;
 | 
			
		||||
        this._open = false;
 | 
			
		||||
        Tweener.removeTweens(this.actor);
 | 
			
		||||
 | 
			
		||||
@@ -991,7 +966,7 @@ LookingGlass.prototype = {
 | 
			
		||||
            this._borderPaintTarget = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Main.popModal(this._entry);
 | 
			
		||||
        Main.popModal(this.actor);
 | 
			
		||||
 | 
			
		||||
        Tweener.addTween(this.actor, { time: 0.5 / St.get_slow_down_factor(),
 | 
			
		||||
                                       transition: 'easeOutQuad',
 | 
			
		||||
 
 | 
			
		||||
@@ -112,11 +112,6 @@ ShellMagnifier.prototype = {
 | 
			
		||||
     *                  [left, top, right, bottom].
 | 
			
		||||
     * @viewPort        Array of integers, [left, top, right, bottom] that defines
 | 
			
		||||
     *                  the position of the ZoomRegion on screen.
 | 
			
		||||
     *
 | 
			
		||||
     * FIXME: The arguments here are redundant, since the width and height of
 | 
			
		||||
     *   the ROI are determined by the viewport and magnification factors.
 | 
			
		||||
     *   We ignore the passed in width and height.
 | 
			
		||||
     *
 | 
			
		||||
     * @return          The newly created ZoomRegion.
 | 
			
		||||
     */
 | 
			
		||||
    createZoomRegion: function(xMagFactor, yMagFactor, roi, viewPort) {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										526
									
								
								js/ui/main.js
									
									
									
									
									
								
							
							
						
						@@ -1,5 +1,11 @@
 | 
			
		||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
 | 
			
		||||
 | 
			
		||||
imports.gi.versions.Clutter = '1.0';
 | 
			
		||||
imports.gi.versions.Gio = '2.0';
 | 
			
		||||
imports.gi.versions.Gdk = '3.0';
 | 
			
		||||
imports.gi.versions.GdkPixbuf = '2.0';
 | 
			
		||||
imports.gi.versions.Gtk = '3.0';
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const DBus = imports.dbus;
 | 
			
		||||
const Gdk = imports.gi.Gdk;
 | 
			
		||||
@@ -12,12 +18,7 @@ const Meta = imports.gi.Meta;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
const AutomountManager = imports.ui.automountManager;
 | 
			
		||||
const AutorunManager = imports.ui.autorunManager;
 | 
			
		||||
const Chrome = imports.ui.chrome;
 | 
			
		||||
const CtrlAltTab = imports.ui.ctrlAltTab;
 | 
			
		||||
const EndSessionDialog = imports.ui.endSessionDialog;
 | 
			
		||||
const PolkitAuthenticationAgent = imports.ui.polkitAuthenticationAgent;
 | 
			
		||||
const Environment = imports.ui.environment;
 | 
			
		||||
const ExtensionSystem = imports.ui.extensionSystem;
 | 
			
		||||
const MessageTray = imports.ui.messageTray;
 | 
			
		||||
@@ -25,7 +26,6 @@ const Overview = imports.ui.overview;
 | 
			
		||||
const Panel = imports.ui.panel;
 | 
			
		||||
const PlaceDisplay = imports.ui.placeDisplay;
 | 
			
		||||
const RunDialog = imports.ui.runDialog;
 | 
			
		||||
const Layout = imports.ui.layout;
 | 
			
		||||
const LookingGlass = imports.ui.lookingGlass;
 | 
			
		||||
const NotificationDaemon = imports.ui.notificationDaemon;
 | 
			
		||||
const WindowAttentionHandler = imports.ui.windowAttentionHandler;
 | 
			
		||||
@@ -34,18 +34,13 @@ const ShellDBus = imports.ui.shellDBus;
 | 
			
		||||
const TelepathyClient = imports.ui.telepathyClient;
 | 
			
		||||
const WindowManager = imports.ui.windowManager;
 | 
			
		||||
const Magnifier = imports.ui.magnifier;
 | 
			
		||||
const XdndHandler = imports.ui.xdndHandler;
 | 
			
		||||
const StatusIconDispatcher = imports.ui.statusIconDispatcher;
 | 
			
		||||
const Util = imports.misc.util;
 | 
			
		||||
 | 
			
		||||
const DEFAULT_BACKGROUND_COLOR = new Clutter.Color();
 | 
			
		||||
DEFAULT_BACKGROUND_COLOR.from_pixel(0x2266bbff);
 | 
			
		||||
 | 
			
		||||
let automountManager = null;
 | 
			
		||||
let autorunManager = null;
 | 
			
		||||
let chrome = null;
 | 
			
		||||
let panel = null;
 | 
			
		||||
let hotCorners = [];
 | 
			
		||||
let placesManager = null;
 | 
			
		||||
let overview = null;
 | 
			
		||||
let runDialog = null;
 | 
			
		||||
@@ -55,35 +50,33 @@ let messageTray = null;
 | 
			
		||||
let notificationDaemon = null;
 | 
			
		||||
let windowAttentionHandler = null;
 | 
			
		||||
let telepathyClient = null;
 | 
			
		||||
let ctrlAltTabManager = null;
 | 
			
		||||
let recorder = null;
 | 
			
		||||
let shellDBusService = null;
 | 
			
		||||
let modalCount = 0;
 | 
			
		||||
let modalActorFocusStack = [];
 | 
			
		||||
let uiGroup = null;
 | 
			
		||||
let magnifier = null;
 | 
			
		||||
let xdndHandler = null;
 | 
			
		||||
let statusIconDispatcher = null;
 | 
			
		||||
let layoutManager = null;
 | 
			
		||||
let _errorLogStack = [];
 | 
			
		||||
let _startDate;
 | 
			
		||||
let _defaultCssStylesheet = null;
 | 
			
		||||
let _cssStylesheet = null;
 | 
			
		||||
 | 
			
		||||
let background = null;
 | 
			
		||||
 | 
			
		||||
function start() {
 | 
			
		||||
    // Monkey patch utility functions into the global proxy;
 | 
			
		||||
    // Add a binding for 'global' in the global JS namespace; (gjs
 | 
			
		||||
    // keeps the web browser convention of having that namespace be
 | 
			
		||||
    // called 'window'.)
 | 
			
		||||
    window.global = Shell.Global.get();
 | 
			
		||||
 | 
			
		||||
    // Now monkey patch utility functions into the global proxy;
 | 
			
		||||
    // This is easier and faster than indirecting down into global
 | 
			
		||||
    // if we want to call back up into JS.
 | 
			
		||||
    global.logError = _logError;
 | 
			
		||||
    global.log = _logDebug;
 | 
			
		||||
 | 
			
		||||
    // Chain up async errors reported from C
 | 
			
		||||
    global.connect('notify-error', function (global, msg, detail) { notifyError(msg, detail); });
 | 
			
		||||
 | 
			
		||||
    Gio.DesktopAppInfo.set_desktop_env('GNOME');
 | 
			
		||||
 | 
			
		||||
    global.grab_dbus_service();
 | 
			
		||||
    shellDBusService = new ShellDBus.GnomeShell();
 | 
			
		||||
    // Force a connection now; dbus.js will do this internally
 | 
			
		||||
    // if we use its name acquisition stuff but we aren't right
 | 
			
		||||
@@ -91,9 +84,7 @@ function start() {
 | 
			
		||||
    // back into sync ones.
 | 
			
		||||
    DBus.session.flush();
 | 
			
		||||
 | 
			
		||||
    // Load the calendar server. Note that we are careful about
 | 
			
		||||
    // not loading any events until the user presses the clock
 | 
			
		||||
    global.launch_calendar_server();
 | 
			
		||||
    Environment.init();
 | 
			
		||||
 | 
			
		||||
    // Ensure ShellWindowTracker and ShellAppUsage are initialized; this will
 | 
			
		||||
    // also initialize ShellAppSystem first.  ShellAppSystem
 | 
			
		||||
@@ -112,8 +103,10 @@ function start() {
 | 
			
		||||
    global.stage.color = DEFAULT_BACKGROUND_COLOR;
 | 
			
		||||
    global.stage.no_clear_hint = true;
 | 
			
		||||
 | 
			
		||||
    _defaultCssStylesheet = global.datadir + '/theme/gnome-shell.css';
 | 
			
		||||
    loadTheme();
 | 
			
		||||
    let themeContext = St.ThemeContext.get_for_stage (global.stage);
 | 
			
		||||
    let stylesheetPath = global.datadir + '/theme/gnome-shell.css';
 | 
			
		||||
    let theme = new St.Theme ({ application_stylesheet: stylesheetPath });
 | 
			
		||||
    themeContext.set_theme (theme);
 | 
			
		||||
 | 
			
		||||
    let shellwm = global.window_manager;
 | 
			
		||||
    shellwm.takeover_keybinding('panel_main_menu');
 | 
			
		||||
@@ -127,15 +120,11 @@ function start() {
 | 
			
		||||
 | 
			
		||||
    // Set up stage hierarchy to group all UI actors under one container.
 | 
			
		||||
    uiGroup = new Clutter.Group();
 | 
			
		||||
    St.set_ui_root(global.stage, uiGroup);
 | 
			
		||||
    global.window_group.reparent(uiGroup);
 | 
			
		||||
    global.overlay_group.reparent(uiGroup);
 | 
			
		||||
    global.stage.add_actor(uiGroup);
 | 
			
		||||
 | 
			
		||||
    layoutManager = new Layout.LayoutManager();
 | 
			
		||||
    placesManager = new PlaceDisplay.PlacesManager();
 | 
			
		||||
    xdndHandler = new XdndHandler.XdndHandler();
 | 
			
		||||
    ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
 | 
			
		||||
    overview = new Overview.Overview();
 | 
			
		||||
    chrome = new Chrome.Chrome();
 | 
			
		||||
    magnifier = new Magnifier.Magnifier();
 | 
			
		||||
@@ -146,12 +135,6 @@ function start() {
 | 
			
		||||
    notificationDaemon = new NotificationDaemon.NotificationDaemon();
 | 
			
		||||
    windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();
 | 
			
		||||
    telepathyClient = new TelepathyClient.Client();
 | 
			
		||||
    automountManager = new AutomountManager.AutomountManager();
 | 
			
		||||
    autorunManager = new AutorunManager.AutorunManager();
 | 
			
		||||
 | 
			
		||||
    layoutManager.init();
 | 
			
		||||
    overview.init();
 | 
			
		||||
    statusIconDispatcher.start(messageTray.actor);
 | 
			
		||||
 | 
			
		||||
    _startDate = new Date();
 | 
			
		||||
 | 
			
		||||
@@ -179,19 +162,11 @@ function start() {
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT, false, -1, 1);
 | 
			
		||||
 | 
			
		||||
    // Provide the bus object for gnome-session to
 | 
			
		||||
    // initiate logouts.
 | 
			
		||||
    EndSessionDialog.init();
 | 
			
		||||
 | 
			
		||||
    // Attempt to become a PolicyKit authentication agent
 | 
			
		||||
    PolkitAuthenticationAgent.init()
 | 
			
		||||
    global.gdk_screen.connect('monitors-changed', _relayout);
 | 
			
		||||
 | 
			
		||||
    ExtensionSystem.init();
 | 
			
		||||
    ExtensionSystem.loadExtensions();
 | 
			
		||||
 | 
			
		||||
    panel.startStatusArea();
 | 
			
		||||
    panel.startupAnimation();
 | 
			
		||||
 | 
			
		||||
    let display = global.screen.get_display();
 | 
			
		||||
@@ -199,250 +174,20 @@ function start() {
 | 
			
		||||
 | 
			
		||||
    global.stage.connect('captured-event', _globalKeyPressHandler);
 | 
			
		||||
 | 
			
		||||
    // Perform initial relayout here
 | 
			
		||||
    _relayout();
 | 
			
		||||
 | 
			
		||||
    _log('info', 'loaded at ' + _startDate);
 | 
			
		||||
    log('GNOME Shell started at ' + _startDate);
 | 
			
		||||
 | 
			
		||||
    Mainloop.idle_add(_removeUnusedWorkspaces);
 | 
			
		||||
 | 
			
		||||
    let perfModuleName = GLib.getenv("SHELL_PERF_MODULE");
 | 
			
		||||
    if (perfModuleName) {
 | 
			
		||||
        let perfOutput = GLib.getenv("SHELL_PERF_OUTPUT");
 | 
			
		||||
        let module = eval('imports.perf.' + perfModuleName + ';');
 | 
			
		||||
        Scripting.runPerfScript(module, perfOutput);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    global.screen.connect('notify::n-workspaces', _nWorkspacesChanged);
 | 
			
		||||
 | 
			
		||||
    global.screen.connect('window-entered-monitor', _windowEnteredMonitor);
 | 
			
		||||
    global.screen.connect('window-left-monitor', _windowLeftMonitor);
 | 
			
		||||
    global.screen.connect('restacked', _windowsRestacked);
 | 
			
		||||
 | 
			
		||||
    _nWorkspacesChanged();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
let _workspaces = [];
 | 
			
		||||
let _checkWorkspacesId = 0;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * When the last window closed on a workspace is a dialog or splash
 | 
			
		||||
 * screen, we assume that it might be an initial window shown before
 | 
			
		||||
 * the main window of an application, and give the app a grace period
 | 
			
		||||
 * where it can map another window before we remove the workspace.
 | 
			
		||||
 */
 | 
			
		||||
const LAST_WINDOW_GRACE_TIME = 1000;
 | 
			
		||||
 | 
			
		||||
function _checkWorkspaces() {
 | 
			
		||||
    let i;
 | 
			
		||||
    let emptyWorkspaces = [];
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < _workspaces.length; i++) {
 | 
			
		||||
        let lastRemoved = _workspaces[i]._lastRemovedWindow;
 | 
			
		||||
        if (lastRemoved &&
 | 
			
		||||
            (lastRemoved.get_window_type() == Meta.WindowType.SPLASHSCREEN ||
 | 
			
		||||
             lastRemoved.get_window_type() == Meta.WindowType.DIALOG ||
 | 
			
		||||
             lastRemoved.get_window_type() == Meta.WindowType.MODAL_DIALOG))
 | 
			
		||||
                emptyWorkspaces[i] = false;
 | 
			
		||||
        else
 | 
			
		||||
            emptyWorkspaces[i] = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let windows = global.get_window_actors();
 | 
			
		||||
    for (i = 0; i < windows.length; i++) {
 | 
			
		||||
        let win = windows[i];
 | 
			
		||||
 | 
			
		||||
        if (win.get_meta_window().is_on_all_workspaces())
 | 
			
		||||
            continue;
 | 
			
		||||
 | 
			
		||||
        let workspaceIndex = win.get_workspace();
 | 
			
		||||
        emptyWorkspaces[workspaceIndex] = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // If we don't have an empty workspace at the end, add one
 | 
			
		||||
    if (!emptyWorkspaces[emptyWorkspaces.length -1]) {
 | 
			
		||||
        global.screen.append_new_workspace(false, global.get_current_time());
 | 
			
		||||
        emptyWorkspaces.push(false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let activeWorkspaceIndex = global.screen.get_active_workspace_index();
 | 
			
		||||
    let removingCurrentWorkspace = (emptyWorkspaces[activeWorkspaceIndex] &&
 | 
			
		||||
                                    activeWorkspaceIndex < emptyWorkspaces.length - 1);
 | 
			
		||||
    // Don't enter the overview when removing multiple empty workspaces at startup
 | 
			
		||||
    let showOverview  = (removingCurrentWorkspace &&
 | 
			
		||||
                         !emptyWorkspaces.every(function(x) { return x; }));
 | 
			
		||||
 | 
			
		||||
    if (removingCurrentWorkspace) {
 | 
			
		||||
        // "Merge" the empty workspace we are removing with the one at the end
 | 
			
		||||
        wm.blockAnimations();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Delete other empty workspaces; do it from the end to avoid index changes
 | 
			
		||||
    for (i = emptyWorkspaces.length - 2; i >= 0; i--) {
 | 
			
		||||
        if (emptyWorkspaces[i])
 | 
			
		||||
            global.screen.remove_workspace(_workspaces[i], global.get_current_time());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (removingCurrentWorkspace) {
 | 
			
		||||
        global.screen.get_workspace_by_index(global.screen.n_workspaces - 1).activate(global.get_current_time());
 | 
			
		||||
        wm.unblockAnimations();
 | 
			
		||||
 | 
			
		||||
        if (!overview.visible && showOverview)
 | 
			
		||||
            overview.show();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _checkWorkspacesId = 0;
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _windowRemoved(workspace, window) {
 | 
			
		||||
    workspace._lastRemovedWindow = window;
 | 
			
		||||
    _queueCheckWorkspaces();
 | 
			
		||||
    Mainloop.timeout_add(LAST_WINDOW_GRACE_TIME, function() {
 | 
			
		||||
        if (workspace._lastRemovedWindow == window) {
 | 
			
		||||
            workspace._lastRemovedWindow = null;
 | 
			
		||||
            _queueCheckWorkspaces();
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _windowLeftMonitor(metaScreen, monitorIndex, metaWin) {
 | 
			
		||||
    // If the window left the primary monitor, that
 | 
			
		||||
    // might make that workspace empty
 | 
			
		||||
    if (monitorIndex == layoutManager.primaryIndex)
 | 
			
		||||
        _queueCheckWorkspaces();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _windowEnteredMonitor(metaScreen, monitorIndex, metaWin) {
 | 
			
		||||
    // If the window entered the primary monitor, that
 | 
			
		||||
    // might make that workspace non-empty
 | 
			
		||||
    if (monitorIndex == layoutManager.primaryIndex)
 | 
			
		||||
        _queueCheckWorkspaces();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _windowsRestacked() {
 | 
			
		||||
    // Figure out where the pointer is in case we lost track of
 | 
			
		||||
    // it during a grab. (In particular, if a trayicon popup menu
 | 
			
		||||
    // is dismissed, see if we need to close the message tray.)
 | 
			
		||||
    global.sync_pointer();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _queueCheckWorkspaces() {
 | 
			
		||||
    if (_checkWorkspacesId == 0)
 | 
			
		||||
        _checkWorkspacesId = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, _checkWorkspaces);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _nWorkspacesChanged() {
 | 
			
		||||
    let oldNumWorkspaces = _workspaces.length;
 | 
			
		||||
    let newNumWorkspaces = global.screen.n_workspaces;
 | 
			
		||||
 | 
			
		||||
    if (oldNumWorkspaces == newNumWorkspaces)
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
    let lostWorkspaces = [];
 | 
			
		||||
    if (newNumWorkspaces > oldNumWorkspaces) {
 | 
			
		||||
        let w;
 | 
			
		||||
 | 
			
		||||
        // Assume workspaces are only added at the end
 | 
			
		||||
        for (w = oldNumWorkspaces; w < newNumWorkspaces; w++)
 | 
			
		||||
            _workspaces[w] = global.screen.get_workspace_by_index(w);
 | 
			
		||||
 | 
			
		||||
        for (w = oldNumWorkspaces; w < newNumWorkspaces; w++) {
 | 
			
		||||
            let workspace = _workspaces[w];
 | 
			
		||||
            workspace._windowAddedId = workspace.connect('window-added', _queueCheckWorkspaces);
 | 
			
		||||
            workspace._windowRemovedId = workspace.connect('window-removed', _windowRemoved);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    } else {
 | 
			
		||||
        // Assume workspaces are only removed sequentially
 | 
			
		||||
        // (e.g. 2,3,4 - not 2,4,7)
 | 
			
		||||
        let removedIndex;
 | 
			
		||||
        let removedNum = oldNumWorkspaces - newNumWorkspaces;
 | 
			
		||||
        for (let w = 0; w < oldNumWorkspaces; w++) {
 | 
			
		||||
            let workspace = global.screen.get_workspace_by_index(w);
 | 
			
		||||
            if (_workspaces[w] != workspace) {
 | 
			
		||||
                removedIndex = w;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let lostWorkspaces = _workspaces.splice(removedIndex, removedNum);
 | 
			
		||||
        lostWorkspaces.forEach(function(workspace) {
 | 
			
		||||
                                   workspace.disconnect(workspace._windowAddedId);
 | 
			
		||||
                                   workspace.disconnect(workspace._windowRemovedId);
 | 
			
		||||
                               });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    _queueCheckWorkspaces();
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * getThemeStylesheet:
 | 
			
		||||
 *
 | 
			
		||||
 * Get the theme CSS file that the shell will load
 | 
			
		||||
 *
 | 
			
		||||
 * Returns: A file path that contains the theme CSS,
 | 
			
		||||
 *          null if using the default
 | 
			
		||||
 */
 | 
			
		||||
function getThemeStylesheet()
 | 
			
		||||
{
 | 
			
		||||
    return _cssStylesheet;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * setThemeStylesheet:
 | 
			
		||||
 * @cssStylesheet: A file path that contains the theme CSS,
 | 
			
		||||
 *                  set it to null to use the default
 | 
			
		||||
 *
 | 
			
		||||
 * Set the theme CSS file that the shell will load
 | 
			
		||||
 */
 | 
			
		||||
function setThemeStylesheet(cssStylesheet)
 | 
			
		||||
{
 | 
			
		||||
    _cssStylesheet = cssStylesheet;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * loadTheme:
 | 
			
		||||
 *
 | 
			
		||||
 * Reloads the theme CSS file
 | 
			
		||||
 */
 | 
			
		||||
function loadTheme() {
 | 
			
		||||
    let themeContext = St.ThemeContext.get_for_stage (global.stage);
 | 
			
		||||
    let previousTheme = themeContext.get_theme();
 | 
			
		||||
 | 
			
		||||
    let cssStylesheet = _defaultCssStylesheet;
 | 
			
		||||
    if (_cssStylesheet != null)
 | 
			
		||||
        cssStylesheet = _cssStylesheet;
 | 
			
		||||
 | 
			
		||||
    let theme = new St.Theme ({ application_stylesheet: cssStylesheet });
 | 
			
		||||
 | 
			
		||||
    if (previousTheme) {
 | 
			
		||||
        let customStylesheets = previousTheme.get_custom_stylesheets();
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < customStylesheets.length; i++)
 | 
			
		||||
            theme.load_stylesheet(customStylesheets[i]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    themeContext.set_theme (theme);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * notifyError:
 | 
			
		||||
 * @msg: An error message
 | 
			
		||||
 * @details: Additional information
 | 
			
		||||
 *
 | 
			
		||||
 * See shell_global_notify_problem().
 | 
			
		||||
 */
 | 
			
		||||
function notifyError(msg, details) {
 | 
			
		||||
    // Also print to stderr so it's logged somewhere
 | 
			
		||||
    if (details)
 | 
			
		||||
        log("error: " + msg + ": " + details);
 | 
			
		||||
    else
 | 
			
		||||
        log("error: " + msg)
 | 
			
		||||
 | 
			
		||||
    let source = new MessageTray.SystemNotificationSource();
 | 
			
		||||
    messageTray.add(source);
 | 
			
		||||
    let notification = new MessageTray.Notification(source, msg, details);
 | 
			
		||||
    notification.setTransient(true);
 | 
			
		||||
    source.notify(notification);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -486,27 +231,46 @@ function _getAndClearErrorStack() {
 | 
			
		||||
    return errors;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function logStackTrace(msg) {
 | 
			
		||||
    try {
 | 
			
		||||
        throw new Error();
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
        // e.stack must have at least two lines, with the first being
 | 
			
		||||
        // logStackTrace() (which we strip off), and the second being
 | 
			
		||||
        // our caller.
 | 
			
		||||
        let trace = e.stack.substr(e.stack.indexOf('\n') + 1);
 | 
			
		||||
        log(msg ? (msg + '\n' + trace) : trace);
 | 
			
		||||
function _relayout() {
 | 
			
		||||
    let primary = global.get_primary_monitor();
 | 
			
		||||
    panel.actor.set_position(primary.x, primary.y);
 | 
			
		||||
    panel.actor.set_size(primary.width, Panel.PANEL_HEIGHT);
 | 
			
		||||
    overview.relayout();
 | 
			
		||||
 | 
			
		||||
    // To avoid updating the position and size of the workspaces
 | 
			
		||||
    // in the overview, we just hide the overview. The positions
 | 
			
		||||
    // will be updated when it is next shown. We do the same for
 | 
			
		||||
    // the calendar popdown.
 | 
			
		||||
    overview.hide();
 | 
			
		||||
    panel.hideCalendar();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// metacity-clutter currently uses the same prefs as plain metacity,
 | 
			
		||||
// which probably means we'll be starting out with multiple workspaces;
 | 
			
		||||
// remove any unused ones. (We do this from an idle handler, because
 | 
			
		||||
// global.get_window_actors() still returns NULL at the point when start()
 | 
			
		||||
// is called.)
 | 
			
		||||
function _removeUnusedWorkspaces() {
 | 
			
		||||
 | 
			
		||||
    let windows = global.get_window_actors();
 | 
			
		||||
    let maxWorkspace = 0;
 | 
			
		||||
    for (let i = 0; i < windows.length; i++) {
 | 
			
		||||
        let win = windows[i];
 | 
			
		||||
 | 
			
		||||
        if (!win.get_meta_window().is_on_all_workspaces() &&
 | 
			
		||||
            win.get_workspace() > maxWorkspace) {
 | 
			
		||||
            maxWorkspace = win.get_workspace();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    let screen = global.screen;
 | 
			
		||||
    if (screen.n_workspaces > maxWorkspace) {
 | 
			
		||||
        for (let w = screen.n_workspaces - 1; w > maxWorkspace; w--) {
 | 
			
		||||
            let workspace = screen.get_workspace_by_index(w);
 | 
			
		||||
            screen.remove_workspace(workspace, 0);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
    });
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// This function encapsulates hacks to make certain global keybindings
 | 
			
		||||
@@ -514,67 +278,57 @@ function getWindowActorsForWorkspace(workspaceIndex) {
 | 
			
		||||
// are disabled with a global grab. (When there is a global grab, then
 | 
			
		||||
// all key events will be delivered to the stage, so ::captured-event
 | 
			
		||||
// on the stage can be used for global keybindings.)
 | 
			
		||||
//
 | 
			
		||||
// We expect to need to conditionally enable just a few keybindings
 | 
			
		||||
// depending on circumstance; the main hackiness here is that we are
 | 
			
		||||
// assuming that keybindings have their default values; really we
 | 
			
		||||
// should be asking Mutter to resolve the key into an action and then
 | 
			
		||||
// base our handling based on the action.
 | 
			
		||||
function _globalKeyPressHandler(actor, event) {
 | 
			
		||||
    if (modalCount == 0)
 | 
			
		||||
        return false;
 | 
			
		||||
    if (event.type() != Clutter.EventType.KEY_PRESS)
 | 
			
		||||
    if (event.type() != Clutter.EventType.KEY_RELEASE)
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
    let symbol = event.get_key_symbol();
 | 
			
		||||
    let keyCode = event.get_key_code();
 | 
			
		||||
    let modifierState = Shell.get_event_state(event);
 | 
			
		||||
    // Check the overview key first, this isn't a Meta.KeyBindingAction yet
 | 
			
		||||
    if (symbol == Clutter.Super_L || symbol == Clutter.Super_R) {
 | 
			
		||||
        // The super key is the default for triggering the overview, and should
 | 
			
		||||
        // get us out of the overview when we are already in it.
 | 
			
		||||
        if (overview.visible)
 | 
			
		||||
            overview.hide();
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Whitelist some of the Metacity actions
 | 
			
		||||
    let display = global.screen.get_display();
 | 
			
		||||
    let activeWorkspaceIndex = global.screen.get_active_workspace_index();
 | 
			
		||||
 | 
			
		||||
    // This relies on the fact that Clutter.ModifierType is the same as Gdk.ModifierType
 | 
			
		||||
    let action = display.get_keybinding_action(keyCode, modifierState);
 | 
			
		||||
 | 
			
		||||
    // The screenshot action should always be available (even if a
 | 
			
		||||
    // modal dialog is present)
 | 
			
		||||
    if (action == Meta.KeyBindingAction.COMMAND_SCREENSHOT) {
 | 
			
		||||
        let gconf = GConf.Client.get_default();
 | 
			
		||||
        let command = gconf.get_string('/apps/metacity/keybinding_commands/command_screenshot');
 | 
			
		||||
        if (command != null && command != '')
 | 
			
		||||
            Util.spawnCommandLine(command);
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Other bindings are only available when the overview is up and
 | 
			
		||||
    // no modal dialog is present.
 | 
			
		||||
    if (!overview.visible || modalCount > 1)
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
    // This isn't a Meta.KeyBindingAction yet
 | 
			
		||||
    if (symbol == Clutter.Super_L || symbol == Clutter.Super_R) {
 | 
			
		||||
        overview.hide();
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    switch (action) {
 | 
			
		||||
        // left/right would effectively act as synonyms for up/down if we enabled them;
 | 
			
		||||
        // but that could be considered confusing; we also disable them in the main view.
 | 
			
		||||
        //
 | 
			
		||||
        // case Meta.KeyBindingAction.WORKSPACE_LEFT:
 | 
			
		||||
        //     wm.actionMoveWorkspaceLeft();
 | 
			
		||||
        //     return true;
 | 
			
		||||
        // case Meta.KeyBindingAction.WORKSPACE_RIGHT:
 | 
			
		||||
        //     wm.actionMoveWorkspaceRight();
 | 
			
		||||
        //     return true;
 | 
			
		||||
        case Meta.KeyBindingAction.WORKSPACE_UP:
 | 
			
		||||
            wm.actionMoveWorkspaceUp();
 | 
			
		||||
        case Meta.KeyBindingAction.COMMAND_SCREENSHOT:
 | 
			
		||||
            let gconf = GConf.Client.get_default();
 | 
			
		||||
            let command = gconf.get_string('/apps/metacity/keybinding_commands/command_screenshot');
 | 
			
		||||
            if (command != null && command != '') {
 | 
			
		||||
                let [ok, len, args] = GLib.shell_parse_argv(command);
 | 
			
		||||
                let p = new Shell.Process({'args' : args});
 | 
			
		||||
                p.run();
 | 
			
		||||
            }
 | 
			
		||||
            return true;
 | 
			
		||||
        case Meta.KeyBindingAction.WORKSPACE_DOWN:
 | 
			
		||||
            wm.actionMoveWorkspaceDown();
 | 
			
		||||
        case Meta.KeyBindingAction.WORKSPACE_LEFT:
 | 
			
		||||
            wm.actionMoveWorkspaceLeft();
 | 
			
		||||
            return true;
 | 
			
		||||
        case Meta.KeyBindingAction.WORKSPACE_RIGHT:
 | 
			
		||||
            wm.actionMoveWorkspaceRight();
 | 
			
		||||
            return true;
 | 
			
		||||
        case Meta.KeyBindingAction.PANEL_RUN_DIALOG:
 | 
			
		||||
        case Meta.KeyBindingAction.COMMAND_2:
 | 
			
		||||
            getRunDialog().open();
 | 
			
		||||
            return true;
 | 
			
		||||
        case Meta.KeyBindingAction.PANEL_MAIN_MENU:
 | 
			
		||||
            overview.hide();
 | 
			
		||||
            return true;
 | 
			
		||||
        case Meta.KeyBindingAction.SWITCH_PANELS:
 | 
			
		||||
            ctrlAltTabManager.popup(modifierState & Clutter.ModifierType.SHIFT_MASK);
 | 
			
		||||
            return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
@@ -582,8 +336,10 @@ function _globalKeyPressHandler(actor, event) {
 | 
			
		||||
 | 
			
		||||
function _findModal(actor) {
 | 
			
		||||
    for (let i = 0; i < modalActorFocusStack.length; i++) {
 | 
			
		||||
        if (modalActorFocusStack[i].actor == actor)
 | 
			
		||||
        let [stackActor, stackFocus] = modalActorFocusStack[i];
 | 
			
		||||
        if (stackActor == actor) {
 | 
			
		||||
            return i;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
@@ -591,29 +347,21 @@ function _findModal(actor) {
 | 
			
		||||
/**
 | 
			
		||||
 * pushModal:
 | 
			
		||||
 * @actor: #ClutterActor which will be given keyboard focus
 | 
			
		||||
 * @timestamp: optional timestamp
 | 
			
		||||
 *
 | 
			
		||||
 * Ensure we are in a mode where all keyboard and mouse input goes to
 | 
			
		||||
 * the stage, and focus @actor. Multiple calls to this function act in
 | 
			
		||||
 * a stacking fashion; the effect will be undone when an equal number
 | 
			
		||||
 * of popModal() invocations have been made.
 | 
			
		||||
 * the stage.  Multiple calls to this function act in a stacking fashion;
 | 
			
		||||
 * the effect will be undone when an equal number of popModal() invocations
 | 
			
		||||
 * have been made.
 | 
			
		||||
 *
 | 
			
		||||
 * Next, record the current Clutter keyboard focus on a stack. If the
 | 
			
		||||
 * modal stack returns to this actor, reset the focus to the actor
 | 
			
		||||
 * which was focused at the time pushModal() was invoked.
 | 
			
		||||
 *
 | 
			
		||||
 * @timestamp is optionally used to associate the call with a specific user
 | 
			
		||||
 * initiated event.  If not provided then the value of
 | 
			
		||||
 * global.get_current_time() is assumed.
 | 
			
		||||
 * Next, record the current Clutter keyboard focus on a stack.  If the modal stack
 | 
			
		||||
 * returns to this actor, reset the focus to the actor which was focused
 | 
			
		||||
 * at the time pushModal() was invoked.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns: true iff we successfully acquired a grab or already had one
 | 
			
		||||
 */
 | 
			
		||||
function pushModal(actor, timestamp) {
 | 
			
		||||
    if (timestamp == undefined)
 | 
			
		||||
        timestamp = global.get_current_time();
 | 
			
		||||
 | 
			
		||||
function pushModal(actor) {
 | 
			
		||||
    if (modalCount == 0) {
 | 
			
		||||
        if (!global.begin_modal(timestamp)) {
 | 
			
		||||
        if (!global.begin_modal(global.get_current_time())) {
 | 
			
		||||
            log('pushModal: invocation of begin_modal failed');
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
@@ -622,80 +370,52 @@ function pushModal(actor, timestamp) {
 | 
			
		||||
    global.set_stage_input_mode(Shell.StageInputMode.FULLSCREEN);
 | 
			
		||||
 | 
			
		||||
    modalCount += 1;
 | 
			
		||||
    let actorDestroyId = actor.connect('destroy', function() {
 | 
			
		||||
    actor.connect('destroy', function() {
 | 
			
		||||
        let index = _findModal(actor);
 | 
			
		||||
        if (index >= 0)
 | 
			
		||||
            modalActorFocusStack.splice(index, 1);
 | 
			
		||||
    });
 | 
			
		||||
    let curFocus = global.stage.get_key_focus();
 | 
			
		||||
    let curFocusDestroyId;
 | 
			
		||||
    if (curFocus != null) {
 | 
			
		||||
        curFocusDestroyId = curFocus.connect('destroy', function() {
 | 
			
		||||
        curFocus.connect('destroy', function() {
 | 
			
		||||
            let index = _findModal(actor);
 | 
			
		||||
            if (index >= 0)
 | 
			
		||||
                modalActorFocusStack[index].actor = null;
 | 
			
		||||
                modalActorFocusStack[index][1] = null;
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    modalActorFocusStack.push({ actor: actor,
 | 
			
		||||
                                focus: curFocus,
 | 
			
		||||
                                destroyId: actorDestroyId,
 | 
			
		||||
                                focusDestroyId: curFocusDestroyId });
 | 
			
		||||
    modalActorFocusStack.push([actor, curFocus]);
 | 
			
		||||
 | 
			
		||||
    global.stage.set_key_focus(actor);
 | 
			
		||||
    global.stage.set_key_focus(null);
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * popModal:
 | 
			
		||||
 * @actor: #ClutterActor passed to original invocation of pushModal().
 | 
			
		||||
 * @timestamp: optional timestamp
 | 
			
		||||
 *
 | 
			
		||||
 * Reverse the effect of pushModal().  If this invocation is undoing
 | 
			
		||||
 * the topmost invocation, then the focus will be restored to the
 | 
			
		||||
 * previous focus at the time when pushModal() was invoked.
 | 
			
		||||
 *
 | 
			
		||||
 * @timestamp is optionally used to associate the call with a specific user
 | 
			
		||||
 * initiated event.  If not provided then the value of
 | 
			
		||||
 * global.get_current_time() is assumed.
 | 
			
		||||
 */
 | 
			
		||||
function popModal(actor, timestamp) {
 | 
			
		||||
    if (timestamp == undefined)
 | 
			
		||||
        timestamp = global.get_current_time();
 | 
			
		||||
 | 
			
		||||
    let focusIndex = _findModal(actor);
 | 
			
		||||
    if (focusIndex < 0) {
 | 
			
		||||
        global.stage.set_key_focus(null);
 | 
			
		||||
        global.end_modal(timestamp);
 | 
			
		||||
        global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
 | 
			
		||||
 | 
			
		||||
        throw new Error('incorrect pop');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
function popModal(actor) {
 | 
			
		||||
    modalCount -= 1;
 | 
			
		||||
 | 
			
		||||
    let record = modalActorFocusStack[focusIndex];
 | 
			
		||||
    record.actor.disconnect(record.destroyId);
 | 
			
		||||
 | 
			
		||||
    if (focusIndex == modalActorFocusStack.length - 1) {
 | 
			
		||||
        if (record.focus)
 | 
			
		||||
            record.focus.disconnect(record.focusDestroyId);
 | 
			
		||||
        global.stage.set_key_focus(record.focus);
 | 
			
		||||
    } else {
 | 
			
		||||
        let t = modalActorFocusStack[modalActorFocusStack.length - 1];
 | 
			
		||||
        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].focus = modalActorFocusStack[i - 1].focus;
 | 
			
		||||
            modalActorFocusStack[i].focusDestroyId = modalActorFocusStack[i - 1].focusDestroyId;
 | 
			
		||||
    let focusIndex = _findModal(actor);
 | 
			
		||||
    if (focusIndex >= 0) {
 | 
			
		||||
        if (focusIndex == modalActorFocusStack.length - 1) {
 | 
			
		||||
            let [stackActor, stackFocus] = modalActorFocusStack[focusIndex];
 | 
			
		||||
            global.stage.set_key_focus(stackFocus);
 | 
			
		||||
        } else {
 | 
			
		||||
            // Remove from the middle, shift the focus chain up
 | 
			
		||||
            for (let i = focusIndex; i < modalActorFocusStack.length - 1; i++) {
 | 
			
		||||
                modalActorFocusStack[i + 1][1] = modalActorFocusStack[i][1];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        modalActorFocusStack.splice(focusIndex, 1);
 | 
			
		||||
    }
 | 
			
		||||
    modalActorFocusStack.splice(focusIndex, 1);
 | 
			
		||||
 | 
			
		||||
    if (modalCount > 0)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    global.end_modal(timestamp);
 | 
			
		||||
    global.end_modal(global.get_current_time());
 | 
			
		||||
    global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1677
									
								
								js/ui/messageTray.js
									
									
									
									
									
								
							
							
						
						@@ -1,276 +0,0 @@
 | 
			
		||||
/* -*- mode: js2; js2-basic-offset: 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 Lang = imports.lang;
 | 
			
		||||
const Meta = imports.gi.Meta;
 | 
			
		||||
const Pango = imports.gi.Pango;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
 | 
			
		||||
const Lightbox = imports.ui.lightbox;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const Tweener = imports.ui.tweener;
 | 
			
		||||
 | 
			
		||||
const OPEN_AND_CLOSE_TIME = 0.1;
 | 
			
		||||
const FADE_OUT_DIALOG_TIME = 1.0;
 | 
			
		||||
 | 
			
		||||
const State = {
 | 
			
		||||
    OPENED: 0,
 | 
			
		||||
    CLOSED: 1,
 | 
			
		||||
    OPENING: 2,
 | 
			
		||||
    CLOSING: 3,
 | 
			
		||||
    FADED_OUT: 4
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function ModalDialog() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ModalDialog.prototype = {
 | 
			
		||||
    _init: function(params) {
 | 
			
		||||
        params = Params.parse(params, { styleClass: null });
 | 
			
		||||
 | 
			
		||||
        this.state = State.CLOSED;
 | 
			
		||||
        this._hasModal = false;
 | 
			
		||||
 | 
			
		||||
        this._group = new St.Group({ visible: false,
 | 
			
		||||
                                     x: 0,
 | 
			
		||||
                                     y: 0 });
 | 
			
		||||
        Main.uiGroup.add_actor(this._group);
 | 
			
		||||
 | 
			
		||||
        let constraint = new Clutter.BindConstraint({ source: global.stage,
 | 
			
		||||
                                                      coordinate: Clutter.BindCoordinate.POSITION | Clutter.BindCoordinate.SIZE });
 | 
			
		||||
        this._group.add_constraint(constraint);
 | 
			
		||||
 | 
			
		||||
        this._group.connect('destroy', Lang.bind(this, this._onGroupDestroy));
 | 
			
		||||
 | 
			
		||||
        this._actionKeys = {};
 | 
			
		||||
        this._group.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
 | 
			
		||||
 | 
			
		||||
        this._lightbox = new Lightbox.Lightbox(this._group,
 | 
			
		||||
                                               { inhibitEvents: true });
 | 
			
		||||
 | 
			
		||||
        this._backgroundBin = new St.Bin();
 | 
			
		||||
 | 
			
		||||
        this._group.add_actor(this._backgroundBin);
 | 
			
		||||
        this._lightbox.highlight(this._backgroundBin);
 | 
			
		||||
 | 
			
		||||
        this._backgroundStack = new Shell.Stack();
 | 
			
		||||
        this._backgroundBin.child = this._backgroundStack;
 | 
			
		||||
 | 
			
		||||
        this._eventBlocker = new Clutter.Group({ reactive: true });
 | 
			
		||||
        this._backgroundStack.add_actor(this._eventBlocker);
 | 
			
		||||
 | 
			
		||||
        this._dialogLayout = new St.BoxLayout({ style_class: 'modal-dialog',
 | 
			
		||||
                                                vertical:    true });
 | 
			
		||||
        if (params.styleClass != null) {
 | 
			
		||||
            this._dialogLayout.add_style_class_name(params.styleClass);
 | 
			
		||||
        }
 | 
			
		||||
        this._backgroundStack.add_actor(this._dialogLayout);
 | 
			
		||||
 | 
			
		||||
        this.contentLayout = new St.BoxLayout({ vertical: true });
 | 
			
		||||
        this._dialogLayout.add(this.contentLayout,
 | 
			
		||||
                               { x_fill:  true,
 | 
			
		||||
                                 y_fill:  true,
 | 
			
		||||
                                 x_align: St.Align.MIDDLE,
 | 
			
		||||
                                 y_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        this._buttonLayout = new St.BoxLayout({ style_class: 'modal-dialog-button-box',
 | 
			
		||||
                                                opacity:     220,
 | 
			
		||||
                                                vertical:    false });
 | 
			
		||||
        this._dialogLayout.add(this._buttonLayout,
 | 
			
		||||
                               { expand:  true,
 | 
			
		||||
                                 x_align: St.Align.MIDDLE,
 | 
			
		||||
                                 y_align: St.Align.END });
 | 
			
		||||
 | 
			
		||||
        global.focus_manager.add_group(this._dialogLayout);
 | 
			
		||||
        this._initialKeyFocus = this._dialogLayout;
 | 
			
		||||
        this._savedKeyFocus = null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setButtons: function(buttons) {
 | 
			
		||||
        this._buttonLayout.destroy_children();
 | 
			
		||||
        this._actionKeys = {};
 | 
			
		||||
 | 
			
		||||
        let i = 0;
 | 
			
		||||
        for (let index in buttons) {
 | 
			
		||||
            let buttonInfo = buttons[index];
 | 
			
		||||
            let label = buttonInfo['label'];
 | 
			
		||||
            let action = buttonInfo['action'];
 | 
			
		||||
            let key = buttonInfo['key'];
 | 
			
		||||
 | 
			
		||||
            let button = new St.Button({ style_class: 'modal-dialog-button',
 | 
			
		||||
                                         reactive:    true,
 | 
			
		||||
                                         can_focus:   true,
 | 
			
		||||
                                         label:       label });
 | 
			
		||||
 | 
			
		||||
            let x_alignment;
 | 
			
		||||
            if (buttons.length == 1)
 | 
			
		||||
                x_alignment = St.Align.END;
 | 
			
		||||
            else if (i == 0)
 | 
			
		||||
                x_alignment = St.Align.START;
 | 
			
		||||
            else if (i == buttons.length - 1)
 | 
			
		||||
                x_alignment = St.Align.END;
 | 
			
		||||
            else
 | 
			
		||||
                x_alignment = St.Align.MIDDLE;
 | 
			
		||||
 | 
			
		||||
            this._initialKeyFocus = button;
 | 
			
		||||
            this._buttonLayout.add(button,
 | 
			
		||||
                                   { expand: true,
 | 
			
		||||
                                     x_fill: false,
 | 
			
		||||
                                     y_fill: false,
 | 
			
		||||
                                     x_align: x_alignment,
 | 
			
		||||
                                     y_align: St.Align.MIDDLE });
 | 
			
		||||
 | 
			
		||||
            button.connect('clicked', action);
 | 
			
		||||
 | 
			
		||||
            if (key)
 | 
			
		||||
                this._actionKeys[key] = action;
 | 
			
		||||
            i++;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onKeyPressEvent: function(object, keyPressEvent) {
 | 
			
		||||
        let symbol = keyPressEvent.get_key_symbol();
 | 
			
		||||
        let action = this._actionKeys[symbol];
 | 
			
		||||
 | 
			
		||||
        if (action)
 | 
			
		||||
            action();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onGroupDestroy: function() {
 | 
			
		||||
        this.emit('destroy');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _fadeOpen: function() {
 | 
			
		||||
        let monitor = Main.layoutManager.focusMonitor;
 | 
			
		||||
 | 
			
		||||
        this._backgroundBin.set_position(monitor.x, monitor.y);
 | 
			
		||||
        this._backgroundBin.set_size(monitor.width, monitor.height);
 | 
			
		||||
 | 
			
		||||
        this.state = State.OPENING;
 | 
			
		||||
 | 
			
		||||
        this._dialogLayout.opacity = 255;
 | 
			
		||||
        this._lightbox.show();
 | 
			
		||||
        this._group.opacity = 0;
 | 
			
		||||
        this._group.show();
 | 
			
		||||
        Tweener.addTween(this._group,
 | 
			
		||||
                         { opacity: 255,
 | 
			
		||||
                           time: OPEN_AND_CLOSE_TIME,
 | 
			
		||||
                           transition: 'easeOutQuad',
 | 
			
		||||
                           onComplete: Lang.bind(this,
 | 
			
		||||
                               function() {
 | 
			
		||||
                                   this.state = State.OPENED;
 | 
			
		||||
                                   this.emit('opened');
 | 
			
		||||
                               })
 | 
			
		||||
                         });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setInitialKeyFocus: function(actor) {
 | 
			
		||||
        this._initialKeyFocus = actor;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    open: function(timestamp) {
 | 
			
		||||
        if (this.state == State.OPENED || this.state == State.OPENING)
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        if (!this.pushModal(timestamp))
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        this._fadeOpen();
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    close: function(timestamp) {
 | 
			
		||||
        if (this.state == State.CLOSED || this.state == State.CLOSING)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this.state = State.CLOSING;
 | 
			
		||||
        this.popModal(timestamp);
 | 
			
		||||
        this._savedKeyFocus = null;
 | 
			
		||||
 | 
			
		||||
        Tweener.addTween(this._group,
 | 
			
		||||
                         { opacity: 0,
 | 
			
		||||
                           time: OPEN_AND_CLOSE_TIME,
 | 
			
		||||
                           transition: 'easeOutQuad',
 | 
			
		||||
                           onComplete: Lang.bind(this,
 | 
			
		||||
                               function() {
 | 
			
		||||
                                   this.state = State.CLOSED;
 | 
			
		||||
                                   this._group.hide();
 | 
			
		||||
                               })
 | 
			
		||||
                         });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Drop modal status without closing the dialog; this makes the
 | 
			
		||||
    // dialog insensitive as well, so it needs to be followed shortly
 | 
			
		||||
    // by either a close() or a pushModal()
 | 
			
		||||
    popModal: function(timestamp) {
 | 
			
		||||
        if (!this._hasModal)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let focus = global.stage.key_focus;
 | 
			
		||||
        if (focus && this._group.contains(focus))
 | 
			
		||||
            this._savedKeyFocus = focus;
 | 
			
		||||
        else
 | 
			
		||||
            this._savedKeyFocus = null;
 | 
			
		||||
        Main.popModal(this._group, timestamp);
 | 
			
		||||
        global.gdk_screen.get_display().sync();
 | 
			
		||||
        this._hasModal = false;
 | 
			
		||||
 | 
			
		||||
        this._eventBlocker.raise_top();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    pushModal: function (timestamp) {
 | 
			
		||||
        if (this._hasModal)
 | 
			
		||||
            return true;
 | 
			
		||||
        if (!Main.pushModal(this._group, timestamp))
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        this._hasModal = true;
 | 
			
		||||
        if (this._savedKeyFocus) {
 | 
			
		||||
            this._savedKeyFocus.grab_key_focus();
 | 
			
		||||
            this._savedKeyFocus = null;
 | 
			
		||||
        } else
 | 
			
		||||
            this._initialKeyFocus.grab_key_focus();
 | 
			
		||||
 | 
			
		||||
        this._eventBlocker.lower_bottom();
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // This method is like close, but fades the dialog out much slower,
 | 
			
		||||
    // and leaves the lightbox in place. Once in the faded out state,
 | 
			
		||||
    // the dialog can be brought back by an open call, or the lightbox
 | 
			
		||||
    // can be dismissed by a close call.
 | 
			
		||||
    //
 | 
			
		||||
    // The main point of this method is to give some indication to the user
 | 
			
		||||
    // that the dialog reponse has been acknowledged but will take a few
 | 
			
		||||
    // moments before being processed.
 | 
			
		||||
    // e.g., if a user clicked "Log Out" then the dialog should go away
 | 
			
		||||
    // imediately, but the lightbox should remain until the logout is
 | 
			
		||||
    // complete.
 | 
			
		||||
    _fadeOutDialog: function(timestamp) {
 | 
			
		||||
        if (this.state == State.CLOSED || this.state == State.CLOSING)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (this.state == State.FADED_OUT)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this.popModal(timestamp);
 | 
			
		||||
        Tweener.addTween(this._dialogLayout,
 | 
			
		||||
                         { opacity: 0,
 | 
			
		||||
                           time:    FADE_OUT_DIALOG_TIME,
 | 
			
		||||
                           transition: 'easeOutQuad',
 | 
			
		||||
                           onComplete: Lang.bind(this,
 | 
			
		||||
                               function() {
 | 
			
		||||
                                   this.state = State.FADED_OUT;
 | 
			
		||||
                               })
 | 
			
		||||
                         });
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
Signals.addSignalMethods(ModalDialog.prototype);
 | 
			
		||||
@@ -1,18 +1,17 @@
 | 
			
		||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const DBus = imports.dbus;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Mainloop = imports.mainloop;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Gettext = imports.gettext.domain('gnome-shell');
 | 
			
		||||
const _ = Gettext.gettext;
 | 
			
		||||
 | 
			
		||||
const Config = imports.misc.config;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const MessageTray = imports.ui.messageTray;
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
const Util = imports.misc.util;
 | 
			
		||||
 | 
			
		||||
let nextNotificationId = 1;
 | 
			
		||||
 | 
			
		||||
@@ -76,11 +75,11 @@ const Urgency = {
 | 
			
		||||
const rewriteRules = {
 | 
			
		||||
    'XChat': [
 | 
			
		||||
        { pattern:     /^XChat: Private message from: (\S*) \(.*\)$/,
 | 
			
		||||
          replacement: '<$1>' },
 | 
			
		||||
          replacement: '<$1>' },
 | 
			
		||||
        { pattern:     /^XChat: New public message from: (\S*) \((.*)\)$/,
 | 
			
		||||
          replacement: '$2 <$1>' },
 | 
			
		||||
          replacement: '$2 <$1>' },
 | 
			
		||||
        { pattern:     /^XChat: Highlighted message from: (\S*) \((.*)\)$/,
 | 
			
		||||
          replacement: '$2 <$1>' }
 | 
			
		||||
          replacement: '$2 <$1>' }
 | 
			
		||||
    ]
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -92,6 +91,16 @@ NotificationDaemon.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        DBus.session.exportObject('/org/freedesktop/Notifications', this);
 | 
			
		||||
 | 
			
		||||
        this._everAcquiredName = false;
 | 
			
		||||
        DBus.session.acquire_name('org.freedesktop.Notifications',
 | 
			
		||||
                                  // We pass MANY_INSTANCES so that if
 | 
			
		||||
                                  // notification-daemon is running, we'll
 | 
			
		||||
                                  // get queued behind it and then get the
 | 
			
		||||
                                  // name after killing it below
 | 
			
		||||
                                  DBus.MANY_INSTANCES,
 | 
			
		||||
                                  Lang.bind(this, this._acquiredName),
 | 
			
		||||
                                  Lang.bind(this, this._lostName));
 | 
			
		||||
 | 
			
		||||
        this._sources = {};
 | 
			
		||||
        this._senderToPid = {};
 | 
			
		||||
        this._notifications = {};
 | 
			
		||||
@@ -106,6 +115,31 @@ NotificationDaemon.prototype = {
 | 
			
		||||
            Lang.bind(this, this._onFocusAppChanged));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _acquiredName: function() {
 | 
			
		||||
        this._everAcquiredName = true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _lostName: function() {
 | 
			
		||||
        if (this._everAcquiredName)
 | 
			
		||||
            log('Lost name org.freedesktop.Notifications!');
 | 
			
		||||
        else if (GLib.getenv('GNOME_SHELL_NO_REPLACE'))
 | 
			
		||||
            log('Failed to acquire org.freedesktop.Notifications');
 | 
			
		||||
        else {
 | 
			
		||||
            log('Failed to acquire org.freedesktop.Notifications; trying again');
 | 
			
		||||
 | 
			
		||||
            // kill the notification-daemon. pkill is more portable
 | 
			
		||||
            // than killall, but on Linux at least it won't match if
 | 
			
		||||
            // you pass more than 15 characters of the process name...
 | 
			
		||||
            // However, if you use the '-f' flag to match the entire
 | 
			
		||||
            // command line, it will work, but we have to be careful
 | 
			
		||||
            // in that case that we don't match 'gedit
 | 
			
		||||
            // notification-daemon.c' or whatever...
 | 
			
		||||
            let p = new Shell.Process({ args: ['pkill', '-f',
 | 
			
		||||
                                               '^([^ ]*/)?(notification-daemon|notify-osd)$']});
 | 
			
		||||
            p.run();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _iconForNotificationData: function(icon, hints, size) {
 | 
			
		||||
        let textureCache = St.TextureCache.get_default();
 | 
			
		||||
 | 
			
		||||
@@ -119,10 +153,10 @@ NotificationDaemon.prototype = {
 | 
			
		||||
                return new St.Icon({ icon_name: icon,
 | 
			
		||||
                                     icon_type: St.IconType.FULLCOLOR,
 | 
			
		||||
                                     icon_size: size });
 | 
			
		||||
        } else if (hints['image-data']) {
 | 
			
		||||
        } else if (hints.icon_data) {
 | 
			
		||||
            let [width, height, rowStride, hasAlpha,
 | 
			
		||||
                 bitsPerSample, nChannels, data] = hints['image-data'];
 | 
			
		||||
            return textureCache.load_from_raw(data, hasAlpha,
 | 
			
		||||
                 bitsPerSample, nChannels, data] = hints.icon_data;
 | 
			
		||||
            return textureCache.load_from_raw(data, data.length, hasAlpha,
 | 
			
		||||
                                              width, height, rowStride, size);
 | 
			
		||||
        } else {
 | 
			
		||||
            let stockIcon;
 | 
			
		||||
@@ -141,46 +175,14 @@ NotificationDaemon.prototype = {
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Returns the source associated with ndata.notification if it is set.
 | 
			
		||||
    // Otherwise, returns the source associated with the pid if one is
 | 
			
		||||
    // stored in this._sources and the notification is not transient.
 | 
			
		||||
    // Otherwise, creates a new source as long as pid is provided.
 | 
			
		||||
    //
 | 
			
		||||
    // Either a pid or ndata.notification is needed to retrieve or
 | 
			
		||||
    // create a source.
 | 
			
		||||
    _getSource: function(title, pid, ndata, sender) {
 | 
			
		||||
        if (!pid && !(ndata && ndata.notification))
 | 
			
		||||
            return null;
 | 
			
		||||
    _newSource: function(title, pid) {
 | 
			
		||||
        let source = new Source(title, pid);
 | 
			
		||||
        this._sources[pid] = source;
 | 
			
		||||
 | 
			
		||||
        // We use notification's source for the notifications we still have
 | 
			
		||||
        // around that are getting replaced because we don't keep sources
 | 
			
		||||
        // for transient notifications in this._sources, but we still want
 | 
			
		||||
        // the notification associated with them to get replaced correctly.
 | 
			
		||||
        if (ndata && ndata.notification)
 | 
			
		||||
            return ndata.notification.source;
 | 
			
		||||
 | 
			
		||||
        let isForTransientNotification = (ndata && ndata.hints['transient'] == true);
 | 
			
		||||
 | 
			
		||||
        // We don't want to override a persistent notification
 | 
			
		||||
        // with a transient one from the same sender, so we
 | 
			
		||||
        // always create a new source object for new transient notifications
 | 
			
		||||
        // and never add it to this._sources .
 | 
			
		||||
        if (!isForTransientNotification && this._sources[pid]) {
 | 
			
		||||
            let source = this._sources[pid];
 | 
			
		||||
            source.setTitle(title);
 | 
			
		||||
            return source;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let source = new Source(title, pid, sender);
 | 
			
		||||
        source.setTransient(isForTransientNotification);
 | 
			
		||||
 | 
			
		||||
        if (!isForTransientNotification) {
 | 
			
		||||
            this._sources[pid] = source;
 | 
			
		||||
            source.connect('destroy', Lang.bind(this,
 | 
			
		||||
                function() {
 | 
			
		||||
                    delete this._sources[pid];
 | 
			
		||||
                }));
 | 
			
		||||
        }
 | 
			
		||||
        source.connect('destroy', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                delete this._sources[pid];
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
        Main.messageTray.add(source);
 | 
			
		||||
        return source;
 | 
			
		||||
@@ -190,13 +192,9 @@ NotificationDaemon.prototype = {
 | 
			
		||||
                     actions, hints, timeout) {
 | 
			
		||||
        let id;
 | 
			
		||||
 | 
			
		||||
        // Filter out chat, presence, calls and invitation notifications from
 | 
			
		||||
        // Empathy, since we handle that information from telepathyClient.js
 | 
			
		||||
        if (appName == 'Empathy' && (hints['category'] == 'im.received' ||
 | 
			
		||||
              hints['category'] == 'x-empathy.im.room-invitation' ||
 | 
			
		||||
              hints['category'] == 'x-empathy.call.incoming' ||
 | 
			
		||||
              hints['category'] == 'presence.online' ||
 | 
			
		||||
              hints['category'] == 'presence.offline')) {
 | 
			
		||||
        // Filter out notifications from Empathy, since we
 | 
			
		||||
        // handle that information from telepathyClient.js
 | 
			
		||||
        if (appName == 'Empathy') {
 | 
			
		||||
            // Ignore replacesId since we already sent back a
 | 
			
		||||
            // NotificationClosed for that id.
 | 
			
		||||
            id = nextNotificationId++;
 | 
			
		||||
@@ -207,6 +205,8 @@ NotificationDaemon.prototype = {
 | 
			
		||||
            return id;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        summary = GLib.markup_escape_text(summary, -1);
 | 
			
		||||
 | 
			
		||||
        let rewrites = rewriteRules[appName];
 | 
			
		||||
        if (rewrites) {
 | 
			
		||||
            for (let i = 0; i < rewrites.length; i++) {
 | 
			
		||||
@@ -218,15 +218,6 @@ NotificationDaemon.prototype = {
 | 
			
		||||
 | 
			
		||||
        hints = Params.parse(hints, { urgency: Urgency.NORMAL }, true);
 | 
			
		||||
 | 
			
		||||
        // Be compatible with the various hints for image data
 | 
			
		||||
        // 'image-data' is the latest name of this hint, introduced in 1.2
 | 
			
		||||
        if (!hints['image-data']) {
 | 
			
		||||
            if (hints['image_data'])
 | 
			
		||||
                hints['image-data'] = hints['image_data']; // version 1.1 of the spec
 | 
			
		||||
            else if (hints['icon_data'])
 | 
			
		||||
                hints['image-data'] = hints['icon_data']; // previous versions of the spec
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let ndata = { appName: appName,
 | 
			
		||||
                      icon: icon,
 | 
			
		||||
                      summary: summary,
 | 
			
		||||
@@ -245,8 +236,7 @@ NotificationDaemon.prototype = {
 | 
			
		||||
 | 
			
		||||
        let sender = DBus.getCurrentMessageContext().sender;
 | 
			
		||||
        let pid = this._senderToPid[sender];
 | 
			
		||||
 | 
			
		||||
        let source = this._getSource(appName, pid, ndata, sender);
 | 
			
		||||
        let source = pid ? this._sources[pid] : null;
 | 
			
		||||
 | 
			
		||||
        if (source) {
 | 
			
		||||
            this._notifyForSource(source, ndata);
 | 
			
		||||
@@ -267,23 +257,16 @@ NotificationDaemon.prototype = {
 | 
			
		||||
                if (!ndata)
 | 
			
		||||
                    return;
 | 
			
		||||
 | 
			
		||||
                source = this._getSource(appName, pid, ndata, sender);
 | 
			
		||||
                this._senderToPid[sender] = pid;
 | 
			
		||||
                source = this._sources[pid];
 | 
			
		||||
 | 
			
		||||
                if (!source)
 | 
			
		||||
                    source = this._newSource(appName, pid);
 | 
			
		||||
                source.connect('destroy', Lang.bind(this,
 | 
			
		||||
                    function() {
 | 
			
		||||
                        delete this._senderToPid[sender];
 | 
			
		||||
                    }));
 | 
			
		||||
 | 
			
		||||
                // We only store sender-pid entries for persistent sources.
 | 
			
		||||
                // Removing the entries once the source is destroyed
 | 
			
		||||
                // would result in the entries associated with transient
 | 
			
		||||
                // sources removed once the notification is shown anyway.
 | 
			
		||||
                // However, keeping these pairs would mean that we would
 | 
			
		||||
                // possibly remove an entry associated with a persistent
 | 
			
		||||
                // source when a transient source for the same sender is
 | 
			
		||||
                // distroyed.
 | 
			
		||||
                if (!source.isTransient) {
 | 
			
		||||
                    this._senderToPid[sender] = pid;
 | 
			
		||||
                    source.connect('destroy', Lang.bind(this,
 | 
			
		||||
                        function() {
 | 
			
		||||
                            delete this._senderToPid[sender];
 | 
			
		||||
                        }));
 | 
			
		||||
                }
 | 
			
		||||
                this._notifyForSource(source, ndata);
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
@@ -298,34 +281,19 @@ NotificationDaemon.prototype = {
 | 
			
		||||
        let iconActor = this._iconForNotificationData(icon, hints, source.ICON_SIZE);
 | 
			
		||||
 | 
			
		||||
        if (notification == null) {
 | 
			
		||||
            notification = new MessageTray.Notification(source, summary, body,
 | 
			
		||||
                                                        { icon: iconActor,
 | 
			
		||||
                                                          bannerMarkup: true });
 | 
			
		||||
            notification = new MessageTray.Notification(source, summary, body, { icon: iconActor });
 | 
			
		||||
            ndata.notification = notification;
 | 
			
		||||
            notification.connect('clicked', Lang.bind(this,
 | 
			
		||||
                function(n) {
 | 
			
		||||
                    this._emitNotificationClosed(id, NotificationClosedReason.DISMISSED);
 | 
			
		||||
                }));
 | 
			
		||||
            notification.connect('destroy', Lang.bind(this,
 | 
			
		||||
                function(n, reason) {
 | 
			
		||||
                function(n) {
 | 
			
		||||
                    delete this._notifications[id];
 | 
			
		||||
                    let notificationClosedReason;
 | 
			
		||||
                    switch (reason) {
 | 
			
		||||
                        case MessageTray.NotificationDestroyedReason.EXPIRED:
 | 
			
		||||
                            notificationClosedReason = NotificationClosedReason.EXPIRED;
 | 
			
		||||
                            break;
 | 
			
		||||
                        case MessageTray.NotificationDestroyedReason.DISMISSED:
 | 
			
		||||
                            notificationClosedReason = NotificationClosedReason.DISMISSED;
 | 
			
		||||
                            break;
 | 
			
		||||
                        case MessageTray.NotificationDestroyedReason.SOURCE_CLOSED:
 | 
			
		||||
                            notificationClosedReason = NotificationClosedReason.APP_CLOSED;
 | 
			
		||||
                            break;
 | 
			
		||||
                    }
 | 
			
		||||
                    this._emitNotificationClosed(id, notificationClosedReason);
 | 
			
		||||
                }));
 | 
			
		||||
            notification.connect('action-invoked', Lang.bind(this,
 | 
			
		||||
                function(n, actionId) {
 | 
			
		||||
                    this._emitActionInvoked(id, actionId);
 | 
			
		||||
                }));
 | 
			
		||||
            notification.connect('action-invoked', Lang.bind(this, this._actionInvoked, source, id));
 | 
			
		||||
        } else {
 | 
			
		||||
            notification.update(summary, body, { icon: iconActor,
 | 
			
		||||
                                                 bannerMarkup: true,
 | 
			
		||||
                                                 clear: true });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -334,33 +302,21 @@ NotificationDaemon.prototype = {
 | 
			
		||||
            for (let i = 0; i < actions.length - 1; i += 2)
 | 
			
		||||
                notification.addButton(actions[i], actions[i + 1]);
 | 
			
		||||
        }
 | 
			
		||||
        switch (hints.urgency) {
 | 
			
		||||
            case Urgency.LOW:
 | 
			
		||||
                notification.setUrgency(MessageTray.Urgency.LOW);
 | 
			
		||||
                break;
 | 
			
		||||
            case Urgency.NORMAL:
 | 
			
		||||
                notification.setUrgency(MessageTray.Urgency.NORMAL);
 | 
			
		||||
                break;
 | 
			
		||||
            case Urgency.CRITICAL:
 | 
			
		||||
                notification.setUrgency(MessageTray.Urgency.CRITICAL);
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
        notification.setResident(hints.resident == true);
 | 
			
		||||
        // 'transient' is a reserved keyword in JS, so we have to retrieve the value
 | 
			
		||||
        // of the 'transient' hint with hints['transient'] rather than hints.transient
 | 
			
		||||
        notification.setTransient(hints['transient'] == true);
 | 
			
		||||
 | 
			
		||||
        notification.setUrgent(hints.urgency == Urgency.CRITICAL);
 | 
			
		||||
 | 
			
		||||
        let sourceIconActor = source.useNotificationIcon ? this._iconForNotificationData(icon, hints, source.ICON_SIZE) : null;
 | 
			
		||||
        source.processNotification(notification, sourceIconActor);
 | 
			
		||||
        source.notify(notification, sourceIconActor);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    CloseNotification: function(id) {
 | 
			
		||||
        let ndata = this._notifications[id];
 | 
			
		||||
        if (ndata) {
 | 
			
		||||
            if (ndata.notification)
 | 
			
		||||
                ndata.notification.destroy(MessageTray.NotificationDestroyedReason.SOURCE_CLOSED);
 | 
			
		||||
                ndata.notification.destroy();
 | 
			
		||||
            delete this._notifications[id];
 | 
			
		||||
        }
 | 
			
		||||
        this._emitNotificationClosed(id, NotificationClosedReason.APP_CLOSED);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    GetCapabilities: function() {
 | 
			
		||||
@@ -380,10 +336,10 @@ NotificationDaemon.prototype = {
 | 
			
		||||
 | 
			
		||||
    GetServerInformation: function() {
 | 
			
		||||
        return [
 | 
			
		||||
            Config.PACKAGE_NAME,
 | 
			
		||||
            'GNOME Shell',
 | 
			
		||||
            'GNOME',
 | 
			
		||||
            Config.PACKAGE_VERSION,
 | 
			
		||||
            '1.2'
 | 
			
		||||
            '0.1', // FIXME, get this from somewhere
 | 
			
		||||
            '1.0'
 | 
			
		||||
        ];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -395,12 +351,17 @@ NotificationDaemon.prototype = {
 | 
			
		||||
        for (let id in this._sources) {
 | 
			
		||||
            let source = this._sources[id];
 | 
			
		||||
            if (source.app == tracker.focus_app) {
 | 
			
		||||
                source.destroyNonResidentNotifications();
 | 
			
		||||
                source.activated();
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _actionInvoked: function(notification, action, source, id) {
 | 
			
		||||
        source.activated();
 | 
			
		||||
        this._emitActionInvoked(id, action);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _emitNotificationClosed: function(id, reason) {
 | 
			
		||||
        DBus.session.emit_signal('/org/freedesktop/Notifications',
 | 
			
		||||
                                 'org.freedesktop.Notifications',
 | 
			
		||||
@@ -416,7 +377,9 @@ NotificationDaemon.prototype = {
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onTrayIconAdded: function(o, icon) {
 | 
			
		||||
        let source = this._getSource(icon.title || icon.wm_class || _("Unknown"), icon.pid, null, null);
 | 
			
		||||
        let source = this._sources[icon.pid];
 | 
			
		||||
        if (!source)
 | 
			
		||||
            source = this._newSource(icon.title || icon.wm_class || _("Unknown"), icon.pid);
 | 
			
		||||
        source.setTrayIcon(icon);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -429,101 +392,41 @@ NotificationDaemon.prototype = {
 | 
			
		||||
 | 
			
		||||
DBus.conformExport(NotificationDaemon.prototype, NotificationDaemonIface);
 | 
			
		||||
 | 
			
		||||
function Source(title, pid, sender) {
 | 
			
		||||
    this._init(title, pid, sender);
 | 
			
		||||
function Source(title, pid) {
 | 
			
		||||
    this._init(title, pid);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Source.prototype = {
 | 
			
		||||
    __proto__:  MessageTray.Source.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function(title, pid, sender) {
 | 
			
		||||
    _init: function(title, pid) {
 | 
			
		||||
        MessageTray.Source.prototype._init.call(this, title);
 | 
			
		||||
 | 
			
		||||
        this._pid = pid;
 | 
			
		||||
        if (sender)
 | 
			
		||||
            // TODO: dbus-glib implementation of watch_name() doesn’t return an id to be used for
 | 
			
		||||
            // unwatch_name() or implement unwatch_name(), however when we move to using GDBus implementation,
 | 
			
		||||
            // we should save the id here and call unwatch_name() with it in destroy().
 | 
			
		||||
            // Moving to GDBus is the work in progress: https://bugzilla.gnome.org/show_bug.cgi?id=648651
 | 
			
		||||
            // and https://bugzilla.gnome.org/show_bug.cgi?id=622921 .
 | 
			
		||||
            DBus.session.watch_name(sender,
 | 
			
		||||
                                    false,
 | 
			
		||||
                                    null,
 | 
			
		||||
                                    Lang.bind(this, this._onNameVanished));
 | 
			
		||||
 | 
			
		||||
        this._setApp();
 | 
			
		||||
        if (this.app)
 | 
			
		||||
            this.title = this.app.get_name();
 | 
			
		||||
        else
 | 
			
		||||
            this.useNotificationIcon = true;
 | 
			
		||||
        this._trayIcon = null;
 | 
			
		||||
        this._isTrayIcon = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onNameVanished: function() {
 | 
			
		||||
        // Destroy the notification source when its sender is removed from DBus.
 | 
			
		||||
        // Only do so if this.app is set to avoid removing "notify-send" sources, senders
 | 
			
		||||
        // of which аre removed from DBus immediately.
 | 
			
		||||
        // Sender being removed from DBus would normally result in a tray icon being removed,
 | 
			
		||||
        // so allow the code path that handles the tray icon being removed to handle that case.
 | 
			
		||||
        if (!this.trayIcon && this.app)
 | 
			
		||||
            this.destroy();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    processNotification: function(notification, icon) {
 | 
			
		||||
    notify: function(notification, icon) {
 | 
			
		||||
        if (!this.app)
 | 
			
		||||
            this._setApp();
 | 
			
		||||
        if (!this.app && icon)
 | 
			
		||||
            this._setSummaryIcon(icon);
 | 
			
		||||
 | 
			
		||||
        let tracker = Shell.WindowTracker.get_default();
 | 
			
		||||
        if (notification.resident && this.app && tracker.focus_app == this.app)
 | 
			
		||||
            this.pushNotification(notification);
 | 
			
		||||
        else
 | 
			
		||||
            this.notify(notification);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    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 (event.get_button() == 1 &&
 | 
			
		||||
            this.notifications.length > 0)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        if (Main.overview.visible) {
 | 
			
		||||
            // We can't just connect to Main.overview's 'hidden' signal,
 | 
			
		||||
            // because it's emitted *before* it calls popModal()...
 | 
			
		||||
            let id = global.connect('notify::stage-input-mode', Lang.bind(this,
 | 
			
		||||
                function () {
 | 
			
		||||
                    global.disconnect(id);
 | 
			
		||||
                    this._trayIcon.click(event);
 | 
			
		||||
                }));
 | 
			
		||||
            Main.overview.hide();
 | 
			
		||||
        } else {
 | 
			
		||||
            this._trayIcon.click(event);
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
        MessageTray.Source.prototype.notify.call(this, notification);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _setApp: function() {
 | 
			
		||||
        if (this.app)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this.app = Shell.WindowTracker.get_default().get_app_from_pid(this._pid);
 | 
			
		||||
        if (!this.app)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        // Only override the icon if we were previously using
 | 
			
		||||
        // notification-based icons (ie, not a trayicon) or if it was unset before
 | 
			
		||||
        if (!this._trayIcon) {
 | 
			
		||||
        // notification-based icons (ie, not a trayicon)
 | 
			
		||||
        if (this.useNotificationIcon) {
 | 
			
		||||
            this.useNotificationIcon = false;
 | 
			
		||||
            this._setSummaryIcon(this.app.create_icon_texture (this.ICON_SIZE));
 | 
			
		||||
        }
 | 
			
		||||
@@ -532,16 +435,17 @@ Source.prototype = {
 | 
			
		||||
    setTrayIcon: function(icon) {
 | 
			
		||||
        this._setSummaryIcon(icon);
 | 
			
		||||
        this.useNotificationIcon = false;
 | 
			
		||||
        this._trayIcon = icon;
 | 
			
		||||
        this._isTrayIcon = true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    open: function(notification) {
 | 
			
		||||
        this.destroyNonResidentNotifications();
 | 
			
		||||
    _notificationClicked: function(notification) {
 | 
			
		||||
        notification.destroy();
 | 
			
		||||
        this.openApp();
 | 
			
		||||
        this.activated();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _lastNotificationRemoved: function() {
 | 
			
		||||
        if (!this._trayIcon)
 | 
			
		||||
    activated: function() {
 | 
			
		||||
        if (!this._isTrayIcon)
 | 
			
		||||
            this.destroy();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -554,9 +458,5 @@ Source.prototype = {
 | 
			
		||||
            let mostRecentWindow = windows[0];
 | 
			
		||||
            Main.activateWindow(mostRecentWindow);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroy: function() {
 | 
			
		||||
        MessageTray.Source.prototype.destroy.call(this);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,19 +1,18 @@
 | 
			
		||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Gtk = imports.gi.Gtk;
 | 
			
		||||
const Meta = imports.gi.Meta;
 | 
			
		||||
const Mainloop = imports.mainloop;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Gdk = imports.gi.Gdk;
 | 
			
		||||
const Gettext = imports.gettext.domain('gnome-shell');
 | 
			
		||||
const _ = Gettext.gettext;
 | 
			
		||||
 | 
			
		||||
const AppDisplay = imports.ui.appDisplay;
 | 
			
		||||
const Dash = imports.ui.dash;
 | 
			
		||||
const DND = imports.ui.dnd;
 | 
			
		||||
const DocDisplay = imports.ui.docDisplay;
 | 
			
		||||
const GenericDisplay = imports.ui.genericDisplay;
 | 
			
		||||
const Lightbox = imports.ui.lightbox;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const MessageTray = imports.ui.messageTray;
 | 
			
		||||
@@ -22,7 +21,6 @@ const PlaceDisplay = imports.ui.placeDisplay;
 | 
			
		||||
const Tweener = imports.ui.tweener;
 | 
			
		||||
const ViewSelector = imports.ui.viewSelector;
 | 
			
		||||
const WorkspacesView = imports.ui.workspacesView;
 | 
			
		||||
const WorkspaceThumbnail = imports.ui.workspaceThumbnail;
 | 
			
		||||
 | 
			
		||||
// Time for initial animation going into Overview mode
 | 
			
		||||
const ANIMATION_TIME = 0.25;
 | 
			
		||||
@@ -30,19 +28,31 @@ const ANIMATION_TIME = 0.25;
 | 
			
		||||
// We split the screen vertically between the dash and the view selector.
 | 
			
		||||
const DASH_SPLIT_FRACTION = 0.1;
 | 
			
		||||
 | 
			
		||||
const DND_WINDOW_SWITCH_TIMEOUT = 1250;
 | 
			
		||||
const SHELL_INFO_HIDE_TIMEOUT = 10;
 | 
			
		||||
 | 
			
		||||
const SwipeScrollDirection = {
 | 
			
		||||
    NONE: 0,
 | 
			
		||||
    HORIZONTAL: 1,
 | 
			
		||||
    VERTICAL: 2
 | 
			
		||||
};
 | 
			
		||||
function Source() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const SwipeScrollResult = {
 | 
			
		||||
    CANCEL: 0,
 | 
			
		||||
    SWIPE: 1,
 | 
			
		||||
    CLICK: 2
 | 
			
		||||
};
 | 
			
		||||
Source.prototype = {
 | 
			
		||||
    __proto__:  MessageTray.Source.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        MessageTray.Source.prototype._init.call(this,
 | 
			
		||||
                                                "System Information");
 | 
			
		||||
        this._setSummaryIcon(this.createNotificationIcon());
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    createNotificationIcon: function() {
 | 
			
		||||
        return new St.Icon({ icon_name: 'info',
 | 
			
		||||
                             icon_type: St.IconType.FULLCOLOR,
 | 
			
		||||
                             icon_size: this.ICON_SIZE });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _notificationClicked: function() {
 | 
			
		||||
        this.destroy();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function ShellInfo() {
 | 
			
		||||
    this._init();
 | 
			
		||||
@@ -51,10 +61,14 @@ function ShellInfo() {
 | 
			
		||||
ShellInfo.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._source = null;
 | 
			
		||||
        this._timeoutId = 0;
 | 
			
		||||
        this._undoCallback = null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onUndoClicked: function() {
 | 
			
		||||
        Mainloop.source_remove(this._timeoutId);
 | 
			
		||||
        this._timeoutId = 0;
 | 
			
		||||
 | 
			
		||||
        if (this._undoCallback)
 | 
			
		||||
            this._undoCallback();
 | 
			
		||||
        this._undoCallback = null;
 | 
			
		||||
@@ -63,9 +77,22 @@ ShellInfo.prototype = {
 | 
			
		||||
            this._source.destroy();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onTimeout: function() {
 | 
			
		||||
        this._timeoutId = 0;
 | 
			
		||||
        if (this._source)
 | 
			
		||||
            this._source.destroy();
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setMessage: function(text, undoCallback, undoLabel) {
 | 
			
		||||
        if (this._timeoutId)
 | 
			
		||||
            Mainloop.source_remove(this._timeoutId);
 | 
			
		||||
 | 
			
		||||
        this._timeoutId = Mainloop.timeout_add_seconds(SHELL_INFO_HIDE_TIMEOUT,
 | 
			
		||||
                                                       Lang.bind(this, this._onTimeout));
 | 
			
		||||
 | 
			
		||||
        if (this._source == null) {
 | 
			
		||||
            this._source = new MessageTray.SystemNotificationSource();
 | 
			
		||||
            this._source = new Source();
 | 
			
		||||
            this._source.connect('destroy', Lang.bind(this,
 | 
			
		||||
                function() {
 | 
			
		||||
                    this._source = null;
 | 
			
		||||
@@ -73,15 +100,11 @@ ShellInfo.prototype = {
 | 
			
		||||
            Main.messageTray.add(this._source);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let notification = null;
 | 
			
		||||
        if (this._source.notifications.length == 0) {
 | 
			
		||||
        let notification = this._source.notification;
 | 
			
		||||
        if (notification == null)
 | 
			
		||||
            notification = new MessageTray.Notification(this._source, text, null);
 | 
			
		||||
        } else {
 | 
			
		||||
            notification = this._source.notifications[0];
 | 
			
		||||
        else
 | 
			
		||||
            notification.update(text, null, { clear: true });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        notification.setTransient(true);
 | 
			
		||||
 | 
			
		||||
        this._undoCallback = undoCallback;
 | 
			
		||||
        if (undoCallback) {
 | 
			
		||||
@@ -101,19 +124,18 @@ function Overview() {
 | 
			
		||||
 | 
			
		||||
Overview.prototype = {
 | 
			
		||||
    _init : function() {
 | 
			
		||||
        this._desktopFade = new St.Bin();
 | 
			
		||||
        global.overlay_group.add_actor(this._desktopFade);
 | 
			
		||||
 | 
			
		||||
        // The actual global.background_actor is inside global.window_group,
 | 
			
		||||
        // which is hidden when displaying the overview, so we display a clone.
 | 
			
		||||
        this._background = new Clutter.Clone({ source: global.background_actor });
 | 
			
		||||
        this._background.hide();
 | 
			
		||||
        global.overlay_group.add_actor(this._background);
 | 
			
		||||
 | 
			
		||||
        this._desktopFade = new St.Bin();
 | 
			
		||||
        global.overlay_group.add_actor(this._desktopFade);
 | 
			
		||||
 | 
			
		||||
        this._spacing = 0;
 | 
			
		||||
 | 
			
		||||
        this._group = new St.Group({ name: 'overview',
 | 
			
		||||
                                     reactive: true });
 | 
			
		||||
        this._group = new St.Group({ name: 'overview' });
 | 
			
		||||
        this._group._delegate = this;
 | 
			
		||||
        this._group.connect('style-changed',
 | 
			
		||||
            Lang.bind(this, function() {
 | 
			
		||||
@@ -121,21 +143,15 @@ Overview.prototype = {
 | 
			
		||||
                let spacing = node.get_length('spacing');
 | 
			
		||||
                if (spacing != this._spacing) {
 | 
			
		||||
                    this._spacing = spacing;
 | 
			
		||||
                    this._relayout();
 | 
			
		||||
                    this.relayout();
 | 
			
		||||
                }
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
        this._scrollDirection = SwipeScrollDirection.NONE;
 | 
			
		||||
        this._scrollAdjustment = null;
 | 
			
		||||
        this._capturedEventId = 0;
 | 
			
		||||
        this._buttonPressId = 0;
 | 
			
		||||
        this.shellInfo = new ShellInfo();
 | 
			
		||||
 | 
			
		||||
        this._workspacesDisplay = null;
 | 
			
		||||
 | 
			
		||||
        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.visible = false;
 | 
			
		||||
        this.animationInProgress = false;
 | 
			
		||||
        this._hideInProgress = false;
 | 
			
		||||
 | 
			
		||||
@@ -151,279 +167,31 @@ Overview.prototype = {
 | 
			
		||||
        this._group.hide();
 | 
			
		||||
        global.overlay_group.add_actor(this._group);
 | 
			
		||||
 | 
			
		||||
        this._coverPane.hide();
 | 
			
		||||
 | 
			
		||||
        // XDND
 | 
			
		||||
        this._dragMonitor = {
 | 
			
		||||
            dragMotion: Lang.bind(this, this._onDragMotion)
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        Main.xdndHandler.connect('drag-begin', Lang.bind(this, this._onDragBegin));
 | 
			
		||||
        Main.xdndHandler.connect('drag-end', Lang.bind(this, this._onDragEnd));
 | 
			
		||||
 | 
			
		||||
        this._windowSwitchTimeoutId = 0;
 | 
			
		||||
        this._windowSwitchTimestamp = 0;
 | 
			
		||||
        this._lastActiveWorkspaceIndex = -1;
 | 
			
		||||
        this._lastHoveredWindow = null;
 | 
			
		||||
        this._needsFakePointerEvent = false;
 | 
			
		||||
 | 
			
		||||
        this.workspaces = null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // The members we construct that are implemented in JS might
 | 
			
		||||
    // want to access the overview as Main.overview to connect
 | 
			
		||||
    // signal handlers and so forth. So we create them after
 | 
			
		||||
    // construction in this init() method.
 | 
			
		||||
    init: function() {
 | 
			
		||||
        this.shellInfo = new ShellInfo();
 | 
			
		||||
 | 
			
		||||
        this.viewSelector = new ViewSelector.ViewSelector();
 | 
			
		||||
        this._group.add_actor(this.viewSelector.actor);
 | 
			
		||||
 | 
			
		||||
        this._workspacesDisplay = new WorkspacesView.WorkspacesDisplay();
 | 
			
		||||
        this.viewSelector.addViewTab('windows', _("Windows"), this._workspacesDisplay.actor, 'text-x-generic');
 | 
			
		||||
        this.viewSelector.addViewTab("Windows", this._workspacesDisplay.actor);
 | 
			
		||||
 | 
			
		||||
        let appView = new AppDisplay.AllAppDisplay();
 | 
			
		||||
        this.viewSelector.addViewTab('applications', _("Applications"), appView.actor, 'system-run');
 | 
			
		||||
        this.viewSelector.addViewTab("Applications", appView.actor);
 | 
			
		||||
 | 
			
		||||
        // Default search providers
 | 
			
		||||
        this.viewSelector.addSearchProvider(new AppDisplay.AppSearchProvider());
 | 
			
		||||
        this.viewSelector.addSearchProvider(new AppDisplay.PrefsSearchProvider());
 | 
			
		||||
        this.viewSelector.addSearchProvider(new PlaceDisplay.PlaceSearchProvider());
 | 
			
		||||
        this.viewSelector.addSearchProvider(new DocDisplay.DocSearchProvider());
 | 
			
		||||
        this.viewSelector.addSearchProvider(new WorkspacesView.WindowSearchProvider());
 | 
			
		||||
 | 
			
		||||
        // TODO - recalculate everything when desktop size changes
 | 
			
		||||
        this.dash = new Dash.Dash();
 | 
			
		||||
        this._group.add_actor(this.dash.actor);
 | 
			
		||||
        this.dash.actor.add_constraint(this.viewSelector.constrainY);
 | 
			
		||||
        this.dash.actor.add_constraint(this.viewSelector.constrainHeight);
 | 
			
		||||
        this._dash = new Dash.Dash();
 | 
			
		||||
        this._group.add_actor(this._dash.actor);
 | 
			
		||||
        this._dash.actor.add_constraint(this.viewSelector.constrainY);
 | 
			
		||||
        this._dash.actor.add_constraint(this.viewSelector.constrainHeight);
 | 
			
		||||
 | 
			
		||||
        // 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');
 | 
			
		||||
        this._coverPane.lower_bottom();
 | 
			
		||||
 | 
			
		||||
        Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._relayout));
 | 
			
		||||
        this._relayout();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDragBegin: function() {
 | 
			
		||||
        DND.addDragMonitor(this._dragMonitor);
 | 
			
		||||
        // Remember the workspace we started from
 | 
			
		||||
        this._lastActiveWorkspaceIndex = global.screen.get_active_workspace_index();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDragEnd: function(time) {
 | 
			
		||||
        // 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._shownTemporarily)  {
 | 
			
		||||
            global.screen.get_workspace_by_index(this._lastActiveWorkspaceIndex).activate(time);
 | 
			
		||||
            this.hideTemporarily();
 | 
			
		||||
        }
 | 
			
		||||
        this._resetWindowSwitchTimeout();
 | 
			
		||||
        this._lastHoveredWindow = null;
 | 
			
		||||
        DND.removeDragMonitor(this._dragMonitor);
 | 
			
		||||
        this.endItemDrag();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _resetWindowSwitchTimeout: function() {
 | 
			
		||||
        if (this._windowSwitchTimeoutId != 0) {
 | 
			
		||||
            Mainloop.source_remove(this._windowSwitchTimeoutId);
 | 
			
		||||
            this._windowSwitchTimeoutId = 0;
 | 
			
		||||
            this._needsFakePointerEvent = false;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _fakePointerEvent: function() {
 | 
			
		||||
        let display = Gdk.Display.get_default();
 | 
			
		||||
        let deviceManager = display.get_device_manager();
 | 
			
		||||
        let pointer = deviceManager.get_client_pointer();
 | 
			
		||||
        let [screen, pointerX, pointerY] = pointer.get_position();
 | 
			
		||||
 | 
			
		||||
        pointer.warp(screen, pointerX, pointerY);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDragMotion: function(dragEvent) {
 | 
			
		||||
        let targetIsWindow = dragEvent.targetActor &&
 | 
			
		||||
                             dragEvent.targetActor._delegate &&
 | 
			
		||||
                             dragEvent.targetActor._delegate.metaWindow &&
 | 
			
		||||
                             !(dragEvent.targetActor._delegate instanceof WorkspaceThumbnail.WindowClone);
 | 
			
		||||
 | 
			
		||||
        this._windowSwitchTimestamp = global.get_current_time();
 | 
			
		||||
 | 
			
		||||
        if (targetIsWindow &&
 | 
			
		||||
            dragEvent.targetActor._delegate.metaWindow == this._lastHoveredWindow)
 | 
			
		||||
            return DND.DragMotionResult.CONTINUE;
 | 
			
		||||
 | 
			
		||||
        this._lastHoveredWindow = null;
 | 
			
		||||
 | 
			
		||||
        this._resetWindowSwitchTimeout();
 | 
			
		||||
 | 
			
		||||
        if (targetIsWindow) {
 | 
			
		||||
            this._lastHoveredWindow = dragEvent.targetActor._delegate.metaWindow;
 | 
			
		||||
            this._windowSwitchTimeoutId = Mainloop.timeout_add(DND_WINDOW_SWITCH_TIMEOUT,
 | 
			
		||||
                                            Lang.bind(this, function() {
 | 
			
		||||
                                                this._needsFakePointerEvent = true;
 | 
			
		||||
                                                Main.activateWindow(dragEvent.targetActor._delegate.metaWindow,
 | 
			
		||||
                                                                    this._windowSwitchTimestamp);
 | 
			
		||||
                                                this.hideTemporarily();
 | 
			
		||||
                                                this._lastHoveredWindow = null;
 | 
			
		||||
                                            }));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return DND.DragMotionResult.CONTINUE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setScrollAdjustment: function(adjustment, direction) {
 | 
			
		||||
        this._scrollAdjustment = adjustment;
 | 
			
		||||
        if (this._scrollAdjustment == null)
 | 
			
		||||
            this._scrollDirection = SwipeScrollDirection.NONE;
 | 
			
		||||
        else
 | 
			
		||||
            this._scrollDirection = direction;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onButtonPress: function(actor, event) {
 | 
			
		||||
        if (this._scrollDirection == SwipeScrollDirection.NONE
 | 
			
		||||
            || event.get_button() != 1)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let [stageX, stageY] = event.get_coords();
 | 
			
		||||
        this._dragStartX = this._dragX = stageX;
 | 
			
		||||
        this._dragStartY = this._dragY = stageY;
 | 
			
		||||
        this._dragStartValue = this._scrollAdjustment.value;
 | 
			
		||||
        this._lastMotionTime = -1; // used to track "stopping" while swipe-scrolling
 | 
			
		||||
        this._capturedEventId = global.stage.connect('captured-event',
 | 
			
		||||
            Lang.bind(this, this._onCapturedEvent));
 | 
			
		||||
        this.emit('swipe-scroll-begin');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onCapturedEvent: function(actor, event) {
 | 
			
		||||
        let stageX, stageY;
 | 
			
		||||
        let threshold = Gtk.Settings.get_default().gtk_dnd_drag_threshold;
 | 
			
		||||
 | 
			
		||||
        switch(event.type()) {
 | 
			
		||||
            case Clutter.EventType.BUTTON_RELEASE:
 | 
			
		||||
                [stageX, stageY] = event.get_coords();
 | 
			
		||||
 | 
			
		||||
                // default to snapping back to the original value
 | 
			
		||||
                let newValue = this._dragStartValue;
 | 
			
		||||
 | 
			
		||||
                let minValue = this._scrollAdjustment.lower;
 | 
			
		||||
                let maxValue = this._scrollAdjustment.upper - this._scrollAdjustment.page_size;
 | 
			
		||||
 | 
			
		||||
                let direction;
 | 
			
		||||
                if (this._scrollDirection == SwipeScrollDirection.HORIZONTAL) {
 | 
			
		||||
                    direction = stageX > this._dragStartX ? -1 : 1;
 | 
			
		||||
                    if (St.Widget.get_default_direction() == St.TextDirection.RTL)
 | 
			
		||||
                        direction *= -1;
 | 
			
		||||
                } else {
 | 
			
		||||
                    direction = stageY > this._dragStartY ? -1 : 1;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // We default to scroll a full page size; both the first
 | 
			
		||||
                // and the last page may be smaller though, so we need to
 | 
			
		||||
                // adjust difference in those cases.
 | 
			
		||||
                let difference = direction * this._scrollAdjustment.page_size;
 | 
			
		||||
                if (this._dragStartValue + difference > maxValue)
 | 
			
		||||
                    difference = maxValue - this._dragStartValue;
 | 
			
		||||
                else if (this._dragStartValue + difference < minValue)
 | 
			
		||||
                    difference = minValue - this._dragStartValue;
 | 
			
		||||
 | 
			
		||||
                // If the user has moved more than half the scroll
 | 
			
		||||
                // difference, we want to "settle" to the new value
 | 
			
		||||
                // even if the user stops dragging rather "throws" by
 | 
			
		||||
                // releasing during the drag.
 | 
			
		||||
                let distance = this._dragStartValue - this._scrollAdjustment.value;
 | 
			
		||||
                let noStop = Math.abs(distance / difference) > 0.5;
 | 
			
		||||
 | 
			
		||||
                // We detect if the user is stopped by comparing the
 | 
			
		||||
                // timestamp of the button release with the timestamp of
 | 
			
		||||
                // the last motion. Experimentally, a difference of 0 or 1
 | 
			
		||||
                // millisecond indicates that the mouse is in motion, a
 | 
			
		||||
                // larger difference indicates that the mouse is stopped.
 | 
			
		||||
                if ((this._lastMotionTime > 0 &&
 | 
			
		||||
                     this._lastMotionTime > event.get_time() - 2) ||
 | 
			
		||||
                    noStop) {
 | 
			
		||||
                    if (this._dragStartValue + difference >= minValue &&
 | 
			
		||||
                        this._dragStartValue + difference <= maxValue)
 | 
			
		||||
                        newValue += difference;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                let result;
 | 
			
		||||
 | 
			
		||||
                // See if the user has moved the mouse enough to trigger
 | 
			
		||||
                // a drag
 | 
			
		||||
                if (Math.abs(stageX - this._dragStartX) < threshold &&
 | 
			
		||||
                    Math.abs(stageY - this._dragStartY) < threshold) {
 | 
			
		||||
                    // no motion? It's a click!
 | 
			
		||||
                    result = SwipeScrollResult.CLICK;
 | 
			
		||||
                    this.emit('swipe-scroll-end', result);
 | 
			
		||||
                } else {
 | 
			
		||||
                    if (newValue == this._dragStartValue)
 | 
			
		||||
                        result = SwipeScrollResult.CANCEL;
 | 
			
		||||
                    else
 | 
			
		||||
                        result = SwipeScrollResult.SWIPE;
 | 
			
		||||
 | 
			
		||||
                    // The event capture handler is disconnected
 | 
			
		||||
                    // while scrolling to the final position, so
 | 
			
		||||
                    // to avoid undesired prelights we raise
 | 
			
		||||
                    // the cover pane.
 | 
			
		||||
                    this._coverPane.raise_top();
 | 
			
		||||
                    this._coverPane.show();
 | 
			
		||||
 | 
			
		||||
                    Tweener.addTween(this._scrollAdjustment,
 | 
			
		||||
                                     { value: newValue,
 | 
			
		||||
                                       time: ANIMATION_TIME,
 | 
			
		||||
                                       transition: 'easeOutQuad',
 | 
			
		||||
                                       onCompleteScope: this,
 | 
			
		||||
                                       onComplete: function() {
 | 
			
		||||
                                          this._coverPane.hide();
 | 
			
		||||
                                          this.emit('swipe-scroll-end',
 | 
			
		||||
                                                    result);
 | 
			
		||||
                                       }
 | 
			
		||||
                                     });
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                global.stage.disconnect(this._capturedEventId);
 | 
			
		||||
                this._capturedEventId = 0;
 | 
			
		||||
 | 
			
		||||
                return result != SwipeScrollResult.CLICK;
 | 
			
		||||
 | 
			
		||||
            case Clutter.EventType.MOTION:
 | 
			
		||||
                [stageX, stageY] = event.get_coords();
 | 
			
		||||
                let dx = this._dragX - stageX;
 | 
			
		||||
                let dy = this._dragY - stageY;
 | 
			
		||||
                let primary = Main.layoutManager.primaryMonitor;
 | 
			
		||||
 | 
			
		||||
                this._dragX = stageX;
 | 
			
		||||
                this._dragY = stageY;
 | 
			
		||||
                this._lastMotionTime = event.get_time();
 | 
			
		||||
 | 
			
		||||
                // See if the user has moved the mouse enough to trigger
 | 
			
		||||
                // a drag
 | 
			
		||||
                if (Math.abs(stageX - this._dragStartX) < threshold &&
 | 
			
		||||
                    Math.abs(stageY - this._dragStartY) < threshold)
 | 
			
		||||
                    return true;
 | 
			
		||||
 | 
			
		||||
                if (this._scrollDirection == SwipeScrollDirection.HORIZONTAL) {
 | 
			
		||||
                    if (St.Widget.get_default_direction() == St.TextDirection.RTL)
 | 
			
		||||
                        this._scrollAdjustment.value -= (dx / primary.width) * this._scrollAdjustment.page_size;
 | 
			
		||||
                    else
 | 
			
		||||
                        this._scrollAdjustment.value += (dx / primary.width) * this._scrollAdjustment.page_size;
 | 
			
		||||
                } else {
 | 
			
		||||
                    this._scrollAdjustment.value += (dy / primary.height) * this._scrollAdjustment.page_size;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return true;
 | 
			
		||||
 | 
			
		||||
            // Block enter/leave events to avoid prelights
 | 
			
		||||
            // during swipe-scroll
 | 
			
		||||
            case Clutter.EventType.ENTER:
 | 
			
		||||
            case Clutter.EventType.LEAVE:
 | 
			
		||||
                return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
        this.workspaces = null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getDesktopClone: function() {
 | 
			
		||||
@@ -440,16 +208,11 @@ Overview.prototype = {
 | 
			
		||||
        return clone;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _relayout: function () {
 | 
			
		||||
        // To avoid updating the position and size of the workspaces
 | 
			
		||||
        // we just hide the overview. The positions will be updated
 | 
			
		||||
        // when it is next shown.
 | 
			
		||||
        this.hide();
 | 
			
		||||
 | 
			
		||||
        let primary = Main.layoutManager.primaryMonitor;
 | 
			
		||||
    relayout: function () {
 | 
			
		||||
        let primary = global.get_primary_monitor();
 | 
			
		||||
        let rtl = (St.Widget.get_default_direction () == St.TextDirection.RTL);
 | 
			
		||||
 | 
			
		||||
        let contentY = Main.panel.actor.height;
 | 
			
		||||
        let contentY = Panel.PANEL_HEIGHT;
 | 
			
		||||
        let contentHeight = primary.height - contentY - Main.messageTray.actor.height;
 | 
			
		||||
 | 
			
		||||
        this._group.set_position(primary.x, primary.y);
 | 
			
		||||
@@ -458,21 +221,21 @@ Overview.prototype = {
 | 
			
		||||
        this._coverPane.set_position(0, contentY);
 | 
			
		||||
        this._coverPane.set_size(primary.width, contentHeight);
 | 
			
		||||
 | 
			
		||||
        let dashWidth = Math.round(DASH_SPLIT_FRACTION * primary.width);
 | 
			
		||||
        let viewWidth = primary.width - dashWidth - this._spacing;
 | 
			
		||||
        let viewWidth = (1.0 - DASH_SPLIT_FRACTION) * primary.width - this._spacing;
 | 
			
		||||
        let viewHeight = contentHeight - 2 * this._spacing;
 | 
			
		||||
        let viewY = contentY + this._spacing;
 | 
			
		||||
        let viewX = rtl ? 0 : dashWidth + this._spacing;
 | 
			
		||||
        let viewX = rtl ? 0
 | 
			
		||||
                        : Math.floor(DASH_SPLIT_FRACTION * primary.width) + this._spacing;
 | 
			
		||||
 | 
			
		||||
        // Set the dash's x position - y is handled by a constraint
 | 
			
		||||
        let dashX;
 | 
			
		||||
        if (rtl) {
 | 
			
		||||
            this.dash.actor.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
 | 
			
		||||
            this._dash.actor.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
 | 
			
		||||
            dashX = primary.width;
 | 
			
		||||
        } else {
 | 
			
		||||
            dashX = 0;
 | 
			
		||||
        }
 | 
			
		||||
        this.dash.actor.set_x(dashX);
 | 
			
		||||
        this._dash.actor.set_x(dashX);
 | 
			
		||||
 | 
			
		||||
        this.viewSelector.actor.set_position(viewX, viewY);
 | 
			
		||||
        this.viewSelector.actor.set_size(viewWidth, viewHeight);
 | 
			
		||||
@@ -484,10 +247,6 @@ Overview.prototype = {
 | 
			
		||||
        this.emit('item-drag-begin');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    cancelledItemDrag: function(source) {
 | 
			
		||||
        this.emit('item-drag-cancelled');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    endItemDrag: function(source) {
 | 
			
		||||
        this.emit('item-drag-end');
 | 
			
		||||
    },
 | 
			
		||||
@@ -496,33 +255,39 @@ Overview.prototype = {
 | 
			
		||||
        this.emit('window-drag-begin');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    cancelledWindowDrag: function(source) {
 | 
			
		||||
        this.emit('window-drag-cancelled');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    endWindowDrag: function(source) {
 | 
			
		||||
        this.emit('window-drag-end');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // show:
 | 
			
		||||
    //
 | 
			
		||||
    // Animates the overview visible and grabs mouse and keyboard input
 | 
			
		||||
    show : function() {
 | 
			
		||||
        if (this._shown)
 | 
			
		||||
            return;
 | 
			
		||||
        // Do this manually instead of using _syncInputMode, to handle failure
 | 
			
		||||
        if (!Main.pushModal(this._group))
 | 
			
		||||
            return;
 | 
			
		||||
        this._modal = true;
 | 
			
		||||
        this._animateVisible();
 | 
			
		||||
        this._shown = true;
 | 
			
		||||
 | 
			
		||||
        this._buttonPressId = this._group.connect('button-press-event',
 | 
			
		||||
            Lang.bind(this, this._onButtonPress));
 | 
			
		||||
    // Returns the scale the Overview has when we just start zooming out
 | 
			
		||||
    // to overview mode. That is, when just the active workspace is showing.
 | 
			
		||||
    getZoomedInScale : function() {
 | 
			
		||||
        return 1 / this.workspaces.getScale();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _animateVisible: function() {
 | 
			
		||||
        if (this.visible || this.animationInProgress)
 | 
			
		||||
    // Returns the position the Overview has when we just start zooming out
 | 
			
		||||
    // to overview mode. That is, when just the active workspace is showing.
 | 
			
		||||
    getZoomedInPosition : function() {
 | 
			
		||||
        let [posX, posY] = this.workspaces.getActiveWorkspacePosition();
 | 
			
		||||
        let scale = this.getZoomedInScale();
 | 
			
		||||
 | 
			
		||||
        return [- posX * scale, - posY * scale];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Returns the current scale of the Overview.
 | 
			
		||||
    getScale : function() {
 | 
			
		||||
        return this.workspaces.actor.scaleX;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Returns the current position of the Overview.
 | 
			
		||||
    getPosition : function() {
 | 
			
		||||
        return [this.workspaces.actor.x, this.workspaces.actor.y];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    show : function() {
 | 
			
		||||
        if (this.visible)
 | 
			
		||||
            return;
 | 
			
		||||
        if (!Main.pushModal(this.viewSelector.actor))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this.visible = true;
 | 
			
		||||
@@ -539,7 +304,9 @@ Overview.prototype = {
 | 
			
		||||
        this._group.show();
 | 
			
		||||
        this._background.show();
 | 
			
		||||
 | 
			
		||||
        this.viewSelector.show();
 | 
			
		||||
        this._workspacesDisplay.show();
 | 
			
		||||
        this._dash.show();
 | 
			
		||||
 | 
			
		||||
        this.workspaces = this._workspacesDisplay.workspacesView;
 | 
			
		||||
        global.overlay_group.add_actor(this.workspaces.actor);
 | 
			
		||||
@@ -557,108 +324,38 @@ Overview.prototype = {
 | 
			
		||||
                             });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._group.opacity = 0;
 | 
			
		||||
        Tweener.addTween(this._group,
 | 
			
		||||
                         { opacity: 255,
 | 
			
		||||
        // Create a zoom out effect. First scale the workspaces view up and
 | 
			
		||||
        // position it so that the active workspace fills up the whole screen,
 | 
			
		||||
        // then transform it to its normal dimensions and position.
 | 
			
		||||
        // The opposite transition is used in hide().
 | 
			
		||||
        this.workspaces.actor.scaleX = this.workspaces.actor.scaleY = this.getZoomedInScale();
 | 
			
		||||
        [this.workspaces.actor.x, this.workspaces.actor.y] = this.getZoomedInPosition();
 | 
			
		||||
        let primary = global.get_primary_monitor();
 | 
			
		||||
        Tweener.addTween(this.workspaces.actor,
 | 
			
		||||
                         { x: primary.x - this._group.x,
 | 
			
		||||
                           y: primary.y - this._group.y,
 | 
			
		||||
                           scaleX: 1,
 | 
			
		||||
                           scaleY: 1,
 | 
			
		||||
                           transition: 'easeOutQuad',
 | 
			
		||||
                           time: ANIMATION_TIME,
 | 
			
		||||
                           onComplete: this._showDone,
 | 
			
		||||
                           onCompleteScope: this
 | 
			
		||||
                          });
 | 
			
		||||
 | 
			
		||||
        // Make the other elements fade in.
 | 
			
		||||
        this._group.opacity = 0;
 | 
			
		||||
        Tweener.addTween(this._group,
 | 
			
		||||
                         { opacity: 255,
 | 
			
		||||
                           transition: 'easeOutQuad',
 | 
			
		||||
                           time: ANIMATION_TIME
 | 
			
		||||
                         });
 | 
			
		||||
 | 
			
		||||
        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._shownTemporarily)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._syncInputMode();
 | 
			
		||||
        this._animateVisible();
 | 
			
		||||
        this._shownTemporarily = true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // hide:
 | 
			
		||||
    //
 | 
			
		||||
    // Reverses the effect of show()
 | 
			
		||||
    hide: function() {
 | 
			
		||||
        if (!this._shown)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (!this._shownTemporarily)
 | 
			
		||||
            this._animateNotVisible();
 | 
			
		||||
 | 
			
		||||
        this._shown = false;
 | 
			
		||||
        this._syncInputMode();
 | 
			
		||||
 | 
			
		||||
        if (this._buttonPressId > 0)
 | 
			
		||||
            this._group.disconnect(this._buttonPressId);
 | 
			
		||||
        this._buttonPressId = 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // hideTemporarily:
 | 
			
		||||
    //
 | 
			
		||||
    // Reverses the effect of showTemporarily()
 | 
			
		||||
    hideTemporarily: function() {
 | 
			
		||||
        if (!this._shownTemporarily)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (!this._shown)
 | 
			
		||||
            this._animateNotVisible();
 | 
			
		||||
 | 
			
		||||
        this._shownTemporarily = false;
 | 
			
		||||
        this._syncInputMode();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    toggle: function() {
 | 
			
		||||
        if (this._shown)
 | 
			
		||||
            this.hide();
 | 
			
		||||
        else
 | 
			
		||||
            this.show();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    //// Private methods ////
 | 
			
		||||
 | 
			
		||||
    _syncInputMode: function() {
 | 
			
		||||
        // We delay input mode changes during animation so that when removing the
 | 
			
		||||
        // overview we don't have a problem with the release of a press/release
 | 
			
		||||
        // going to an application.
 | 
			
		||||
        if (this.animationInProgress)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (this._shown) {
 | 
			
		||||
            if (!this._modal) {
 | 
			
		||||
                if (Main.pushModal(this._group))
 | 
			
		||||
                    this._modal = true;
 | 
			
		||||
                else
 | 
			
		||||
                    this.hide();
 | 
			
		||||
            }
 | 
			
		||||
        } else if (this._shownTemporarily) {
 | 
			
		||||
            if (this._modal) {
 | 
			
		||||
                Main.popModal(this._group);
 | 
			
		||||
                this._modal = false;
 | 
			
		||||
            }
 | 
			
		||||
            global.stage_input_mode = Shell.StageInputMode.FULLSCREEN;
 | 
			
		||||
        } else {
 | 
			
		||||
            if (this._modal) {
 | 
			
		||||
                Main.popModal(this._group);
 | 
			
		||||
                this._modal = false;
 | 
			
		||||
            }
 | 
			
		||||
            else if (global.stage_input_mode == Shell.StageInputMode.FULLSCREEN)
 | 
			
		||||
                global.stage_input_mode = Shell.StageInputMode.NORMAL;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _animateNotVisible: function() {
 | 
			
		||||
        if (!this.visible || this.animationInProgress)
 | 
			
		||||
        if (!this.visible || this._hideInProgress)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this.animationInProgress = true;
 | 
			
		||||
@@ -675,32 +372,63 @@ Overview.prototype = {
 | 
			
		||||
 | 
			
		||||
        this.workspaces.hide();
 | 
			
		||||
 | 
			
		||||
        // Make other elements fade out.
 | 
			
		||||
        Tweener.addTween(this._group,
 | 
			
		||||
                         { opacity: 0,
 | 
			
		||||
        // Create a zoom in effect by transforming the workspaces view so that
 | 
			
		||||
        // the active workspace fills up the whole screen. The opposite
 | 
			
		||||
        // transition is used in show().
 | 
			
		||||
        let scale = this.getZoomedInScale();
 | 
			
		||||
        let [posX, posY] = this.getZoomedInPosition();
 | 
			
		||||
        Tweener.addTween(this.workspaces.actor,
 | 
			
		||||
                         { x: posX,
 | 
			
		||||
                           y: posY,
 | 
			
		||||
                           scaleX: scale,
 | 
			
		||||
                           scaleY: scale,
 | 
			
		||||
                           transition: 'easeOutQuad',
 | 
			
		||||
                           time: ANIMATION_TIME,
 | 
			
		||||
                           onComplete: this._hideDone,
 | 
			
		||||
                           onCompleteScope: this
 | 
			
		||||
                          });
 | 
			
		||||
 | 
			
		||||
        // Make other elements fade out.
 | 
			
		||||
        Tweener.addTween(this._group,
 | 
			
		||||
                         { opacity: 0,
 | 
			
		||||
                           transition: 'easeOutQuad',
 | 
			
		||||
                           time: ANIMATION_TIME
 | 
			
		||||
                         });
 | 
			
		||||
 | 
			
		||||
        this._coverPane.raise_top();
 | 
			
		||||
        this._coverPane.show();
 | 
			
		||||
        this.emit('hiding');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    toggle: function() {
 | 
			
		||||
        if (this.visible)
 | 
			
		||||
            this.hide();
 | 
			
		||||
        else
 | 
			
		||||
            this.show();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * getWorkspacesForWindow:
 | 
			
		||||
     * @metaWindow: A #MetaWindow
 | 
			
		||||
     *
 | 
			
		||||
     * Returns the Workspaces object associated with the given window.
 | 
			
		||||
     * This method is not be accessible if the overview is not open
 | 
			
		||||
     * and will return %null.
 | 
			
		||||
     */
 | 
			
		||||
    getWorkspacesForWindow: function(metaWindow) {
 | 
			
		||||
        return this.workspaces;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    //// Private methods ////
 | 
			
		||||
 | 
			
		||||
    _showDone: function() {
 | 
			
		||||
        if (this._hideInProgress)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this.animationInProgress = false;
 | 
			
		||||
        this._desktopFade.hide();
 | 
			
		||||
        this._coverPane.hide();
 | 
			
		||||
        this._coverPane.lower_bottom();
 | 
			
		||||
 | 
			
		||||
        this.emit('shown');
 | 
			
		||||
        // Handle any calls to hide* while we were showing
 | 
			
		||||
        if (!this._shown && !this._shownTemporarily)
 | 
			
		||||
            this._animateNotVisible();
 | 
			
		||||
 | 
			
		||||
        this._syncInputMode();
 | 
			
		||||
        global.sync_pointer();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _hideDone: function() {
 | 
			
		||||
@@ -710,6 +438,8 @@ Overview.prototype = {
 | 
			
		||||
        this.workspaces = null;
 | 
			
		||||
 | 
			
		||||
        this._workspacesDisplay.hide();
 | 
			
		||||
        this.viewSelector.hide();
 | 
			
		||||
        this._dash.hide();
 | 
			
		||||
 | 
			
		||||
        this._desktopFade.hide();
 | 
			
		||||
        this._background.hide();
 | 
			
		||||
@@ -719,20 +449,10 @@ Overview.prototype = {
 | 
			
		||||
        this.animationInProgress = false;
 | 
			
		||||
        this._hideInProgress = false;
 | 
			
		||||
 | 
			
		||||
        this._coverPane.hide();
 | 
			
		||||
        this._coverPane.lower_bottom();
 | 
			
		||||
 | 
			
		||||
        Main.popModal(this.viewSelector.actor);
 | 
			
		||||
        this.emit('hidden');
 | 
			
		||||
        // Handle any calls to show* while we were hiding
 | 
			
		||||
        if (this._shown || this._shownTemporarily)
 | 
			
		||||
            this._animateVisible();
 | 
			
		||||
 | 
			
		||||
        this._syncInputMode();
 | 
			
		||||
 | 
			
		||||
        // Fake a pointer event if requested
 | 
			
		||||
        if (this._needsFakePointerEvent) {
 | 
			
		||||
            this._fakePointerEvent();
 | 
			
		||||
            this._needsFakePointerEvent = false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
Signals.addSignalMethods(Overview.prototype);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										956
									
								
								js/ui/panel.js
									
									
									
									
									
								
							
							
						
						@@ -1,9 +1,7 @@
 | 
			
		||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Gtk = imports.gi.Gtk;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const PopupMenu = imports.ui.popupMenu;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
@@ -22,63 +20,40 @@ Button.prototype = {
 | 
			
		||||
                                  track_hover: true });
 | 
			
		||||
        this.actor._delegate = this;
 | 
			
		||||
        this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
 | 
			
		||||
        this.actor.connect('key-press-event', Lang.bind(this, this._onSourceKeyPress));
 | 
			
		||||
        this.menu = new PopupMenu.PopupMenu(this.actor, menuAlignment, St.Side.TOP, 0);
 | 
			
		||||
        this.actor.connect('key-press-event', Lang.bind(this, this._onKeyPress));
 | 
			
		||||
        this.menu = new PopupMenu.PopupMenu(this.actor, menuAlignment, St.Side.TOP, /* FIXME */ 0);
 | 
			
		||||
        this.menu.connect('open-state-changed', Lang.bind(this, this._onOpenStateChanged));
 | 
			
		||||
        this.menu.actor.connect('key-press-event', Lang.bind(this, this._onMenuKeyPress));
 | 
			
		||||
        Main.chrome.addActor(this.menu.actor, { affectsStruts: false });
 | 
			
		||||
        Main.chrome.addActor(this.menu.actor, { visibleInOverview: true,
 | 
			
		||||
                                                affectsStruts: false });
 | 
			
		||||
        this.menu.actor.hide();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onButtonPress: function(actor, event) {
 | 
			
		||||
        if (!this.menu.isOpen) {
 | 
			
		||||
            // 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 monitor = Main.layoutManager.primaryMonitor;
 | 
			
		||||
            this.menu.actor.style = ('max-height: ' +
 | 
			
		||||
                                     Math.round(monitor.height - Main.panel.actor.height) +
 | 
			
		||||
                                     'px;');
 | 
			
		||||
        }
 | 
			
		||||
        this.menu.toggle();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onSourceKeyPress: function(actor, event) {
 | 
			
		||||
    _onKeyPress: function(actor, event) {
 | 
			
		||||
        let symbol = event.get_key_symbol();
 | 
			
		||||
        if (symbol == Clutter.KEY_space || symbol == Clutter.KEY_Return) {
 | 
			
		||||
            this.menu.toggle();
 | 
			
		||||
            return true;
 | 
			
		||||
        } else if (symbol == Clutter.KEY_Escape && this.menu.isOpen) {
 | 
			
		||||
            this.menu.close();
 | 
			
		||||
            return true;
 | 
			
		||||
        } else if (symbol == Clutter.KEY_Down) {
 | 
			
		||||
            if (!this.menu.isOpen)
 | 
			
		||||
                this.menu.toggle();
 | 
			
		||||
            this.menu.actor.navigate_focus(this.actor, Gtk.DirectionType.DOWN, false);
 | 
			
		||||
            this.menu.activateFirst();
 | 
			
		||||
            return true;
 | 
			
		||||
        } else
 | 
			
		||||
            return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onMenuKeyPress: function(actor, event) {
 | 
			
		||||
        let symbol = event.get_key_symbol();
 | 
			
		||||
        if (symbol == Clutter.KEY_Left || symbol == Clutter.KEY_Right) {
 | 
			
		||||
            let focusManager = St.FocusManager.get_for_stage(global.stage);
 | 
			
		||||
            let group = focusManager.get_group(this.actor);
 | 
			
		||||
            if (group) {
 | 
			
		||||
                let direction = (symbol == Clutter.KEY_Left) ? Gtk.DirectionType.LEFT : Gtk.DirectionType.RIGHT;
 | 
			
		||||
                group.navigate_focus(this.actor, direction, false);
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onOpenStateChanged: function(menu, open) {
 | 
			
		||||
        if (open)
 | 
			
		||||
            this.actor.add_style_pseudo_class('active');
 | 
			
		||||
        else
 | 
			
		||||
            this.actor.remove_style_pseudo_class('active');
 | 
			
		||||
        if (open) {
 | 
			
		||||
            this.actor.add_style_pseudo_class('pressed');
 | 
			
		||||
            let focus = global.stage.get_key_focus();
 | 
			
		||||
            if (!focus || (focus != this.actor && !menu.contains(focus)))
 | 
			
		||||
                this.actor.grab_key_focus();
 | 
			
		||||
        } else
 | 
			
		||||
            this.actor.remove_style_pseudo_class('pressed');
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -96,7 +71,7 @@ SystemStatusButton.prototype = {
 | 
			
		||||
    __proto__: Button.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function(iconName,tooltipText) {
 | 
			
		||||
        Button.prototype._init.call(this, 0.0);
 | 
			
		||||
        Button.prototype._init.call(this, St.Align.START);
 | 
			
		||||
        this._iconActor = new St.Icon({ icon_name: iconName,
 | 
			
		||||
                                        icon_type: St.IconType.SYMBOLIC,
 | 
			
		||||
                                        style_class: 'system-status-icon' });
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
 | 
			
		||||
 | 
			
		||||
const GConf = imports.gi.GConf;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
@@ -7,12 +8,17 @@ const Lang = imports.lang;
 | 
			
		||||
const Mainloop = imports.mainloop;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Gettext = imports.gettext.domain('gnome-shell');
 | 
			
		||||
const _ = Gettext.gettext;
 | 
			
		||||
 | 
			
		||||
const DND = imports.ui.dnd;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
const Search = imports.ui.search;
 | 
			
		||||
const Util = imports.misc.util;
 | 
			
		||||
 | 
			
		||||
const NAUTILUS_PREFS_DIR = '/apps/nautilus/preferences';
 | 
			
		||||
const DESKTOP_IS_HOME_KEY = NAUTILUS_PREFS_DIR + '/desktop_is_home_dir';
 | 
			
		||||
 | 
			
		||||
const PLACES_ICON_SIZE = 16;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Represents a place object, which is most normally a bookmark entry,
 | 
			
		||||
@@ -57,21 +63,6 @@ PlaceInfo.prototype = {
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Helper function to translate launch parameters into a GAppLaunchContext
 | 
			
		||||
function _makeLaunchContext(params)
 | 
			
		||||
{
 | 
			
		||||
    params = Params.parse(params, { workspace: null,
 | 
			
		||||
                                    timestamp: null });
 | 
			
		||||
 | 
			
		||||
    let launchContext = global.create_app_launch_context();
 | 
			
		||||
    if (params.workspace != null)
 | 
			
		||||
        launchContext.set_desktop(params.workspace.index());
 | 
			
		||||
    if (params.timestamp != null)
 | 
			
		||||
        launchContext.set_timestamp(params.timestamp);
 | 
			
		||||
 | 
			
		||||
    return launchContext;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function PlaceDeviceInfo(mount) {
 | 
			
		||||
    this._init(mount);
 | 
			
		||||
}
 | 
			
		||||
@@ -91,9 +82,9 @@ PlaceDeviceInfo.prototype = {
 | 
			
		||||
        return St.TextureCache.get_default().load_gicon(null, icon, size);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    launch: function(params) {
 | 
			
		||||
    launch: function() {
 | 
			
		||||
        Gio.app_info_launch_default_for_uri(this._mount.get_root().get_uri(),
 | 
			
		||||
                                            _makeLaunchContext(params));
 | 
			
		||||
                                            global.create_app_launch_context());
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    isRemovable: function() {
 | 
			
		||||
@@ -125,15 +116,20 @@ PlaceDeviceInfo.prototype = {
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
function PlacesManager() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PlacesManager.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        let gconf = GConf.Client.get_default();
 | 
			
		||||
        gconf.add_dir(NAUTILUS_PREFS_DIR, GConf.ClientPreloadType.PRELOAD_NONE);
 | 
			
		||||
 | 
			
		||||
        this._defaultPlaces = [];
 | 
			
		||||
        this._mounts = [];
 | 
			
		||||
        this._bookmarks = [];
 | 
			
		||||
        this._isDesktopHome = gconf.get_bool(DESKTOP_IS_HOME_KEY);
 | 
			
		||||
 | 
			
		||||
        let homeFile = Gio.file_new_for_path (GLib.get_home_dir());
 | 
			
		||||
        let homeUri = homeFile.get_uri();
 | 
			
		||||
@@ -143,8 +139,8 @@ PlacesManager.prototype = {
 | 
			
		||||
            function(size) {
 | 
			
		||||
                return St.TextureCache.get_default().load_gicon(null, homeIcon, size);
 | 
			
		||||
            },
 | 
			
		||||
            function(params) {
 | 
			
		||||
                Gio.app_info_launch_default_for_uri(homeUri, _makeLaunchContext(params));
 | 
			
		||||
            function() {
 | 
			
		||||
                Gio.app_info_launch_default_for_uri(homeUri, global.create_app_launch_context());
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        let desktopPath = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DESKTOP);
 | 
			
		||||
@@ -156,8 +152,8 @@ PlacesManager.prototype = {
 | 
			
		||||
            function(size) {
 | 
			
		||||
                return St.TextureCache.get_default().load_gicon(null, desktopIcon, size);
 | 
			
		||||
            },
 | 
			
		||||
            function(params) {
 | 
			
		||||
                Gio.app_info_launch_default_for_uri(desktopUri, _makeLaunchContext(params));
 | 
			
		||||
            function() {
 | 
			
		||||
                Gio.app_info_launch_default_for_uri(desktopUri, global.create_app_launch_context());
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        this._connect = new PlaceInfo('special:connect', _("Connect to..."),
 | 
			
		||||
@@ -166,16 +162,40 @@ PlacesManager.prototype = {
 | 
			
		||||
                                     icon_type: St.IconType.FULLCOLOR,
 | 
			
		||||
                                     icon_size: size });
 | 
			
		||||
            },
 | 
			
		||||
            function (params) {
 | 
			
		||||
                // BUG: nautilus-connect-server doesn't have a desktop file, so we can't
 | 
			
		||||
                // launch it with the workspace from params. It's probably pretty rare
 | 
			
		||||
                // and odd to drag this place onto a workspace in any case
 | 
			
		||||
 | 
			
		||||
                Util.spawn(['nautilus-connect-server']);
 | 
			
		||||
            function () {
 | 
			
		||||
                new Shell.Process({ args: ['nautilus-connect-server'] }).run();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        let networkApp = null;
 | 
			
		||||
        try {
 | 
			
		||||
            networkApp = Shell.AppSystem.get_default().load_from_desktop_file('gnome-network-scheme.desktop');
 | 
			
		||||
        } catch(e) {
 | 
			
		||||
            try {
 | 
			
		||||
                networkApp = Shell.AppSystem.get_default().load_from_desktop_file('network-scheme.desktop');
 | 
			
		||||
            } catch(e) {
 | 
			
		||||
                log('Cannot create "Network" item, .desktop file not found or corrupt.');
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (networkApp != null) {
 | 
			
		||||
            this._network = new PlaceInfo('special:network', networkApp.get_name(),
 | 
			
		||||
                function(size) {
 | 
			
		||||
                    return networkApp.create_icon_texture(size);
 | 
			
		||||
                },
 | 
			
		||||
                function () {
 | 
			
		||||
                    networkApp.launch();
 | 
			
		||||
                });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._defaultPlaces.push(this._home);
 | 
			
		||||
        this._defaultPlaces.push(this._desktopMenu);
 | 
			
		||||
 | 
			
		||||
        this._desktopMenuIndex = this._defaultPlaces.length;
 | 
			
		||||
        if (!this._isDesktopHome)
 | 
			
		||||
            this._defaultPlaces.push(this._desktopMenu);
 | 
			
		||||
 | 
			
		||||
        if (this._network)
 | 
			
		||||
            this._defaultPlaces.push(this._network);
 | 
			
		||||
 | 
			
		||||
        this._defaultPlaces.push(this._connect);
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
@@ -209,6 +229,9 @@ PlacesManager.prototype = {
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        this._reloadBookmarks();
 | 
			
		||||
 | 
			
		||||
        gconf.notify_add(DESKTOP_IS_HOME_KEY, Lang.bind(this, this._updateDesktopMenuVisibility));
 | 
			
		||||
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateDevices: function() {
 | 
			
		||||
@@ -268,7 +291,10 @@ PlacesManager.prototype = {
 | 
			
		||||
        if (!GLib.file_test(this._bookmarksPath, GLib.FileTest.EXISTS))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let bookmarksContent = Shell.get_file_contents_utf8_sync(this._bookmarksPath);
 | 
			
		||||
        let [success, bookmarksContent, len] = GLib.file_get_contents(this._bookmarksPath);
 | 
			
		||||
 | 
			
		||||
        if (!success)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let bookmarks = bookmarksContent.split('\n');
 | 
			
		||||
 | 
			
		||||
@@ -303,8 +329,8 @@ PlacesManager.prototype = {
 | 
			
		||||
                function(size) {
 | 
			
		||||
                    return St.TextureCache.get_default().load_gicon(null, icon, size);
 | 
			
		||||
                },
 | 
			
		||||
                function(params) {
 | 
			
		||||
                    Gio.app_info_launch_default_for_uri(bookmark, _makeLaunchContext(params));
 | 
			
		||||
                function() {
 | 
			
		||||
                    Gio.app_info_launch_default_for_uri(bookmark, global.create_app_launch_context());
 | 
			
		||||
                });
 | 
			
		||||
            this._bookmarks.push(item);
 | 
			
		||||
        }
 | 
			
		||||
@@ -314,6 +340,21 @@ PlacesManager.prototype = {
 | 
			
		||||
        this.emit('places-updated');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateDesktopMenuVisibility: function() {
 | 
			
		||||
        let gconf = GConf.Client.get_default();
 | 
			
		||||
        this._isDesktopHome = gconf.get_boolean(DESKTOP_IS_HOME_KEY);
 | 
			
		||||
 | 
			
		||||
        if (this._isDesktopHome)
 | 
			
		||||
            this._removeById(this._defaultPlaces, 'special:desktop');
 | 
			
		||||
        else
 | 
			
		||||
            this._defaultPlaces.splice(this._desktopMenuIndex, 0,
 | 
			
		||||
                                       this._desktopMenu);
 | 
			
		||||
 | 
			
		||||
        /* See comment in _updateDevices for explanation why there are two signals. */
 | 
			
		||||
        this.emit('defaults-updated');
 | 
			
		||||
        this.emit('places-updated');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _addMount: function(mount) {
 | 
			
		||||
        let devItem = new PlaceDeviceInfo(mount);
 | 
			
		||||
        this._mounts.push(devItem);
 | 
			
		||||
@@ -361,8 +402,150 @@ PlacesManager.prototype = {
 | 
			
		||||
        sourceArray.splice(this._lookupIndexById(sourceArray, id), 1);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Signals.addSignalMethods(PlacesManager.prototype);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * An entry in the places menu.
 | 
			
		||||
 * @info The corresponding PlaceInfo to populate this entry.
 | 
			
		||||
 */
 | 
			
		||||
function DashPlaceDisplayItem(info) {
 | 
			
		||||
    this._init(info);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DashPlaceDisplayItem.prototype = {
 | 
			
		||||
    _init: function(info) {
 | 
			
		||||
        this.name = info.name;
 | 
			
		||||
        this._info = info;
 | 
			
		||||
        this._icon = info.iconFactory(PLACES_ICON_SIZE);
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.Clickable({ style_class: 'places-item',
 | 
			
		||||
                                        reactive: true,
 | 
			
		||||
                                        x_align: St.Align.START,
 | 
			
		||||
                                        x_fill: true });
 | 
			
		||||
 | 
			
		||||
        let box = new St.BoxLayout({ style_class: 'places-item-box' });
 | 
			
		||||
        this.actor.set_child(box);
 | 
			
		||||
 | 
			
		||||
        let bin = new St.Bin({ child: this._icon });
 | 
			
		||||
        box.add(bin);
 | 
			
		||||
 | 
			
		||||
        let text = new St.Label({ text: info.name });
 | 
			
		||||
        box.add(text, { expand: true, x_fill: true });
 | 
			
		||||
 | 
			
		||||
        if (info.isRemovable()) {
 | 
			
		||||
            let removeIcon = new St.Icon({ icon_name: 'media-eject',
 | 
			
		||||
                                           icon_type: St.IconType.FULLCOLOR,
 | 
			
		||||
                                           icon_size: PLACES_ICON_SIZE });
 | 
			
		||||
            let removeIconBox = new St.Clickable({ child: removeIcon,
 | 
			
		||||
                                                   reactive: true });
 | 
			
		||||
            box.add(removeIconBox);
 | 
			
		||||
            removeIconBox.connect('clicked',
 | 
			
		||||
                                  Lang.bind(this, function() {
 | 
			
		||||
                                                  this._info.remove();
 | 
			
		||||
                                            }));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.actor.connect('clicked', Lang.bind(this, this._onClicked));
 | 
			
		||||
 | 
			
		||||
        this.actor._delegate = this;
 | 
			
		||||
        this._draggable = DND.makeDraggable(this.actor);
 | 
			
		||||
        this._draggable.connect('drag-begin',
 | 
			
		||||
                                Lang.bind(this, function() {
 | 
			
		||||
                                    Main.overview.beginItemDrag(this);
 | 
			
		||||
                                }));
 | 
			
		||||
        this._draggable.connect('drag-end',
 | 
			
		||||
                                Lang.bind(this, function() {
 | 
			
		||||
                                    Main.overview.endItemDrag(this);
 | 
			
		||||
                                }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onClicked: function(b) {
 | 
			
		||||
        this._info.launch();
 | 
			
		||||
        Main.overview.hide();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getDragActorSource: function() {
 | 
			
		||||
        return this._icon;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getDragActor: function(stageX, stageY) {
 | 
			
		||||
        return this._info.iconFactory(PLACES_ICON_SIZE);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    //// Drag and drop methods ////
 | 
			
		||||
 | 
			
		||||
    shellWorkspaceLaunch: function() {
 | 
			
		||||
        this._info.launch();
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function DashPlaceDisplay() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DashPlaceDisplay.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
 | 
			
		||||
        // Places is divided semi-arbitrarily into left and right; a grid would
 | 
			
		||||
        // look better in that there would be an even number of items left+right,
 | 
			
		||||
        // but it seems like we want some sort of differentiation between actions
 | 
			
		||||
        // like "Connect to server..." and regular folders
 | 
			
		||||
        this.actor = new St.Table({ style_class: 'places-section',
 | 
			
		||||
                                    homogeneous: true });
 | 
			
		||||
 | 
			
		||||
        this._defaultsList = [];
 | 
			
		||||
        this._bookmarksList = [];
 | 
			
		||||
        this._mountsList = [];
 | 
			
		||||
 | 
			
		||||
        Main.placesManager.connect('defaults-updated', Lang.bind(this, this._updateDefaults));
 | 
			
		||||
        Main.placesManager.connect('bookmarks-updated', Lang.bind(this, this._updateBookmarks));
 | 
			
		||||
        Main.placesManager.connect('mounts-updated', Lang.bind(this, this._updateMounts));
 | 
			
		||||
 | 
			
		||||
        this._updateDefaults();
 | 
			
		||||
        this._updateMounts();
 | 
			
		||||
        this._updateBookmarks();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateDefaults: function() {
 | 
			
		||||
        for (let i = 0; i < this._defaultsList.length; i++)
 | 
			
		||||
            this._defaultsList[i].destroy();
 | 
			
		||||
 | 
			
		||||
        this._defaultsList = [];
 | 
			
		||||
        let places = Main.placesManager.getDefaultPlaces();
 | 
			
		||||
        for (let i = 0; i < places.length; i++) {
 | 
			
		||||
            this._defaultsList[i] = new DashPlaceDisplayItem(places[i]).actor;
 | 
			
		||||
            this.actor.add(this._defaultsList[i], {row: i, col: 0});
 | 
			
		||||
        }
 | 
			
		||||
        this._updateMounts();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateMounts: function() {
 | 
			
		||||
        for (let i = 0; i < this._mountsList.length; i++)
 | 
			
		||||
            this._mountsList[i].destroy();
 | 
			
		||||
 | 
			
		||||
        this._mountsList = [];
 | 
			
		||||
        let places = Main.placesManager.getMounts();
 | 
			
		||||
        for (let i = 0; i < places.length; i++) {
 | 
			
		||||
             this._mountsList[i] = new DashPlaceDisplayItem(places[i]).actor;
 | 
			
		||||
             this.actor.add(this._mountsList[i], {row: this._defaultsList.length + i, col: 0});
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateBookmarks: function() {
 | 
			
		||||
        for (let i = 0; i < this._bookmarksList.length; i++)
 | 
			
		||||
            this._bookmarksList[i].destroy();
 | 
			
		||||
 | 
			
		||||
        this._bookmarksList = [];
 | 
			
		||||
        let places = Main.placesManager.getBookmarks();
 | 
			
		||||
        for (let i = 0; i < places.length; i ++) {
 | 
			
		||||
            this._bookmarksList[i] = new DashPlaceDisplayItem(places[i]).actor;
 | 
			
		||||
            this.actor.add(this._bookmarksList[i], {row: i, col: 1});
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Signals.addSignalMethods(DashPlaceDisplay.prototype);
 | 
			
		||||
 | 
			
		||||
function PlaceSearchProvider() {
 | 
			
		||||
    this._init();
 | 
			
		||||
@@ -381,15 +564,12 @@ PlaceSearchProvider.prototype = {
 | 
			
		||||
            return null;
 | 
			
		||||
        return { 'id': resultId,
 | 
			
		||||
                 'name': placeInfo.name,
 | 
			
		||||
                 'createIcon': function(size) {
 | 
			
		||||
                                   return placeInfo.iconFactory(size);
 | 
			
		||||
                               }
 | 
			
		||||
               };
 | 
			
		||||
                 'icon': placeInfo.iconFactory(Search.RESULT_ICON_SIZE) };
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    activateResult: function(id, params) {
 | 
			
		||||
    activateResult: function(id) {
 | 
			
		||||
        let placeInfo = Main.placesManager.lookupPlaceById(id);
 | 
			
		||||
        placeInfo.launch(params);
 | 
			
		||||
        placeInfo.launch();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _compareResultMeta: function (idA, idB) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,410 +0,0 @@
 | 
			
		||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright 2010 Red Hat, Inc
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 2, or (at your option)
 | 
			
		||||
 * any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 | 
			
		||||
 * 02111-1307, USA.
 | 
			
		||||
*
 | 
			
		||||
 * Author: David Zeuthen <davidz@redhat.com>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Pango = imports.gi.Pango;
 | 
			
		||||
const Gdm = imports.gi.Gdm;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const Mainloop = imports.mainloop;
 | 
			
		||||
const Polkit = imports.gi.Polkit;
 | 
			
		||||
const PolkitAgent = imports.gi.PolkitAgent;
 | 
			
		||||
 | 
			
		||||
const ModalDialog = imports.ui.modalDialog;
 | 
			
		||||
 | 
			
		||||
function AuthenticationDialog(actionId, message, cookie, userNames) {
 | 
			
		||||
    this._init(actionId, message, cookie, userNames);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AuthenticationDialog.prototype = {
 | 
			
		||||
    __proto__: ModalDialog.ModalDialog.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function(actionId, message, cookie, userNames) {
 | 
			
		||||
        ModalDialog.ModalDialog.prototype._init.call(this, { styleClass: 'polkit-dialog' });
 | 
			
		||||
 | 
			
		||||
        this.actionId = actionId;
 | 
			
		||||
        this.message = message;
 | 
			
		||||
        this.userNames = userNames;
 | 
			
		||||
        this._wasDismissed = false;
 | 
			
		||||
        this._completed = false;
 | 
			
		||||
 | 
			
		||||
        let mainContentBox = new St.BoxLayout({ style_class: 'polkit-dialog-main-layout',
 | 
			
		||||
                                                vertical: false });
 | 
			
		||||
        this.contentLayout.add(mainContentBox,
 | 
			
		||||
                               { x_fill: true,
 | 
			
		||||
                                 y_fill: true });
 | 
			
		||||
 | 
			
		||||
        let icon = new St.Icon({ icon_name: 'dialog-password-symbolic' });
 | 
			
		||||
        mainContentBox.add(icon,
 | 
			
		||||
                           { x_fill:  true,
 | 
			
		||||
                             y_fill:  false,
 | 
			
		||||
                             x_align: St.Align.END,
 | 
			
		||||
                             y_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        let messageBox = new St.BoxLayout({ style_class: 'polkit-dialog-message-layout',
 | 
			
		||||
                                            vertical: true });
 | 
			
		||||
        mainContentBox.add(messageBox,
 | 
			
		||||
                           { y_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        this._subjectLabel = new St.Label({ style_class: 'polkit-dialog-headline',
 | 
			
		||||
                                            text: _("Authentication Required") });
 | 
			
		||||
 | 
			
		||||
        messageBox.add(this._subjectLabel,
 | 
			
		||||
                       { y_fill:  false,
 | 
			
		||||
                         y_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        this._descriptionLabel = new St.Label({ style_class: 'polkit-dialog-description',
 | 
			
		||||
                                                text: message });
 | 
			
		||||
        this._descriptionLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
 | 
			
		||||
        this._descriptionLabel.clutter_text.line_wrap = true;
 | 
			
		||||
 | 
			
		||||
        messageBox.add(this._descriptionLabel,
 | 
			
		||||
                       { y_fill:  true,
 | 
			
		||||
                         y_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        if (userNames.length > 1) {
 | 
			
		||||
            log('polkitAuthenticationAgent: Received ' + userNames.length +
 | 
			
		||||
                ' identities that can be used for authentication. Only ' +
 | 
			
		||||
                'considering the first one.');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let userName = userNames[0];
 | 
			
		||||
 | 
			
		||||
        this._user = Gdm.UserManager.ref_default().get_user(userName);
 | 
			
		||||
        let userRealName = this._user.get_real_name()
 | 
			
		||||
        this._userLoadedId = this._user.connect('notify::is_loaded',
 | 
			
		||||
                                                Lang.bind(this, this._onUserChanged));
 | 
			
		||||
        this._userChangedId = this._user.connect('changed',
 | 
			
		||||
                                                 Lang.bind(this, this._onUserChanged));
 | 
			
		||||
 | 
			
		||||
        // Special case 'root'
 | 
			
		||||
        let userIsRoot = false;
 | 
			
		||||
        if (userName == 'root') {
 | 
			
		||||
            userIsRoot = true;
 | 
			
		||||
            userRealName = _("Administrator");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (userIsRoot) {
 | 
			
		||||
            let userLabel = new St.Label(({ style_class: 'polkit-dialog-user-root-label',
 | 
			
		||||
                                            text: userRealName }));
 | 
			
		||||
            messageBox.add(userLabel);
 | 
			
		||||
        } else {
 | 
			
		||||
            let userBox = new St.BoxLayout({ style_class: 'polkit-dialog-user-layout',
 | 
			
		||||
                                             vertical: false });
 | 
			
		||||
            messageBox.add(userBox);
 | 
			
		||||
            this._userIcon = new St.Icon();
 | 
			
		||||
            this._userIcon.hide();
 | 
			
		||||
            userBox.add(this._userIcon,
 | 
			
		||||
                        { x_fill:  true,
 | 
			
		||||
                          y_fill:  false,
 | 
			
		||||
                          x_align: St.Align.END,
 | 
			
		||||
                          y_align: St.Align.START });
 | 
			
		||||
            let userLabel = new St.Label(({ style_class: 'polkit-dialog-user-label',
 | 
			
		||||
                                            text: userRealName }));
 | 
			
		||||
            userBox.add(userLabel,
 | 
			
		||||
                        { x_fill:  true,
 | 
			
		||||
                          y_fill:  false,
 | 
			
		||||
                          x_align: St.Align.END,
 | 
			
		||||
                          y_align: St.Align.MIDDLE });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._onUserChanged();
 | 
			
		||||
 | 
			
		||||
        this._passwordBox = new St.BoxLayout({ vertical: false });
 | 
			
		||||
        messageBox.add(this._passwordBox);
 | 
			
		||||
        this._passwordLabel = new St.Label(({ style_class: 'polkit-dialog-password-label' }));
 | 
			
		||||
        this._passwordBox.add(this._passwordLabel);
 | 
			
		||||
        this._passwordEntry = new St.Entry({ style_class: 'polkit-dialog-password-entry',
 | 
			
		||||
                                             text: "",
 | 
			
		||||
                                             can_focus: true});
 | 
			
		||||
        this._passwordEntry.clutter_text.connect('activate', Lang.bind(this, this._onEntryActivate));
 | 
			
		||||
        this._passwordBox.add(this._passwordEntry,
 | 
			
		||||
                              {expand: true });
 | 
			
		||||
        this._passwordBox.hide();
 | 
			
		||||
 | 
			
		||||
        this._errorMessageLabel = new St.Label({ style_class: 'polkit-dialog-error-label' });
 | 
			
		||||
        this._errorMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
 | 
			
		||||
        this._errorMessageLabel.clutter_text.line_wrap = true;
 | 
			
		||||
        messageBox.add(this._errorMessageLabel);
 | 
			
		||||
        this._errorMessageLabel.hide();
 | 
			
		||||
 | 
			
		||||
        this._infoMessageLabel = new St.Label({ style_class: 'polkit-dialog-info-label' });
 | 
			
		||||
        this._infoMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
 | 
			
		||||
        this._infoMessageLabel.clutter_text.line_wrap = true;
 | 
			
		||||
        messageBox.add(this._infoMessageLabel);
 | 
			
		||||
        this._infoMessageLabel.hide();
 | 
			
		||||
 | 
			
		||||
        /* text is intentionally non-blank otherwise the height is not the same as for
 | 
			
		||||
         * infoMessage and errorMessageLabel - but it is still invisible because
 | 
			
		||||
         * gnome-shell.css sets the color to be transparent
 | 
			
		||||
         */
 | 
			
		||||
        this._nullMessageLabel = new St.Label({ style_class: 'polkit-dialog-null-label',
 | 
			
		||||
                                                text: 'abc'});
 | 
			
		||||
        this._nullMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
 | 
			
		||||
        this._nullMessageLabel.clutter_text.line_wrap = true;
 | 
			
		||||
        messageBox.add(this._nullMessageLabel);
 | 
			
		||||
        this._nullMessageLabel.show();
 | 
			
		||||
 | 
			
		||||
        this.setButtons([{ label: _("Cancel"),
 | 
			
		||||
                           action: Lang.bind(this, this.cancel),
 | 
			
		||||
                           key:    Clutter.Escape
 | 
			
		||||
                         },
 | 
			
		||||
                         { label:  _("Authenticate"),
 | 
			
		||||
                           action: Lang.bind(this, this._onAuthenticateButtonPressed)
 | 
			
		||||
                         }]);
 | 
			
		||||
 | 
			
		||||
        this._doneEmitted = false;
 | 
			
		||||
 | 
			
		||||
        this._identityToAuth = Polkit.UnixUser.new_for_name(userName);
 | 
			
		||||
        this._cookie = cookie;
 | 
			
		||||
 | 
			
		||||
        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));
 | 
			
		||||
 | 
			
		||||
        // Delay focus grab to avoid ModalDialog stealing focus with
 | 
			
		||||
        // its buttons
 | 
			
		||||
        this.connect('opened',
 | 
			
		||||
                     Lang.bind(this, function() {
 | 
			
		||||
                         this._passwordEntry.grab_key_focus();
 | 
			
		||||
                     }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    startAuthentication: function() {
 | 
			
		||||
        this._session.initiate();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _ensureOpen: function() {
 | 
			
		||||
        // NOTE: ModalDialog.open() is safe to call if the dialog is
 | 
			
		||||
        // already open - it just returns true without side-effects
 | 
			
		||||
        if (!this.open(global.get_current_time())) {
 | 
			
		||||
            // This can fail if e.g. unable to get input grab
 | 
			
		||||
            //
 | 
			
		||||
            // In an ideal world this wouldn't happen (because the
 | 
			
		||||
            // Shell is in complete control of the session) but that's
 | 
			
		||||
            // just not how things work right now.
 | 
			
		||||
            //
 | 
			
		||||
            // One way to make this happen is by running 'sleep 3;
 | 
			
		||||
            // pkexec bash' and then opening a popup menu.
 | 
			
		||||
            //
 | 
			
		||||
            // We could add retrying if this turns out to be a problem
 | 
			
		||||
 | 
			
		||||
            log('polkitAuthenticationAgent: Failed to show modal dialog.' +
 | 
			
		||||
                ' Dismissing authentication request for action-id ' + this.actionId +
 | 
			
		||||
                ' cookie ' + this._cookie);
 | 
			
		||||
            this._emitDone(false, true);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _emitDone: function(keepVisible, dismissed) {
 | 
			
		||||
        if (!this._doneEmitted) {
 | 
			
		||||
            this._doneEmitted = true;
 | 
			
		||||
            this.emit('done', keepVisible, dismissed);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onEntryActivate: function() {
 | 
			
		||||
        let response = this._passwordEntry.get_text();
 | 
			
		||||
        this._session.response(response);
 | 
			
		||||
        // When the user responds, dismiss already shown info and
 | 
			
		||||
        // error texts (if any)
 | 
			
		||||
        this._errorMessageLabel.hide();
 | 
			
		||||
        this._infoMessageLabel.hide();
 | 
			
		||||
        this._nullMessageLabel.show();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onAuthenticateButtonPressed: function() {
 | 
			
		||||
        this._onEntryActivate();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onSessionCompleted: function(session, gainedAuthorization) {
 | 
			
		||||
        if (this._completed)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._completed = true;
 | 
			
		||||
 | 
			
		||||
        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),
 | 
			
		||||
             * show "Sorry, that didn't work. Please try again."
 | 
			
		||||
             */
 | 
			
		||||
            if (!this._errorMessageLabel.visible && !this._wasDismissed) {
 | 
			
		||||
                /* Translators: "that didn't work" refers to the fact that the
 | 
			
		||||
                 * requested authentication was not gained; this can happen
 | 
			
		||||
                 * because of an authentication error (like invalid password),
 | 
			
		||||
                 * for instance. */
 | 
			
		||||
                this._errorMessageLabel.set_text(_("Sorry, that didn\'t work. Please try again."));
 | 
			
		||||
                this._errorMessageLabel.show();
 | 
			
		||||
                this._infoMessageLabel.hide();
 | 
			
		||||
                this._nullMessageLabel.hide();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        this._emitDone(!gainedAuthorization, false);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onSessionRequest: function(session, request, echo_on) {
 | 
			
		||||
        // Cheap localization trick
 | 
			
		||||
        if (request == 'Password:')
 | 
			
		||||
            this._passwordLabel.set_text(_("Password:"));
 | 
			
		||||
        else
 | 
			
		||||
            this._passwordLabel.set_text(request);
 | 
			
		||||
 | 
			
		||||
        if (echo_on)
 | 
			
		||||
            this._passwordEntry.clutter_text.set_password_char('');
 | 
			
		||||
        else
 | 
			
		||||
            this._passwordEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
 | 
			
		||||
 | 
			
		||||
        this._passwordBox.show();
 | 
			
		||||
        this._passwordEntry.set_text('');
 | 
			
		||||
        this._passwordEntry.grab_key_focus();
 | 
			
		||||
        this._ensureOpen();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onSessionShowError: function(session, text) {
 | 
			
		||||
        this._passwordEntry.set_text('');
 | 
			
		||||
        this._errorMessageLabel.set_text(text);
 | 
			
		||||
        this._errorMessageLabel.show();
 | 
			
		||||
        this._infoMessageLabel.hide();
 | 
			
		||||
        this._nullMessageLabel.hide();
 | 
			
		||||
        this._ensureOpen();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onSessionShowInfo: function(session, text) {
 | 
			
		||||
        this._passwordEntry.set_text('');
 | 
			
		||||
        this._infoMessageLabel.set_text(text);
 | 
			
		||||
        this._infoMessageLabel.show();
 | 
			
		||||
        this._errorMessageLabel.hide();
 | 
			
		||||
        this._nullMessageLabel.hide();
 | 
			
		||||
        this._ensureOpen();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroySession: function() {
 | 
			
		||||
        if (this._session) {
 | 
			
		||||
            if (!this._completed)
 | 
			
		||||
                this._session.cancel();
 | 
			
		||||
            this._session = null;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onUserChanged: function() {
 | 
			
		||||
        if (this._user.is_loaded) {
 | 
			
		||||
            if (this._userIcon) {
 | 
			
		||||
                let iconFileName = this._user.get_icon_file();
 | 
			
		||||
                let iconFile = Gio.file_new_for_path(iconFileName);
 | 
			
		||||
                let icon;
 | 
			
		||||
                if (iconFile.query_exists(null)) {
 | 
			
		||||
                    icon = new Gio.FileIcon({file: iconFile});
 | 
			
		||||
                } else {
 | 
			
		||||
                    icon = new Gio.ThemedIcon({name: 'avatar-default'});
 | 
			
		||||
                }
 | 
			
		||||
                this._userIcon.set_gicon (icon);
 | 
			
		||||
                this._userIcon.show();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    cancel: function() {
 | 
			
		||||
        this._wasDismissed = true;
 | 
			
		||||
        this.close(global.get_current_time());
 | 
			
		||||
        this._emitDone(false, true);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
Signals.addSignalMethods(AuthenticationDialog.prototype);
 | 
			
		||||
 | 
			
		||||
function AuthenticationAgent() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AuthenticationAgent.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._native = new Shell.PolkitAuthenticationAgent();
 | 
			
		||||
        this._native.connect('initiate', Lang.bind(this, this._onInitiate));
 | 
			
		||||
        this._native.connect('cancel', Lang.bind(this, this._onCancel));
 | 
			
		||||
        this._currentDialog = null;
 | 
			
		||||
        this._isCompleting = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onInitiate: function(nativeAgent, actionId, message, iconName, cookie, userNames) {
 | 
			
		||||
        this._currentDialog = new AuthenticationDialog(actionId, message, cookie, userNames);
 | 
			
		||||
 | 
			
		||||
        // We actually don't want to open the dialog until we know for
 | 
			
		||||
        // sure that we're going to interact with the user. For
 | 
			
		||||
        // example, if the password for the identity to auth is blank
 | 
			
		||||
        // (which it will be on a live CD) then there will be no
 | 
			
		||||
        // conversation at all... of course, we don't *know* that
 | 
			
		||||
        // until we actually try it.
 | 
			
		||||
        //
 | 
			
		||||
        // See https://bugzilla.gnome.org/show_bug.cgi?id=643062 for more
 | 
			
		||||
        // discussion.
 | 
			
		||||
 | 
			
		||||
        this._currentDialog.connect('done', Lang.bind(this, this._onDialogDone));
 | 
			
		||||
        this._currentDialog.startAuthentication();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onCancel: function(nativeAgent) {
 | 
			
		||||
        this._completeRequest(false, false);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDialogDone: function(dialog, keepVisible, dismissed) {
 | 
			
		||||
        this._completeRequest(keepVisible, dismissed);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _reallyCompleteRequest: function(dismissed) {
 | 
			
		||||
        this._currentDialog.close();
 | 
			
		||||
        this._currentDialog.destroySession();
 | 
			
		||||
        this._currentDialog = null;
 | 
			
		||||
        this._isCompleting = false;
 | 
			
		||||
 | 
			
		||||
        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);
 | 
			
		||||
                                           }));
 | 
			
		||||
        } else {
 | 
			
		||||
            this._reallyCompleteRequest(wasDismissed);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function init() {
 | 
			
		||||
    let agent = new AuthenticationAgent();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1261
									
								
								js/ui/popupMenu.js
									
									
									
									
									
								
							
							
						
						@@ -8,26 +8,19 @@ const Meta = imports.gi.Meta;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const Gettext = imports.gettext.domain('gnome-shell');
 | 
			
		||||
const _ = Gettext.gettext;
 | 
			
		||||
 | 
			
		||||
const FileUtils = imports.misc.fileUtils;
 | 
			
		||||
const Lightbox = imports.ui.lightbox;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const ModalDialog = imports.ui.modalDialog;
 | 
			
		||||
const Tweener = imports.ui.tweener;
 | 
			
		||||
const Util = imports.misc.util;
 | 
			
		||||
const History = imports.misc.history;
 | 
			
		||||
 | 
			
		||||
const MAX_FILE_DELETED_BEFORE_INVALID = 10;
 | 
			
		||||
 | 
			
		||||
const HISTORY_KEY = 'command-history';
 | 
			
		||||
const HISTORY_LIMIT = 512;
 | 
			
		||||
 | 
			
		||||
const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown';
 | 
			
		||||
const DISABLE_COMMAND_LINE_KEY = 'disable-command-line';
 | 
			
		||||
 | 
			
		||||
const TERMINAL_SCHEMA = 'org.gnome.desktop.default-applications.terminal';
 | 
			
		||||
const EXEC_KEY = 'exec';
 | 
			
		||||
const EXEC_ARG_KEY = 'exec-arg';
 | 
			
		||||
 | 
			
		||||
const DIALOG_GROW_TIME = 0.1;
 | 
			
		||||
const DIALOG_FADE_TIME = 0.1;
 | 
			
		||||
 | 
			
		||||
function CommandCompleter() {
 | 
			
		||||
    this._init();
 | 
			
		||||
@@ -69,6 +62,25 @@ CommandCompleter.prototype = {
 | 
			
		||||
        this._update(0);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onGetEnumerateComplete : function(obj, res) {
 | 
			
		||||
        this._enumerator = obj.enumerate_children_finish(res);
 | 
			
		||||
        this._enumerator.next_files_async(100, GLib.PRIORITY_LOW, null, Lang.bind(this, this._onNextFileComplete));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onNextFileComplete : function(obj, res) {
 | 
			
		||||
        let files = obj.next_files_finish(res);
 | 
			
		||||
        for (let i = 0; i < files.length; i++) {
 | 
			
		||||
            this._childs[this._i].push(files[i].get_name());
 | 
			
		||||
        }
 | 
			
		||||
        if (files.length) {
 | 
			
		||||
            this._enumerator.next_files_async(100, GLib.PRIORITY_LOW, null, Lang.bind(this, this._onNextFileComplete));
 | 
			
		||||
        } else {
 | 
			
		||||
            this._enumerator.close(null);
 | 
			
		||||
            this._enumerator = null;
 | 
			
		||||
            this._update(this._i + 1);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    update : function() {
 | 
			
		||||
        if (this._valid)
 | 
			
		||||
            return;
 | 
			
		||||
@@ -88,12 +100,7 @@ CommandCompleter.prototype = {
 | 
			
		||||
        }
 | 
			
		||||
        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);
 | 
			
		||||
        }));
 | 
			
		||||
        file.enumerate_children_async(Gio.FILE_ATTRIBUTE_STANDARD_NAME, Gio.FileQueryInfoFlags.NONE, GLib.PRIORITY_LOW, null, Lang.bind(this, this._onGetEnumerateComplete));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onChanged : function(m, f, of, type) {
 | 
			
		||||
@@ -168,17 +175,22 @@ function RunDialog() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
RunDialog.prototype = {
 | 
			
		||||
__proto__: ModalDialog.ModalDialog.prototype,
 | 
			
		||||
    _init : function() {
 | 
			
		||||
        ModalDialog.ModalDialog.prototype._init.call(this, { styleClass: 'run-dialog' });
 | 
			
		||||
        this._isOpen = false;
 | 
			
		||||
 | 
			
		||||
        this._lockdownSettings = new Gio.Settings({ schema: LOCKDOWN_SCHEMA });
 | 
			
		||||
        this._terminalSettings = new Gio.Settings({ schema: TERMINAL_SCHEMA });
 | 
			
		||||
        global.settings.connect('changed::development-tools', Lang.bind(this, function () {
 | 
			
		||||
            this._enableInternalCommands = global.settings.get_boolean('development-tools');
 | 
			
		||||
        }));
 | 
			
		||||
        this._enableInternalCommands = global.settings.get_boolean('development-tools');
 | 
			
		||||
 | 
			
		||||
        this._history = global.settings.get_strv(HISTORY_KEY);
 | 
			
		||||
        this._historyIndex = -1;
 | 
			
		||||
 | 
			
		||||
        global.settings.connect('changed::' + HISTORY_KEY, Lang.bind(this, function() {
 | 
			
		||||
            this._history = global.settings.get_strv(HISTORY_KEY);
 | 
			
		||||
            this._historyIndex = this._history.length;
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        this._internalCommands = { 'lg':
 | 
			
		||||
                                   Lang.bind(this, function() {
 | 
			
		||||
                                       Main.createLookingGlass().open();
 | 
			
		||||
@@ -194,67 +206,76 @@ __proto__: ModalDialog.ModalDialog.prototype,
 | 
			
		||||
                                   }),
 | 
			
		||||
 | 
			
		||||
                                   'debugexit': Lang.bind(this, function() {
 | 
			
		||||
                                       Meta.quit(Meta.ExitCode.ERROR);
 | 
			
		||||
                                   }),
 | 
			
		||||
 | 
			
		||||
                                   // rt is short for "reload theme"
 | 
			
		||||
                                   'rt': Lang.bind(this, function() {
 | 
			
		||||
                                       Main.loadTheme();
 | 
			
		||||
                                       Meta.exit(Meta.ExitCode.ERROR);
 | 
			
		||||
                                   })
 | 
			
		||||
                                 };
 | 
			
		||||
 | 
			
		||||
        // All actors are inside _group. We create it initially
 | 
			
		||||
        // hidden then show it in show()
 | 
			
		||||
        this._group = new Clutter.Group({ visible: false,
 | 
			
		||||
                                          x: 0, y: 0 });
 | 
			
		||||
        Main.uiGroup.add_actor(this._group);
 | 
			
		||||
 | 
			
		||||
        this._lightbox = new Lightbox.Lightbox(this._group,
 | 
			
		||||
                                               { inhibitEvents: true });
 | 
			
		||||
 | 
			
		||||
        this._box = new St.Bin({ x_align: St.Align.MIDDLE,
 | 
			
		||||
                                 y_align: St.Align.MIDDLE });
 | 
			
		||||
 | 
			
		||||
        this._group.add_actor(this._box);
 | 
			
		||||
        this._lightbox.highlight(this._box);
 | 
			
		||||
 | 
			
		||||
        let dialogBox = new St.BoxLayout({ style_class: 'run-dialog', vertical: true });
 | 
			
		||||
 | 
			
		||||
        this._box.set_child(dialogBox);
 | 
			
		||||
 | 
			
		||||
        let label = new St.Label({ style_class: 'run-dialog-label',
 | 
			
		||||
                                   text: _("Please enter a command:") });
 | 
			
		||||
 | 
			
		||||
        this.contentLayout.add(label, { y_align: St.Align.START });
 | 
			
		||||
        dialogBox.add(label, { expand: true, y_fill: false });
 | 
			
		||||
 | 
			
		||||
        let entry = new St.Entry({ style_class: 'run-dialog-entry' });
 | 
			
		||||
 | 
			
		||||
        this._entryText = entry.clutter_text;
 | 
			
		||||
        this.contentLayout.add(entry, { y_align: St.Align.START });
 | 
			
		||||
        this.setInitialKeyFocus(this._entryText);
 | 
			
		||||
        dialogBox.add(entry, { expand: true });
 | 
			
		||||
 | 
			
		||||
        this._errorBox = new St.BoxLayout({ style_class: 'run-dialog-error-box' });
 | 
			
		||||
        this._errorBox = new St.BoxLayout();
 | 
			
		||||
 | 
			
		||||
        this.contentLayout.add(this._errorBox, { expand: true });
 | 
			
		||||
        dialogBox.add(this._errorBox, { expand: true });
 | 
			
		||||
 | 
			
		||||
        let errorIcon = new St.Icon({ icon_name: 'dialog-error', icon_size: 24, style_class: 'run-dialog-error-icon' });
 | 
			
		||||
        let errorIcon = new St.Button({ style_class: 'run-dialog-error-icon' });
 | 
			
		||||
 | 
			
		||||
        this._errorBox.add(errorIcon, { y_align: St.Align.MIDDLE });
 | 
			
		||||
        this._errorBox.add(errorIcon);
 | 
			
		||||
 | 
			
		||||
        this._commandError = false;
 | 
			
		||||
 | 
			
		||||
        this._errorMessage = new St.Label({ style_class: 'run-dialog-error-label' });
 | 
			
		||||
        this._errorMessage.clutter_text.line_wrap = true;
 | 
			
		||||
 | 
			
		||||
        this._errorBox.add(this._errorMessage, { expand: true,
 | 
			
		||||
                                                 y_align: St.Align.MIDDLE,
 | 
			
		||||
                                                 y_fill: false });
 | 
			
		||||
        this._errorBox.add(this._errorMessage, { expand: true });
 | 
			
		||||
 | 
			
		||||
        this._errorBox.hide();
 | 
			
		||||
 | 
			
		||||
        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 });
 | 
			
		||||
        this._entryText.connect('key-press-event', Lang.bind(this, function(o, e) {
 | 
			
		||||
            let symbol = e.get_key_symbol();
 | 
			
		||||
            if (symbol == Clutter.Down) {
 | 
			
		||||
                this._setCommandFromHistory(this._historyIndex++);
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
            if (symbol == Clutter.Up) {
 | 
			
		||||
                this._setCommandFromHistory(this._historyIndex--);
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
            if (symbol == Clutter.Return || symbol == Clutter.KP_Enter) {
 | 
			
		||||
                this.popModal();
 | 
			
		||||
                if (Shell.get_event_state(e) & Clutter.ModifierType.CONTROL_MASK)
 | 
			
		||||
                    this._run(o.get_text(), true);
 | 
			
		||||
                else
 | 
			
		||||
                    this._run(o.get_text(), false);
 | 
			
		||||
                if (!this._commandError)
 | 
			
		||||
                    this.close();
 | 
			
		||||
                else {
 | 
			
		||||
                    if (!this.pushModal())
 | 
			
		||||
                        this.close();
 | 
			
		||||
                }
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
            if (symbol == Clutter.Escape) {
 | 
			
		||||
                this.close();
 | 
			
		||||
@@ -300,10 +321,22 @@ __proto__: ModalDialog.ModalDialog.prototype,
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _saveHistory : function() {
 | 
			
		||||
        if (this._history.length > HISTORY_LIMIT) {
 | 
			
		||||
            this._history.splice(0, this._history.length - HISTORY_LIMIT);
 | 
			
		||||
        }
 | 
			
		||||
        global.settings.set_strv(HISTORY_KEY, this._history);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _run : function(input, inTerminal) {
 | 
			
		||||
        let command = input;
 | 
			
		||||
 | 
			
		||||
        this._history.addItem(input);
 | 
			
		||||
        if (this._history.length == 0 ||
 | 
			
		||||
            this._history[this._history.length - 1] != input) {
 | 
			
		||||
            this._history.push(input);
 | 
			
		||||
            this._saveHistory();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._commandError = false;
 | 
			
		||||
        let f;
 | 
			
		||||
        if (this._enableInternalCommands)
 | 
			
		||||
@@ -314,12 +347,11 @@ __proto__: ModalDialog.ModalDialog.prototype,
 | 
			
		||||
            f();
 | 
			
		||||
        } else if (input) {
 | 
			
		||||
            try {
 | 
			
		||||
                if (inTerminal) {
 | 
			
		||||
                    let exec = this._terminalSettings.get_string(EXEC_KEY);
 | 
			
		||||
                    let exec_arg = this._terminalSettings.get_string(EXEC_ARG_KEY);
 | 
			
		||||
                    command = exec + ' ' + exec_arg + ' ' + input;
 | 
			
		||||
                }
 | 
			
		||||
                Util.trySpawnCommandLine(command);
 | 
			
		||||
                if (inTerminal)
 | 
			
		||||
                    command = 'gnome-terminal -x ' + input;
 | 
			
		||||
                let [ok, len, args] = GLib.shell_parse_argv(command);
 | 
			
		||||
                let p = new Shell.Process({ 'args' : args });
 | 
			
		||||
                p.run();
 | 
			
		||||
            } catch (e) {
 | 
			
		||||
                // Mmmh, that failed - see if @input matches an existing file
 | 
			
		||||
                let path = null;
 | 
			
		||||
@@ -333,57 +365,88 @@ __proto__: ModalDialog.ModalDialog.prototype,
 | 
			
		||||
 | 
			
		||||
                if (GLib.file_test(path, GLib.FileTest.EXISTS)) {
 | 
			
		||||
                    let file = Gio.file_new_for_path(path);
 | 
			
		||||
                    try {
 | 
			
		||||
                        Gio.app_info_launch_default_for_uri(file.get_uri(),
 | 
			
		||||
                                                            global.create_app_launch_context());
 | 
			
		||||
                    } catch (e) {
 | 
			
		||||
                        // The exception from gjs contains an error string like:
 | 
			
		||||
                        //     Error invoking Gio.app_info_launch_default_for_uri: No application
 | 
			
		||||
                        //     is registered as handling this file
 | 
			
		||||
                        // We are only interested in the part after the first colon.
 | 
			
		||||
                        let message = e.message.replace(/[^:]*: *(.+)/, '$1');
 | 
			
		||||
                        this._showError(message);
 | 
			
		||||
                    }
 | 
			
		||||
                    Gio.app_info_launch_default_for_uri(file.get_uri(),
 | 
			
		||||
                                                        global.create_app_launch_context());
 | 
			
		||||
                } else {
 | 
			
		||||
                    this._showError(e.message);
 | 
			
		||||
                    this._commandError = true;
 | 
			
		||||
                    // The exception contains an error string like:
 | 
			
		||||
                    // Error invoking Shell.run: Failed to execute child
 | 
			
		||||
                    // process "foo" (No such file or directory)
 | 
			
		||||
                    // We are only interested in the actual error, so parse
 | 
			
		||||
                    //that out.
 | 
			
		||||
                    let m = /.+\((.+)\)/.exec(e);
 | 
			
		||||
                    let errorStr = _("Execution of '%s' failed:").format(command) + '\n' + m[1];
 | 
			
		||||
                    this._errorMessage.set_text(errorStr);
 | 
			
		||||
 | 
			
		||||
                    this._errorBox.show();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _showError : function(message) {
 | 
			
		||||
        this._commandError = true;
 | 
			
		||||
    _setCommandFromHistory: function(lastI) {
 | 
			
		||||
        if (this._historyIndex < 0)
 | 
			
		||||
            this._historyIndex = 0;
 | 
			
		||||
        if (this._historyIndex > this._history.length)
 | 
			
		||||
            this._historyIndex = this._history.length;
 | 
			
		||||
 | 
			
		||||
        this._errorMessage.set_text(message);
 | 
			
		||||
 | 
			
		||||
        if (!this._errorBox.visible) {
 | 
			
		||||
            let [errorBoxMinHeight, errorBoxNaturalHeight] = this._errorBox.get_preferred_height(-1);
 | 
			
		||||
 | 
			
		||||
            let parentActor = this._errorBox.get_parent();
 | 
			
		||||
            Tweener.addTween(parentActor,
 | 
			
		||||
                             { height: parentActor.height + errorBoxNaturalHeight,
 | 
			
		||||
                               time: DIALOG_GROW_TIME,
 | 
			
		||||
                               transition: 'easeOutQuad',
 | 
			
		||||
                               onComplete: Lang.bind(this,
 | 
			
		||||
                                                     function() {
 | 
			
		||||
                                                         parentActor.set_height(-1);
 | 
			
		||||
                                                         this._errorBox.show();
 | 
			
		||||
                                                     })
 | 
			
		||||
                             });
 | 
			
		||||
        let text = this._entryText.get_text();
 | 
			
		||||
        if (text) {
 | 
			
		||||
            this._history[lastI] = text;
 | 
			
		||||
        }
 | 
			
		||||
        if (this._history[this._historyIndex]) {
 | 
			
		||||
            this._entryText.set_text(this._history[this._historyIndex]);
 | 
			
		||||
        } else
 | 
			
		||||
            this._entryText.set_text('');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    open: function() {
 | 
			
		||||
        this._history.lastItem();
 | 
			
		||||
        this._errorBox.hide();
 | 
			
		||||
        this._entryText.set_text('');
 | 
			
		||||
        this._commandError = false;
 | 
			
		||||
 | 
			
		||||
        if (this._lockdownSettings.get_boolean(DISABLE_COMMAND_LINE_KEY))
 | 
			
		||||
    open : function() {
 | 
			
		||||
        if (this._isOpen) // Already shown
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        ModalDialog.ModalDialog.prototype.open.call(this);
 | 
			
		||||
        if (!Main.pushModal(this._group))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        // Position the dialog on the current monitor
 | 
			
		||||
        let monitor = global.get_focus_monitor();
 | 
			
		||||
 | 
			
		||||
        this._historyIndex = this._history.length;
 | 
			
		||||
 | 
			
		||||
        this._box.set_position(monitor.x, monitor.y);
 | 
			
		||||
        this._box.set_size(monitor.width, monitor.height);
 | 
			
		||||
 | 
			
		||||
        this._isOpen = true;
 | 
			
		||||
        this._lightbox.show();
 | 
			
		||||
        this._group.opacity = 0;
 | 
			
		||||
        this._group.show();
 | 
			
		||||
        Tweener.addTween(this._group,
 | 
			
		||||
                         { opacity: 255,
 | 
			
		||||
                           time: DIALOG_FADE_TIME,
 | 
			
		||||
                           transition: 'easeOutQuad'
 | 
			
		||||
                         });
 | 
			
		||||
 | 
			
		||||
        global.stage.set_key_focus(this._entryText);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    close : function() {
 | 
			
		||||
        if (!this._isOpen)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._isOpen = false;
 | 
			
		||||
        this._commandError = false;
 | 
			
		||||
 | 
			
		||||
        Main.popModal(this._group);
 | 
			
		||||
 | 
			
		||||
        Tweener.addTween(this._group,
 | 
			
		||||
                         { opacity: 0,
 | 
			
		||||
                           time: DIALOG_FADE_TIME,
 | 
			
		||||
                           transition: 'easeOutQuad',
 | 
			
		||||
                           onComplete: Lang.bind(this, function() {
 | 
			
		||||
                               this._errorBox.hide();
 | 
			
		||||
                               this._group.hide();
 | 
			
		||||
                               this._entryText.set_text('');
 | 
			
		||||
                           })
 | 
			
		||||
                         });
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
Signals.addSignalMethods(RunDialog.prototype);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,11 @@
 | 
			
		||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
 | 
			
		||||
 | 
			
		||||
const DBus = imports.dbus;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const Mainloop = imports.mainloop;
 | 
			
		||||
 | 
			
		||||
const Meta = imports.gi.Meta;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
 | 
			
		||||
// This module provides functionality for driving the shell user interface
 | 
			
		||||
// in an automated fashion. The primary current use case for this is
 | 
			
		||||
// automated performance testing (see runPerfScript()), but it could
 | 
			
		||||
@@ -70,104 +68,6 @@ function waitLeisure() {
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const PerfHelperIface = {
 | 
			
		||||
    name: 'org.gnome.Shell.PerfHelper',
 | 
			
		||||
    methods: [{ name: 'CreateWindow', inSignature: 'iibb', outSignature: '' },
 | 
			
		||||
              { name: 'WaitWindows', inSignature: '', outSignature: '' },
 | 
			
		||||
              { name: 'DestroyWindows', inSignature: '', outSignature: ''}]
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const PerfHelper = function () {
 | 
			
		||||
    this._init();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
PerfHelper.prototype = {
 | 
			
		||||
     _init: function() {
 | 
			
		||||
         DBus.session.proxifyObject(this, 'org.gnome.Shell.PerfHelper', '/org/gnome/Shell/PerfHelper');
 | 
			
		||||
     }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
DBus.proxifyPrototype(PerfHelper.prototype, PerfHelperIface);
 | 
			
		||||
 | 
			
		||||
let _perfHelper = null;
 | 
			
		||||
function _getPerfHelper() {
 | 
			
		||||
    if (_perfHelper == null)
 | 
			
		||||
        _perfHelper = new PerfHelper();
 | 
			
		||||
 | 
			
		||||
    return _perfHelper;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * createTestWindow:
 | 
			
		||||
 * @width: width of window, in pixels
 | 
			
		||||
 * @height: height of window, in pixels
 | 
			
		||||
 * @alpha: whether the window should be alpha transparent
 | 
			
		||||
 * @maximized: whethe the window should be created maximized
 | 
			
		||||
 *
 | 
			
		||||
 * Creates a window using gnome-shell-perf-helper for testing purposes.
 | 
			
		||||
 * While this function can be used with yield in an automation
 | 
			
		||||
 * script to pause until the D-Bus call to the helper process returns,
 | 
			
		||||
 * because of the normal X asynchronous mapping process, to actually wait
 | 
			
		||||
 * until the window has been mapped and exposed, use waitTestWindows().
 | 
			
		||||
 */
 | 
			
		||||
function createTestWindow(width, height, alpha, maximized) {
 | 
			
		||||
    let cb;
 | 
			
		||||
    let perfHelper = _getPerfHelper();
 | 
			
		||||
 | 
			
		||||
    perfHelper.CreateWindowRemote(width, height, alpha, maximized,
 | 
			
		||||
                                  function(result, excp) {
 | 
			
		||||
                                      if (cb)
 | 
			
		||||
                                          cb();
 | 
			
		||||
                                  });
 | 
			
		||||
 | 
			
		||||
    return function(callback) {
 | 
			
		||||
        cb = callback;
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * waitTestWindows:
 | 
			
		||||
 *
 | 
			
		||||
 * Used within an automation script to pause until all windows previously
 | 
			
		||||
 * created with createTestWindow have been mapped and exposed.
 | 
			
		||||
 */
 | 
			
		||||
function waitTestWindows() {
 | 
			
		||||
    let cb;
 | 
			
		||||
    let perfHelper = _getPerfHelper();
 | 
			
		||||
 | 
			
		||||
    perfHelper.WaitWindowsRemote(function(result, excp) {
 | 
			
		||||
                                     if (cb)
 | 
			
		||||
                                         cb();
 | 
			
		||||
                                 });
 | 
			
		||||
 | 
			
		||||
    return function(callback) {
 | 
			
		||||
        cb = callback;
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * destroyTestWindows:
 | 
			
		||||
 *
 | 
			
		||||
 * Destroys all windows previously created with createTestWindow().
 | 
			
		||||
 * While this function can be used with yield in an automation
 | 
			
		||||
 * script to pause until the D-Bus call to the helper process returns,
 | 
			
		||||
 * this doesn't guarantee that Mutter has actually finished the destroy
 | 
			
		||||
 * process because of normal X asynchronicity.
 | 
			
		||||
 */
 | 
			
		||||
function destroyTestWindows() {
 | 
			
		||||
    let cb;
 | 
			
		||||
    let perfHelper = _getPerfHelper();
 | 
			
		||||
 | 
			
		||||
    perfHelper.DestroyWindowsRemote(function(result, excp) {
 | 
			
		||||
                                        if (cb)
 | 
			
		||||
                                            cb();
 | 
			
		||||
                                    });
 | 
			
		||||
 | 
			
		||||
    return function(callback) {
 | 
			
		||||
        cb = callback;
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * defineScriptEvent
 | 
			
		||||
 * @name: The event will be called script.<name>
 | 
			
		||||
@@ -247,14 +147,18 @@ function _collect(scriptModule, outputFile) {
 | 
			
		||||
        Shell.write_string_to_stream(out, '"events":\n');
 | 
			
		||||
        Shell.PerfLog.get_default().dump_events(out);
 | 
			
		||||
 | 
			
		||||
        let monitors = Main.layoutManager.monitors;
 | 
			
		||||
        let primary = Main.layoutManager.primaryIndex;
 | 
			
		||||
        let monitors = global.get_monitors()
 | 
			
		||||
        let primary = global.get_primary_monitor()
 | 
			
		||||
        Shell.write_string_to_stream(out, ',\n"monitors":\n[');
 | 
			
		||||
        for (let i = 0; i < monitors.length; i++) {
 | 
			
		||||
            let monitor = monitors[i];
 | 
			
		||||
            let is_primary = (monitor.x == primary.x &&
 | 
			
		||||
                              monitor.y == primary.y &&
 | 
			
		||||
                              monitor.width == primary.width &&
 | 
			
		||||
                              monitor.height == primary.height);
 | 
			
		||||
            if (i != 0)
 | 
			
		||||
                Shell.write_string_to_stream(out, ', ');
 | 
			
		||||
            Shell.write_string_to_stream(out, '"%s%dx%d+%d+%d"'.format(i == primary ? "*" : "",
 | 
			
		||||
            Shell.write_string_to_stream(out, '"%s%dx%d+%d+%d"'.format(is_primary ? "*" : "",
 | 
			
		||||
                                                                       monitor.width, monitor.height,
 | 
			
		||||
                                                                       monitor.x, monitor.y));
 | 
			
		||||
        }
 | 
			
		||||
@@ -263,21 +167,7 @@ function _collect(scriptModule, outputFile) {
 | 
			
		||||
        Shell.write_string_to_stream(out, ',\n"metrics":\n[ ');
 | 
			
		||||
        let first = true;
 | 
			
		||||
        for (let name in scriptModule.METRICS) {
 | 
			
		||||
            let metric = scriptModule.METRICS[name];
 | 
			
		||||
            // Extra checks here because JSON.stringify generates
 | 
			
		||||
            // invalid JSON for undefined values
 | 
			
		||||
            if (metric.description == null) {
 | 
			
		||||
                log("Error: No description found for metric " + name);
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            if (metric.units == null) {
 | 
			
		||||
                log("Error: No units found for metric " + name);
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            if (metric.value == null) {
 | 
			
		||||
                log("Error: No value found for metric " + name);
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            let metric = scriptModule.METRICS[name]; 
 | 
			
		||||
 | 
			
		||||
            if (!first)
 | 
			
		||||
                Shell.write_string_to_stream(out, ',\n  ');
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										125
									
								
								js/ui/search.js
									
									
									
									
									
								
							
							
						
						@@ -1,16 +1,8 @@
 | 
			
		||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
 | 
			
		||||
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Util = imports.misc.util;
 | 
			
		||||
 | 
			
		||||
const FileUtils = imports.misc.fileUtils;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
 | 
			
		||||
const DISABLED_OPEN_SEARCH_PROVIDERS_KEY = 'disabled-open-search-providers';
 | 
			
		||||
const RESULT_ICON_SIZE = 48;
 | 
			
		||||
 | 
			
		||||
// Not currently referenced by the search API, but
 | 
			
		||||
// this enumeration can be useful for provider
 | 
			
		||||
@@ -157,12 +149,11 @@ SearchProvider.prototype = {
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * getResultMeta:
 | 
			
		||||
     * getResultInfo:
 | 
			
		||||
     * @id: Result identifier string
 | 
			
		||||
     *
 | 
			
		||||
     * Return an object with 'id', 'name', (both strings) and 'createIcon'
 | 
			
		||||
     * (function(size) returning a Clutter.Texture) properties which describe
 | 
			
		||||
     * the given search result.
 | 
			
		||||
     * Return an object with 'id', 'name', (both strings) and 'icon' (Clutter.Texture)
 | 
			
		||||
     * properties which describe the given search result.
 | 
			
		||||
     */
 | 
			
		||||
    getResultMeta: function(id) {
 | 
			
		||||
        throw new Error('Not implemented');
 | 
			
		||||
@@ -205,105 +196,21 @@ SearchProvider.prototype = {
 | 
			
		||||
     */
 | 
			
		||||
    activateResult: function(id) {
 | 
			
		||||
        throw new Error('Not implemented');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * expandSearch:
 | 
			
		||||
     *
 | 
			
		||||
     * Called when the user clicks on the header for this
 | 
			
		||||
     * search section.  Should typically launch an external program
 | 
			
		||||
     * displaying search results for that item type.
 | 
			
		||||
     */
 | 
			
		||||
    expandSearch: function(terms) {
 | 
			
		||||
        throw new Error('Not implemented');
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
Signals.addSignalMethods(SearchProvider.prototype);
 | 
			
		||||
 | 
			
		||||
function OpenSearchSystem() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
OpenSearchSystem.prototype = {
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._providers = [];
 | 
			
		||||
        global.settings.connect('changed::' + DISABLED_OPEN_SEARCH_PROVIDERS_KEY, Lang.bind(this, this._refresh));
 | 
			
		||||
        this._refresh();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getProviders: function() {
 | 
			
		||||
        let res = [];
 | 
			
		||||
        for (let i = 0; i < this._providers.length; i++)
 | 
			
		||||
            res.push({ id: i, name: this._providers[i].name });
 | 
			
		||||
 | 
			
		||||
        return res;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setSearchTerms: function(terms) {
 | 
			
		||||
        this._terms = terms;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _checkSupportedProviderLanguage: function(provider) {
 | 
			
		||||
        if (provider.url.search(/{language}/) == -1)
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        let langs = GLib.get_language_names();
 | 
			
		||||
 | 
			
		||||
        langs.push('en');
 | 
			
		||||
        let lang = null;
 | 
			
		||||
        for (let i = 0; i < langs.length; i++) {
 | 
			
		||||
            for (let k = 0; k < provider.langs.length; k++) {
 | 
			
		||||
                if (langs[i] == provider.langs[k])
 | 
			
		||||
                    lang = langs[i];
 | 
			
		||||
            }
 | 
			
		||||
            if (lang)
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
        provider.lang = lang;
 | 
			
		||||
        return lang != null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    activateResult: function(id, params) {
 | 
			
		||||
        let searchTerms = this._terms.join(' ');
 | 
			
		||||
 | 
			
		||||
        let url = this._providers[id].url.replace('{searchTerms}', encodeURIComponent(searchTerms));
 | 
			
		||||
        if (url.match('{language}'))
 | 
			
		||||
            url = url.replace('{language}', this._providers[id].lang);
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            Gio.app_info_launch_default_for_uri(url, global.create_app_launch_context());
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
            // TODO: remove this after glib will be removed from moduleset
 | 
			
		||||
            // In the default jhbuild, gio is in our prefix but gvfs is not
 | 
			
		||||
            Util.spawn(['gvfs-open', url])
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Main.overview.hide();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _addProvider: function(fileName) {
 | 
			
		||||
        let path = global.datadir + '/search_providers/' + fileName;
 | 
			
		||||
        let source = Shell.get_file_contents_utf8_sync(path);
 | 
			
		||||
        let [success, name, url, langs, icon_uri] = Shell.parse_search_provider(source);
 | 
			
		||||
        let provider ={ name: name,
 | 
			
		||||
                        url: url,
 | 
			
		||||
                        id: this._providers.length,
 | 
			
		||||
                        icon_uri: icon_uri,
 | 
			
		||||
                        langs: langs };
 | 
			
		||||
        if (this._checkSupportedProviderLanguage(provider)) {
 | 
			
		||||
            this._providers.push(provider);
 | 
			
		||||
            this.emit('changed');
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _refresh: function() {
 | 
			
		||||
        this._providers = [];
 | 
			
		||||
        let names = global.settings.get_strv(DISABLED_OPEN_SEARCH_PROVIDERS_KEY);
 | 
			
		||||
        let file = Gio.file_new_for_path(global.datadir + '/search_providers');
 | 
			
		||||
        FileUtils.listDirAsync(file, Lang.bind(this, function(files) {
 | 
			
		||||
            for (let i = 0; i < files.length; i++) {
 | 
			
		||||
                let enabled = true;
 | 
			
		||||
                let name = files[i].get_name();
 | 
			
		||||
                for (let k = 0; k < names.length; k++)
 | 
			
		||||
                    if (names[k] == name)
 | 
			
		||||
                        enabled = false;
 | 
			
		||||
                if (enabled)
 | 
			
		||||
                    this._addProvider(name);
 | 
			
		||||
            }
 | 
			
		||||
        }));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
Signals.addSignalMethods(OpenSearchSystem.prototype);
 | 
			
		||||
 | 
			
		||||
function SearchSystem() {
 | 
			
		||||
    this._init();
 | 
			
		||||
}
 | 
			
		||||
@@ -334,7 +241,7 @@ SearchSystem.prototype = {
 | 
			
		||||
    updateSearch: function(searchString) {
 | 
			
		||||
        searchString = searchString.replace(/^\s+/g, '').replace(/\s+$/g, '');
 | 
			
		||||
        if (searchString == '')
 | 
			
		||||
            return [];
 | 
			
		||||
            return null;
 | 
			
		||||
 | 
			
		||||
        let terms = searchString.split(/\s+/);
 | 
			
		||||
        let isSubSearch = terms.length == this._previousTerms.length;
 | 
			
		||||
 
 | 
			
		||||
@@ -2,17 +2,17 @@
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Gettext = imports.gettext.domain('gnome-shell');
 | 
			
		||||
const _ = Gettext.gettext;
 | 
			
		||||
const Gtk = imports.gi.Gtk;
 | 
			
		||||
const Meta = imports.gi.Meta;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
const DND = imports.ui.dnd;
 | 
			
		||||
const IconGrid = imports.ui.iconGrid;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const Overview = imports.ui.overview;
 | 
			
		||||
const Search = imports.ui.search;
 | 
			
		||||
 | 
			
		||||
const MAX_SEARCH_RESULTS_ROWS = 1;
 | 
			
		||||
const MAX_SEARCH_RESULTS_ROWS = 2;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
function SearchResult(provider, metaInfo, terms) {
 | 
			
		||||
@@ -23,12 +23,11 @@ SearchResult.prototype = {
 | 
			
		||||
    _init: function(provider, metaInfo, terms) {
 | 
			
		||||
        this.provider = provider;
 | 
			
		||||
        this.metaInfo = metaInfo;
 | 
			
		||||
        this.actor = new St.Button({ style_class: 'search-result',
 | 
			
		||||
                                     reactive: true,
 | 
			
		||||
                                     x_align: St.Align.START,
 | 
			
		||||
                                     y_fill: true });
 | 
			
		||||
        this.actor = new St.Clickable({ style_class: 'search-result',
 | 
			
		||||
                                        reactive: true,
 | 
			
		||||
                                        x_align: St.Align.START,
 | 
			
		||||
                                        y_fill: true });
 | 
			
		||||
        this.actor._delegate = this;
 | 
			
		||||
        this._dragActorSource = null;
 | 
			
		||||
 | 
			
		||||
        let content = provider.createResultActor(metaInfo, terms);
 | 
			
		||||
        if (content == null) {
 | 
			
		||||
@@ -36,13 +35,10 @@ SearchResult.prototype = {
 | 
			
		||||
                                   reactive: true,
 | 
			
		||||
                                   track_hover: true });
 | 
			
		||||
            let icon = new IconGrid.BaseIcon(this.metaInfo['name'],
 | 
			
		||||
                                             { createIcon: this.metaInfo['createIcon'] });
 | 
			
		||||
                                             { createIcon: Lang.bind(this, function(size) {
 | 
			
		||||
                                                 return this.metaInfo['icon'];
 | 
			
		||||
                                             })});
 | 
			
		||||
            content.set_child(icon.actor);
 | 
			
		||||
            this._dragActorSource = icon.icon;
 | 
			
		||||
            this.actor.label_actor = icon.label;
 | 
			
		||||
        } else {
 | 
			
		||||
            if (content._delegate && content._delegate.getDragActorSource)
 | 
			
		||||
                this._dragActorSource = content._delegate.getDragActorSource();
 | 
			
		||||
        }
 | 
			
		||||
        this._content = content;
 | 
			
		||||
        this.actor.set_child(content);
 | 
			
		||||
@@ -54,10 +50,6 @@ SearchResult.prototype = {
 | 
			
		||||
                          Lang.bind(this, function() {
 | 
			
		||||
                              Main.overview.beginItemDrag(this);
 | 
			
		||||
                          }));
 | 
			
		||||
        draggable.connect('drag-cancelled',
 | 
			
		||||
                          Lang.bind(this, function() {
 | 
			
		||||
                              Main.overview.cancelledItemDrag(this);
 | 
			
		||||
                          }));
 | 
			
		||||
        draggable.connect('drag-end',
 | 
			
		||||
                          Lang.bind(this, function() {
 | 
			
		||||
                              Main.overview.endItemDrag(this);
 | 
			
		||||
@@ -76,26 +68,23 @@ SearchResult.prototype = {
 | 
			
		||||
        Main.overview.toggle();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onResultClicked: function(actor) {
 | 
			
		||||
    _onResultClicked: function(actor, event) {
 | 
			
		||||
        this.activate();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getDragActorSource: function() {
 | 
			
		||||
        if (this._dragActorSource)
 | 
			
		||||
            return this._dragActorSource;
 | 
			
		||||
        // not exactly right, but alignment problems are hard to notice
 | 
			
		||||
        return this._content;
 | 
			
		||||
        return this.metaInfo['icon'];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getDragActor: function(stageX, stageY) {
 | 
			
		||||
        return this.metaInfo['createIcon'](Main.overview.dash.iconSize);
 | 
			
		||||
        return new Clutter.Clone({ source: this.metaInfo['icon'] });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    shellWorkspaceLaunch: function(params) {
 | 
			
		||||
    shellWorkspaceLaunch: function() {
 | 
			
		||||
        if (this.provider.dragActivateResult)
 | 
			
		||||
            this.provider.dragActivateResult(this.metaInfo.id, params);
 | 
			
		||||
            this.provider.dragActivateResult(this.metaInfo.id);
 | 
			
		||||
        else
 | 
			
		||||
            this.provider.activateResult(this.metaInfo.id, params);
 | 
			
		||||
            this.provider.activateResult(this.metaInfo.id);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -112,30 +101,8 @@ GridSearchResults.prototype = {
 | 
			
		||||
        this._grid = new IconGrid.IconGrid({ rowLimit: MAX_SEARCH_RESULTS_ROWS,
 | 
			
		||||
                                             xAlign: St.Align.START });
 | 
			
		||||
        this.actor = new St.Bin({ x_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        this.actor.set_child(this._grid.actor);
 | 
			
		||||
        this.selectionIndex = -1;
 | 
			
		||||
        this._width = 0;
 | 
			
		||||
        this.actor.connect('notify::width', Lang.bind(this, function() {
 | 
			
		||||
            this._width = this.actor.width;
 | 
			
		||||
            Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
 | 
			
		||||
                this._tryAddResults();
 | 
			
		||||
            }));
 | 
			
		||||
        }));
 | 
			
		||||
        this._notDisplayedResult = [];
 | 
			
		||||
        this._terms = [];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _tryAddResults: function() {
 | 
			
		||||
        let canDisplay = this._grid.childrenInRow(this._width) * MAX_SEARCH_RESULTS_ROWS
 | 
			
		||||
                         - this._grid.visibleItemsCount();
 | 
			
		||||
 | 
			
		||||
        for (let i = Math.min(this._notDisplayedResult.length, canDisplay); i > 0; i--) {
 | 
			
		||||
            let result = this._notDisplayedResult.shift();
 | 
			
		||||
            let meta = this.provider.getResultMeta(result);
 | 
			
		||||
            let display = new SearchResult(this.provider, meta, this._terms);
 | 
			
		||||
            this._grid.addItem(display.actor);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getVisibleResultCount: function() {
 | 
			
		||||
@@ -143,15 +110,15 @@ GridSearchResults.prototype = {
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    renderResults: function(results, terms) {
 | 
			
		||||
        // copy the lists
 | 
			
		||||
        this._notDisplayedResult = results.slice(0);
 | 
			
		||||
        this._terms = terms.slice(0);
 | 
			
		||||
        this._tryAddResults();
 | 
			
		||||
        for (let i = 0; i < results.length; i++) {
 | 
			
		||||
            let result = results[i];
 | 
			
		||||
            let meta = this.provider.getResultMeta(result);
 | 
			
		||||
            let display = new SearchResult(this.provider, meta, terms);
 | 
			
		||||
            this._grid.addItem(display.actor);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    clear: function () {
 | 
			
		||||
        this._terms = [];
 | 
			
		||||
        this._notDisplayedResult = [];
 | 
			
		||||
        this._grid.removeAll();
 | 
			
		||||
        this.selectionIndex = -1;
 | 
			
		||||
    },
 | 
			
		||||
@@ -182,41 +149,28 @@ GridSearchResults.prototype = {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
function SearchResults(searchSystem, openSearchSystem) {
 | 
			
		||||
    this._init(searchSystem, openSearchSystem);
 | 
			
		||||
function SearchResults(searchSystem) {
 | 
			
		||||
    this._init(searchSystem);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SearchResults.prototype = {
 | 
			
		||||
    _init: function(searchSystem, openSearchSystem) {
 | 
			
		||||
    _init: function(searchSystem) {
 | 
			
		||||
        this._searchSystem = searchSystem;
 | 
			
		||||
        this._openSearchSystem = openSearchSystem;
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.BoxLayout({ name: 'searchResults',
 | 
			
		||||
                                        vertical: true });
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.Bin({ name: 'searchResults',
 | 
			
		||||
                                  y_align: St.Align.START,
 | 
			
		||||
                                  x_align: St.Align.START,
 | 
			
		||||
                                  x_fill: true });
 | 
			
		||||
        this._content = new St.BoxLayout({ name: 'searchResultsContent',
 | 
			
		||||
                                           vertical: true });
 | 
			
		||||
 | 
			
		||||
        let scrollView = new St.ScrollView({ x_fill: true,
 | 
			
		||||
                                             y_fill: false,
 | 
			
		||||
                                             style_class: 'vfade' });
 | 
			
		||||
                                             vshadows: true });
 | 
			
		||||
        scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
 | 
			
		||||
        scrollView.add_actor(this._content);
 | 
			
		||||
 | 
			
		||||
        this.actor.add(scrollView, { x_fill: true,
 | 
			
		||||
                                     y_fill: false,
 | 
			
		||||
                                     expand: true,
 | 
			
		||||
                                     x_align: St.Align.START,
 | 
			
		||||
                                     y_align: St.Align.START });
 | 
			
		||||
        this.actor.connect('notify::mapped', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                if (!this.actor.mapped)
 | 
			
		||||
                    return;
 | 
			
		||||
 | 
			
		||||
                let adjustment = scrollView.vscroll.adjustment;
 | 
			
		||||
                let direction = Overview.SwipeScrollDirection.VERTICAL;
 | 
			
		||||
                Main.overview.setScrollAdjustment(adjustment, direction);
 | 
			
		||||
            }));
 | 
			
		||||
        this.actor.set_child(scrollView);
 | 
			
		||||
 | 
			
		||||
        this._statusText = new St.Label({ style_class: 'search-statustext' });
 | 
			
		||||
        this._content.add(this._statusText);
 | 
			
		||||
@@ -225,60 +179,23 @@ SearchResults.prototype = {
 | 
			
		||||
        this._providerMeta = [];
 | 
			
		||||
        for (let i = 0; i < this._providers.length; i++)
 | 
			
		||||
            this.createProviderMeta(this._providers[i]);
 | 
			
		||||
 | 
			
		||||
        this._searchProvidersBox = new St.BoxLayout({ style_class: 'search-providers-box' });
 | 
			
		||||
        this.actor.add(this._searchProvidersBox);
 | 
			
		||||
 | 
			
		||||
        this._openSearchProviders = [];
 | 
			
		||||
        this._openSearchSystem.connect('changed', Lang.bind(this, this._updateOpenSearchProviderButtons));
 | 
			
		||||
        this._updateOpenSearchProviderButtons();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateOpenSearchProviderButtons: function() {
 | 
			
		||||
        this._selectedOpenSearchButton = -1;
 | 
			
		||||
        for (let i = 0; i < this._openSearchProviders.length; i++)
 | 
			
		||||
            this._openSearchProviders[i].actor.destroy();
 | 
			
		||||
        this._openSearchProviders = this._openSearchSystem.getProviders();
 | 
			
		||||
        for (let i = 0; i < this._openSearchProviders.length; i++)
 | 
			
		||||
            this._createOpenSearchProviderButton(this._openSearchProviders[i]);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateOpenSearchButtonState: function() {
 | 
			
		||||
         for (let i = 0; i < this._openSearchProviders.length; i++) {
 | 
			
		||||
             if (i == this._selectedOpenSearchButton)
 | 
			
		||||
                 this._openSearchProviders[i].actor.add_style_pseudo_class('selected');
 | 
			
		||||
             else
 | 
			
		||||
                 this._openSearchProviders[i].actor.remove_style_pseudo_class('selected');
 | 
			
		||||
         }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createOpenSearchProviderButton: function(provider) {
 | 
			
		||||
        let button = new St.Button({ style_class: 'dash-search-button',
 | 
			
		||||
                                     reactive: true,
 | 
			
		||||
                                     x_fill: true,
 | 
			
		||||
                                     y_align: St.Align.MIDDLE });
 | 
			
		||||
        let bin = new St.Bin({ x_fill: false,
 | 
			
		||||
                               x_align:St.Align.MIDDLE });
 | 
			
		||||
        button.connect('clicked', Lang.bind(this, function() {
 | 
			
		||||
            this._openSearchSystem.activateResult(provider.id);
 | 
			
		||||
        }));
 | 
			
		||||
        let title = new St.Label({ text: provider.name,
 | 
			
		||||
                                   style_class: 'dash-search-button-label' });
 | 
			
		||||
 | 
			
		||||
        button.label_actor = title;
 | 
			
		||||
        bin.set_child(title);
 | 
			
		||||
        button.set_child(bin);
 | 
			
		||||
        provider.actor = button;
 | 
			
		||||
 | 
			
		||||
        this._searchProvidersBox.add(button);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    createProviderMeta: function(provider) {
 | 
			
		||||
        let providerBox = new St.BoxLayout({ style_class: 'search-section',
 | 
			
		||||
                                             vertical: true });
 | 
			
		||||
        let title = new St.Label({ style_class: 'search-section-header',
 | 
			
		||||
                                   text: provider.title });
 | 
			
		||||
        providerBox.add(title);
 | 
			
		||||
        let titleButton = new St.Button({ style_class: 'search-section-header',
 | 
			
		||||
                                          reactive: true,
 | 
			
		||||
                                          x_fill: true,
 | 
			
		||||
                                          y_fill: true });
 | 
			
		||||
        titleButton.connect('clicked', Lang.bind(this, function () { this._onHeaderClicked(provider); }));
 | 
			
		||||
        providerBox.add(titleButton);
 | 
			
		||||
        let titleBox = new St.BoxLayout();
 | 
			
		||||
        titleButton.set_child(titleBox);
 | 
			
		||||
        let title = new St.Label({ text: provider.title });
 | 
			
		||||
        let count = new St.Label();
 | 
			
		||||
        titleBox.add(title, { expand: true });
 | 
			
		||||
        titleBox.add(count);
 | 
			
		||||
 | 
			
		||||
        let resultDisplayBin = new St.Bin({ style_class: 'search-section-results',
 | 
			
		||||
                                            x_fill: true,
 | 
			
		||||
@@ -291,7 +208,8 @@ SearchResults.prototype = {
 | 
			
		||||
        resultDisplayBin.set_child(resultDisplay.actor);
 | 
			
		||||
 | 
			
		||||
        this._providerMeta.push({ actor: providerBox,
 | 
			
		||||
                                  resultDisplay: resultDisplay });
 | 
			
		||||
                                  resultDisplay: resultDisplay,
 | 
			
		||||
                                  count: count });
 | 
			
		||||
        this._content.add(providerBox);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -309,8 +227,6 @@ SearchResults.prototype = {
 | 
			
		||||
        this._searchSystem.reset();
 | 
			
		||||
        this._statusText.hide();
 | 
			
		||||
        this._clearDisplay();
 | 
			
		||||
        this._selectedOpenSearchButton = -1;
 | 
			
		||||
        this._updateOpenSearchButtonState();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    startingSearch: function() {
 | 
			
		||||
@@ -331,37 +247,30 @@ SearchResults.prototype = {
 | 
			
		||||
        if (results.length == 0) {
 | 
			
		||||
            this._statusText.set_text(_("No matching results."));
 | 
			
		||||
            this._statusText.show();
 | 
			
		||||
            return true;
 | 
			
		||||
        } else {
 | 
			
		||||
            this._selectedOpenSearchButton = -1;
 | 
			
		||||
            this._updateOpenSearchButtonState();
 | 
			
		||||
            this._statusText.hide();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let terms = this._searchSystem.getTerms();
 | 
			
		||||
        this._openSearchSystem.setSearchTerms(terms);
 | 
			
		||||
 | 
			
		||||
        // To avoid CSS transitions causing flickering
 | 
			
		||||
        // of the selection when the first search result
 | 
			
		||||
        // stays the same, we hide the content while
 | 
			
		||||
        // filling in the results and setting the initial
 | 
			
		||||
        // selection.
 | 
			
		||||
        this._content.hide();
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < results.length; i++) {
 | 
			
		||||
            let [provider, providerResults] = results[i];
 | 
			
		||||
            let meta = this._metaForProvider(provider);
 | 
			
		||||
            meta.actor.show();
 | 
			
		||||
            meta.resultDisplay.renderResults(providerResults, terms);
 | 
			
		||||
            meta.count.set_text('' + providerResults.length);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this._selectedOpenSearchButton == -1)
 | 
			
		||||
            this.selectDown(false);
 | 
			
		||||
 | 
			
		||||
        this._content.show();
 | 
			
		||||
        this.selectDown(false);
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onHeaderClicked: function(provider) {
 | 
			
		||||
        provider.expandSearch(this._searchSystem.getTerms());
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _modifyActorSelection: function(resultDisplay, up) {
 | 
			
		||||
        let success;
 | 
			
		||||
        let index = resultDisplay.getSelectionIndex();
 | 
			
		||||
@@ -375,26 +284,16 @@ SearchResults.prototype = {
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    selectUp: function(recursing) {
 | 
			
		||||
        if (this._selectedOpenSearchButton == -1) {
 | 
			
		||||
            for (let i = this._selectedProvider; i >= 0; i--) {
 | 
			
		||||
                let meta = this._providerMeta[i];
 | 
			
		||||
                if (!meta.actor.visible)
 | 
			
		||||
                    continue;
 | 
			
		||||
                let success = this._modifyActorSelection(meta.resultDisplay, true);
 | 
			
		||||
                if (success) {
 | 
			
		||||
                    this._selectedProvider = i;
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
        for (let i = this._selectedProvider; i >= 0; i--) {
 | 
			
		||||
            let meta = this._providerMeta[i];
 | 
			
		||||
            if (!meta.actor.visible)
 | 
			
		||||
                continue;
 | 
			
		||||
            let success = this._modifyActorSelection(meta.resultDisplay, true);
 | 
			
		||||
            if (success) {
 | 
			
		||||
                this._selectedProvider = i;
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this._selectedOpenSearchButton == -1)
 | 
			
		||||
            this._selectedOpenSearchButton = this._openSearchProviders.length;
 | 
			
		||||
        this._selectedOpenSearchButton--;
 | 
			
		||||
        this._updateOpenSearchButtonState();
 | 
			
		||||
        if (this._selectedOpenSearchButton >= 0)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (this._providerMeta.length > 0 && !recursing) {
 | 
			
		||||
            this._selectedProvider = this._providerMeta.length - 1;
 | 
			
		||||
            this.selectUp(true);
 | 
			
		||||
@@ -403,30 +302,18 @@ SearchResults.prototype = {
 | 
			
		||||
 | 
			
		||||
    selectDown: function(recursing) {
 | 
			
		||||
        let current = this._selectedProvider;
 | 
			
		||||
        if (this._selectedOpenSearchButton == -1) {
 | 
			
		||||
            if (current == -1)
 | 
			
		||||
                current = 0;
 | 
			
		||||
            for (let i = current; i < this._providerMeta.length; i++) {
 | 
			
		||||
                let meta = this._providerMeta[i];
 | 
			
		||||
                if (!meta.actor.visible)
 | 
			
		||||
                    continue;
 | 
			
		||||
                 let success = this._modifyActorSelection(meta.resultDisplay, false);
 | 
			
		||||
                 if (success) {
 | 
			
		||||
                    this._selectedProvider = i;
 | 
			
		||||
                    return;
 | 
			
		||||
                 }
 | 
			
		||||
        if (current == -1)
 | 
			
		||||
            current = 0;
 | 
			
		||||
        for (let i = current; i < this._providerMeta.length; i++) {
 | 
			
		||||
            let meta = this._providerMeta[i];
 | 
			
		||||
            if (!meta.actor.visible)
 | 
			
		||||
                continue;
 | 
			
		||||
            let success = this._modifyActorSelection(meta.resultDisplay, false);
 | 
			
		||||
            if (success) {
 | 
			
		||||
                this._selectedProvider = i;
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        this._selectedOpenSearchButton++;
 | 
			
		||||
 | 
			
		||||
        if (this._selectedOpenSearchButton < this._openSearchProviders.length) {
 | 
			
		||||
            this._updateOpenSearchButtonState();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._selectedOpenSearchButton = -1;
 | 
			
		||||
        this._updateOpenSearchButtonState();
 | 
			
		||||
 | 
			
		||||
        if (this._providerMeta.length > 0 && !recursing) {
 | 
			
		||||
            this._selectedProvider = 0;
 | 
			
		||||
            this.selectDown(true);
 | 
			
		||||
@@ -434,13 +321,6 @@ SearchResults.prototype = {
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    activateSelected: function() {
 | 
			
		||||
        if (this._selectedOpenSearchButton != -1) {
 | 
			
		||||
            let provider = this._openSearchProviders[this._selectedOpenSearchButton];
 | 
			
		||||
            this._openSearchSystem.activateResult(provider.id);
 | 
			
		||||
            Main.overview.hide();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let current = this._selectedProvider;
 | 
			
		||||
        if (current < 0)
 | 
			
		||||
            return;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,405 +0,0 @@
 | 
			
		||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
 | 
			
		||||
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const Gtk = imports.gi.Gtk;
 | 
			
		||||
const Pango = imports.gi.Pango;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const MessageTray = imports.ui.messageTray;
 | 
			
		||||
const ModalDialog = imports.ui.modalDialog;
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
 | 
			
		||||
const LIST_ITEM_ICON_SIZE = 48;
 | 
			
		||||
 | 
			
		||||
/* ------ Common Utils ------- */
 | 
			
		||||
function _setLabelText(label, text) {
 | 
			
		||||
    if (text) {
 | 
			
		||||
        label.set_text(text);
 | 
			
		||||
        label.show();
 | 
			
		||||
    } else {
 | 
			
		||||
        label.set_text('');
 | 
			
		||||
        label.hide();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _setButtonsForChoices(dialog, choices) {
 | 
			
		||||
    let buttons = [];
 | 
			
		||||
 | 
			
		||||
    for (let idx = 0; idx < choices.length; idx++) {
 | 
			
		||||
        let button = idx;
 | 
			
		||||
        buttons.unshift({ label: choices[idx],
 | 
			
		||||
                          action: Lang.bind(dialog, function() {
 | 
			
		||||
                              dialog.emit('response', button);
 | 
			
		||||
                          })});
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    dialog.setButtons(buttons);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _setLabelsForMessage(dialog, message) {
 | 
			
		||||
    let labels = message.split('\n');
 | 
			
		||||
 | 
			
		||||
    _setLabelText(dialog.subjectLabel, labels[0]);
 | 
			
		||||
    if (labels.length > 1)
 | 
			
		||||
        _setLabelText(dialog.descriptionLabel, labels[1]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* -------------------------------------------------------- */
 | 
			
		||||
 | 
			
		||||
function ListItem(app) {
 | 
			
		||||
    this._init(app);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ListItem.prototype = {
 | 
			
		||||
    _init: function(app) {
 | 
			
		||||
        this._app = app;
 | 
			
		||||
 | 
			
		||||
        let layout = new St.BoxLayout({ vertical: false});
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.Button({ style_class: 'show-processes-dialog-app-list-item',
 | 
			
		||||
                                     can_focus: true,
 | 
			
		||||
                                     child: layout,
 | 
			
		||||
                                     reactive: true,
 | 
			
		||||
                                     x_align: St.Align.START,
 | 
			
		||||
                                     x_fill: true });
 | 
			
		||||
 | 
			
		||||
        this._icon = this._app.create_icon_texture(LIST_ITEM_ICON_SIZE);
 | 
			
		||||
 | 
			
		||||
        let iconBin = new St.Bin({ style_class: 'show-processes-dialog-app-list-item-icon',
 | 
			
		||||
                                   child: this._icon });
 | 
			
		||||
        layout.add(iconBin);
 | 
			
		||||
 | 
			
		||||
        this._nameLabel = new St.Label({ text: this._app.get_name(),
 | 
			
		||||
                                         style_class: 'show-processes-dialog-app-list-item-name' });
 | 
			
		||||
        let labelBin = new St.Bin({ y_align: St.Align.MIDDLE,
 | 
			
		||||
                                    child: this._nameLabel });
 | 
			
		||||
        layout.add(labelBin);
 | 
			
		||||
 | 
			
		||||
        this.actor.connect('clicked', Lang.bind(this, this._onClicked));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onClicked: function() {
 | 
			
		||||
        this.emit('activate');
 | 
			
		||||
        this._app.activate(-1);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
Signals.addSignalMethods(ListItem.prototype);
 | 
			
		||||
 | 
			
		||||
function ShellMountOperation(source, params) {
 | 
			
		||||
    this._init(source, params);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ShellMountOperation.prototype = {
 | 
			
		||||
    _init: function(source, params) {
 | 
			
		||||
        params = Params.parse(params, { reaskPassword: false });
 | 
			
		||||
 | 
			
		||||
        this._reaskPassword = params.reaskPassword;
 | 
			
		||||
 | 
			
		||||
        this._dialog = null;
 | 
			
		||||
        this._processesDialog = null;
 | 
			
		||||
 | 
			
		||||
        this.mountOp = new Shell.MountOperation();
 | 
			
		||||
 | 
			
		||||
        this.mountOp.connect('ask-question',
 | 
			
		||||
                             Lang.bind(this, this._onAskQuestion));
 | 
			
		||||
        this.mountOp.connect('ask-password',
 | 
			
		||||
                             Lang.bind(this, this._onAskPassword));
 | 
			
		||||
        this.mountOp.connect('show-processes-2',
 | 
			
		||||
                             Lang.bind(this, this._onShowProcesses2));
 | 
			
		||||
        this.mountOp.connect('aborted',
 | 
			
		||||
                             Lang.bind(this, this._onAborted));
 | 
			
		||||
 | 
			
		||||
        this._icon = new St.Icon({ gicon: source.get_icon(),
 | 
			
		||||
                                   style_class: 'shell-mount-operation-icon' });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onAskQuestion: function(op, message, choices) {
 | 
			
		||||
        this._dialog = new ShellMountQuestionDialog(this._icon);
 | 
			
		||||
 | 
			
		||||
        this._dialog.connect('response',
 | 
			
		||||
                               Lang.bind(this, function(object, choice) {
 | 
			
		||||
                                   this.mountOp.set_choice(choice);
 | 
			
		||||
                                   this.mountOp.reply(Gio.MountOperationResult.HANDLED);
 | 
			
		||||
 | 
			
		||||
                                   this._dialog.close(global.get_current_time());
 | 
			
		||||
                                   this._dialog = null;
 | 
			
		||||
                               }));
 | 
			
		||||
 | 
			
		||||
        this._dialog.update(message, choices);
 | 
			
		||||
        this._dialog.open(global.get_current_time());
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onAskPassword: function(op, message) {
 | 
			
		||||
        this._notificationShowing = true;
 | 
			
		||||
        this._source = new ShellMountPasswordSource(message, this._icon, this._reaskPassword);
 | 
			
		||||
 | 
			
		||||
        this._source.connect('password-ready',
 | 
			
		||||
                             Lang.bind(this, function(source, password) {
 | 
			
		||||
                                 this.mountOp.set_password(password);
 | 
			
		||||
                                 this.mountOp.reply(Gio.MountOperationResult.HANDLED);
 | 
			
		||||
 | 
			
		||||
                                 this._notificationShowing = false;
 | 
			
		||||
                                 this._source.destroy();
 | 
			
		||||
                             }));
 | 
			
		||||
 | 
			
		||||
        this._source.connect('destroy',
 | 
			
		||||
                             Lang.bind(this, function() {
 | 
			
		||||
                                 if (!this._notificationShowing)
 | 
			
		||||
                                     return;
 | 
			
		||||
 | 
			
		||||
                                 this._notificationShowing = false;
 | 
			
		||||
                                 this.mountOp.reply(Gio.MountOperationResult.ABORTED);
 | 
			
		||||
                             }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onAborted: function(op) {
 | 
			
		||||
        if (!this._dialog)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._dialog.close(global.get_current_time());
 | 
			
		||||
        this._dialog = null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onShowProcesses2: function(op) {
 | 
			
		||||
        let processes = op.get_show_processes_pids();
 | 
			
		||||
        let choices = op.get_show_processes_choices();
 | 
			
		||||
        let message = op.get_show_processes_message();
 | 
			
		||||
 | 
			
		||||
        if (!this._processesDialog) {
 | 
			
		||||
            this._processesDialog = new ShellProcessesDialog(this._icon);
 | 
			
		||||
            this._dialog = this._processesDialog;
 | 
			
		||||
 | 
			
		||||
            this._processesDialog.connect('response', 
 | 
			
		||||
                                          Lang.bind(this, function(object, choice) {
 | 
			
		||||
                                              if (choice == -1) {
 | 
			
		||||
                                                  this.mountOp.reply(Gio.MountOperationResult.ABORTED);
 | 
			
		||||
                                              } else {
 | 
			
		||||
                                                  this.mountOp.set_choice(choice);
 | 
			
		||||
                                                  this.mountOp.reply(Gio.MountOperationResult.HANDLED);
 | 
			
		||||
                                              }
 | 
			
		||||
 | 
			
		||||
                                              this._processesDialog.close(global.get_current_time());
 | 
			
		||||
                                              this._dialog = null;
 | 
			
		||||
                                          }));
 | 
			
		||||
            this._processesDialog.open(global.get_current_time());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._processesDialog.update(message, processes, choices);
 | 
			
		||||
    },
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function ShellMountQuestionDialog(icon) {
 | 
			
		||||
    this._init(icon);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ShellMountQuestionDialog.prototype = {
 | 
			
		||||
    __proto__: ModalDialog.ModalDialog.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function(icon) {
 | 
			
		||||
        ModalDialog.ModalDialog.prototype._init.call(this, { styleClass: 'mount-question-dialog' });
 | 
			
		||||
 | 
			
		||||
        let mainContentLayout = new St.BoxLayout();
 | 
			
		||||
        this.contentLayout.add(mainContentLayout, { x_fill: true,
 | 
			
		||||
                                                    y_fill: false });
 | 
			
		||||
 | 
			
		||||
        this._iconBin = new St.Bin({ child: icon });
 | 
			
		||||
        mainContentLayout.add(this._iconBin,
 | 
			
		||||
                              { x_fill:  true,
 | 
			
		||||
                                y_fill:  false,
 | 
			
		||||
                                x_align: St.Align.END,
 | 
			
		||||
                                y_align: St.Align.MIDDLE });
 | 
			
		||||
 | 
			
		||||
        let messageLayout = new St.BoxLayout({ vertical: true });
 | 
			
		||||
        mainContentLayout.add(messageLayout,
 | 
			
		||||
                              { y_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        this.subjectLabel = new St.Label({ style_class: 'mount-question-dialog-subject' });
 | 
			
		||||
 | 
			
		||||
        messageLayout.add(this.subjectLabel,
 | 
			
		||||
                          { y_fill:  false,
 | 
			
		||||
                            y_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        this.descriptionLabel = new St.Label({ style_class: 'mount-question-dialog-description' });
 | 
			
		||||
        this.descriptionLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
 | 
			
		||||
        this.descriptionLabel.clutter_text.line_wrap = true;
 | 
			
		||||
 | 
			
		||||
        messageLayout.add(this.descriptionLabel,
 | 
			
		||||
                          { y_fill:  true,
 | 
			
		||||
                            y_align: St.Align.START });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    update: function(message, choices) {
 | 
			
		||||
        _setLabelsForMessage(this, message);
 | 
			
		||||
        _setButtonsForChoices(this, choices);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
Signals.addSignalMethods(ShellMountQuestionDialog.prototype);
 | 
			
		||||
 | 
			
		||||
function ShellMountPasswordSource(message, icon, reaskPassword) {
 | 
			
		||||
    this._init(message, icon, reaskPassword);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ShellMountPasswordSource.prototype = {
 | 
			
		||||
    __proto__: MessageTray.Source.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function(message, icon, reaskPassword) {
 | 
			
		||||
        let strings = message.split('\n');
 | 
			
		||||
        MessageTray.Source.prototype._init.call(this, strings[0]);
 | 
			
		||||
 | 
			
		||||
        this._notification = new ShellMountPasswordNotification(this, strings, icon, reaskPassword);
 | 
			
		||||
 | 
			
		||||
        // add ourselves as a source, and popup the notification
 | 
			
		||||
        Main.messageTray.add(this);
 | 
			
		||||
        this.notify(this._notification);
 | 
			
		||||
    },
 | 
			
		||||
}
 | 
			
		||||
Signals.addSignalMethods(ShellMountPasswordSource.prototype);
 | 
			
		||||
 | 
			
		||||
function ShellMountPasswordNotification(source, strings, icon, reaskPassword) {
 | 
			
		||||
    this._init(source, strings, icon, reaskPassword);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ShellMountPasswordNotification.prototype = {
 | 
			
		||||
    __proto__: MessageTray.Notification.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function(source, strings, icon, reaskPassword) {
 | 
			
		||||
        MessageTray.Notification.prototype._init.call(this, source,
 | 
			
		||||
                                                      strings[0], null,
 | 
			
		||||
                                                      { customContent: true,
 | 
			
		||||
                                                        icon: icon });
 | 
			
		||||
 | 
			
		||||
        // set the notification to transient and urgent, so that it
 | 
			
		||||
        // expands out
 | 
			
		||||
        this.setTransient(true);
 | 
			
		||||
        this.setUrgency(MessageTray.Urgency.CRITICAL);
 | 
			
		||||
 | 
			
		||||
        if (strings[1])
 | 
			
		||||
            this.addBody(strings[1]);
 | 
			
		||||
 | 
			
		||||
        if (reaskPassword) {
 | 
			
		||||
            let label = new St.Label({ style_class: 'mount-password-reask',
 | 
			
		||||
                                       text: _("Wrong password, please try again") });
 | 
			
		||||
 | 
			
		||||
            this.addActor(label);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._responseEntry = new St.Entry({ style_class: 'mount-password-entry',
 | 
			
		||||
                                             can_focus: true });
 | 
			
		||||
        this.setActionArea(this._responseEntry);
 | 
			
		||||
 | 
			
		||||
        this._responseEntry.clutter_text.connect('activate',
 | 
			
		||||
                                                 Lang.bind(this, this._onEntryActivated));
 | 
			
		||||
        this._responseEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
 | 
			
		||||
 | 
			
		||||
        this._responseEntry.grab_key_focus();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onEntryActivated: function() {
 | 
			
		||||
        let text = this._responseEntry.get_text();
 | 
			
		||||
        if (text == '')
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this.source.emit('password-ready', text);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function ShellProcessesDialog(icon) {
 | 
			
		||||
    this._init(icon);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ShellProcessesDialog.prototype = {
 | 
			
		||||
    __proto__: ModalDialog.ModalDialog.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function(icon) {
 | 
			
		||||
        ModalDialog.ModalDialog.prototype._init.call(this, { styleClass: 'show-processes-dialog' });
 | 
			
		||||
 | 
			
		||||
        let mainContentLayout = new St.BoxLayout();
 | 
			
		||||
        this.contentLayout.add(mainContentLayout, { x_fill: true,
 | 
			
		||||
                                                    y_fill: false });
 | 
			
		||||
 | 
			
		||||
        this._iconBin = new St.Bin({ child: icon });
 | 
			
		||||
        mainContentLayout.add(this._iconBin,
 | 
			
		||||
                              { x_fill:  true,
 | 
			
		||||
                                y_fill:  false,
 | 
			
		||||
                                x_align: St.Align.END,
 | 
			
		||||
                                y_align: St.Align.MIDDLE });
 | 
			
		||||
 | 
			
		||||
        let messageLayout = new St.BoxLayout({ vertical: true });
 | 
			
		||||
        mainContentLayout.add(messageLayout,
 | 
			
		||||
                              { y_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        this.subjectLabel = new St.Label({ style_class: 'show-processes-dialog-subject' });
 | 
			
		||||
 | 
			
		||||
        messageLayout.add(this.subjectLabel,
 | 
			
		||||
                          { y_fill:  false,
 | 
			
		||||
                            y_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        this.descriptionLabel = new St.Label({ style_class: 'show-processes-dialog-description' });
 | 
			
		||||
        this.descriptionLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
 | 
			
		||||
        this.descriptionLabel.clutter_text.line_wrap = true;
 | 
			
		||||
 | 
			
		||||
        messageLayout.add(this.descriptionLabel,
 | 
			
		||||
                          { y_fill:  true,
 | 
			
		||||
                            y_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        let scrollView = new St.ScrollView({ style_class: 'show-processes-dialog-app-list'});
 | 
			
		||||
        scrollView.set_policy(Gtk.PolicyType.NEVER,
 | 
			
		||||
                              Gtk.PolicyType.AUTOMATIC);
 | 
			
		||||
        this.contentLayout.add(scrollView,
 | 
			
		||||
                               { x_fill: true,
 | 
			
		||||
                                 y_fill: true });
 | 
			
		||||
        scrollView.hide();
 | 
			
		||||
 | 
			
		||||
        this._applicationList = new St.BoxLayout({ vertical: true });
 | 
			
		||||
        scrollView.add_actor(this._applicationList,
 | 
			
		||||
                             { x_fill:  true,
 | 
			
		||||
                               y_fill:  true,
 | 
			
		||||
                               x_align: St.Align.START,
 | 
			
		||||
                               y_align: St.Align.MIDDLE });
 | 
			
		||||
 | 
			
		||||
        this._applicationList.connect('actor-added',
 | 
			
		||||
                                      Lang.bind(this, function() {
 | 
			
		||||
                                          if (this._applicationList.get_children().length == 1)
 | 
			
		||||
                                              scrollView.show();
 | 
			
		||||
                                      }));
 | 
			
		||||
 | 
			
		||||
        this._applicationList.connect('actor-removed',
 | 
			
		||||
                                      Lang.bind(this, function() {
 | 
			
		||||
                                          if (this._applicationList.get_children().length == 0)
 | 
			
		||||
                                              scrollView.hide();
 | 
			
		||||
                                      }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _setAppsForPids: function(pids) {
 | 
			
		||||
        // remove all the items
 | 
			
		||||
        this._applicationList.destroy_children();
 | 
			
		||||
 | 
			
		||||
        pids.forEach(Lang.bind(this, function(pid) {
 | 
			
		||||
            let tracker = Shell.WindowTracker.get_default();
 | 
			
		||||
            let app = tracker.get_app_from_pid(pid);
 | 
			
		||||
 | 
			
		||||
            if (!app)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            let item = new ListItem(app);
 | 
			
		||||
            this._applicationList.add(item.actor, { x_fill: true });
 | 
			
		||||
 | 
			
		||||
            item.connect('activate',
 | 
			
		||||
                         Lang.bind(this, function() {
 | 
			
		||||
                             // use -1 to indicate Cancel
 | 
			
		||||
                             this.emit('response', -1);
 | 
			
		||||
                         }));
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    update: function(message, processes, choices) {
 | 
			
		||||
        this._setAppsForPids(processes);
 | 
			
		||||
        _setLabelsForMessage(this, message);
 | 
			
		||||
        _setButtonsForChoices(this, choices);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
Signals.addSignalMethods(ShellProcessesDialog.prototype);
 | 
			
		||||
@@ -13,15 +13,21 @@ const St = imports.gi.St;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const PanelMenu = imports.ui.panelMenu;
 | 
			
		||||
const PopupMenu = imports.ui.popupMenu;
 | 
			
		||||
const Util = imports.misc.util;
 | 
			
		||||
 | 
			
		||||
const A11Y_SCHEMA = 'org.gnome.desktop.a11y.keyboard';
 | 
			
		||||
const KEY_STICKY_KEYS_ENABLED = 'stickykeys-enable';
 | 
			
		||||
const KEY_BOUNCE_KEYS_ENABLED = 'bouncekeys-enable';
 | 
			
		||||
const KEY_SLOW_KEYS_ENABLED   = 'slowkeys-enable';
 | 
			
		||||
const KEY_MOUSE_KEYS_ENABLED  = 'mousekeys-enable';
 | 
			
		||||
const Gettext = imports.gettext.domain('gnome-shell');
 | 
			
		||||
const _ = Gettext.gettext;
 | 
			
		||||
 | 
			
		||||
const APPLICATIONS_SCHEMA = 'org.gnome.desktop.a11y.applications';
 | 
			
		||||
const A11Y_SCHEMA = "org.gnome.desktop.a11y.keyboard";
 | 
			
		||||
const KEY_STICKY_KEYS_ENABLED = "stickykeys-enable";
 | 
			
		||||
const KEY_BOUNCE_KEYS_ENABLED = "bouncekeys-enable";
 | 
			
		||||
const KEY_SLOW_KEYS_ENABLED   = "slowkeys-enable";
 | 
			
		||||
const KEY_MOUSE_KEYS_ENABLED  = "mousekeys-enable";
 | 
			
		||||
 | 
			
		||||
const AT_SCREEN_KEYBOARD_SCHEMA = "org.gnome.desktop.default-applications.at.mobility";
 | 
			
		||||
const AT_SCREEN_READER_SCHEMA   = "org.gnome.desktop.default-applications.at.visual";
 | 
			
		||||
 | 
			
		||||
const XSETTINGS_SCHEMA = "org.gnome.settings-daemon.plugins.xsettings";
 | 
			
		||||
const KEY_DPI = "dpi";
 | 
			
		||||
 | 
			
		||||
const DPI_LOW_REASONABLE_VALUE  = 50;
 | 
			
		||||
const DPI_HIGH_REASONABLE_VALUE = 500;
 | 
			
		||||
@@ -29,16 +35,32 @@ 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 DPI_DEFAULT        = 96;
 | 
			
		||||
 | 
			
		||||
const KEY_META_DIR       = '/apps/metacity/general';
 | 
			
		||||
const KEY_VISUAL_BELL = KEY_META_DIR + '/visual_bell';
 | 
			
		||||
const KEY_META_DIR       = "/apps/metacity/general";
 | 
			
		||||
const KEY_VISUAL_BELL = KEY_META_DIR + "/visual_bell";
 | 
			
		||||
 | 
			
		||||
const DESKTOP_INTERFACE_SCHEMA = 'org.gnome.desktop.interface';
 | 
			
		||||
const KEY_GTK_THEME      = 'gtk-theme';
 | 
			
		||||
const KEY_ICON_THEME     = 'icon-theme';
 | 
			
		||||
const KEY_TEXT_SCALING_FACTOR = 'text-scaling-factor';
 | 
			
		||||
const DESKTOP_INTERFACE_SCHEMA = "org.gnome.desktop.interface";
 | 
			
		||||
const KEY_GTK_THEME      = "gtk-theme";
 | 
			
		||||
const KEY_ICON_THEME     = "icon-theme";
 | 
			
		||||
 | 
			
		||||
const HIGH_CONTRAST_THEME = 'HighContrast';
 | 
			
		||||
const HIGH_CONTRAST_THEME = "HighContrast";
 | 
			
		||||
 | 
			
		||||
function getDPIFromX() {
 | 
			
		||||
    let screen = global.get_gdk_screen();
 | 
			
		||||
    if (screen) {
 | 
			
		||||
        let width_dpi = (screen.get_width() / (screen.get_width_mm() / 25.4));
 | 
			
		||||
        let height_dpi = (screen.get_height() / (screen.get_height_mm() / 25.4));
 | 
			
		||||
        if (width_dpi < DPI_LOW_REASONABLE_VALUE
 | 
			
		||||
            || width_dpi > DPI_HIGH_REASONABLE_VALUE
 | 
			
		||||
            || height_dpi < DPI_LOW_REASONABLE_VALUE
 | 
			
		||||
            || height_dpi > DPI_HIGH_REASONABLE_VALUE)
 | 
			
		||||
            return DPI_DEFAULT;
 | 
			
		||||
        else
 | 
			
		||||
            return (width_dpi + height_dpi) / 2;
 | 
			
		||||
    }
 | 
			
		||||
    return DPI_DEFAULT;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function ATIndicator() {
 | 
			
		||||
    this._init.apply(this, arguments);
 | 
			
		||||
@@ -57,20 +79,17 @@ ATIndicator.prototype = {
 | 
			
		||||
        let highContrast = this._buildHCItem();
 | 
			
		||||
        this.menu.addMenuItem(highContrast);
 | 
			
		||||
 | 
			
		||||
        let magnifier = this._buildItem(_("Zoom"), APPLICATIONS_SCHEMA,
 | 
			
		||||
                                                   'screen-magnifier-enabled');
 | 
			
		||||
        let magnifier = this._buildMagItem();
 | 
			
		||||
        this.menu.addMenuItem(magnifier);
 | 
			
		||||
 | 
			
		||||
        let textZoom = this._buildFontItem();
 | 
			
		||||
        this.menu.addMenuItem(textZoom);
 | 
			
		||||
 | 
			
		||||
//        let screenReader = this._buildItem(_("Screen Reader"), APPLICATIONS_SCHEMA,
 | 
			
		||||
//                                                               'screen-reader-enabled');
 | 
			
		||||
//        this.menu.addMenuItem(screenReader);
 | 
			
		||||
        let screenReader = this._buildItem(_("Screen Reader"), AT_SCREEN_READER_SCHEMA, 'startup');
 | 
			
		||||
        this.menu.addMenuItem(screenReader);
 | 
			
		||||
 | 
			
		||||
//        let screenKeyboard = this._buildItem(_("Screen Keyboard"), APPLICATIONS_SCHEMA,
 | 
			
		||||
//                                                                   'screen-keyboard-enabled');
 | 
			
		||||
//        this.menu.addMenuItem(screenKeyboard);
 | 
			
		||||
        let screenKeyboard = this._buildItem(_("Screen Keyboard"), AT_SCREEN_KEYBOARD_SCHEMA, 'startup');
 | 
			
		||||
        this.menu.addMenuItem(screenKeyboard);
 | 
			
		||||
 | 
			
		||||
        let visualBell = this._buildItemGConf(_("Visual Alerts"), client, KEY_VISUAL_BELL);
 | 
			
		||||
        this.menu.addMenuItem(visualBell);
 | 
			
		||||
@@ -89,9 +108,8 @@ ATIndicator.prototype = {
 | 
			
		||||
 | 
			
		||||
        this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
 | 
			
		||||
        this.menu.addAction(_("Universal Access Settings"), function() {
 | 
			
		||||
            Main.overview.hide();
 | 
			
		||||
            let app = Shell.AppSystem.get_default().get_app('gnome-universal-access-panel.desktop');
 | 
			
		||||
            app.activate(-1);
 | 
			
		||||
            let p = new Shell.Process({ args: ['gnome-control-center','universal-access'] });
 | 
			
		||||
            p.run();
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -149,12 +167,9 @@ ATIndicator.prototype = {
 | 
			
		||||
                if (enabled) {
 | 
			
		||||
                    settings.set_string(KEY_GTK_THEME, HIGH_CONTRAST_THEME);
 | 
			
		||||
                    settings.set_string(KEY_ICON_THEME, HIGH_CONTRAST_THEME);
 | 
			
		||||
                } else if(!hasHC) {
 | 
			
		||||
                } else {
 | 
			
		||||
                    settings.set_string(KEY_GTK_THEME, gtkTheme);
 | 
			
		||||
                    settings.set_string(KEY_ICON_THEME, iconTheme);
 | 
			
		||||
                } else {
 | 
			
		||||
                    settings.reset(KEY_GTK_THEME);
 | 
			
		||||
                    settings.reset(KEY_ICON_THEME);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        settings.connect('changed::' + KEY_GTK_THEME, function() {
 | 
			
		||||
@@ -175,23 +190,42 @@ ATIndicator.prototype = {
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _buildFontItem: function() {
 | 
			
		||||
        let settings = new Gio.Settings({ schema: DESKTOP_INTERFACE_SCHEMA });
 | 
			
		||||
        let settings = new Gio.Settings({ schema: XSETTINGS_SCHEMA });
 | 
			
		||||
 | 
			
		||||
        let factor = settings.get_double(KEY_TEXT_SCALING_FACTOR);
 | 
			
		||||
        let initial_setting = (factor > 1.0);
 | 
			
		||||
        // we assume this never changes (which is not true if resolution
 | 
			
		||||
        // is changed, but we would need XRandR events for that)
 | 
			
		||||
        let x_value = getDPIFromX();
 | 
			
		||||
        let user_value;
 | 
			
		||||
        function on_get() {
 | 
			
		||||
            user_value = settings.get_double(KEY_DPI);
 | 
			
		||||
            return (user_value - (DPI_FACTOR_LARGE * x_value) > -1);
 | 
			
		||||
        }
 | 
			
		||||
        let initial_setting = on_get();
 | 
			
		||||
        let default_value = initial_setting ? x_value : user_value;
 | 
			
		||||
        let widget = this._buildItemExtended(_("Large Text"),
 | 
			
		||||
            initial_setting,
 | 
			
		||||
            settings.is_writable(KEY_TEXT_SCALING_FACTOR),
 | 
			
		||||
            settings.is_writable(KEY_DPI),
 | 
			
		||||
            function (enabled) {
 | 
			
		||||
                if (enabled)
 | 
			
		||||
                    settings.set_double(KEY_TEXT_SCALING_FACTOR,
 | 
			
		||||
                                        DPI_FACTOR_LARGE);
 | 
			
		||||
                    settings.set_double(KEY_DPI, DPI_FACTOR_LARGE * default_value);
 | 
			
		||||
                else
 | 
			
		||||
                    settings.reset(KEY_TEXT_SCALING_FACTOR);
 | 
			
		||||
                    settings.set_double(KEY_DPI, default_value);
 | 
			
		||||
            });
 | 
			
		||||
        settings.connect('changed::' + KEY_TEXT_SCALING_FACTOR, function() {
 | 
			
		||||
            let factor = settings.get_double(KEY_TEXT_SCALING_FACTOR);
 | 
			
		||||
            let active = (factor > 1.0);
 | 
			
		||||
        settings.connect('changed::' + KEY_DPI, function() {
 | 
			
		||||
            let active = on_get();
 | 
			
		||||
            default_value = active ? x_value : user_value;
 | 
			
		||||
            widget.setToggleState(active);
 | 
			
		||||
        });
 | 
			
		||||
        return widget;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _buildMagItem: function() {
 | 
			
		||||
        let mag = Main.magnifier;
 | 
			
		||||
        let widget = this._buildItemExtended(_("Zoom"),
 | 
			
		||||
            mag.isActive(),
 | 
			
		||||
            true,
 | 
			
		||||
            Lang.bind(mag, mag.setActive));
 | 
			
		||||
        mag.connect('active-changed', function(magnifier, active) {
 | 
			
		||||
            widget.setToggleState(active);
 | 
			
		||||
        });
 | 
			
		||||
        return widget;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,525 +0,0 @@
 | 
			
		||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Gdk = imports.gi.Gdk;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const GnomeBluetoothApplet = imports.gi.GnomeBluetoothApplet;
 | 
			
		||||
const Gtk = imports.gi.Gtk;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Mainloop = imports.mainloop;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const MessageTray = imports.ui.messageTray;
 | 
			
		||||
const PanelMenu = imports.ui.panelMenu;
 | 
			
		||||
const PopupMenu = imports.ui.popupMenu;
 | 
			
		||||
 | 
			
		||||
const ConnectionState = {
 | 
			
		||||
    DISCONNECTED: 0,
 | 
			
		||||
    CONNECTED: 1,
 | 
			
		||||
    DISCONNECTING: 2,
 | 
			
		||||
    CONNECTING: 3
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function Indicator() {
 | 
			
		||||
    this._init.apply(this, arguments);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Indicator.prototype = {
 | 
			
		||||
    __proto__: PanelMenu.SystemStatusButton.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        PanelMenu.SystemStatusButton.prototype._init.call(this, 'bluetooth-disabled', null);
 | 
			
		||||
 | 
			
		||||
        GLib.spawn_command_line_sync ('pkill -f "^bluetooth-applet$"');
 | 
			
		||||
        this._applet = new GnomeBluetoothApplet.Applet();
 | 
			
		||||
 | 
			
		||||
        this._killswitch = new PopupMenu.PopupSwitchMenuItem(_("Bluetooth"), false);
 | 
			
		||||
        this._applet.connect('notify::killswitch-state', Lang.bind(this, this._updateKillswitch));
 | 
			
		||||
        this._killswitch.connect('toggled', Lang.bind(this, function() {
 | 
			
		||||
            let current_state = this._applet.killswitch_state;
 | 
			
		||||
            if (current_state != GnomeBluetoothApplet.KillswitchState.HARD_BLOCKED &&
 | 
			
		||||
                current_state != GnomeBluetoothApplet.KillswitchState.NO_ADAPTER) {
 | 
			
		||||
                this._applet.killswitch_state = this._killswitch.state ?
 | 
			
		||||
                    GnomeBluetoothApplet.KillswitchState.UNBLOCKED:
 | 
			
		||||
                    GnomeBluetoothApplet.KillswitchState.SOFT_BLOCKED;
 | 
			
		||||
            } else
 | 
			
		||||
                this._killswitch.setToggleState(false);
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        this._discoverable = new PopupMenu.PopupSwitchMenuItem(_("Visibility"), this._applet.discoverable);
 | 
			
		||||
        this._applet.connect('notify::discoverable', Lang.bind(this, function() {
 | 
			
		||||
            this._discoverable.setToggleState(this._applet.discoverable);
 | 
			
		||||
        }));
 | 
			
		||||
        this._discoverable.connect('toggled', Lang.bind(this, function() {
 | 
			
		||||
            this._applet.discoverable = this._discoverable.state;
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        this._updateKillswitch();
 | 
			
		||||
        this.menu.addMenuItem(this._killswitch);
 | 
			
		||||
        this.menu.addMenuItem(this._discoverable);
 | 
			
		||||
        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.PopupSeparatorMenuItem()];
 | 
			
		||||
        this._hasDevices = false;
 | 
			
		||||
        this._deviceSep = this._fullMenuItems[0]; // hidden if no device exists
 | 
			
		||||
 | 
			
		||||
        this._fullMenuItems[1].connect('activate', function() {
 | 
			
		||||
            GLib.spawn_command_line_async('bluetooth-sendto');
 | 
			
		||||
        });
 | 
			
		||||
        this._fullMenuItems[2].connect('activate', function() {
 | 
			
		||||
            GLib.spawn_command_line_async('bluetooth-wizard');
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < this._fullMenuItems.length; i++) {
 | 
			
		||||
            let item = this._fullMenuItems[i];
 | 
			
		||||
            this.menu.addMenuItem(item);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._deviceItemPosition = 3;
 | 
			
		||||
        this._deviceItems = [];
 | 
			
		||||
        this._applet.connect('devices-changed', Lang.bind(this, this._updateDevices));
 | 
			
		||||
        this._updateDevices();
 | 
			
		||||
 | 
			
		||||
        this._applet.connect('notify::show-full-menu', Lang.bind(this, this._updateFullMenu));
 | 
			
		||||
        this._updateFullMenu();
 | 
			
		||||
 | 
			
		||||
        this.menu.addAction(_("Bluetooth Settings"), function() {
 | 
			
		||||
            Main.overview.hide()
 | 
			
		||||
            let app = Shell.AppSystem.get_default().get_app('bluetooth-properties.desktop');
 | 
			
		||||
            app.activate(-1);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        this._applet.connect('pincode-request', Lang.bind(this, this._pinRequest));
 | 
			
		||||
        this._applet.connect('confirm-request', Lang.bind(this, this._confirmRequest));
 | 
			
		||||
        this._applet.connect('auth-request', Lang.bind(this, this._authRequest));
 | 
			
		||||
        this._applet.connect('cancel-request', Lang.bind(this, this._cancelRequest));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateKillswitch: function() {
 | 
			
		||||
        let current_state = this._applet.killswitch_state;
 | 
			
		||||
        let on = current_state == GnomeBluetoothApplet.KillswitchState.UNBLOCKED;
 | 
			
		||||
        let has_adapter = current_state != GnomeBluetoothApplet.KillswitchState.NO_ADAPTER;
 | 
			
		||||
        let can_toggle = current_state != GnomeBluetoothApplet.KillswitchState.NO_ADAPTER &&
 | 
			
		||||
                         current_state != GnomeBluetoothApplet.KillswitchState.HARD_BLOCKED;
 | 
			
		||||
 | 
			
		||||
        this._killswitch.setToggleState(on);
 | 
			
		||||
        if (can_toggle)
 | 
			
		||||
            this._killswitch.setStatus(null);
 | 
			
		||||
        else
 | 
			
		||||
            /* TRANSLATORS: this means that bluetooth was disabled by hardware rfkill */
 | 
			
		||||
            this._killswitch.setStatus(_("hardware disabled"));
 | 
			
		||||
 | 
			
		||||
        if (has_adapter)
 | 
			
		||||
            this.actor.show();
 | 
			
		||||
        else
 | 
			
		||||
            this.actor.hide();
 | 
			
		||||
 | 
			
		||||
        if (on) {
 | 
			
		||||
            this._discoverable.actor.show();
 | 
			
		||||
            this.setIcon('bluetooth-active');
 | 
			
		||||
        } else {
 | 
			
		||||
            this._discoverable.actor.hide();
 | 
			
		||||
            this.setIcon('bluetooth-disabled');
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateDevices: function() {
 | 
			
		||||
        let devices = this._applet.get_devices();
 | 
			
		||||
 | 
			
		||||
        let newlist = [ ];
 | 
			
		||||
        for (let i = 0; i < this._deviceItems.length; i++) {
 | 
			
		||||
            let item = this._deviceItems[i];
 | 
			
		||||
            let destroy = true;
 | 
			
		||||
            for (let j = 0; j < devices.length; j++) {
 | 
			
		||||
                if (item._device.device_path == devices[j].device_path) {
 | 
			
		||||
                    this._updateDeviceItem(item, devices[j]);
 | 
			
		||||
                    destroy = false;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (destroy)
 | 
			
		||||
                item.destroy();
 | 
			
		||||
            else
 | 
			
		||||
                newlist.push(item);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._deviceItems = newlist;
 | 
			
		||||
        this._hasDevices = newlist.length > 0;
 | 
			
		||||
        for (let i = 0; i < devices.length; i++) {
 | 
			
		||||
            let d = devices[i];
 | 
			
		||||
            if (d._item)
 | 
			
		||||
                continue;
 | 
			
		||||
            let item = this._createDeviceItem(d);
 | 
			
		||||
            if (item) {
 | 
			
		||||
                this.menu.addMenuItem(item, this._deviceItemPosition + this._deviceItems.length);
 | 
			
		||||
                this._deviceItems.push(item);
 | 
			
		||||
                this._hasDevices = true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (this._hasDevices)
 | 
			
		||||
            this._deviceSep.actor.show();
 | 
			
		||||
        else
 | 
			
		||||
            this._deviceSep.actor.hide();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateDeviceItem: function(item, device) {
 | 
			
		||||
        if (!device.can_connect && device.capabilities == GnomeBluetoothApplet.Capabilities.NONE) {
 | 
			
		||||
            item.destroy();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let prevDevice = item._device;
 | 
			
		||||
        let prevCapabilities = prevDevice.capabilities;
 | 
			
		||||
        let prevCanConnect = prevDevice.can_connect;
 | 
			
		||||
 | 
			
		||||
        // adopt the new device object
 | 
			
		||||
        item._device = device;
 | 
			
		||||
        device._item = item;
 | 
			
		||||
 | 
			
		||||
        // update properties
 | 
			
		||||
        item.label.text = device.alias;
 | 
			
		||||
 | 
			
		||||
        if (prevCapabilities != device.capabilities ||
 | 
			
		||||
            prevCanConnect != device.can_connect) {
 | 
			
		||||
            // need to rebuild the submenu
 | 
			
		||||
            item.menu.removeAll();
 | 
			
		||||
            this._buildDeviceSubMenu(item, device);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // update connected property
 | 
			
		||||
        if (device.can_connect)
 | 
			
		||||
            item._connectedMenuitem.setToggleState(device.connected);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createDeviceItem: function(device) {
 | 
			
		||||
        if (!device.can_connect && device.capabilities == GnomeBluetoothApplet.Capabilities.NONE)
 | 
			
		||||
            return null;
 | 
			
		||||
        let item = new PopupMenu.PopupSubMenuMenuItem(device.alias);
 | 
			
		||||
 | 
			
		||||
        // adopt the device object, and add a back link
 | 
			
		||||
        item._device = device;
 | 
			
		||||
        device._item = item;
 | 
			
		||||
 | 
			
		||||
        this._buildDeviceSubMenu(item, device);
 | 
			
		||||
 | 
			
		||||
        return item;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _buildDeviceSubMenu: function(item, device) {
 | 
			
		||||
        if (device.can_connect) {
 | 
			
		||||
            item._connected = device.connected;
 | 
			
		||||
            item._connectedMenuitem = new PopupMenu.PopupSwitchMenuItem(_("Connection"), device.connected);
 | 
			
		||||
            item._connectedMenuitem.connect('toggled', Lang.bind(this, function() {
 | 
			
		||||
                if (item._connected > ConnectionState.CONNECTED) {
 | 
			
		||||
                    // operation already in progress, revert
 | 
			
		||||
                    // (should not happen anyway)
 | 
			
		||||
                    menuitem.setToggleState(menuitem.state);
 | 
			
		||||
                }
 | 
			
		||||
                if (item._connected) {
 | 
			
		||||
                    item._connected = ConnectionState.DISCONNECTING;
 | 
			
		||||
                    menuitem.setStatus(_("disconnecting..."));
 | 
			
		||||
                    this._applet.disconnect_device(item._device.device_path, function(applet, success) {
 | 
			
		||||
                        if (success) { // apply
 | 
			
		||||
                            item._connected = ConnectionState.DISCONNECTED;
 | 
			
		||||
                            menuitem.setToggleState(false);
 | 
			
		||||
                        } else { // revert
 | 
			
		||||
                            item._connected = ConnectionState.CONNECTED;
 | 
			
		||||
                            menuitem.setToggleState(true);
 | 
			
		||||
                        }
 | 
			
		||||
                        menuitem.setStatus(null);
 | 
			
		||||
                    });
 | 
			
		||||
                } else {
 | 
			
		||||
                    item._connected = ConnectionState.CONNECTING;
 | 
			
		||||
                    menuitem.setStatus(_("connecting..."));
 | 
			
		||||
                    this._applet.connect_device(item._device.device_path, function(applet, success) {
 | 
			
		||||
                        if (success) { // apply
 | 
			
		||||
                            item._connected = ConnectionState.CONNECTED;
 | 
			
		||||
                            menuitem.setToggleState(true);
 | 
			
		||||
                        } else { // revert
 | 
			
		||||
                            item._connected = ConnectionState.DISCONNECTED;
 | 
			
		||||
                            menuitem.setToggleState(false);
 | 
			
		||||
                        }
 | 
			
		||||
                        menuitem.setStatus(null);
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
            item.menu.addMenuItem(item._connectedMenuitem);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (device.capabilities & GnomeBluetoothApplet.Capabilities.OBEX_PUSH) {
 | 
			
		||||
            item.menu.addAction(_("Send Files..."), Lang.bind(this, function() {
 | 
			
		||||
                this._applet.send_to_address(device.bdaddr, device.alias);
 | 
			
		||||
            }));
 | 
			
		||||
        }
 | 
			
		||||
        if (device.capabilities & GnomeBluetoothApplet.Capabilities.OBEX_FILE_TRANSFER) {
 | 
			
		||||
            item.menu.addAction(_("Browse Files..."), Lang.bind(this, function(event) {
 | 
			
		||||
                this._applet.browse_address(device.bdaddr, event.get_time(),
 | 
			
		||||
                    Lang.bind(this, function(applet, result) {
 | 
			
		||||
                        try {
 | 
			
		||||
                            applet.browse_address_finish(result);
 | 
			
		||||
                        } catch (e) {
 | 
			
		||||
                            this._ensureSource();
 | 
			
		||||
                            this._source.notify(new MessageTray.Notification(this._source,
 | 
			
		||||
                                 _("Bluetooth"),
 | 
			
		||||
                                 _("Error browsing device"),
 | 
			
		||||
                                 { body: _("The requested device cannot be browsed, error is '%s'").format(e) }));
 | 
			
		||||
                        }
 | 
			
		||||
                    }));
 | 
			
		||||
            }));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        switch (device.type) {
 | 
			
		||||
        case GnomeBluetoothApplet.Type.KEYBOARD:
 | 
			
		||||
            item.menu.addAction(_("Keyboard Settings"), function() {
 | 
			
		||||
                GLib.spawn_command_line_async('gnome-control-center keyboard');
 | 
			
		||||
            });
 | 
			
		||||
            break;
 | 
			
		||||
        case GnomeBluetoothApplet.Type.MOUSE:
 | 
			
		||||
            item.menu.addAction(_("Mouse Settings"), function() {
 | 
			
		||||
                GLib.spawn_command_line_async('gnome-control-center mouse');
 | 
			
		||||
            });
 | 
			
		||||
            break;
 | 
			
		||||
        case GnomeBluetoothApplet.Type.HEADSET:
 | 
			
		||||
        case GnomeBluetoothApplet.Type.HEADPHONES:
 | 
			
		||||
        case GnomeBluetoothApplet.Type.OTHER_AUDIO:
 | 
			
		||||
            item.menu.addAction(_("Sound Settings"), function() {
 | 
			
		||||
                GLib.spawn_command_line_async('gnome-control-center sound');
 | 
			
		||||
            });
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateFullMenu: function() {
 | 
			
		||||
        if (this._applet.show_full_menu) {
 | 
			
		||||
            this._showAll(this._fullMenuItems);
 | 
			
		||||
            if (this._hasDevices)
 | 
			
		||||
                this._showAll(this._deviceItems);
 | 
			
		||||
            else
 | 
			
		||||
                this._deviceSep.actor.hide();
 | 
			
		||||
        } else {
 | 
			
		||||
            this._hideAll(this._fullMenuItems);
 | 
			
		||||
            this._hideAll(this._deviceItems);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _showAll: function(items) {
 | 
			
		||||
        for (let i = 0; i < items.length; i++)
 | 
			
		||||
            items[i].actor.show();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _hideAll: function(items) {
 | 
			
		||||
        for (let i = 0; i < items.length; i++)
 | 
			
		||||
            items[i].actor.hide();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _destroyAll: function(items) {
 | 
			
		||||
        for (let i = 0; i < items.length; i++)
 | 
			
		||||
            items[i].destroy();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _ensureSource: function() {
 | 
			
		||||
        if (!this._source) {
 | 
			
		||||
            this._source = new Source();
 | 
			
		||||
            Main.messageTray.add(this._source);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _authRequest: function(applet, device_path, name, long_name, uuid) {
 | 
			
		||||
        this._ensureSource();
 | 
			
		||||
        this._source.notify(new AuthNotification(this._source, this._applet, device_path, name, long_name, uuid));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _confirmRequest: function(applet, device_path, name, long_name, pin) {
 | 
			
		||||
        this._ensureSource();
 | 
			
		||||
        this._source.notify(new ConfirmNotification(this._source, this._applet, device_path, name, long_name, pin));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _pinRequest: function(applet, device_path, name, long_name, numeric) {
 | 
			
		||||
        this._ensureSource();
 | 
			
		||||
        this._source.notify(new PinNotification(this._source, this._applet, device_path, name, long_name, numeric));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _cancelRequest: function() {
 | 
			
		||||
        this._source.destroy();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function Source() {
 | 
			
		||||
    this._init.apply(this, arguments);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Source.prototype = {
 | 
			
		||||
    __proto__: MessageTray.Source.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        MessageTray.Source.prototype._init.call(this, _("Bluetooth"));
 | 
			
		||||
 | 
			
		||||
        this._setSummaryIcon(this.createNotificationIcon());
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    notify: function(notification) {
 | 
			
		||||
        this._private_destroyId = notification.connect('destroy', Lang.bind(this, function(notification) {
 | 
			
		||||
            if (this.notification == notification) {
 | 
			
		||||
                // the destroyed notification is the last for this source
 | 
			
		||||
                this.notification.disconnect(this._private_destroyId);
 | 
			
		||||
                this.destroy();
 | 
			
		||||
            }
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        MessageTray.Source.prototype.notify.call(this, notification);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    createNotificationIcon: function() {
 | 
			
		||||
        return new St.Icon({ icon_name: 'bluetooth-active',
 | 
			
		||||
                             icon_type: St.IconType.SYMBOLIC,
 | 
			
		||||
                             icon_size: this.ICON_SIZE });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function AuthNotification() {
 | 
			
		||||
    this._init.apply(this, arguments);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AuthNotification.prototype = {
 | 
			
		||||
    __proto__: MessageTray.Notification.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function(source, applet, device_path, name, long_name, uuid) {
 | 
			
		||||
        MessageTray.Notification.prototype._init.call(this,
 | 
			
		||||
                                                      source,
 | 
			
		||||
                                                      _("Bluetooth"),
 | 
			
		||||
                                                      _("Authorization request from %s").format(name),
 | 
			
		||||
                                                      { customContent: true });
 | 
			
		||||
        this.setResident(true);
 | 
			
		||||
 | 
			
		||||
        this._applet = applet;
 | 
			
		||||
        this._devicePath = device_path;
 | 
			
		||||
        this.addBody(_("Device %s wants access to the service '%s'").format(long_name, uuid));
 | 
			
		||||
 | 
			
		||||
        this.addButton('always-grant', _("Always grant access"));
 | 
			
		||||
        this.addButton('grant', _("Grant this time only"));
 | 
			
		||||
        this.addButton('reject', _("Reject"));
 | 
			
		||||
 | 
			
		||||
        this.connect('action-invoked', Lang.bind(this, function(self, action) {
 | 
			
		||||
            switch (action) {
 | 
			
		||||
            case 'always-grant':
 | 
			
		||||
                this._applet.agent_reply_auth(this._devicePath, true, true);
 | 
			
		||||
                break;
 | 
			
		||||
            case 'grant':
 | 
			
		||||
                this._applet.agent_reply_auth(this._devicePath, true, false);
 | 
			
		||||
                break;
 | 
			
		||||
            case 'reject':
 | 
			
		||||
            default:
 | 
			
		||||
                this._applet.agent_reply_auth(this._devicePath, false, false);
 | 
			
		||||
            }
 | 
			
		||||
            this.destroy();
 | 
			
		||||
        }));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function ConfirmNotification() {
 | 
			
		||||
    this._init.apply(this, arguments);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ConfirmNotification.prototype = {
 | 
			
		||||
    __proto__: MessageTray.Notification.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function(source, applet, device_path, name, long_name, pin) {
 | 
			
		||||
        MessageTray.Notification.prototype._init.call(this,
 | 
			
		||||
                                                      source,
 | 
			
		||||
                                                      _("Bluetooth"),
 | 
			
		||||
                                                      _("Pairing confirmation for %s").format(name),
 | 
			
		||||
                                                      { customContent: true });
 | 
			
		||||
        this.setResident(true);
 | 
			
		||||
 | 
			
		||||
        this._applet = applet;
 | 
			
		||||
        this._devicePath = device_path;
 | 
			
		||||
        this.addBody(_("Device %s wants to pair with this computer").format(long_name));
 | 
			
		||||
        this.addBody(_("Please confirm whether the PIN '%s' matches the one on the device.").format(pin));
 | 
			
		||||
 | 
			
		||||
        this.addButton('matches', _("Matches"));
 | 
			
		||||
        this.addButton('does-not-match', _("Does not match"));
 | 
			
		||||
 | 
			
		||||
        this.connect('action-invoked', Lang.bind(this, function(self, action) {
 | 
			
		||||
            if (action == 'matches')
 | 
			
		||||
                this._applet.agent_reply_confirm(this._devicePath, true);
 | 
			
		||||
            else
 | 
			
		||||
                this._applet.agent_reply_confirm(this._devicePath, false);
 | 
			
		||||
            this.destroy();
 | 
			
		||||
        }));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function PinNotification() {
 | 
			
		||||
    this._init.apply(this, arguments);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PinNotification.prototype = {
 | 
			
		||||
    __proto__: MessageTray.Notification.prototype,
 | 
			
		||||
 | 
			
		||||
    _init: function(source, applet, device_path, name, long_name, numeric) {
 | 
			
		||||
        MessageTray.Notification.prototype._init.call(this,
 | 
			
		||||
                                                      source,
 | 
			
		||||
                                                      _("Bluetooth"),
 | 
			
		||||
                                                      _("Pairing request for %s").format(name),
 | 
			
		||||
                                                      { customContent: true });
 | 
			
		||||
        this.setResident(true);
 | 
			
		||||
 | 
			
		||||
        this._applet = applet;
 | 
			
		||||
        this._devicePath = device_path;
 | 
			
		||||
        this._numeric = numeric;
 | 
			
		||||
        this.addBody(_("Device %s wants to pair with this computer").format(long_name));
 | 
			
		||||
        this.addBody(_("Please enter the PIN mentioned on the device."));
 | 
			
		||||
 | 
			
		||||
        this._entry = new St.Entry();
 | 
			
		||||
        this._entry.connect('key-release-event', Lang.bind(this, function(entry, event) {
 | 
			
		||||
            let key = event.get_key_symbol();
 | 
			
		||||
            if (key == Clutter.KEY_Return) {
 | 
			
		||||
                this.emit('action-invoked', 'ok');
 | 
			
		||||
                return true;
 | 
			
		||||
            } else if (key == Clutter.KEY_Escape) {
 | 
			
		||||
                this.emit('action-invoked', 'cancel');
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
            return false;
 | 
			
		||||
        }));
 | 
			
		||||
        this.addActor(this._entry);
 | 
			
		||||
 | 
			
		||||
        this.addButton('ok', _("OK"));
 | 
			
		||||
        this.addButton('cancel', _("Cancel"));
 | 
			
		||||
 | 
			
		||||
        this.connect('action-invoked', Lang.bind(this, function(self, action) {
 | 
			
		||||
            if (action == 'ok') {
 | 
			
		||||
                if (this._numeric) {
 | 
			
		||||
                    let num = parseInt(this._entry.text);
 | 
			
		||||
                    if (isNaN(num)) {
 | 
			
		||||
                        // user reply was empty, or was invalid
 | 
			
		||||
                        // cancel the operation
 | 
			
		||||
                        num = -1;
 | 
			
		||||
                    }
 | 
			
		||||
                    this._applet.agent_reply_passkey(this._devicePath, num);
 | 
			
		||||
                } else
 | 
			
		||||
                    this._applet.agent_reply_pincode(this._devicePath, this._entry.text);
 | 
			
		||||
            } else {
 | 
			
		||||
                if (this._numeric)
 | 
			
		||||
                    this._applet.agent_reply_passkey(this._devicePath, -1);
 | 
			
		||||
                else
 | 
			
		||||
                    this._applet.agent_reply_pincode(this._devicePath, null);
 | 
			
		||||
            }
 | 
			
		||||
            this.destroy();
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    grabFocus: function(lockTray) {
 | 
			
		||||
        MessageTray.Notification.prototype.grabFocus.call(this, lockTray);
 | 
			
		||||
        global.stage.set_key_focus(this._entry);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||