Compare commits

..

2 Commits

Author SHA1 Message Date
36c11009f5 loginDialog: make spinner and session menu button share position
They never need to be shown at the same time, and the design has
the UI fade between them.

This commit implements that.

https://bugzilla.gnome.org/show_bug.cgi?id=702818
2013-06-26 10:05:32 -04:00
df6f6b7368 loginDialog: Move the session list to a PopupMenu
There are some issues with the existing session menu. First, it looks
kinda bad. It seems like it's hanging around there, but it doesn't really know
what to do with itself.

Second, when it expands down it requires that the buttons below move
down with it. This kind of movement is awkward and looks a bit weird.

Third, it's current position makes the "dialog" tall and unwieldy when
you add things like messages for finger print readers or authentication errors.

This commit moves the session list to a menu behind a button to address
the above problems.

Some updates to patch by Ray Strode.

https://bugzilla.gnome.org/show_bug.cgi?id=702818
2013-06-26 10:05:04 -04:00
241 changed files with 57279 additions and 51072 deletions

7
.gitignore vendored
View File

@ -19,8 +19,6 @@ configure
data/50-gnome-shell-*.xml data/50-gnome-shell-*.xml
data/gnome-shell.desktop data/gnome-shell.desktop
data/gnome-shell.desktop.in data/gnome-shell.desktop.in
data/gnome-shell-wayland.desktop
data/gnome-shell-wayland.desktop.in
data/gnome-shell-extension-prefs.desktop data/gnome-shell-extension-prefs.desktop
data/gnome-shell-extension-prefs.desktop.in data/gnome-shell-extension-prefs.desktop.in
data/gschemas.compiled data/gschemas.compiled
@ -43,8 +41,6 @@ docs/reference/*/xml/
docs/reference/shell/doc-gen-* docs/reference/shell/doc-gen-*
gtk-doc.make gtk-doc.make
js/misc/config.js js/misc/config.js
js/js-resources.c
js/js-resources.h
intltool-extract.in intltool-extract.in
intltool-merge.in intltool-merge.in
intltool-update.in intltool-update.in
@ -75,14 +71,13 @@ src/calendar-server/evolution-calendar.desktop.in
src/calendar-server/org.gnome.Shell.CalendarServer.service src/calendar-server/org.gnome.Shell.CalendarServer.service
src/gnome-shell src/gnome-shell
src/gnome-shell-calendar-server src/gnome-shell-calendar-server
src/gnome-shell-extension-prefs
src/gnome-shell-extension-tool src/gnome-shell-extension-tool
src/gnome-shell-extension-prefs
src/gnome-shell-hotplug-sniffer src/gnome-shell-hotplug-sniffer
src/gnome-shell-jhbuild src/gnome-shell-jhbuild
src/gnome-shell-perf-helper src/gnome-shell-perf-helper
src/gnome-shell-perf-tool src/gnome-shell-perf-tool
src/gnome-shell-real src/gnome-shell-real
src/gnome-shell-wayland
src/hotplug-sniffer/org.gnome.Shell.HotplugSniffer.service src/hotplug-sniffer/org.gnome.Shell.HotplugSniffer.service
src/run-js-test src/run-js-test
src/test-recorder src/test-recorder

View File

@ -138,8 +138,8 @@ GObjects, although this feature isn't used very often in the Shell itself.
_init: function(icon, label) { _init: function(icon, label) {
this.parent({ reactive: false }); this.parent({ reactive: false });
this.actor.add_child(icon); this.addActor(icon);
this.actor.add_child(label); this.addActor(label);
}, },
open: function() { open: function() {

View File

@ -1,11 +1,7 @@
# Point to our macro directory and pick up user flags from the environment # Point to our macro directory and pick up user flags from the environment
ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS} ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
SUBDIRS = data js src tests po docs SUBDIRS = data js src browser-plugin tests po docs
if BUILD_BROWSER_PLUGIN
SUBDIRS += browser-plugin
endif
if ENABLE_MAN if ENABLE_MAN
SUBDIRS += man SUBDIRS += man

347
NEWS
View File

@ -1,350 +1,3 @@
3.11.3
======
* Fix fade effect of desktop icons [Florian; #707671]
* Fix issues with background management code [Jasper; #709313]
* Use new Glib facilities for application search [Jasper; #711631]
* Add focus indication to session menu button [Sebastien; #710539]
* Fix hover tracking for StEntries [Jasper; #706749]
* Fix reentrancy issue in message tray [Jasper; #711694]
* Tone down zoom animation on login/unlock [Jasper; #712362]
* Allow specifying monitor for OSD [Carlos; #712664]
* Fix resetting prompt on user switch [Ray; #710456]
* Stop using gnome-bluetooth-applet [Bastien; #719341]
* Add support for EAP-FAST password requests [Dan; #719813]
* Fix entry focus of chat notifications [Jasper; #709853]
* Make window previews keyboard navigatable [Jasper; #644306]
* Fix app switcher order with dialog windows [Florian; #719824]
* Allow remote search providers without icons [Debarshi; #719965]
* Fix various alignment issues in RTL locales [Yosef; #712638, #712596,
#712594, #712600, #712579]
* Misc. bug fixes and cleanups [Jasper, Florian, Giovanni, Dan; #712727,
#712753, #719378, #719730, #719803, #710115, #720017, #719815, #719567,
#720298]
Contributors:
Giovanni Campagna, Carlos Garnacho, Sebastien Lafargue, Tim Lunn,
Florian Müllner, Bastien Nocera, Yosef Or Boczko, Debarshi Ray,
Jasper St. Pierre, Ray Strode, Dan Williams
Translations:
Kjartan Maraas [nb], Reinout van Schouwen [nl], Rafael Ferreira [pt_BR],
Mattias Põldaru [et], Emin Tufan Çetin [tr], Jiri Grönroos [fi],
Khaled Hosny [ar], Fran Diéguez [gl], Victor Ibragimov [tg],
Daniel Mustieles [es]
3.11.2
======
* Cache search result display actors [Jasper; #704912]
* Use username in userWidget if real name doesn't fit [Jasper; #706851]
* Support shell_global_reexec_self() on OpenBSD [Antoine; #709571]
* Support disabling browser plugin [Colin; #711218]
* Restore support for 'disable-restart-buttons' [Florian; #711244]
* Validate parameters of exposed DBus methods [Florian; #699752]
* Connect applications to systemd journal if available [Colin; #711626]
* Misc bug fixes and cleanups [Florian, Jasper; #711205, #698486, #711416,
#644306, #711555, #709806, #711631, #711732]
Contributors:
Cosimo Cecchi, Antoine Jacoutot, Florian Müllner, Jasper St. Pierre,
Rico Tzschichholz, Colin Walters
Translations:
Yuri Myasoedov [ru], Kjartan Maraas [nb], Efstathios Iosifidis [el],
Benjamin Steinwender [de], eternalhui [zh_CN], Shantha kumar [ta]
3.11.1
======
* power: Use UPower directly instead of gnome-settings-daemon [Bastien; #710273]
* Implement support for new GTK+ notification API [Jasper, Giovanni, Florian;
#710137, #710596]
* gdm: Don't allow user-list to fill up the entire screen [Florian; #710555]
* Don't autostart remote search providers at login [Giovanni; #708830]
* Fix spacing in end-session dialog [Sebastien; #710543]
* Prepare for js24 [Tim; #711052]
* Misc bug fixes and cleanups [Jasper, Florian, Adel, Tim, Sebastien; #710347,
#710144, #710541, #691409, #710745, #688331, #704912]
Contributors:
Giovanni Campagna, Adel Gadllah, Sebastien Lafargue, Tim Lunn,
Florian Müllner, Bastien Nocera, Jasper St. Pierre, Rico Tzschichholz
Translations:
Stas Solovey [ru], Yosef Or Boczko [he], Rafael Ferreira [pt_BR]
3.10.1
======
* Make sure lock screen is drawn once before switching user [Giovanni; #708051]
* Fix signal strength indicators in network selector [Jasper; #708442]
* Scroll search results when focusing provider icons [Jasper; #708868]
* Add separate hover/active states to page indicators [Carlos; #708852]
* Tweak appearance of user name and avatar [Yash; #702309]
* Hide "Turn On" in network menu when disabled by hardware [Giovanni; #709635]
* Cancel open keyring prompts when the screen is locked [Florian; #708910]
* Differentiate "Not Connected" and "Off" in network menu [Giovanni; #709043]
* Make network settings items point to the right device [Giovanni; #709246]
* Remove animation of window preview titles [Sebastien; #709392]
* Add 'Notifications' switch to tray menu [Florian; #707073]
* Make dropdown arrows consistent [Carlos; #709564]
* power: Use icon from primary device for status [Jasper; #709925]
* Fix XDND drags to overview [Adel; #708887]
* Fix workspace switcher disappearing with too many workspaces [Jasper; #694881]
* Handle search results with 'special:' prefix specially [Giovanni; #707055]
* gdm: Support pre-authenticated logins from oVirt [Vinzenz; #702162]
* Use ARROW role for labels representing arrows [Alejandro; #710120]
* Make selected view in app picker persistent [Florian; #710042]
* Make network selector navigable by keyboard [Alejandro; #710144]
* Misc bug fixes [Florian, Adel, Jasper, Aleksander, Giovanni, Dan, Michael,
Tim; #709034, #709263, #698486, #709286, #709248, #709543, #696564, #703265,
#709638, #709866, #709998, #710019, #710104, #710115]
Contributors:
Giovanni Campagna, Michael Catanzaro, Vinzenz Feenstra, Adel Gadllah,
Yash Girdhar, Sebastien Lafargue, Tim Lunn, Aleksander Morgado,
Florian Müllner, Alejandro Piñeiro, Carlos Soriano, Jasper St. Pierre,
Dieter Verfaillie, Dan Winship
Translations:
Inaki Larranaga Murgoitio [eu], Christian Kirbach [de], Muhammet Kara [tr],
Aurimas Černius [lt], Ryan Lortie [eo], Rūdolfs Mazurs [lv],
Dušan Kazik [sk], Fran Diéguez [gl], Enrico Nicoletto [pt_BR],
Kjartan Maraas [nb], Victor Ibragimov [tg], Matej Urbančič [sl],
A S Alam [pa], Nilamdyuti Goswami [as], Daniel Mustieles [es],
Cheng-Chia Tseng [zh_HK, zh_TW], Mattias Põldaru [et], Kenneth Nielsen [da],
Milo Casagrande [it], Marek Černocký [cs], Ihar Hrachyshka [be],
Мирослав Николић [sr, sr@latin], Arash Mousavi [fa], Yuri Myasoedov [ru],
Gil Forcada [ca], Carles Ferrando [ca@valencia], Andika Triwidada [id],
Timo Jyrinki [fi], Piotr Drąg [pl], Rafael Ferreira [pt_BR],
Gabor Kelemen [hu], Yosef Or Boczko [he], Daniel Korostil [uk],
Wouter Bolsterlee [nl], António Lima [pt]
3.10.0.1
=========
* Fix login screen [Ray; #708691]
Contributors:
Ray Strode, Giovanni Campagna, Jasper St. Pierree
Translations:
Kjartan Maraas [nb], Marek Černocký [cs], A S Alam [pa], Daniel Mustieles [es],
Ihar Hrachyshka [be], Chao-Hsiung Liao [zh_HK], Nilamdyuti Goswami [as],
Yuri Myasoedov [ru], Baurzhan Muftakhidinov [kk]
3.10.0
======
* Fix fade effect in ScrollViews [Carlos; #708256]
* network: Resync when activating connection changes [Jasper; #708322]
* Close run dialog when the screen locks [Florian; #708218]
* Fix entry growing out of password dialogs [Florian; #708324, #703833]
* Vertically center labels in submenu items [Jasper; #708330]
* https://bugzilla.gnome.org/show_bug.cgi?id=708387 [Mike; #708387]
* Fix bluetooth icon not being added to status menu [Jasper; #708541]
* Fix GNOME 2 keyring dialogs appearing on lock screen [Florian; #708187]
* Fix passwords being cleared twice when authentication fails [Florian; #708186]
* Fix message tray appearing in a11y popup on login screen [Florian; #708380]
* Increase width of aggregate menu popup [Adel; #708472]
Contributors:
Adel Gadllah, Mike Gorse, Ryan Lortie, Florian Müllner, Frédéric Péters,
Carlos Soriano, Jasper St. Pierre, Rico Tzschichholz
Translations:
Daniel Șerbănescu [ro], Ryan Lortie [eo], Ihar Hrachyshka [be],
A S Alam [pa], Jiro Matsuzawa [ja], Chao-Hsiung Liao [zh_HK, zh_TW],
Piotr Drąg [pl], Kristjan SCHMIDT [eo], Daniel Korostil [uk],
Rūdolfs Mazurs [lv], Reinout van Schouwen [nl], Yosef Or Boczko [he],
Fran Diéguez [gl], António Lima [pt], Andika Triwidada [id],
Alexandre Franke [fr], Rafael Ferreira [pt_BR], Milo Casagrande [it],
Kenneth Nielsen [da], Matej Urbančič [sl]
3.9.92
======
* Don't show page indicators if there's only one page [Florian; #707363]
* Make :active style of app and non-app results consistent [Jakub; #704714]
* Fade app pages when scrolled [Florian; #707409]
* Don't block scrolling on page indicators [Carlos; #707609]
* Tweak visual appearance of folder views [Florian; #707662]
* Don't put minimized apps at the end of the app switcher [Florian; #707663]
* Merge the wayland branch [Giovanni, Neil; #707467]
* Make search entry behave better in RTL locales [Matthias, Florian; #705779]
* Allow to change app pages with pageUp/pageDown keys [Carlos; #707979]
* Set approriate a11y states on expandable menu items [Alejandro; #708038]
* Improve page indicator animation [Carlos; #707565]
* Misc bug fixes and cleanups [Florian, Olivier, Jasper, Giovanni, Magdalen,
Adel, Carlos, Rico, Joanmarie; #707308, #707430, #707508, #707557, #707600,
#707614, #707666, #707814, #707806, #707801, #707889, #707892, #707935,
#707842, #707940, #707996, #708007, #708009, #708020, #707580, #708080]
Contributors:
Magdalen Berns, Olivier Blin, Giovanni Campagna, Matthias Clasen,
Joanmarie Diggs, Adel Gadllah, Florian Müllner, Alejandro Piñeiro,
Neil Roberts, Carlos Soriano, Jasper St. Pierre, Jakub Steiner,
Rico Tzschichholz
Translations:
Rafael Ferreira [pt_BR], Fran Diéguez [gl], Daniel Mustieles [es],
Aurimas Černius [lt], Luca Ferretti [it], Piotr Drąg [pl],
Chao-Hsiung Liao [zh_HK, zh_TW], Timo Jyrinki [fi], Daniel Korostil [uk],
Dušan Kazik [sk], Adam Matoušek [cs], Marek Černocký [cs],
Jiro Matsuzawa [ja], Yuri Myasoedov [ru], Tobias Endrigkeit [de],
Kjartan Maraas [nb], Victor Ibragimov [tg], Мирослав Николић [sr, sr@latin],
A S Alam [pa], Khaled Hosny [ar], Andika Triwidada [id],
Nilamdyuti Goswami [as], Ihar Hrachyshka [be], Rūdolfs Mazurs [lv],
Mattias Põldaru [et], Gabor Kelemen [hu], Bruce Cowan [en_GB],
Matej Urbančič [sl], Enrico Nicoletto [pt_BR], Benjamin Steinwender [de],
Changwoo Ryu [ko], Kris Thomsen [da], Alexandre Franke [fr],
Evgeny Bobkin [ru], Baurzhan Muftakhidinov [kk], Peter Mráz [sk],
Inaki Larranaga Murgoitio [eu], Yosef Or Boczko [he]
3.9.91
======
* Improve submenu styling [Jakub; #706037]
* Fix changing slider values via keyboard [Alejandro; #706386]
* Fix accessibility of sliders [Alejandro; #706391]
* Tweak system actions style [Jakub; #706638]
* Add support for auth without username / fix Not Listed? [Ray; #706607]
* Dash: Don't show tooltips for apps with open popups [Giovanni; #705611]
* Implement new end-session/power-off dialog design [Jasper, Matthias; #706612]
* Implement building separate binaries for x11 and wayland [Giovanni; #705497]
* authPrompt: Fix controls moving when showing messages [Ray; #706670]
* Tweak padding between system status icons [Allan; #706796]
* Add a generic accessible usable by JS code [Alejandro; #648623]
* Improve keynav and accessibility of the calendar [Alejandro; #706903]
* Update to new NetworkManager APIs [Jasper; #706098]
* Hide system actions section in the lock screen [Jasper; #706852]
* Don't show other logged in users at log out [Giovanni; #707124]
* Remove "Session" subtitle heading in login dialog [Jasper; #707072]
* dash: Reload favorites when installed apps change [Giovanni; #706878]
* Don't open overview after closing last window on workspace [Florian; #662581]
* Add FocusApp DBus method [Giovanni; #654086]
* Add ShowApplications DBus method [Giovanni; #698743]
* Implement new app picker design [Carlos, Florian; #706081]
* Improve frequent apps being empty by default [Carlos, Florian; #694710]
* Extend clickable area of page indicators [Giovanni; #707314]
* Misc bug fixes [Ray, Giovanni, Jasper, Emmanuele; #706542, #706654, #706005,
#706681, #706841, #706843, #707064, #706262, #707197, #707269]
Contributors:
Emmanuele Bassi, Giovanni Campagna, Matthias Clasen, Allan Day, Adel Gadllah,
Florian Müllner, Alejandro Piñeiro, Carlos Soriano, Jasper St. Pierre,
Jakub Steiner, Ray Strode, Seán de Búrca
Translations:
Piotr Drąg [pl], Kjartan Maraas [nb], Victor Ibragimov [tg],
Enrico Nicoletto [pt_BR], Benjamin Steinwender [de],
Baurzhan Muftakhidinov [kk], Aurimas Černius [lt], Seán de Búrca [ga],
Fran Diéguez [gl], Daniel Mustieles [es], Dušan Kazik [sk],
Matej Urbančič [sl], Andika Triwidada [id], Jordi Mas [ca],
Ihar Hrachyshka [be]
3.9.90
======
* workspaceThumbnails: Exclude transient windows when shifting workspaces
[Bradley; #705174]
* Never show a horizontal scrollbar on lock screen [Jasper; #704327]
* authPrompt: Fix disable-user-list / Not Listed? [Ray; #705370]
* Animate the lock screen notification transitions [Giovanni; #687660]
* Wake up the screen when new notifications appear [Giovanni; #703084]
* Use StartupWMClass for application matching [Giovanni; #673657, #705801]
* dateMenu: Add style class for the clock label [Jonh; #705634]
* keyboard: Translate IBus IME name if possible [Daiki; #695673]
* power: Display single digit minutes correctly [Sebastian; #705803]
* Implement new aggregate status menu [Jasper; #705845]
* Improve triangle animation when expanding sub-menus [Tarun; #703109]
* Fix alignment of search provider icons [Tarun; #695760]
* Slide dash and workspace switcher on overview transitions [Tarun; #694262]
* Respect always-show-universal-access-status setting [Tanner; #705733]
* Handle .desktop files with capital letters [Giovanni; #706252]
* authPrompt: Add smartcard support [Ray; #683437]
* Fix call notifications in busy mode [Emilio; #666221]
* Improve triangle animation when expanding sub-menus [Tarun; #703109]
* Move message tray menu to a tray button [Jasper; #699272]
* Wi-fi dialog improvements [Jasper, Allan; #705916, #706136]
* Work towards running as wayland compositor [Giovanni]
- Switch to Mutter abstraction layer for cursor tracking [#705911]
- Add confirmation dialog for display changes [#706208]
* Use a different background in screen shield [Giovanni; #688210]
* Add fade animation before blanking the screen [Giovanni; #699112]
* Misc. bugfixes and cleanups [Jasper, Giovanni, Adel, Colin, Ray, Florian,
Magdalen; #704448, #702536, #686855, #695581, #700901, #701761, #701495,
#701848, #697833, #701731, #705664, #705840, #705898, #706089, #706153,
#704646, #706262, #706324, #703810, #703811, #704015, #706232, #705917,
#706536]
Contributors:
Magdalen Berns, Giovanni Campagna, Allan Day, Tanner Doshier, Adel Gadllah,
Sebastian Keller, Tarun Kumar Joshi, Florian Müllner, Bradley Pankow,
Emilio Pozuelo Monfort, Jasper St. Pierre, Ray Strode, Rico Tzschichholz,
Daiki Ueno, Colin Walters, Jonh Wendell
Translations:
Kjartan Maraas [nb], Aurimas Černius [lt], Yaron Shahrabani [he],
Fran Diéguez [gl], Gabor Kelemen [hu],
Juan Diego Martins da Costa Cruz [pt_BR], Inaki Larranaga Murgoitio [eu],
Yuri Myasoedov [ru], Daniel Mustieles [es], Seán de Búrca [ga],
Khaled Hosny [ar], Victor Ibragimov [tg], Friedel Wolff [af],
Marek Černocký [cs], Matej Urbančič [sl], A S Alam [pa],
Rafael Ferreira [pt_BR], Andika Triwidada [id], Dušan Kazik [sk]
3.9.5
=====
* Fix width changes of the calendar popup [Florian; #704200]
* Work towards aggregate status menu [Jasper; #702539, #704336, #704368,
#704670]
* Update design of lock screen notifications [Allan; #702305]
* Don't show empty backgroundMenu [Michael; #703868]
* Add option to limit app switcher to current workspace [Adel; #703538]
* Consolidate design of login screen and unlock dialog [Ray; #702308, #704795]
* Respect hasWorkspace property of session mode [Jasper; #698593]
* Fix fade of app menu icon in RTL locales [Jasper; #704583]
* Destroy notifications when the close button is clicked [Adel; #687016]
* Fix clicks on legacy tray icons in the message tray [Florian; #704095]
* authPrompt: Fade out message if users start to type [Ray; #704817]
* Export timestamps of global shortcuts on DBus [Bastien; #704859]
* Fix duplicate search provider results [Jasper; #700283]
* Misc bug fixes and cleanups [Lionel, Florian, Emilio, Ray, Jasper; #703859,
#703540, #704077, #703997, #704318, #704347, #704265, #704411, #704430,
#704347, #704453, #704471, #704542, #704707, #703905, #705037]
Contributors:
Allan Day, Adel Gadllah, Lionel Landwerlin, Florian Müllner, Bastien Nocera,
Emilio Pozuelo Monfort, Jasper St. Pierre, Ray Strode, Colin Walters,
Michael Wood
Translations:
eternalhui [zh_CN], Victor Ibragimov [tg], Dušan Kazik [sk],
Jiro Matsuzawa [ja], Kjartan Maraas [nb], Milo Casagrande [it],
Marek Černocký [cs], Daniel Mustieles [es], Benjamin Steinwender [de]
3.9.4
=====
* Fix chat entries not being focused when expanded [Jasper; #698778]
* Fix alignment of "Not Listed?" label [Mathieu; #702307]
* Fix alignment of time stamps in chat notifications [Carlos; #687809]
* Round the ends of slider trough [Jasper; #702825]
* Add support for "box-shadow: none" [Cosimo; #702782]
* Keep chrome below popup windows [Florian; #702338]
* Move the session list to a popup menu [Ray; #702818]
* Fix autorun notifications for "non-native" volumes [Matthias; #703418]
* dnd: Speed up by not picking on each motion event [Jasper; #703443]
* Fix management of asynchronous background loading [Lionel; #703001]
* Rework focus handling [Jasper; #700735]
* Optimize box-shadow rendering [Lionel; #689858]
* Remove support for fixed positioning in BoxLayouts [Florian; #703808]
* Misc bug fixes and cleanups [Adel, Jasper, Florian, Ray, Lionel, Emilio;
#702849, #610279, #703132, #703105, #703160, #703126, #703304, #703403,
#698593, #703442, #703565, #700901, #703874, #703807, #703893, #703909]
Contributors:
Mathieu Bridon, Giovanni Campagna, Cosimo Cecchi, Matthias Clasen,
Fran Diéguez, Adel Gadllah, Lionel Landwerlin, Florian Müllner,
Emilio Pozuelo Monfort, Carlos Soriano, Jasper St. Pierre, Ray Strode
Translations:
Baurzhan Muftakhidinov [kk], Marek Černocký [cs], Daniel Mustieles [es],
Fran Diéguez [gl], Kjartan Maraas [nb], Andika Triwidada [id],
Benjamin Steinwender [de], Nguyễn Thái Ngọc Duy [vi], Trần Ngọc Quân [vi]
3.9.3 3.9.3
===== =====
* Don't push window thumbs when workspace switcher is hidden [Jasper; #701167] * Don't push window thumbs when workspace switcher is hidden [Jasper; #701167]

View File

@ -17,4 +17,5 @@ libgnome_shell_browser_plugin_la_SOURCES = \
libgnome_shell_browser_plugin_la_CFLAGS = \ libgnome_shell_browser_plugin_la_CFLAGS = \
$(BROWSER_PLUGIN_CFLAGS) \ $(BROWSER_PLUGIN_CFLAGS) \
-DG_DISABLE_DEPRECATED \
-DG_LOG_DOMAIN=\"GnomeShellBrowserPlugin\" -DG_LOG_DOMAIN=\"GnomeShellBrowserPlugin\"

View File

@ -1,5 +1,5 @@
AC_PREREQ(2.63) AC_PREREQ(2.63)
AC_INIT([gnome-shell],[3.11.3],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell]) AC_INIT([gnome-shell],[3.9.3],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
AC_CONFIG_HEADERS([config.h]) AC_CONFIG_HEADERS([config.h])
AC_CONFIG_SRCDIR([src/shell-global.c]) AC_CONFIG_SRCDIR([src/shell-global.c])
@ -16,7 +16,6 @@ m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
# Checks for programs. # Checks for programs.
AC_PROG_CC AC_PROG_CC
AC_PROG_CXX
# Initialize libtool # Initialize libtool
LT_PREREQ([2.2.6]) LT_PREREQ([2.2.6])
@ -25,6 +24,9 @@ LT_INIT([disable-static])
# i18n # i18n
IT_PROG_INTLTOOL([0.40]) IT_PROG_INTLTOOL([0.40])
AM_GNU_GETTEXT([external])
AM_GNU_GETTEXT_VERSION([0.17])
GETTEXT_PACKAGE=gnome-shell GETTEXT_PACKAGE=gnome-shell
AC_SUBST(GETTEXT_PACKAGE) AC_SUBST(GETTEXT_PACKAGE)
AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE", AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE",
@ -51,32 +53,17 @@ if $PKG_CONFIG --exists gstreamer-1.0 '>=' $GSTREAMER_MIN_VERSION ; then
AC_MSG_RESULT(yes) AC_MSG_RESULT(yes)
build_recorder=true build_recorder=true
recorder_modules="gstreamer-1.0 gstreamer-base-1.0 x11 gtk+-3.0" recorder_modules="gstreamer-1.0 gstreamer-base-1.0 x11 gtk+-3.0"
PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-1.0) PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-1.0 xfixes)
else else
AC_MSG_RESULT(no) AC_MSG_RESULT(no)
fi fi
AM_CONDITIONAL(BUILD_RECORDER, $build_recorder) AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
AC_ARG_ENABLE([systemd],
AS_HELP_STRING([--enable-systemd], [Use systemd]),
[enable_systemd=$enableval],
[enable_systemd=auto])
AS_IF([test x$enable_systemd != xno], [
AC_MSG_CHECKING([for libsystemd-journal])
PKG_CHECK_EXISTS([libsystemd-journal],
[have_systemd=yes
AC_DEFINE([HAVE_SYSTEMD], [1], [Define if we have systemd])],
[have_systemd=no])
AC_MSG_RESULT($have_systemd)
])
AC_MSG_RESULT($enable_systemd)
CLUTTER_MIN_VERSION=1.13.4 CLUTTER_MIN_VERSION=1.13.4
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1 GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
GJS_MIN_VERSION=1.39.0 GJS_MIN_VERSION=1.35.4
MUTTER_MIN_VERSION=3.11.1 MUTTER_MIN_VERSION=3.9.3
GTK_MIN_VERSION=3.7.9 GTK_MIN_VERSION=3.7.9
GIO_MIN_VERSION=2.37.0 GIO_MIN_VERSION=2.37.0
LIBECAL_MIN_VERSION=3.5.3 LIBECAL_MIN_VERSION=3.5.3
@ -86,65 +73,54 @@ POLKIT_MIN_VERSION=0.100
STARTUP_NOTIFICATION_MIN_VERSION=0.11 STARTUP_NOTIFICATION_MIN_VERSION=0.11
GCR_MIN_VERSION=3.7.5 GCR_MIN_VERSION=3.7.5
GNOME_DESKTOP_REQUIRED_VERSION=3.7.90 GNOME_DESKTOP_REQUIRED_VERSION=3.7.90
GNOME_MENUS_REQUIRED_VERSION=3.5.3
NETWORKMANAGER_MIN_VERSION=0.9.8 NETWORKMANAGER_MIN_VERSION=0.9.8
PULSE_MIN_VERS=2.0 PULSE_MIN_VERS=2.0
# Collect more than 20 libraries for a prize! # Collect more than 20 libraries for a prize!
SHARED_PCS="gio-unix-2.0 >= $GIO_MIN_VERSION PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
libxml-2.0 libxml-2.0
gtk+-3.0 >= $GTK_MIN_VERSION gtk+-3.0 >= $GTK_MIN_VERSION
atk-bridge-2.0 atk-bridge-2.0
libmutter >= $MUTTER_MIN_VERSION
gjs-internals-1.0 >= $GJS_MIN_VERSION gjs-internals-1.0 >= $GJS_MIN_VERSION
libgnome-menu-3.0 >= $GNOME_MENUS_REQUIRED_VERSION
$recorder_modules $recorder_modules
gdk-x11-3.0 libsoup-2.4 gdk-x11-3.0 libsoup-2.4
xtst
clutter-x11-1.0 >= $CLUTTER_MIN_VERSION clutter-x11-1.0 >= $CLUTTER_MIN_VERSION
clutter-glx-1.0 >= $CLUTTER_MIN_VERSION clutter-glx-1.0 >= $CLUTTER_MIN_VERSION
libstartup-notification-1.0 >= $STARTUP_NOTIFICATION_MIN_VERSION libstartup-notification-1.0 >= $STARTUP_NOTIFICATION_MIN_VERSION
gobject-introspection-1.0 >= $GOBJECT_INTROSPECTION_MIN_VERSION gobject-introspection-1.0 >= $GOBJECT_INTROSPECTION_MIN_VERSION
libcanberra libcanberra-gtk3 libcanberra libcanberra-gtk3
telepathy-glib >= $TELEPATHY_GLIB_MIN_VERSION telepathy-glib >= $TELEPATHY_GLIB_MIN_VERSION
polkit-agent-1 >= $POLKIT_MIN_VERSION polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes
libnm-glib libnm-util >= $NETWORKMANAGER_MIN_VERSION libnm-glib libnm-util >= $NETWORKMANAGER_MIN_VERSION
libnm-gtk >= $NETWORKMANAGER_MIN_VERSION libnm-gtk >= $NETWORKMANAGER_MIN_VERSION
libsecret-unstable gcr-base-3 >= $GCR_MIN_VERSION" libsecret-unstable gcr-base-3 >= $GCR_MIN_VERSION)
if test x$have_systemd = xyes; then
SHARED_PCS="${SHARED_PCS} libsystemd-journal"
fi
PKG_CHECK_MODULES(GNOME_SHELL, $SHARED_PCS)
PKG_CHECK_MODULES(MUTTER, libmutter >= $MUTTER_MIN_VERSION)
PKG_CHECK_MODULES(MUTTER_WAYLAND, [libmutter-wayland >= $MUTTER_MIN_VERSION],
[MUTTER_WAYLAND_TYPELIB_DIR=`$PKG_CONFIG --variable=typelibdir libmutter-wayland`
AC_SUBST(MUTTER_WAYLAND_TYPELIB_DIR)
have_mutter_wayland=yes],
[have_mutter_wayland=no])
AM_CONDITIONAL(HAVE_MUTTER_WAYLAND, test $have_mutter_wayland != no)
PKG_CHECK_MODULES(GNOME_SHELL_JS, gio-2.0 gjs-internals-1.0 >= $GJS_MIN_VERSION) PKG_CHECK_MODULES(GNOME_SHELL_JS, gio-2.0 gjs-internals-1.0 >= $GJS_MIN_VERSION)
PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 >= 0.6.8 x11) PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 >= 0.6.8 x11)
PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0) PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0)
PKG_CHECK_MODULES(SHELL_HOTPLUG_SNIFFER, gio-2.0 gdk-pixbuf-2.0) PKG_CHECK_MODULES(SHELL_HOTPLUG_SNIFFER, gio-2.0 gdk-pixbuf-2.0)
PKG_CHECK_MODULES(BROWSER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION json-glib-1.0 >= 0.13.2)
PKG_CHECK_MODULES(TRAY, gtk+-3.0) PKG_CHECK_MODULES(TRAY, gtk+-3.0)
PKG_CHECK_MODULES(GVC, libpulse >= $PULSE_MIN_VERS libpulse-mainloop-glib gobject-2.0) PKG_CHECK_MODULES(GVC, libpulse >= $PULSE_MIN_VERS libpulse-mainloop-glib gobject-2.0)
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.7.4) PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.7.4)
PKG_CHECK_MODULES(CARIBOU, caribou-1.0 >= 0.4.8) PKG_CHECK_MODULES(CARIBOU, caribou-1.0 >= 0.4.8)
AC_ARG_ENABLE(browser-plugin, AC_MSG_CHECKING([for bluetooth support])
[AS_HELP_STRING([--enable-browser-plugin], PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.9.0],
[Enable browser plugin [default=yes]])],, [BLUETOOTH_DIR=`$PKG_CONFIG --variable=applet_libdir gnome-bluetooth-1.0`
enable_browser_plugin=yes) BLUETOOTH_LIBS=`$PKG_CONFIG --variable=applet_libs gnome-bluetooth-1.0`
AS_IF([test x$enable_browser_plugin = xyes], [ AC_SUBST([BLUETOOTH_LIBS],["$BLUETOOTH_LIBS"])
PKG_CHECK_MODULES(BROWSER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION json-glib-1.0 >= 0.13.2) AC_SUBST([BLUETOOTH_DIR],["$BLUETOOTH_DIR"])
]) AC_DEFINE_UNQUOTED([BLUETOOTH_DIR],["$BLUETOOTH_DIR"],[Path to installed GnomeBluetooth typelib and library])
AM_CONDITIONAL(BUILD_BROWSER_PLUGIN, test x$enable_browser_plugin = xyes) AC_DEFINE([HAVE_BLUETOOTH],[1],[Define if you have libgnome-bluetooth-applet])
AC_SUBST([HAVE_BLUETOOTH],[1])
PKG_CHECK_MODULES(BLUETOOTH, gnome-bluetooth-1.0 >= 3.9.0, AC_MSG_RESULT([yes])],
[AC_DEFINE([HAVE_BLUETOOTH],[1],[Define if you have libgnome-bluetooth-applet])
AC_SUBST([HAVE_BLUETOOTH],[1])],
[AC_DEFINE([HAVE_BLUETOOTH],[0]) [AC_DEFINE([HAVE_BLUETOOTH],[0])
AC_SUBST([HAVE_BLUETOOTH],[0])]) AC_SUBST([HAVE_BLUETOOTH],[0])
AC_MSG_RESULT([no])])
PKG_CHECK_MODULES(CALENDAR_SERVER, libecal-1.2 >= $LIBECAL_MIN_VERSION libedataserver-1.2 >= $LIBEDATASERVER_MIN_VERSION gio-2.0) PKG_CHECK_MODULES(CALENDAR_SERVER, libecal-1.2 >= $LIBECAL_MIN_VERSION libedataserver-1.2 >= $LIBEDATASERVER_MIN_VERSION gio-2.0)
AC_SUBST(CALENDAR_SERVER_CFLAGS) AC_SUBST(CALENDAR_SERVER_CFLAGS)
@ -156,17 +132,13 @@ AC_SUBST([GNOME_KEYBINDINGS_KEYSDIR])
GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION]) GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION])
MUTTER_GIR_DIR=`$PKG_CONFIG --variable=girdir libmutter` MUTTER_GIR_DIR=`$PKG_CONFIG --variable=girdir libmutter`
AC_SUBST(MUTTER_GIR_DIR)
MUTTER_TYPELIB_DIR=`$PKG_CONFIG --variable=typelibdir libmutter` MUTTER_TYPELIB_DIR=`$PKG_CONFIG --variable=typelibdir libmutter`
AC_SUBST(MUTTER_GIR_DIR)
AC_SUBST(MUTTER_TYPELIB_DIR) AC_SUBST(MUTTER_TYPELIB_DIR)
GJS_CONSOLE=`$PKG_CONFIG --variable=gjs_console gjs-1.0` GJS_CONSOLE=`$PKG_CONFIG --variable=gjs_console gjs-1.0`
AC_SUBST(GJS_CONSOLE) AC_SUBST(GJS_CONSOLE)
GLIB_COMPILE_RESOURCES=`$PKG_CONFIG --variable glib_compile_resources gio-2.0`
AC_SUBST(GLIB_COMPILE_RESOURCES)
AC_CHECK_FUNCS(fdwalk) AC_CHECK_FUNCS(fdwalk)
AC_CHECK_FUNCS(mallinfo) AC_CHECK_FUNCS(mallinfo)
AC_CHECK_HEADERS([sys/resource.h]) AC_CHECK_HEADERS([sys/resource.h])
@ -201,6 +173,10 @@ AM_CONDITIONAL(ENABLE_MAN, test "$enable_man" != no)
GNOME_COMPILE_WARNINGS([error]) GNOME_COMPILE_WARNINGS([error])
AC_ARG_ENABLE(jhbuild-wrapper-script,
AS_HELP_STRING([--enable-jhbuild-wrapper-script],[Make "gnome-shell" script work for jhbuild]),,enable_jhbuild_wrapper_script=no)
AM_CONDITIONAL(USE_JHBUILD_WRAPPER_SCRIPT, test "x$enable_jhbuild_wrapper_script" = xyes)
BROWSER_PLUGIN_DIR="${BROWSER_PLUGIN_DIR:-"\${libdir}/mozilla/plugins"}" BROWSER_PLUGIN_DIR="${BROWSER_PLUGIN_DIR:-"\${libdir}/mozilla/plugins"}"
AC_ARG_VAR([BROWSER_PLUGIN_DIR],[Where to install the plugin to]) AC_ARG_VAR([BROWSER_PLUGIN_DIR],[Where to install the plugin to])

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>
<KeyListEntries schema="org.gnome.shell.keybindings"
group="system"
_name="Screenshots"
wm_name="GNOME Shell"
package="gnome-shell">
<KeyListEntry name="toggle-recording"
_description="Record a screencast"/>
</KeyListEntries>

View File

@ -1,9 +1,8 @@
wandadir = $(pkgdatadir)
dist_wanda_DATA = wanda.png
desktopdir=$(datadir)/applications desktopdir=$(datadir)/applications
desktop_DATA = gnome-shell.desktop gnome-shell-extension-prefs.desktop desktop_DATA = gnome-shell.desktop gnome-shell-extension-prefs.desktop
if HAVE_MUTTER_WAYLAND
desktop_DATA += gnome-shell-wayland.desktop
endif HAVE_MUTTER_WAYLAND
# We substitute in bindir so it works as an autostart # We substitute in bindir so it works as an autostart
# file when built in a non-system prefix # file when built in a non-system prefix
@ -42,10 +41,6 @@ dist_theme_DATA = \
theme/message-tray-background.png \ theme/message-tray-background.png \
theme/more-results.svg \ theme/more-results.svg \
theme/noise-texture.png \ theme/noise-texture.png \
theme/page-indicator-active.svg \
theme/page-indicator-inactive.svg \
theme/page-indicator-checked.svg \
theme/page-indicator-hover.svg \
theme/panel-button-border.svg \ theme/panel-button-border.svg \
theme/panel-button-highlight-narrow.svg \ theme/panel-button-highlight-narrow.svg \
theme/panel-button-highlight-wide.svg \ theme/panel-button-highlight-wide.svg \
@ -61,7 +56,10 @@ dist_theme_DATA = \
theme/ws-switch-arrow-down.png theme/ws-switch-arrow-down.png
keysdir = @GNOME_KEYBINDINGS_KEYSDIR@ keysdir = @GNOME_KEYBINDINGS_KEYSDIR@
keys_in_files = 50-gnome-shell-system.xml.in keys_in_files = \
50-gnome-shell-screenshot.xml.in \
50-gnome-shell-system.xml.in \
$(NULL)
keys_DATA = $(keys_in_files:.xml.in=.xml) keys_DATA = $(keys_in_files:.xml.in=.xml)
gsettings_SCHEMAS = org.gnome.shell.gschema.xml gsettings_SCHEMAS = org.gnome.shell.gschema.xml
@ -86,7 +84,6 @@ convert_DATA = gnome-shell-overrides.convert
EXTRA_DIST = \ EXTRA_DIST = \
gnome-shell.desktop.in.in \ gnome-shell.desktop.in.in \
gnome-shell-wayland.desktop.in.in \
gnome-shell-extension-prefs.desktop.in.in \ gnome-shell-extension-prefs.desktop.in.in \
$(introspection_DATA) \ $(introspection_DATA) \
$(menu_DATA) \ $(menu_DATA) \
@ -96,7 +93,6 @@ EXTRA_DIST = \
CLEANFILES = \ CLEANFILES = \
gnome-shell.desktop.in \ gnome-shell.desktop.in \
gnome-shell-wayland.desktop.in \
gnome-shell-extension-prefs.in \ gnome-shell-extension-prefs.in \
$(desktop_DATA) \ $(desktop_DATA) \
$(keys_DATA) \ $(keys_DATA) \

View File

@ -1,15 +0,0 @@
[Desktop Entry]
Type=Application
_Name=GNOME Shell (wayland compositor)
_Comment=Window management and application launching
Exec=@bindir@/mutter-launch -- gnome-shell-wayland --wayland
X-GNOME-Bugzilla-Bugzilla=GNOME
X-GNOME-Bugzilla-Product=gnome-shell
X-GNOME-Bugzilla-Component=general
X-GNOME-Bugzilla-Version=@VERSION@
Categories=GNOME;GTK;Core;
OnlyShowIn=GNOME;
NoDisplay=true
X-GNOME-Autostart-Phase=DisplayServer
X-GNOME-Autostart-Notify=true
X-GNOME-AutoRestart=false

View File

@ -37,13 +37,6 @@
application view, rather than being displayed inline in the main view. application view, rather than being displayed inline in the main view.
</_description> </_description>
</key> </key>
<key name="app-picker-view" type="u">
<default>0</default>
<summary>App Picker View</summary>
<description>
Index of the currently selected view in the application picker.
</description>
</key>
<key name="command-history" type="as"> <key name="command-history" type="as">
<default>[]</default> <default>[]</default>
<_summary>History for command (Alt-F2) dialog</_summary> <_summary>History for command (Alt-F2) dialog</_summary>
@ -52,6 +45,16 @@
<default>[]</default> <default>[]</default>
<_summary>History for the looking glass dialog</_summary> <_summary>History for the looking glass dialog</_summary>
</key> </key>
<key name="saved-im-presence" type="i">
<default>1</default>
<_summary>Internally used to store the last IM presence explicitly set by the user. The
value here is from the TpConnectionPresenceType enumeration.</_summary>
</key>
<key name="saved-session-presence" type="i">
<default>0</default>
<_summary>Internally used to store the last session presence status for the user. The
value here is from the GsmPresenceStatus enumeration.</_summary>
</key>
<key name="always-show-log-out" type="b"> <key name="always-show-log-out" type="b">
<default>false</default> <default>false</default>
<_summary>Always show the 'Log out' menuitem in the user menu.</_summary> <_summary>Always show the 'Log out' menuitem in the user menu.</_summary>
@ -71,6 +74,7 @@
</_description> </_description>
</key> </key>
<child name="calendar" schema="org.gnome.shell.calendar"/> <child name="calendar" schema="org.gnome.shell.calendar"/>
<child name="recorder" schema="org.gnome.shell.recorder"/>
<child name="keybindings" schema="org.gnome.shell.keybindings"/> <child name="keybindings" schema="org.gnome.shell.keybindings"/>
<child name="keyboard" schema="org.gnome.shell.keyboard"/> <child name="keyboard" schema="org.gnome.shell.keyboard"/>
</schema> </schema>
@ -124,6 +128,13 @@
Keybinding to focus the active notification. Keybinding to focus the active notification.
</_description> </_description>
</key> </key>
<key name="toggle-recording" type="as">
<default><![CDATA[['<Control><Shift><Alt>r']]]></default>
<_summary>Keybinding to toggle the screen recorder</_summary>
<_description>
Keybinding to start/stop the builtin screen recorder.
</_description>
</key>
</schema> </schema>
<schema id="org.gnome.shell.keyboard" path="/org/gnome/shell/keyboard/" <schema id="org.gnome.shell.keyboard" path="/org/gnome/shell/keyboard/"
@ -137,16 +148,41 @@
</key> </key>
</schema> </schema>
<schema id="org.gnome.shell.app-switcher" <schema id="org.gnome.shell.recorder" path="/org/gnome/shell/recorder/"
path="/org/gnome/shell/app-switcher/"
gettext-domain="@GETTEXT_PACKAGE@"> gettext-domain="@GETTEXT_PACKAGE@">
<key type="b" name="current-workspace-only"> <key name="framerate" type="i">
<default>false</default> <default>30</default>
<summary>Limit switcher to current workspace.</summary> <_summary>Framerate used for recording screencasts.</_summary>
<description> <_description>
If true, only applications that have windows on the current workspace are shown in the switcher. The framerate of the resulting screencast recordered
Otherwise, all applications are included. by GNOME Shell's screencast recorder in frames-per-second.
</description> </_description>
</key>
<key name="pipeline" type="s">
<default>''</default>
<_summary>The gstreamer pipeline used to encode the screencast</_summary>
<_description>
Sets the GStreamer pipeline used to encode recordings.
It follows the syntax used for gst-launch. The pipeline should have
an unconnected sink pad where the recorded video is recorded. It will
normally have a unconnected source pad; output from that pad
will be written into the output file. However the pipeline can also
take care of its own output - this might be used to send the output
to an icecast server via shout2send or similar. When unset or set
to an empty value, the default pipeline will be used. This is currently
'vp8enc min_quantizer=13 max_quantizer=13 cpu-used=5 deadline=1000000 threads=%T ! queue ! webmmux'
and records to WEBM using the VP8 codec. %T is used as a placeholder
for a guess at the optimal thread count on the system.
</_description>
</key>
<key name="file-extension" type="s">
<default>'webm'</default>
<_summary>File extension used for storing the screencast</_summary>
<_description>
The filename for recorded screencasts will be a unique filename
based on the current date, and use this extension. It should be
changed when recording to a different container format.
</_description>
</key> </key>
</schema> </schema>
@ -223,10 +259,10 @@
<key name="focus-change-on-pointer-rest" type="b"> <key name="focus-change-on-pointer-rest" type="b">
<default>true</default> <default>true</default>
<_summary>Delay focus changes in mouse mode until the pointer stops moving</_summary> <summary>Delay focus changes in mouse mode until the pointer stops moving</summary>
<_description> <description>
This key overrides the key in org.gnome.mutter when running GNOME Shell. This key overrides the key in org.gnome.mutter when running GNOME Shell.
</_description> </description>
</key> </key>
</schema> </schema>
</schemalist> </schemalist>

View File

@ -101,7 +101,7 @@ StScrollBar StButton#vhandle:active {
/* Check Boxes */ /* Check Boxes */
.check-box StBoxLayout { .check-box ShellGenericContainer {
spacing: .8em; spacing: .8em;
} }
@ -127,20 +127,20 @@ StScrollBar StButton#vhandle:active {
.slider { .slider {
height: 1em; height: 1em;
min-width: 15em;
-slider-height: 0.3em; -slider-height: 0.3em;
-slider-background-color: #333333; -slider-background-color: #333333;
-slider-border-color: #5f5f5f; -slider-border-color: #5f5f5f;
-slider-active-background-color: #76b0ec; -slider-active-background-color: #76b0ec;
-slider-active-border-color: #1f6dbc; -slider-active-border-color: #1f6dbc;
-slider-border-width: 1px; -slider-border-width: 1px;
-slider-handle-radius: 6px; -slider-handle-radius: 0.5em;
} }
/* PopupMenu */ /* PopupMenu */
.popup-menu-ornament { .popup-menu-ornament {
text-align: right; text-align: center;
width: 1em;
} }
.popup-menu-boxpointer, .popup-menu-boxpointer,
@ -157,17 +157,13 @@ StScrollBar StButton#vhandle:active {
min-width: 200px; min-width: 200px;
} }
.unicode-arrow {
font-size: 120%;
}
.popup-submenu-menu-item:open { .popup-submenu-menu-item:open {
background-color: #333333; background-color: #4c4c4c;
} }
.popup-sub-menu { .popup-sub-menu {
background-gradient-start: rgba(80,80,80,0.3); background-gradient-start: rgba(80,80,80,0.3);
background-gradient-end: rgba(80,80,80,0.4); background-gradient-end: rgba(80,80,80,0.7);
background-gradient-direction: vertical; background-gradient-direction: vertical;
box-shadow: inset 0px 2px 4px rgba(0,0,0,0.9); box-shadow: inset 0px 2px 4px rgba(0,0,0,0.9);
} }
@ -193,6 +189,13 @@ StScrollBar StButton#vhandle:active {
border-width: 0px; border-width: 0px;
} }
.popup-combo-menu {
background-color: rgba(0,0,0,0.9);
padding: 1em 0em;
border: 1px solid #5f5f5f;
border-radius: 9px;
}
/* The remaining popup-menu sizing is all done in ems, so that if you /* The remaining popup-menu sizing is all done in ems, so that if you
* override .popup-menu.font-size, everything else will scale with it. * override .popup-menu.font-size, everything else will scale with it.
*/ */
@ -201,15 +204,8 @@ StScrollBar StButton#vhandle:active {
} }
.popup-menu-item { .popup-menu-item {
spacing: 12px; padding: .4em 1.75em;
} spacing: 1em;
.popup-menu-item:ltr {
padding: .4em 1.75em .4em 0em;
}
.popup-menu-item:rtl {
padding: .4em 0em .4em 1.75em;
} }
.popup-menu-item:active { .popup-menu-item:active {
@ -223,6 +219,10 @@ StScrollBar StButton#vhandle:active {
.popup-image-menu-item { .popup-image-menu-item {
} }
.popup-combobox-item {
spacing: 1em;
}
.popup-separator-menu-item { .popup-separator-menu-item {
-gradient-height: 1px; -gradient-height: 1px;
-gradient-start: rgba(255,255,255,0.0); -gradient-start: rgba(255,255,255,0.0);
@ -236,6 +236,10 @@ StScrollBar StButton#vhandle:active {
font-weight: bold; font-weight: bold;
} }
.popup-device-menu-item {
spacing: .5em;
}
.popup-status-menu-item { .popup-status-menu-item {
font-weight: normal; font-weight: normal;
color: #999; color: #999;
@ -245,10 +249,19 @@ StScrollBar StButton#vhandle:active {
color: white; color: white;
} }
.popup-subtitle-menu-item, .popup-subtitle-menu-item:insensitive {
font-weight: bold;
color: white;
}
.popup-menu-icon { .popup-menu-icon {
icon-size: 1.09em; icon-size: 1.09em;
} }
.popup-battery-percentage {
padding-left: 24px;
}
/* Switches */ /* Switches */
.toggle-switch { .toggle-switch {
width: 65px; width: 65px;
@ -273,54 +286,10 @@ StScrollBar StButton#vhandle:active {
background-size: contain; background-size: contain;
} }
/* Network */ .nm-menu-item-icons {
.nm-dialog {
max-height: 500px;
min-height: 450px;
min-width: 470px;
}
.nm-dialog-content {
spacing: 20px;
}
.nm-dialog-header-hbox {
spacing: 10px;
}
.nm-dialog-header-icon {
icon-size: 32px;
}
.nm-dialog-scroll-view {
border: 2px solid #666;
border-radius: 6px;
}
.nm-dialog-header {
font-weight: bold;
}
.nm-dialog-item {
font-size: 12pt;
border-bottom: 1px solid #666;
padding: 12px;
spacing: 20px;
}
.nm-dialog-item:selected {
background-color: #333;
}
.nm-dialog-icons {
spacing: .5em; spacing: .5em;
} }
.nm-dialog-icon {
icon-size: 16px;
}
/* Buttons */ /* Buttons */
.candidate-page-button, .candidate-page-button,
@ -358,6 +327,10 @@ StScrollBar StButton#vhandle:active {
border-width: 2px; border-width: 2px;
} }
.app-view-control:focus {
padding: 3px;
}
.app-view-control:first-child:ltr:focus, .app-view-control:first-child:ltr:focus,
.app-view-control:last-child:rtl:focus { .app-view-control:last-child:rtl:focus {
border-right-width: 1px; border-right-width: 1px;
@ -392,8 +365,7 @@ StScrollBar StButton#vhandle:active {
.modal-dialog-button, .modal-dialog-button,
.notification-button, .notification-button,
.hotplug-notification-item, .hotplug-notification-item,
.app-view-controls, .app-view-controls {
#screenShieldNotifications {
border-radius: 18px; border-radius: 18px;
} }
@ -502,6 +474,10 @@ StScrollBar StButton#vhandle:active {
height: 1.86em; height: 1.86em;
} }
#panel.lock-screen {
background-color: rgba(0,0,0,0.3);
}
#panel.unlock-screen, #panel.unlock-screen,
#panel.login-screen { #panel.login-screen {
background-color: transparent; background-color: transparent;
@ -628,56 +604,64 @@ StScrollBar StButton#vhandle:active {
-boxpointer-gap: 4px; -boxpointer-gap: 4px;
} }
.panel-status-indicators-box, .panel-status-button-box {
.panel-status-menu-box { spacing: 4px;
spacing: 2px; }
.lock-screen-status-button-box {
spacing: 8px;
}
/* User Menu */
#panelUserMenu {
spacing: 4px;
}
.status-chooser {
spacing: .4em;
}
.status-chooser .popup-menu-item,
.status-chooser-combo .popup-menu-item {
padding: .4em;
}
.status-chooser-user-icon {
border: 2px solid #8b8b8b;
border-radius: 5px;
width: 48pt;
height: 48pt;
background-size: contain;
}
.status-chooser-user-icon:hover {
border: 2px solid #bbbbbb;
}
.status-chooser-user-name {
font-weight: bold;
font-size: 1.3em;
min-width: 120pt;
}
.status-chooser-combo {
border: 1px solid transparent;
}
.status-chooser-combo.popup-combo-menu {
padding: .4em 0em;
border-radius: 4px;
border: 1px solid #5f5f5f;
}
.status-chooser-status-item,
.status-chooser-combo .popup-combobox-item {
spacing: .4em;
} }
.system-status-icon { .system-status-icon {
icon-size: 1.09em; icon-size: 1.09em;
padding: 0 5px;
}
.aggregate-menu {
width: 360px;
}
.aggregate-menu .popup-menu-icon {
padding: 0 4px;
}
.system-switch-user-submenu-icon {
icon-size: 24px;
border: 1px solid #8b8b8b;
}
.system-menu-action {
color: #e6e6e6;
border-radius: 32px; /* wish we could do 50% */
padding: 13px;
border: 1px solid #5f5f5f; /* using rgba() is flaky unfortunately */
}
.system-menu-action:hover,
.system-menu-action:focus {
color: white;
background-color: #4c4c4c;
border: none;
padding: 14px;
}
.system-menu-action:active {
color: black;
background-color: #6f6f6f;
}
.system-menu-action > StIcon {
icon-size: 16px;
}
.screencast-indicator {
color: #ff0000;
} }
/* Overview */ /* Overview */
@ -690,9 +674,7 @@ StScrollBar StButton#vhandle:active {
padding-bottom: 32px; padding-bottom: 32px;
} }
.workspace-thumbnails { .workspace-thumbnails-background {
spacing: 11px;
visible-width: 32px; /* Amount visible before hovering */
border: 1px solid rgba(128, 128, 128, 0.4); border: 1px solid rgba(128, 128, 128, 0.4);
border-right: 0px; border-right: 0px;
border-radius: 9px 0px 0px 9px; border-radius: 9px 0px 0px 9px;
@ -700,13 +682,18 @@ StScrollBar StButton#vhandle:active {
padding: 11px 7px 11px 11px; padding: 11px 7px 11px 11px;
} }
.workspace-thumbnails:rtl { .workspace-thumbnails-background:rtl {
border-right: 1px; border-right: 1px;
border-left: 0px; border-left: 0px;
border-radius: 0px 9px 9px 0px; border-radius: 0px 9px 9px 0px;
padding: 11px 11px 11px 7px; padding: 11px 11px 11px 7px;
} }
.workspace-thumbnails {
spacing: 11px;
visible-width: 32px; /* Amount visible before hovering */
}
.workspace-thumbnail-indicator { .workspace-thumbnail-indicator {
border: 4px solid rgba(255,255,255,0.7); border: 4px solid rgba(255,255,255,0.7);
border-radius: 4px; border-radius: 4px;
@ -902,9 +889,9 @@ StScrollBar StButton#vhandle:active {
/* Application Launchers, Grid and List results */ /* Application Launchers, Grid and List results */
.icon-grid { .icon-grid {
spacing: 30px; spacing: 36px;
-shell-grid-horizontal-item-size: 136px; -shell-grid-horizontal-item-size: 118px;
-shell-grid-vertical-item-size: 136px; -shell-grid-vertical-item-size: 118px;
} }
.icon-grid .overview-icon { .icon-grid .overview-icon {
@ -912,6 +899,7 @@ StScrollBar StButton#vhandle:active {
} }
.app-display { .app-display {
padding: 8px;
spacing: 20px; spacing: 20px;
} }
@ -923,43 +911,11 @@ StScrollBar StButton#vhandle:active {
padding: 4px 32px; padding: 4px 32px;
} }
.app-view-control:focus {
padding: 3px 31px;
}
.search-display > StBoxLayout, .search-display > StBoxLayout,
.all-apps, .all-apps > StBoxLayout,
.frequent-apps > StBoxLayout { .frequent-apps > StBoxLayout {
/* horizontal padding to make sure scrollbars or dash don't overlap content */ /* horizontal padding to make sure scrollbars or dash don't overlap content */
padding: 0px 88px 10px 88px; padding: 0px 88px;
}
.page-indicator {
padding: 15px 20px;
}
.page-indicator .page-indicator-icon {
width: 18px;
height: 18px;
background-image: url(page-indicator-inactive.svg);
}
.page-indicator:hover .page-indicator-icon {
background-image: url(page-indicator-hover.svg);
}
.page-indicator:active .page-indicator-icon {
background-image: url(page-indicator-active.svg);
}
.page-indicator:checked .page-indicator-icon,
.page-indicator:checked:active .page-indicator-icon {
background-image: url(page-indicator-checked.svg);
}
.no-frequent-applications-label {
font-size: 18pt;
color: #999999;
} }
.app-folder-icon { .app-folder-icon {
@ -991,39 +947,27 @@ StScrollBar StButton#vhandle:active {
background-image: url("more-results.svg"); background-image: url("more-results.svg");
} }
.app-well-app > .overview-icon.overview-icon-with-label,
.grid-search-result .overview-icon.overview-icon-with-label {
/* since the label controls its own spacing, it is visually more
consistent to use different padding values for top and bottom */
padding: 10px 8px 5px 8px;
spacing: 4px;
}
.app-well-app > .overview-icon, .app-well-app > .overview-icon,
.show-apps > .overview-icon, .show-apps > .overview-icon,
.search-provider-icon, .search-provider-icon,
.list-search-result, .list-search-result,
.grid-search-result .overview-icon { .grid-search-result .overview-icon {
border-radius: 4px; border-radius: 4px;
padding: 6px; padding: 3px;
border: 1px rgba(0,0,0,0); border: 1px rgba(0,0,0,0);
transition-duration: 100ms; transition-duration: 100ms;
text-align: center; text-align: center;
} }
.search-provider-icon {
padding: 15px;
}
.app-folder-popup { .app-folder-popup {
-arrow-border-radius: 8px; -arrow-border-radius: 8px;
-arrow-background-color: rgba(0,0,0,0.3); -arrow-background-color: black;
-arrow-base: 24px; -arrow-base: 24px;
-arrow-rise: 11px; -arrow-rise: 11px;
} }
.app-folder-popup-bin { .app-folder-popup-bin {
padding: 5px; padding: 15px;
} }
.app-well-app.running > .overview-icon { .app-well-app.running > .overview-icon {
@ -1033,7 +977,7 @@ StScrollBar StButton#vhandle:active {
} }
.app-well-app.app-folder > .overview-icon { .app-well-app.app-folder > .overview-icon {
background-color: rgba(0,0,0,0.3); background-color: rgba(0,0,0,0.5);
} }
.app-well-app:hover > .overview-icon, .app-well-app:hover > .overview-icon,
@ -1048,7 +992,7 @@ StScrollBar StButton#vhandle:active {
} }
.app-display .app-well-app > .overview-icon { .app-display .app-well-app > .overview-icon {
border-radius: 4px; border-radius: 10px;
} }
.list-search-result:hover .list-search-result-description { .list-search-result:hover .list-search-result-description {
@ -1070,14 +1014,12 @@ StScrollBar StButton#vhandle:active {
.app-well-app:checked > .overview-icon, .app-well-app:checked > .overview-icon,
.app-well-app:active > .overview-icon, .app-well-app:active > .overview-icon,
.show-apps:checked > .overview-icon, .show-apps:checked > .overview-icon,
.show-apps:active > .overview-icon, .show-apps:active > .overview-icon {
.search-provider-icon:active,
.list-search-result:active {
background-gradient-start: rgba(255, 255, 255, .05); background-gradient-start: rgba(255, 255, 255, .05);
background-gradient-end: rgba(255, 255, 255, .15); background-gradient-end: rgba(255, 255, 255, .15);
background-gradient-direction: vertical; background-gradient-direction: vertical;
border-radius: 4px; border-radius: 4px;
box-shadow: inset 0px 1px 2px 0px rgba(0, 0, 0, 0.7); box-shadow: inset 0px 1px 2px 0px rgba(0, 0, 0, 1);
transition-duration: 100ms; transition-duration: 100ms;
} }
@ -1190,7 +1132,7 @@ StScrollBar StButton#vhandle:active {
padding: 4px; padding: 4px;
} }
.lg-extensions-list { .lg-extension-list {
padding: 4px; padding: 4px;
spacing: 6px; spacing: 6px;
} }
@ -1242,10 +1184,6 @@ StScrollBar StButton#vhandle:active {
padding-top: 8px; padding-top: 8px;
} }
.calendar-month-label:focus {
background-color: #999999;
}
.calendar-change-month-back { .calendar-change-month-back {
width: 18px; width: 18px;
height: 12px; height: 12px;
@ -1291,10 +1229,6 @@ StScrollBar StButton#vhandle:active {
color: #eeeeec; color: #eeeeec;
} }
.datemenu-date-label:focus {
background-color: #999999;
}
.calendar-day-base { .calendar-day-base {
font-size: 9pt; font-size: 9pt;
text-align: center; text-align: center;
@ -1363,7 +1297,7 @@ StScrollBar StButton#vhandle:active {
} }
.events-table { .events-table {
width: 320px; min-width: 320px;
spacing-columns: 6pt; spacing-columns: 6pt;
padding: 0 1.4em; padding: 0 1.4em;
} }
@ -1436,20 +1370,9 @@ StScrollBar StButton#vhandle:active {
height: 72px; height: 72px;
} }
.message-tray-menu-button StIcon { .no-messages-label {
padding: 0 20px; font-family: cantarell, sans-serif;
color: #aaaaaa; font-size: 11pt;
icon-size: 24px;
}
.message-tray-menu-button:hover StIcon,
.message-tray-menu-button:active StIcon,
.message-tray-menu-button:focus StIcon {
color: #eeeeee;
}
.no-messages-label,
.no-networks-label {
color: #999999; color: #999999;
} }
@ -1885,6 +1808,8 @@ StScrollBar StButton#vhandle:active {
} }
.modal-dialog-button { .modal-dialog-button {
margin-left: 10px;
margin-right: 10px;
padding: 4px 32px 5px; padding: 4px 32px 5px;
} }
@ -1923,10 +1848,6 @@ StScrollBar StButton#vhandle:active {
spacing: 42px; spacing: 42px;
} }
.end-session-dialog-list {
padding-top: 20px;
}
.end-session-dialog-subject { .end-session-dialog-subject {
padding-left: 17px; padding-left: 17px;
padding-bottom: 20px; padding-bottom: 20px;
@ -1944,7 +1865,6 @@ StScrollBar StButton#vhandle:active {
.end-session-dialog-description:rtl { .end-session-dialog-description:rtl {
padding-right: 17px; padding-right: 17px;
text-align: right;
} }
.end-session-dialog-logout-icon { .end-session-dialog-logout-icon {
@ -1961,39 +1881,50 @@ StScrollBar StButton#vhandle:active {
height: 32px; height: 32px;
} }
.end-session-dialog-inhibitor-layout {
spacing: 16px;
max-height: 200px;
padding-right: 50px;
padding-left: 50px;
}
.end-session-dialog-session-list,
.end-session-dialog-app-list { .end-session-dialog-app-list {
spacing: 1em; font-size: 10pt;
max-height: 200px;
padding-top: 42px;
padding-left: 49px;
padding-right: 32px;
} }
.end-session-dialog-list-header { .end-session-dialog-app-list:rtl {
font-weight: bold; padding-right: 49px;
padding-left: 32px;
} }
.end-session-dialog-list-header:rtl { .end-session-dialog-app-list-item {
text-align: right; color: #ccc;
} }
.end-session-dialog-app-list-item, .end-session-dialog-app-list-item:hover {
.end-session-dialog-session-list-item { color: white;
spacing: 1em;
} }
.end-session-dialog-app-list-item-name, .end-session-dialog-app-list-item:ltr {
.end-session-dialog-session-list-item-name { padding-right: 1em;
font-weight: bold; }
.end-session-dialog-app-list-item:rtl {
padding-left: 1em;
}
.end-session-dialog-app-list-item-icon:ltr {
padding-right: 17px;
}
.end-session-dialog-app-list-item-icon:rtl {
padding-left: 17px;
}
.end-session-dialog-app-list-item-name {
font-size: 10pt;
} }
.end-session-dialog-app-list-item-description { .end-session-dialog-app-list-item-description {
color: #cccccc; font-size: 8pt;
font-size: 10pt; color: #444444;
} }
/* ShellMountOperation Dialogs */ /* ShellMountOperation Dialogs */
@ -2100,10 +2031,6 @@ StScrollBar StButton#vhandle:active {
color: #666666; color: #666666;
} }
.prompt-dialog-description:rtl {
text-align: right;
}
.prompt-dialog-password-box { .prompt-dialog-password-box {
spacing: 1em; spacing: 1em;
padding-bottom: 1em; padding-bottom: 1em;
@ -2282,16 +2209,6 @@ StScrollBar StButton#vhandle:active {
/* Login Dialog */ /* Login Dialog */
.framed-user-icon {
border: 2px solid #8b8b8b;
border-radius: 3px;
background-size: contain;
}
.framed-user-icon:hover {
border: 2px solid #bbbbbb;
}
.login-dialog-banner { .login-dialog-banner {
font-size: 10pt; font-size: 10pt;
font-weight: bold; font-weight: bold;
@ -2311,10 +2228,18 @@ StScrollBar StButton#vhandle:active {
/* Reset border and background */ /* Reset border and background */
border: none; border: none;
background-color: transparent; background-color: transparent;
padding-bottom: 80px;
padding-top: 80px;
border-radius: 16px;
min-height: 150px;
max-height: 700px;
min-width: 350px;
} }
.login-dialog-button-box { .login-dialog-prompt-login-hint-message {
spacing: 5px; font-size: 10.5pt;
} }
.login-dialog-user-list-view { .login-dialog-user-list-view {
@ -2324,14 +2249,11 @@ StScrollBar StButton#vhandle:active {
.login-dialog-user-list { .login-dialog-user-list {
spacing: 12px; spacing: 12px;
padding: .2em; padding: .2em;
width: 23em;
} }
.login-dialog-user-list-item { .login-dialog-user-list-item {
border-radius: 5px; border-radius: 10px;
padding: .2em; padding: .2em;
color: #bfbfbf;
text-shadow: black 0px 2px 2px;
} }
.login-dialog-user-list-item:ltr { .login-dialog-user-list-item:ltr {
@ -2342,6 +2264,23 @@ StScrollBar StButton#vhandle:active {
padding-left: 1em; padding-left: 1em;
} }
.login-dialog-user-list-item .login-dialog-user-list-item-name {
font-size: 20pt;
padding-left: 1em;
}
.login-dialog-user-list:expanded .login-dialog-user-list-item {
color: #666666;
}
.login-dialog-user-list-item,
.login-dialog-user-list-item:hover .login-dialog-user-list-item-name,
.login-dialog-user-list:expanded .login-dialog-user-list-item:focus .login-dialog-user-list-item-name,
.login-dialog-user-list:expanded .login-dialog-user-list-item:logged-in {
color: white;
text-shadow: black 0px 2px 2px;
}
.login-dialog-user-list-item:hover { .login-dialog-user-list-item:hover {
background-color: rgba(255,255,255,0.1); background-color: rgba(255,255,255,0.1);
} }
@ -2368,18 +2307,18 @@ StScrollBar StButton#vhandle:active {
background-color: #8b8b8b; background-color: #8b8b8b;
} }
.login-dialog-user-list-item-icon {
border: 2px solid #8b8b8b;
border-radius: 8px;
width: 64px;
height: 64px;
}
.login-dialog-not-listed-label { .login-dialog-not-listed-label {
font-size: 10.5pt; font-size: 10.5pt;
font-weight: bold; font-weight: bold;
color: #666666; color: #666666;
padding-top: 1em; padding-top: 1em;
}
.login-dialog-user-selection-box {
padding: 100px 0;
}
.login-dialog-user-selection-box .login-dialog-not-listed-label {
padding-left: 2px; padding-left: 2px;
} }
@ -2400,13 +2339,15 @@ StScrollBar StButton#vhandle:active {
padding-top: 24px; padding-top: 24px;
padding-bottom: 12px; padding-bottom: 12px;
spacing: 8px; spacing: 8px;
width: 23em;
} }
.login-dialog-prompt-label { .login-dialog-prompt-label {
color: #eeeeee; color: #eeeeee;
font-size: 14px; font-size: 14px;
padding-top: 11px; }
.login-dialog-prompt-entry {
width: 15em;
} }
.login-dialog-session-list-button StIcon { .login-dialog-session-list-button StIcon {
@ -2418,7 +2359,6 @@ StScrollBar StButton#vhandle:active {
} }
.login-dialog-session-list-button:hover, .login-dialog-session-list-button:hover,
.login-dialog-session-list-button:focus,
.login-dialog-session-list-button:active { .login-dialog-session-list-button:active {
color: white; color: white;
} }
@ -2469,44 +2409,24 @@ StScrollBar StButton#vhandle:active {
background-color: rgba(102, 102, 102, 0.15); background-color: rgba(102, 102, 102, 0.15);
} }
.login-dialog-message {
padding-top: 4px;
padding-bottom: 16px;
min-height: 2em;
}
.login-dialog-message-warning { .login-dialog-message-warning {
color: orange; color: orange;
} }
.login-dialog-message-hint { .user-widget {
padding-top: 0px; spacing: .4em;
padding-bottom: 20px;
} }
.user-widget-label { .user-widget-label {
font-size: 20px; font-size: 16pt;
font-weight: bold; font-weight: bold;
text-align: left; text-align: left;
color:white; padding-left: 15px;
text-shadow: black 0px 4px 3px 0px; text-shadow: black 0px 4px 3px 0px;
} }
.user-widget-label:ltr {
padding-left: 18px;
}
.user-widget-label:rtl {
padding-right: 18px;
}
/* Screen shield */ /* Screen shield */
#panel.lock-screen,
#screenShieldNotifications {
background-color: rgba(0,0,0,0.3);
}
.screen-shield-background { .screen-shield-background {
background: black; background: black;
box-shadow: 0px 4px 8px rgba(0,0,0,0.9); box-shadow: 0px 4px 8px rgba(0,0,0,0.9);
@ -2551,27 +2471,33 @@ StScrollBar StButton#vhandle:active {
} }
#screenShieldNotifications { #screenShieldNotifications {
border-radius: 8px;
background-color: rgba(0.0, 0.0, 0.0, 0.9);
border: 2px solid #868686;
max-height: 500px; max-height: 500px;
padding: 12px; padding: 18px 0;
box-shadow: .5em .5em 20px rgba(0, 0, 0, 0.5);
} }
.screen-shield-notifications-box { .screen-shield-notifications-box {
spacing: 12px; spacing: 18px;
width: 30em; max-width: 34em;
} }
.screen-shield-notification-source { .screen-shield-notification-source {
padding: 3px 6px; padding: 13px 24px;
spacing: 5px; spacing: 5px;
} }
.screen-shield-notification-label { .screen-shield-notification-label {
font-size: 1.2em;
font-weight: bold; font-weight: bold;
padding: 0px 0px 0px 12px; padding: 0px 18px;
color: #babdb6;
} }
.screen-shield-notification-count-text { .screen-shield-notification-count-text {
padding: 0px 0px 0px 12px; padding: 0px 18px;
} }
/* Remove background from notifications, otherwise /* Remove background from notifications, otherwise
@ -2589,31 +2515,6 @@ StScrollBar StButton#vhandle:active {
padding-bottom: 0px; padding-bottom: 0px;
} }
#screenShieldNotifications .notification-button,
#screenShieldNotifications .notification-icon-button {
border: 1px rgba(255,255,255,0.5);
}
#screenShieldNotifications StScrollBar StBin#trough {
background-color: rgba(0,0,0,0.2);
}
#screenShieldNotifications StScrollBar StButton#vhandle,
#screenShieldNotifications StScrollBar StButton#hhandle {
background-color: rgba(0,0,0,0.3);
border: none;
}
#screenShieldNotifications StScrollBar StButton#vhandle:hover,
#screenShieldNotifications StScrollBar StButton#hhandle {
background-color: rgba(0,0,0,0.6);
}
#screenShieldNotifications StScrollBar StButton#vhandle:active,
#screenShieldNotifications StScrollBar StButton#hhandle {
background-color: rgba(0,0,0,0.8);
}
.input-source-switcher-symbol { .input-source-switcher-symbol {
font-size: 34pt; font-size: 34pt;
width: 96px; width: 96px;

View File

@ -14,7 +14,7 @@
height="16" height="16"
id="svg12430" id="svg12430"
version="1.1" version="1.1"
inkscape:version="0.48.4 r9939" inkscape:version="0.48.3.1 r9886"
sodipodi:docname="more-results.svg"> sodipodi:docname="more-results.svg">
<defs <defs
id="defs12432" /> id="defs12432" />
@ -25,18 +25,18 @@
borderopacity="1.0" borderopacity="1.0"
inkscape:pageopacity="1" inkscape:pageopacity="1"
inkscape:pageshadow="2" inkscape:pageshadow="2"
inkscape:zoom="90.509668" inkscape:zoom="1"
inkscape:cx="6.5009792" inkscape:cx="8.3155237"
inkscape:cy="8.3589595" inkscape:cy="0.89548874"
inkscape:document-units="px" inkscape:document-units="px"
inkscape:current-layer="g14642-3-0" inkscape:current-layer="g14642-3-0"
showgrid="false" showgrid="false"
borderlayer="true" borderlayer="true"
inkscape:showpageshadow="false" inkscape:showpageshadow="false"
inkscape:window-width="1440" inkscape:window-width="2560"
inkscape:window-height="840" inkscape:window-height="1376"
inkscape:window-x="0" inkscape:window-x="1200"
inkscape:window-y="27" inkscape:window-y="187"
inkscape:window-maximized="1"> inkscape:window-maximized="1">
<inkscape:grid <inkscape:grid
type="xygrid" type="xygrid"
@ -90,11 +90,6 @@
transform="translate(-2,0)" transform="translate(-2,0)"
width="16" width="16"
height="16" /> height="16" />
<path
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
d="M 7 5 L 7 7 L 5 7 L 5 9 L 7 9 L 7 11 L 9 11 L 9 9 L 11 9 L 11 7 L 9 7 L 9 5 L 7 5 z "
transform="translate(141.99984,397.99107)"
id="rect3757" />
<path <path
inkscape:connector-curvature="0" inkscape:connector-curvature="0"
style="color:#bebebe;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible" style="color:#bebebe;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible"

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@ -1,71 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="18"
height="18"
id="svg4703"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="page-indicator-pushed.svg">
<defs
id="defs4705" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="31.392433"
inkscape:cx="1.0245308"
inkscape:cy="13.3715"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:grid-bbox="true"
inkscape:document-units="px"
inkscape:window-width="2560"
inkscape:window-height="1374"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1">
<inkscape:grid
type="xygrid"
id="grid6140" />
</sodipodi:namedview>
<metadata
id="metadata4708">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer"
transform="translate(0,2)">
<path
transform="matrix(0.54617904,0,0,0.62523128,-1131.9904,-392.39214)"
d="m 2099.9808,638.83099 a 10.985409,9.5964489 0 1 1 -21.9708,0 10.985409,9.5964489 0 1 1 21.9708,0 z"
sodipodi:ry="9.5964489"
sodipodi:rx="10.985409"
sodipodi:cy="638.83099"
sodipodi:cx="2088.9954"
id="path4711"
style="fill:#fdffff;fill-opacity:1;stroke:none"
sodipodi:type="arc" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -1,67 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="18"
height="18"
id="svg4703"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="page-indicator-active.svg">
<defs
id="defs4705" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="22.197802"
inkscape:cx="2.1522887"
inkscape:cy="16.782904"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:grid-bbox="true"
inkscape:document-units="px"
inkscape:window-width="1920"
inkscape:window-height="1021"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1" />
<metadata
id="metadata4708">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer"
transform="translate(0,2)">
<path
transform="matrix(0.72823872,0,0,0.8336417,-1512.2872,-525.55618)"
d="m 2099.9808,638.83099 c 0,5.29998 -4.9184,9.59645 -10.9854,9.59645 -6.0671,0 -10.9854,-4.29647 -10.9854,-9.59645 0,-5.29997 4.9183,-9.59645 10.9854,-9.59645 6.067,0 10.9854,4.29648 10.9854,9.59645 z"
sodipodi:ry="9.5964489"
sodipodi:rx="10.985409"
sodipodi:cy="638.83099"
sodipodi:cx="2088.9954"
id="path4711"
style="fill:#fdffff;fill-opacity:0.94117647;stroke:none"
sodipodi:type="arc" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -1,67 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="18"
height="18"
id="svg5266"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="page-indicator-inactive.svg">
<defs
id="defs5268" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="11.313709"
inkscape:cx="-2.307566"
inkscape:cy="17.859535"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:grid-bbox="true"
inkscape:document-units="px"
inkscape:window-width="2560"
inkscape:window-height="1374"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1" />
<metadata
id="metadata5271">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer"
transform="translate(0,2)">
<path
sodipodi:type="arc"
style="fill:none;fill-opacity:0;stroke:#ffffff;stroke-width:2.93356276000000005;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="path5274"
sodipodi:cx="2088.9954"
sodipodi:cy="638.83099"
sodipodi:rx="10.985409"
sodipodi:ry="9.5964489"
d="m 2099.9808,638.83099 c 0,5.29998 -4.9184,9.59645 -10.9854,9.59645 -6.0671,0 -10.9854,-4.29647 -10.9854,-9.59645 0,-5.29997 4.9183,-9.59645 10.9854,-9.59645 6.067,0 10.9854,4.29648 10.9854,9.59645 z"
transform="matrix(0.63720887,0,0,0.72943648,-1322.1264,-458.98661)" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -1,67 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="18"
height="18"
id="svg5266"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="page-indicator-inactive.svg">
<defs
id="defs5268" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="11.313709"
inkscape:cx="-2.307566"
inkscape:cy="17.859535"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:grid-bbox="true"
inkscape:document-units="px"
inkscape:window-width="2560"
inkscape:window-height="1374"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1" />
<metadata
id="metadata5271">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer"
transform="translate(0,2)">
<path
sodipodi:type="arc"
style="fill:none;fill-opacity:0;stroke:#ffffff;stroke-width:2.93356276000000005;stroke-miterlimit:4;stroke-opacity:0.39215686000000000;stroke-dasharray:none"
id="path5274"
sodipodi:cx="2088.9954"
sodipodi:cy="638.83099"
sodipodi:rx="10.985409"
sodipodi:ry="9.5964489"
d="m 2099.9808,638.83099 c 0,5.29998 -4.9184,9.59645 -10.9854,9.59645 -6.0671,0 -10.9854,-4.29647 -10.9854,-9.59645 0,-5.29997 4.9183,-9.59645 10.9854,-9.59645 6.067,0 10.9854,4.29648 10.9854,9.59645 z"
transform="matrix(0.63720887,0,0,0.72943648,-1322.1264,-458.98661)" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.2 KiB

BIN
data/wanda.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -112,7 +112,7 @@ expand_content_files=
# e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS) # e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS)
# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib) # e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib)
GTKDOC_CFLAGS=$(GNOME_SHELL_CFLAGS) GTKDOC_CFLAGS=$(GNOME_SHELL_CFLAGS)
GTKDOC_LIBS=$(GNOME_SHELL_LIBS) $(top_builddir)/src/libgnome-shell-menu.la $(top_builddir)/src/libgnome-shell-base.la $(top_builddir)/src/libgnome-shell.la GTKDOC_LIBS=$(GNOME_SHELL_LIBS) $(BLUETOOTH_LIBS) $(top_builddir)/src/libgnome-shell.la
# This includes the standard gtk-doc make rules, copied by gtkdocize. # This includes the standard gtk-doc make rules, copied by gtkdocize.
include $(top_srcdir)/gtk-doc.make include $(top_srcdir)/gtk-doc.make

View File

@ -48,6 +48,7 @@
<xi:include href="xml/shell-global.xml"/> <xi:include href="xml/shell-global.xml"/>
<xi:include href="xml/shell-keybinding-modes.xml"/> <xi:include href="xml/shell-keybinding-modes.xml"/>
<xi:include href="xml/shell-wm.xml"/> <xi:include href="xml/shell-wm.xml"/>
<xi:include href="xml/shell-xfixes-cursor.xml"/>
<xi:include href="xml/shell-util.xml"/> <xi:include href="xml/shell-util.xml"/>
<xi:include href="xml/shell-mount-operation.xml"/> <xi:include href="xml/shell-mount-operation.xml"/>
<xi:include href="xml/shell-network-agent.xml"/> <xi:include href="xml/shell-network-agent.xml"/>

View File

@ -1,5 +1,7 @@
NULL = NULL =
BUILT_SOURCES =
EXTRA_DIST = misc/config.js.in
CLEANFILES = misc/config.js
misc/config.js: misc/config.js.in Makefile misc/config.js: misc/config.js.in Makefile
[ -d $(@D) ] || $(mkdir_p) $(@D) ; \ [ -d $(@D) ] || $(mkdir_p) $(@D) ; \
@ -12,26 +14,107 @@ misc/config.js: misc/config.js.in Makefile
-e "s|[@]sysconfdir@|$(sysconfdir)|g" \ -e "s|[@]sysconfdir@|$(sysconfdir)|g" \
$< > $@ $< > $@
js_resource_files = $(shell $(GLIB_COMPILE_RESOURCES) --sourcedir=$(srcdir) --generate-dependencies $(srcdir)/js-resources.gresource.xml) jsdir = $(pkgdatadir)/js
js-resources.h: js-resources.gresource.xml $(js_resource_files) misc/config.js
$(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(srcdir) --sourcedir=$(builddir) --generate --c-name shell_js_resources $<
js-resources.c: js-resources.gresource.xml $(js_resource_files) misc/config.js
$(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(srcdir) --sourcedir=$(builddir) --generate --c-name shell_js_resources $<
js_built_sources = js-resources.c js-resources.h nobase_dist_js_DATA = \
gdm/batch.js \
BUILT_SOURCES += $(js_built_sources) gdm/fingerprint.js \
gdm/loginDialog.js \
all-local: $(js_built_sources) gdm/powerMenu.js \
gdm/realmd.js \
js_resource_dist_files = $(filter-out misc/config.js, $(js_resource_files)) gdm/util.js \
extensionPrefs/main.js \
EXTRA_DIST = \ misc/config.js \
$(js_resource_dist_files) \ misc/extensionUtils.js \
js-resources.gresource.xml \ misc/fileUtils.js \
misc/config.js.in \ misc/gnomeSession.js \
$(NULL) misc/hash.js \
misc/history.js \
CLEANFILES = \ misc/jsParse.js \
$(js_built_sources) \ misc/loginManager.js \
misc/modemManager.js \
misc/params.js \
misc/util.js \
perf/core.js \
ui/altTab.js \
ui/animation.js \
ui/appDisplay.js \
ui/appFavorites.js \
ui/backgroundMenu.js \
ui/background.js \
ui/boxpointer.js \
ui/calendar.js \
ui/checkBox.js \
ui/ctrlAltTab.js \
ui/dash.js \
ui/dateMenu.js \
ui/dnd.js \
ui/endSessionDialog.js \
ui/extensionSystem.js \
ui/extensionDownloader.js \
ui/environment.js \
ui/ibusCandidatePopup.js\
ui/grabHelper.js \
ui/iconGrid.js \
ui/keyboard.js \
ui/layout.js \
ui/lightbox.js \
ui/lookingGlass.js \
ui/magnifier.js \
ui/magnifierDBus.js \
ui/main.js \
ui/messageTray.js \
ui/modalDialog.js \
ui/separator.js \
ui/sessionMode.js \
ui/shellEntry.js \
ui/shellMountOperation.js \
ui/slider.js \
ui/notificationDaemon.js \
ui/osdWindow.js \
ui/overview.js \
ui/overviewControls.js \
ui/panel.js \
ui/panelMenu.js \
ui/pointerWatcher.js \
ui/popupMenu.js \
ui/remoteSearch.js \
ui/remoteMenu.js \
ui/runDialog.js \
ui/screencast.js \
ui/screenshot.js \
ui/screenShield.js \
ui/scripting.js \
ui/search.js \
ui/searchDisplay.js \
ui/shellDBus.js \
ui/status/accessibility.js \
ui/status/keyboard.js \
ui/status/lockScreenMenu.js \
ui/status/network.js \
ui/status/power.js \
ui/status/volume.js \
ui/status/bluetooth.js \
ui/switcherPopup.js \
ui/tweener.js \
ui/unlockDialog.js \
ui/userMenu.js \
ui/userWidget.js \
ui/viewSelector.js \
ui/wanda.js \
ui/windowAttentionHandler.js \
ui/windowManager.js \
ui/workspace.js \
ui/workspaceThumbnail.js \
ui/workspacesView.js \
ui/workspaceSwitcherPopup.js \
ui/xdndHandler.js \
ui/components/__init__.js \
ui/components/autorunManager.js \
ui/components/automountManager.js \
ui/components/networkAgent.js \
ui/components/polkitAgent.js \
ui/components/recorder.js \
ui/components/telepathyClient.js \
ui/components/keyring.js \
$(NULL) $(NULL)

View File

@ -13,15 +13,13 @@ const _ = Gettext.gettext;
const Config = imports.misc.config; const Config = imports.misc.config;
const ExtensionUtils = imports.misc.extensionUtils; const ExtensionUtils = imports.misc.extensionUtils;
const GnomeShellIface = '<node> \ const GnomeShellIface = <interface name="org.gnome.Shell.Extensions">
<interface name="org.gnome.Shell.Extensions"> \ <signal name="ExtensionStatusChanged">
<signal name="ExtensionStatusChanged"> \ <arg type="s" name="uuid"/>
<arg type="s" name="uuid"/> \ <arg type="i" name="state"/>
<arg type="i" name="state"/> \ <arg type="s" name="error"/>
<arg type="s" name="error"/> \ </signal>
</signal> \ </interface>;
</interface> \
</node>';
const GnomeShellProxy = Gio.DBusProxy.makeProxyWrapper(GnomeShellIface); const GnomeShellProxy = Gio.DBusProxy.makeProxyWrapper(GnomeShellIface);
@ -206,11 +204,11 @@ const Application = new Lang.Class({
_scanExtensions: function() { _scanExtensions: function() {
let finder = new ExtensionUtils.ExtensionFinder(); let finder = new ExtensionUtils.ExtensionFinder();
finder.connect('extension-found', Lang.bind(this, this._extensionFound)); finder.connect('extension-found', Lang.bind(this, this._extensionFound));
finder.connect('extensions-loaded', Lang.bind(this, this._extensionsLoaded));
finder.scanExtensions(); finder.scanExtensions();
this._extensionsLoaded();
}, },
_extensionFound: function(finder, extension) { _extensionFound: function(signals, extension) {
let iter = this._model.append(); let iter = this._model.append();
this._model.set(iter, [0, 1], [extension.uuid, extension.metadata.name]); this._model.set(iter, [0, 1], [extension.uuid, extension.metadata.name]);
this._extensionIters[extension.uuid] = iter; this._extensionIters[extension.uuid] = iter;

View File

@ -1,506 +0,0 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Lang = imports.lang;
const Signals = imports.signals;
const St = imports.gi.St;
const Animation = imports.ui.animation;
const Batch = imports.gdm.batch;
const GdmUtil = imports.gdm.util;
const Params = imports.misc.params;
const ShellEntry = imports.ui.shellEntry;
const Tweener = imports.ui.tweener;
const UserWidget = imports.ui.userWidget;
const DEFAULT_BUTTON_WELL_ICON_SIZE = 24;
const DEFAULT_BUTTON_WELL_ANIMATION_DELAY = 1.0;
const DEFAULT_BUTTON_WELL_ANIMATION_TIME = 0.3;
const MESSAGE_FADE_OUT_ANIMATION_TIME = 0.5;
const AuthPromptMode = {
UNLOCK_ONLY: 0,
UNLOCK_OR_LOG_IN: 1
};
const AuthPromptStatus = {
NOT_VERIFYING: 0,
VERIFYING: 1,
VERIFICATION_FAILED: 2,
VERIFICATION_SUCCEEDED: 3
};
const BeginRequestType = {
PROVIDE_USERNAME: 0,
DONT_PROVIDE_USERNAME: 1
};
const AuthPrompt = new Lang.Class({
Name: 'AuthPrompt',
_init: function(gdmClient, mode) {
this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
this._gdmClient = gdmClient;
this._mode = mode;
let reauthenticationOnly;
if (this._mode == AuthPromptMode.UNLOCK_ONLY)
reauthenticationOnly = true;
else if (this._mode == AuthPromptMode.UNLOCK_OR_LOG_IN)
reauthenticationOnly = false;
this._userVerifier = new GdmUtil.ShellUserVerifier(this._gdmClient, { reauthenticationOnly: reauthenticationOnly });
this._userVerifier.connect('ask-question', Lang.bind(this, this._onAskQuestion));
this._userVerifier.connect('show-message', Lang.bind(this, this._onShowMessage));
this._userVerifier.connect('verification-failed', Lang.bind(this, this._onVerificationFailed));
this._userVerifier.connect('verification-complete', Lang.bind(this, this._onVerificationComplete));
this._userVerifier.connect('reset', Lang.bind(this, this._onReset));
this._userVerifier.connect('smartcard-status-changed', Lang.bind(this, this._onSmartcardStatusChanged));
this._userVerifier.connect('ovirt-user-authenticated', Lang.bind(this, this._onOVirtUserAuthenticated));
this.smartcardDetected = this._userVerifier.smartcardDetected;
this.connect('next', Lang.bind(this, function() {
this.updateSensitivity(false);
this.startSpinning();
if (this._queryingService) {
this._userVerifier.answerQuery(this._queryingService, this._entry.text);
} else {
this._preemptiveAnswer = this._entry.text;
}
}));
this.actor = new St.BoxLayout({ style_class: 'login-dialog-prompt-layout',
vertical: true });
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
this.actor.connect('key-press-event',
Lang.bind(this, function(actor, event) {
if (event.get_key_symbol() == Clutter.KEY_Escape) {
this.cancel();
}
return Clutter.EVENT_PROPAGATE;
}));
this._userWell = new St.Bin({ x_fill: true,
x_align: St.Align.START });
this.actor.add(this._userWell,
{ x_align: St.Align.START,
x_fill: true,
y_fill: true,
expand: true });
this._label = new St.Label({ style_class: 'login-dialog-prompt-label' });
this.actor.add(this._label,
{ expand: true,
x_fill: false,
y_fill: true,
x_align: St.Align.START });
this._entry = new St.Entry({ style_class: 'login-dialog-prompt-entry',
can_focus: true });
ShellEntry.addContextMenu(this._entry, { isPassword: true });
this.actor.add(this._entry,
{ expand: true,
x_fill: true,
y_fill: false,
x_align: St.Align.START });
this._entry.grab_key_focus();
this._message = new St.Label({ opacity: 0,
styleClass: 'login-dialog-message' });
this._message.clutter_text.line_wrap = true;
this.actor.add(this._message, { x_fill: false, x_align: St.Align.START, y_align: St.Align.START });
this._buttonBox = new St.BoxLayout({ style_class: 'login-dialog-button-box',
vertical: false });
this.actor.add(this._buttonBox,
{ expand: true,
x_align: St.Align.MIDDLE,
y_align: St.Align.END });
this._defaultButtonWell = new St.Widget({ layout_manager: new Clutter.BinLayout() });
this._defaultButtonWellActor = null;
this._initButtons();
let spinnerIcon = global.datadir + '/theme/process-working.svg';
this._spinner = new Animation.AnimatedIcon(spinnerIcon, DEFAULT_BUTTON_WELL_ICON_SIZE);
this._spinner.actor.opacity = 0;
this._spinner.actor.show();
this._defaultButtonWell.add_child(this._spinner.actor);
},
_onDestroy: function() {
this._userVerifier.clear();
this._userVerifier.disconnectAll();
this._userVerifier = null;
},
_initButtons: function() {
this.cancelButton = new St.Button({ style_class: 'modal-dialog-button',
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
reactive: true,
can_focus: true,
label: _("Cancel") });
this.cancelButton.connect('clicked',
Lang.bind(this, function() {
this.cancel();
}));
this._buttonBox.add(this.cancelButton,
{ expand: false,
x_fill: false,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.END });
this._buttonBox.add(this._defaultButtonWell,
{ expand: true,
x_fill: false,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.MIDDLE });
this.nextButton = new St.Button({ style_class: 'modal-dialog-button',
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
reactive: true,
can_focus: true,
label: _("Next") });
this.nextButton.connect('clicked',
Lang.bind(this, function() {
this.emit('next');
}));
this.nextButton.add_style_pseudo_class('default');
this._buttonBox.add(this.nextButton,
{ expand: false,
x_fill: false,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.END });
this._updateNextButtonSensitivity(this._entry.text.length > 0);
this._entry.clutter_text.connect('text-changed',
Lang.bind(this, function() {
if (!this._userVerifier.hasPendingMessages)
this._fadeOutMessage();
this._updateNextButtonSensitivity(this._entry.text.length > 0);
}));
this._entry.clutter_text.connect('activate', Lang.bind(this, function() {
this.emit('next');
}));
},
_onAskQuestion: function(verifier, serviceName, question, passwordChar) {
if (this._preemptiveAnswer) {
if (this._queryingService)
this._userVerifier.answerQuery(this._queryingService, this._preemptiveAnswer);
this._preemptiveAnswer = null;
return;
}
if (this._queryingService)
this.clear();
this._queryingService = serviceName;
this.setPasswordChar(passwordChar);
this.setQuestion(question);
if (passwordChar) {
if (this._userVerifier.reauthenticating)
this.nextButton.label = _("Unlock");
else
this.nextButton.label = C_("button", "Sign In");
} else {
this.nextButton.label = _("Next");
}
this.updateSensitivity(true);
this.emit('prompted');
},
_onOVirtUserAuthenticated: function() {
if (this.verificationStatus != AuthPromptStatus.VERIFICATION_SUCCEEDED)
this.reset();
},
_onSmartcardStatusChanged: function() {
this.smartcardDetected = this._userVerifier.smartcardDetected;
// Most of the time we want to reset if the user inserts or removes
// a smartcard. Smartcard insertion "preempts" what the user was
// doing, and smartcard removal aborts the preemption.
// The exceptions are: 1) Don't reset on smartcard insertion if we're already verifying
// with a smartcard
// 2) Don't reset if we've already succeeded at verification and
// the user is getting logged in.
if (this._userVerifier.serviceIsDefault(GdmUtil.SMARTCARD_SERVICE_NAME) &&
this.verificationStatus == AuthPromptStatus.VERIFYING &&
this.smartcardDetected)
return;
if (this.verificationStatus != AuthPromptStatus.VERIFICATION_SUCCEEDED)
this.reset();
},
_onShowMessage: function(userVerifier, message, type) {
this.setMessage(message, type);
this.emit('prompted');
},
_onVerificationFailed: function() {
this._queryingService = null;
this.clear();
this.updateSensitivity(true);
this.setActorInDefaultButtonWell(null);
this.verificationStatus = AuthPromptStatus.VERIFICATION_FAILED;
},
_onVerificationComplete: function() {
this.verificationStatus = AuthPromptStatus.VERIFICATION_SUCCEEDED;
},
_onReset: function() {
this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
this.reset();
},
addActorToDefaultButtonWell: function(actor) {
this._defaultButtonWell.add_child(actor);
},
setActorInDefaultButtonWell: function(actor, animate) {
if (!this._defaultButtonWellActor &&
!actor)
return;
let oldActor = this._defaultButtonWellActor;
if (oldActor)
Tweener.removeTweens(oldActor);
let isSpinner;
if (actor == this._spinner.actor)
isSpinner = true;
else
isSpinner = false;
if (this._defaultButtonWellActor != actor && oldActor) {
if (!animate) {
oldActor.opacity = 0;
} else {
Tweener.addTween(oldActor,
{ opacity: 0,
time: DEFAULT_BUTTON_WELL_ANIMATION_TIME,
delay: DEFAULT_BUTTON_WELL_ANIMATION_DELAY,
transition: 'linear',
onCompleteScope: this,
onComplete: function() {
if (isSpinner) {
if (this._spinner)
this._spinner.stop();
}
}
});
}
}
if (actor) {
if (isSpinner)
this._spinner.play();
if (!animate)
actor.opacity = 255;
else
Tweener.addTween(actor,
{ opacity: 255,
time: DEFAULT_BUTTON_WELL_ANIMATION_TIME,
delay: DEFAULT_BUTTON_WELL_ANIMATION_DELAY,
transition: 'linear' });
}
this._defaultButtonWellActor = actor;
},
startSpinning: function() {
this.setActorInDefaultButtonWell(this._spinner.actor, true);
},
stopSpinning: function() {
this.setActorInDefaultButtonWell(null, false);
},
clear: function() {
this._entry.text = '';
this.stopSpinning();
},
setPasswordChar: function(passwordChar) {
this._entry.clutter_text.set_password_char(passwordChar);
this._entry.menu.isPassword = passwordChar != '';
},
setQuestion: function(question) {
this._label.set_text(question);
this._label.show();
this._entry.show();
this._entry.grab_key_focus();
},
getAnswer: function() {
let text;
if (this._preemptiveAnswer) {
text = this._preemptiveAnswer;
this._preemptiveAnswer = null;
} else {
text = this._entry.get_text();
}
return text;
},
_fadeOutMessage: function() {
if (this._message.opacity == 0)
return;
Tweener.removeTweens(this._message);
Tweener.addTween(this._message,
{ opacity: 0,
time: MESSAGE_FADE_OUT_ANIMATION_TIME,
transition: 'easeOutQuad'
});
},
setMessage: function(message, type) {
if (type == GdmUtil.MessageType.ERROR)
this._message.add_style_class_name('login-dialog-message-warning');
else
this._message.remove_style_class_name('login-dialog-message-warning');
if (type == GdmUtil.MessageType.HINT)
this._message.add_style_class_name('login-dialog-message-hint');
else
this._message.remove_style_class_name('login-dialog-message-hint');
if (message) {
Tweener.removeTweens(this._message);
this._message.text = message;
this._message.opacity = 255;
} else {
this._message.opacity = 0;
}
},
_updateNextButtonSensitivity: function(sensitive) {
this.nextButton.reactive = sensitive;
this.nextButton.can_focus = sensitive;
},
updateSensitivity: function(sensitive) {
this._updateNextButtonSensitivity(sensitive);
this._entry.reactive = sensitive;
this._entry.clutter_text.editable = sensitive;
},
hide: function() {
this.setActorInDefaultButtonWell(null, true);
this.actor.hide();
this._message.opacity = 0;
this.setUser(null);
this.updateSensitivity(true);
this._entry.set_text('');
},
setUser: function(user) {
if (user) {
let userWidget = new UserWidget.UserWidget(user);
this._userWell.set_child(userWidget.actor);
} else {
this._userWell.set_child(null);
}
},
reset: function() {
let oldStatus = this.verificationStatus;
this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
if (oldStatus == AuthPromptStatus.VERIFYING)
this._userVerifier.cancel();
this._queryingService = null;
this.clear();
this._message.opacity = 0;
this.setUser(null);
this.stopSpinning();
if (oldStatus == AuthPromptStatus.VERIFICATION_FAILED)
this.emit('failed');
let beginRequestType;
if (this._mode == AuthPromptMode.UNLOCK_ONLY) {
// The user is constant at the unlock screen, so it will immediately
// respond to the request with the username
beginRequestType = BeginRequestType.PROVIDE_USERNAME;
} else if (this._userVerifier.serviceIsForeground(GdmUtil.OVIRT_SERVICE_NAME) ||
(this.smartcardDetected &&
this._userVerifier.serviceIsForeground(GdmUtil.SMARTCARD_SERVICE_NAME))) {
// We don't need to know the username if the user preempted the login screen
// with a smartcard or with preauthenticated oVirt credentials
beginRequestType = BeginRequestType.DONT_PROVIDE_USERNAME;
} else {
// In all other cases, we should get the username up front.
beginRequestType = BeginRequestType.PROVIDE_USERNAME;
}
this.emit('reset', beginRequestType);
},
addCharacter: function(unichar) {
if (!this._entry.visible)
return;
this._entry.grab_key_focus();
this._entry.clutter_text.insert_unichar(unichar);
},
begin: function(params) {
params = Params.parse(params, { userName: null,
hold: null });
this.updateSensitivity(false);
let hold = params.hold;
if (!hold)
hold = new Batch.Hold();
this._userVerifier.begin(params.userName, hold);
this.verificationStatus = AuthPromptStatus.VERIFYING;
},
finish: function(onComplete) {
if (!this._userVerifier.hasPendingMessages) {
onComplete();
return;
}
let signalId = this._userVerifier.connect('no-more-messages',
Lang.bind(this, function() {
this._userVerifier.disconnect(signalId);
onComplete();
}));
},
cancel: function() {
this.reset();
this.emit('cancelled');
}
});
Signals.addSignalMethods(AuthPrompt.prototype);

View File

@ -5,13 +5,11 @@ const Lang = imports.lang;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const Signals = imports.signals; const Signals = imports.signals;
const FprintManagerIface = '<node> \ const FprintManagerIface = <interface name='net.reactivated.Fprint.Manager'>
<interface name="net.reactivated.Fprint.Manager"> \ <method name='GetDefaultDevice'>
<method name="GetDefaultDevice"> \ <arg type='o' direction='out' />
<arg type="o" direction="out" /> \ </method>
</method> \ </interface>;
</interface> \
</node>';
const FprintManagerInfo = Gio.DBusInterfaceInfo.new_for_xml(FprintManagerIface); const FprintManagerInfo = Gio.DBusInterfaceInfo.new_for_xml(FprintManagerIface);

View File

@ -32,7 +32,7 @@ const Shell = imports.gi.Shell;
const Signals = imports.signals; const Signals = imports.signals;
const St = imports.gi.St; const St = imports.gi.St;
const AuthPrompt = imports.gdm.authPrompt; const Animation = imports.ui.animation;
const Batch = imports.gdm.batch; const Batch = imports.gdm.batch;
const BoxPointer = imports.ui.boxpointer; const BoxPointer = imports.ui.boxpointer;
const CtrlAltTab = imports.ui.ctrlAltTab; const CtrlAltTab = imports.ui.ctrlAltTab;
@ -46,6 +46,9 @@ const UserWidget = imports.ui.userWidget;
const _FADE_ANIMATION_TIME = 0.25; const _FADE_ANIMATION_TIME = 0.25;
const _SCROLL_ANIMATION_TIME = 0.5; const _SCROLL_ANIMATION_TIME = 0.5;
const _WORK_SPINNER_ICON_SIZE = 24;
const _WORK_SPINNER_ANIMATION_DELAY = 1.0;
const _WORK_SPINNER_ANIMATION_TIME = 0.3;
const _TIMED_LOGIN_IDLE_THRESHOLD = 5.0; const _TIMED_LOGIN_IDLE_THRESHOLD = 5.0;
const _LOGO_ICON_HEIGHT = 48; const _LOGO_ICON_HEIGHT = 48;
@ -59,7 +62,7 @@ const UserListItem = new Lang.Class({
this._userChangedId = this.user.connect('changed', this._userChangedId = this.user.connect('changed',
Lang.bind(this, this._onUserChanged)); Lang.bind(this, this._onUserChanged));
let layout = new St.BoxLayout({ vertical: true }); let layout = new St.BoxLayout({ vertical: false });
this.actor = new St.Button({ style_class: 'login-dialog-user-list-item', this.actor = new St.Button({ style_class: 'login-dialog-user-list-item',
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE, button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
can_focus: true, can_focus: true,
@ -68,21 +71,47 @@ const UserListItem = new Lang.Class({
x_align: St.Align.START, x_align: St.Align.START,
x_fill: true }); x_fill: true });
this._userWidget = new UserWidget.UserWidget(this.user); this._userAvatar = new UserWidget.Avatar(this.user,
layout.add(this._userWidget.actor); { styleClass: 'login-dialog-user-list-item-icon' });
layout.add(this._userAvatar.actor);
let textLayout = new St.BoxLayout({ style_class: 'login-dialog-user-list-item-text-box',
vertical: true });
layout.add(textLayout, { expand: true });
this._nameLabel = new St.Label({ style_class: 'login-dialog-user-list-item-name' });
this.actor.label_actor = this._nameLabel;
textLayout.add(this._nameLabel,
{ y_fill: false,
y_align: St.Align.MIDDLE,
expand: true });
this._timedLoginIndicator = new St.Bin({ style_class: 'login-dialog-timed-login-indicator', this._timedLoginIndicator = new St.Bin({ style_class: 'login-dialog-timed-login-indicator',
scale_x: 0 }); scale_x: 0 });
layout.add(this._timedLoginIndicator); textLayout.add(this._timedLoginIndicator,
{ x_fill: true,
x_align: St.Align.MIDDLE,
y_fill: false,
y_align: St.Align.END });
this.actor.connect('clicked', Lang.bind(this, this._onClicked)); this.actor.connect('clicked', Lang.bind(this, this._onClicked));
this._onUserChanged(); this._onUserChanged();
}, },
_onUserChanged: function() { _onUserChanged: function() {
this._nameLabel.set_text(this.user.get_real_name());
this._userAvatar.update();
this._updateLoggedIn(); this._updateLoggedIn();
}, },
syncStyleClasses: function() {
this._updateLoggedIn();
if (global.stage.get_key_focus() == this.actor)
this.actor.add_style_pseudo_class('focus');
else
this.actor.remove_style_pseudo_class('focus');
},
_updateLoggedIn: function() { _updateLoggedIn: function() {
if (this.user.is_logged_in()) if (this.user.is_logged_in())
this.actor.add_style_pseudo_class('logged-in'); this.actor.add_style_pseudo_class('logged-in');
@ -168,6 +197,7 @@ const UserList = new Lang.Class({
for (let userName in this._items) { for (let userName in this._items) {
let item = this._items[userName]; let item = this._items[userName];
item.actor.sync_hover(); item.actor.sync_hover();
item.syncStyleClasses();
} }
}, },
@ -260,8 +290,8 @@ const UserList = new Lang.Class({
}); });
Signals.addSignalMethods(UserList.prototype); Signals.addSignalMethods(UserList.prototype);
const SessionMenuButton = new Lang.Class({ const SessionList = new Lang.Class({
Name: 'SessionMenuButton', Name: 'SessionList',
_init: function() { _init: function() {
let gearIcon = new St.Icon({ icon_name: 'emblem-system-symbolic' }); let gearIcon = new St.Icon({ icon_name: 'emblem-system-symbolic' });
@ -287,6 +317,10 @@ const SessionMenuButton = new Lang.Class({
this._button.remove_style_pseudo_class('active'); this._button.remove_style_pseudo_class('active');
})); }));
let subtitle = new PopupMenu.PopupMenuItem(_("Session"), { style_class: 'popup-subtitle-menu-item',
reactive: false });
this._menu.addMenuItem(subtitle);
this._manager = new PopupMenu.PopupMenuManager({ actor: this._button }); this._manager = new PopupMenu.PopupMenuManager({ actor: this._button });
this._manager.addMenu(this._menu); this._manager.addMenu(this._menu);
@ -355,14 +389,13 @@ const SessionMenuButton = new Lang.Class({
} }
} }
}); });
Signals.addSignalMethods(SessionMenuButton.prototype); Signals.addSignalMethods(SessionList.prototype);
const LoginDialog = new Lang.Class({ const LoginDialog = new Lang.Class({
Name: 'LoginDialog', Name: 'LoginDialog',
_init: function(parentActor) { _init: function(parentActor) {
this.actor = new St.Widget({ accessible_role: Atk.Role.WINDOW, this.actor = new St.Widget({ accessible_role: Atk.Role.WINDOW,
layout_manager: new Clutter.BinLayout(),
style_class: 'login-dialog', style_class: 'login-dialog',
visible: false }); visible: false });
@ -371,10 +404,10 @@ const LoginDialog = new Lang.Class({
parentActor.add_child(this.actor); parentActor.add_child(this.actor);
this._userManager = AccountsService.UserManager.get_default() this._userManager = AccountsService.UserManager.get_default()
let gdmClient = new Gdm.Client(); this._greeterClient = new Gdm.Client();
if (GLib.getenv('GDM_GREETER_TEST') != '1') { if (GLib.getenv('GDM_GREETER_TEST') != '1') {
this._greeter = gdmClient.get_greeter_sync(null); this._greeter = this._greeterClient.get_greeter_sync(null);
this._greeter.connect('default-session-name-changed', this._greeter.connect('default-session-name-changed',
Lang.bind(this, this._onDefaultSessionChanged)); Lang.bind(this, this._onDefaultSessionChanged));
@ -385,6 +418,15 @@ const LoginDialog = new Lang.Class({
Lang.bind(this, this._onTimedLoginRequested)); Lang.bind(this, this._onTimedLoginRequested));
} }
this._userVerifier = new GdmUtil.ShellUserVerifier(this._greeterClient);
this._userVerifier.connect('ask-question', Lang.bind(this, this._askQuestion));
this._userVerifier.connect('show-message', Lang.bind(this, this._showMessage));
this._userVerifier.connect('verification-failed', Lang.bind(this, this._verificationFailed));
this._userVerifier.connect('reset', Lang.bind(this, this._reset));
this._userVerifier.connect('show-login-hint', Lang.bind(this, this._showLoginHint));
this._userVerifier.connect('hide-login-hint', Lang.bind(this, this._hideLoginHint));
this._verifyingUser = false;
this._settings = new Gio.Settings({ schema: GdmUtil.LOGIN_SCREEN_SCHEMA }); this._settings = new Gio.Settings({ schema: GdmUtil.LOGIN_SCREEN_SCHEMA });
this._settings.connect('changed::' + GdmUtil.BANNER_MESSAGE_KEY, this._settings.connect('changed::' + GdmUtil.BANNER_MESSAGE_KEY,
@ -401,12 +443,10 @@ const LoginDialog = new Lang.Class({
Lang.bind(this, this._updateLogoTexture)); Lang.bind(this, this._updateLogoTexture));
this._userSelectionBox = new St.BoxLayout({ style_class: 'login-dialog-user-selection-box', this._userSelectionBox = new St.BoxLayout({ style_class: 'login-dialog-user-selection-box',
x_align: Clutter.ActorAlign.CENTER, vertical: true });
y_align: Clutter.ActorAlign.CENTER, this._userSelectionBox.add_constraint(new Clutter.AlignConstraint({ source: this.actor,
x_expand: true, align_axis: Clutter.AlignAxis.BOTH,
y_expand: true, factor: 0.5 }));
vertical: true,
visible: false });
this.actor.add_child(this._userSelectionBox); this.actor.add_child(this._userSelectionBox);
this._bannerLabel = new St.Label({ style_class: 'login-dialog-banner', this._bannerLabel = new St.Label({ style_class: 'login-dialog-banner',
@ -420,11 +460,61 @@ const LoginDialog = new Lang.Class({
x_fill: true, x_fill: true,
y_fill: true }); y_fill: true });
this._authPrompt = new AuthPrompt.AuthPrompt(gdmClient, AuthPrompt.AuthPromptMode.UNLOCK_OR_LOG_IN); this._promptBox = new St.BoxLayout({ style_class: 'login-dialog-prompt-layout',
this._authPrompt.connect('prompted', Lang.bind(this, this._onPrompted)); vertical: true });
this._authPrompt.connect('reset', Lang.bind(this, this._onReset));
this._authPrompt.hide(); this._promptBox.connect('button-press-event',
this.actor.add_child(this._authPrompt.actor); Lang.bind(this, function(actor, event) {
if (event.get_key_symbol() == Clutter.KEY_Escape) {
this.cancel();
}
}));
this._promptBox.add_constraint(new Clutter.AlignConstraint({ source: this.actor,
align_axis: Clutter.AlignAxis.BOTH,
factor: 0.5 }));
this.actor.add_child(this._promptBox);
this._promptUser = new St.Bin({ x_fill: true,
x_align: St.Align.START });
this._promptBox.add(this._promptUser,
{ x_align: St.Align.START,
x_fill: true,
y_fill: true,
expand: true });
this._promptLabel = new St.Label({ style_class: 'login-dialog-prompt-label' });
this._promptBox.add(this._promptLabel,
{ expand: true,
x_fill: true,
y_fill: true,
x_align: St.Align.START });
this._promptEntry = new St.Entry({ style_class: 'login-dialog-prompt-entry',
can_focus: true });
this._promptEntryTextChangedId = 0;
this._promptEntryActivateId = 0;
this._promptBox.add(this._promptEntry,
{ expand: true,
x_fill: true,
y_fill: false,
x_align: St.Align.START });
this._promptMessage = new St.Label({ visible: false });
this._promptBox.add(this._promptMessage, { x_fill: true });
this._promptLoginHint = new St.Label({ style_class: 'login-dialog-prompt-login-hint-message' });
this._promptLoginHint.hide();
this._promptBox.add(this._promptLoginHint);
this._buttonBox = new St.BoxLayout({ style_class: 'modal-dialog-button-box',
vertical: false });
this._promptBox.add(this._buttonBox,
{ expand: true,
x_align: St.Align.MIDDLE,
y_align: St.Align.END });
this._cancelButton = null;
this._signInButton = null;
this._promptBox.hide();
// translators: this message is shown below the user list on the // translators: this message is shown below the user list on the
// login screen. It can be activated to reveal an entry for // login screen. It can be activated to reveal an entry for
@ -439,20 +529,21 @@ const LoginDialog = new Lang.Class({
x_align: St.Align.START, x_align: St.Align.START,
x_fill: true }); x_fill: true });
this._notListedButton.connect('clicked', Lang.bind(this, this._hideUserListAskForUsernameAndBeginVerification)); this._notListedButton.connect('clicked', Lang.bind(this, this._hideUserListAndLogIn));
this._notListedButton.hide();
this._userSelectionBox.add(this._notListedButton, this._userSelectionBox.add(this._notListedButton,
{ expand: false, { expand: false,
x_align: St.Align.START, x_align: St.Align.START,
x_fill: true }); x_fill: true });
this._logoBin = new St.Widget({ style_class: 'login-dialog-logo-bin', this._logoBin = new St.Bin({ style_class: 'login-dialog-logo-bin', y_expand: true });
x_align: Clutter.ActorAlign.CENTER, this._logoBin.set_y_align(Clutter.ActorAlign.END);
y_align: Clutter.ActorAlign.END, this._logoBin.add_constraint(new Clutter.AlignConstraint({ source: this.actor,
x_expand: true, align_axis: Clutter.AlignAxis.X_AXIS,
y_expand: true }); factor: 0.5 }));
this._logoBin.add_constraint(new Clutter.AlignConstraint({ source: this.actor,
align_axis: Clutter.AlignAxis.Y_AXIS,
factor: 1.0 }));
this.actor.add_child(this._logoBin); this.actor.add_child(this._logoBin);
this._updateLogo(); this._updateLogo();
@ -473,42 +564,43 @@ const LoginDialog = new Lang.Class({
this._onUserListActivated(item); this._onUserListActivated(item);
})); }));
this._defaultButtonWell = new St.Widget();
this._sessionMenuButton = new SessionMenuButton(); this._sessionList = new SessionList();
this._sessionMenuButton.connect('session-activated', this._sessionList.connect('session-activated',
Lang.bind(this, function(list, sessionId) { Lang.bind(this, function(list, sessionId) {
this._greeter.call_select_session_sync (sessionId, null); this._greeter.call_select_session_sync (sessionId, null);
})); }));
this._sessionMenuButton.actor.opacity = 0; this._sessionList.actor.opacity = 0;
this._sessionMenuButton.actor.show(); this._sessionList.actor.show();
this._authPrompt.addActorToDefaultButtonWell(this._sessionMenuButton.actor); this._defaultButtonWell.add_child(this._sessionList.actor);
let spinnerIcon = global.datadir + '/theme/process-working.svg';
this._workSpinner = new Animation.AnimatedIcon(spinnerIcon, _WORK_SPINNER_ICON_SIZE);
this._workSpinner.actor.opacity = 0;
this._workSpinner.actor.show();
this._defaultButtonWell.add_child(this._workSpinner.actor);
this._sessionList.actor.add_constraint(new Clutter.AlignConstraint({ source: this._workSpinner.actor,
align_axis: Clutter.AlignAxis.BOTH,
factor: 0.5 }));
}, },
_updateDisableUserList: function() { _updateDisableUserList: function() {
let disableUserList = this._settings.get_boolean(GdmUtil.DISABLE_USER_LIST_KEY); let disableUserList = this._settings.get_boolean(GdmUtil.DISABLE_USER_LIST_KEY);
// If this is the first time around, set initial focus
if (this._disableUserList == undefined && disableUserList)
this.setInitialKeyFocus(this._promptEntry);
if (disableUserList != this._disableUserList) { if (disableUserList != this._disableUserList) {
this._disableUserList = disableUserList; this._disableUserList = disableUserList;
if (this._authPrompt.verificationStatus == AuthPrompt.AuthPromptStatus.NOT_VERIFYING) if (!this._verifyingUser)
this._authPrompt.reset(); this._reset();
} }
}, },
_updateCancelButton: function() {
let cancelVisible;
// Hide the cancel button if the user list is disabled and we're asking for
// a username
if (this._authPrompt.verificationStatus == AuthPrompt.AuthPromptStatus.NOT_VERIFYING && this._disableUserList)
cancelVisible = false;
else
cancelVisible = true;
this._authPrompt.cancelButton.visible = cancelVisible;
},
_updateBanner: function() { _updateBanner: function() {
let enabled = this._settings.get_boolean(GdmUtil.BANNER_MESSAGE_KEY); let enabled = this._settings.get_boolean(GdmUtil.BANNER_MESSAGE_KEY);
let text = this._settings.get_string(GdmUtil.BANNER_MESSAGE_TEXT_KEY); let text = this._settings.get_string(GdmUtil.BANNER_MESSAGE_TEXT_KEY);
@ -525,10 +617,11 @@ const LoginDialog = new Lang.Class({
if (this._logoFileUri != uri) if (this._logoFileUri != uri)
return; return;
this._logoBin.destroy_all_children(); let icon = null;
if (this._logoFileUri) if (this._logoFileUri)
this._logoBin.add_child(this._textureCache.load_uri_async(this._logoFileUri, icon = this._textureCache.load_uri_async(this._logoFileUri,
-1, _LOGO_ICON_HEIGHT)); -1, _LOGO_ICON_HEIGHT);
this._logoBin.set_child(icon);
}, },
_updateLogo: function() { _updateLogo: function() {
@ -538,53 +631,267 @@ const LoginDialog = new Lang.Class({
this._updateLogoTexture(this._textureCache, this._logoFileUri); this._updateLogoTexture(this._textureCache, this._logoFileUri);
}, },
_onPrompted: function() { _reset: function() {
this._sessionMenuButton.updateSensitivity(true); this._userVerifier.clear();
if (this._shouldShowSessionMenuButton()) this._updateSensitivity(true);
this._authPrompt.setActorInDefaultButtonWell(this._sessionMenuButton.actor); this._promptMessage.hide();
this._showPrompt(); this._user = null;
this._verifyingUser = false;
if (this._disableUserList)
this._hideUserListAndLogIn();
else
this._showUserList();
}, },
_onReset: function(authPrompt, beginRequest) { _setWorking: function(working) {
this._sessionMenuButton.updateSensitivity(true); if (!this._workSpinner)
return;
this._user = null; Tweener.removeTweens(this._workSpinner.actor);
if (working) {
if (this._sessionList.actor.opacity > 0)
Tweener.addTween(this._sessionList.actor,
{ opacity: 0,
delay: _WORK_SPINNER_ANIMATION_DELAY,
time: _WORK_SPINNER_ANIMATION_TIME,
transition: 'linear'
});
if (beginRequest == AuthPrompt.BeginRequestType.PROVIDE_USERNAME) { this._workSpinner.play();
if (!this._disableUserList) Tweener.addTween(this._workSpinner.actor,
this._showUserList(); { opacity: 255,
else delay: _WORK_SPINNER_ANIMATION_DELAY,
this._hideUserListAskForUsernameAndBeginVerification(); time: _WORK_SPINNER_ANIMATION_TIME,
transition: 'linear'
});
} else { } else {
this._hideUserListAndBeginVerification(); if (this._sessionList.actor.opacity == 0 && this._shouldShowSessionList())
Tweener.addTween(this._sessionList.actor,
{ opacity: 255,
delay: _WORK_SPINNER_ANIMATION_DELAY,
time: _WORK_SPINNER_ANIMATION_TIME,
transition: 'linear'
});
Tweener.addTween(this._workSpinner.actor,
{ opacity: 0,
time: _WORK_SPINNER_ANIMATION_TIME,
transition: 'linear',
onCompleteScope: this,
onComplete: function() {
if (this._workSpinner)
this._workSpinner.stop();
}
});
} }
}, },
_onDefaultSessionChanged: function(client, sessionId) { _verificationFailed: function() {
this._sessionMenuButton.setActiveSession(sessionId); this._promptEntry.text = '';
this._updateSensitivity(true);
this._setWorking(false);
}, },
_shouldShowSessionMenuButton: function() { _onDefaultSessionChanged: function(client, sessionId) {
if (this._authPrompt.verificationStatus != AuthPrompt.AuthPromptStatus.VERIFYING && this._sessionList.setActiveSession(sessionId);
this._authPrompt.verificationStatus != AuthPrompt.AuthPromptStatus.VERIFICATION_FAILED) },
_showMessage: function(userVerifier, message, styleClass) {
if (message) {
this._promptMessage.text = message;
this._promptMessage.styleClass = styleClass;
this._promptMessage.show();
} else {
this._promptMessage.hide();
}
},
_showLoginHint: function(verifier, message) {
this._promptLoginHint.set_text(message)
this._promptLoginHint.show();
this._promptLoginHint.opacity = 255;
},
_hideLoginHint: function() {
this._promptLoginHint.hide();
this._promptLoginHint.set_text('');
},
cancel: function() {
if (this._verifyingUser)
this._userVerifier.cancel();
else
this._reset();
},
_shouldShowSessionList: function() {
if (this._verifyingUser)
return true;
if (!this._user)
return false; return false;
if (this._user && this._user.is_loaded && this._user.is_logged_in()) if (this._user.is_logged_in)
return false; return false;
return true; return true;
}, },
_showPrompt: function() { _showPrompt: function(forSecret) {
if (this._authPrompt.actor.visible) this._promptLabel.show();
return; this._promptEntry.show();
this._authPrompt.actor.opacity = 0; this._promptLoginHint.opacity = 0;
this._authPrompt.actor.show(); this._promptLoginHint.show();
Tweener.addTween(this._authPrompt.actor, this._promptBox.opacity = 0;
this._promptBox.show();
Tweener.addTween(this._promptBox,
{ opacity: 255, { opacity: 255,
time: _FADE_ANIMATION_TIME, time: _FADE_ANIMATION_TIME,
transition: 'easeOutQuad' }); transition: 'easeOutQuad' });
if (this._shouldShowSessionList()) {
this._sessionList.actor.opacity = 255;
} else {
this._sessionList.actor.opacity = 0;
}
this._promptEntry.grab_key_focus();
let hold = new Batch.Hold();
let tasks = [function() {
this._prepareDialog(forSecret, hold);
},
hold];
let batch = new Batch.ConcurrentBatch(this, tasks);
return batch.run();
},
_prepareDialog: function(forSecret, hold) {
this._buttonBox.visible = true;
this._buttonBox.remove_all_children();
if (!this._disableUserList || this._verifyingUser) {
this._cancelButton = new St.Button({ style_class: 'modal-dialog-button',
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
reactive: true,
can_focus: true,
label: _("Cancel") });
this._cancelButton.connect('clicked',
Lang.bind(this, function() {
this.cancel();
}));
this._buttonBox.add(this._cancelButton,
{ expand: false,
x_fill: false,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.END });
}
this._buttonBox.add(this._defaultButtonWell,
{ expand: true,
x_fill: false,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.MIDDLE });
this._signInButton = new St.Button({ style_class: 'modal-dialog-button',
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
reactive: true,
can_focus: true,
label: forSecret ? C_("button", "Sign In") : _("Next") });
this._signInButton.connect('clicked',
Lang.bind(this, function() {
hold.release();
}));
this._signInButton.add_style_pseudo_class('default');
this._buttonBox.add(this._signInButton,
{ expand: false,
x_fill: false,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.END });
this._updateSignInButtonSensitivity(this._promptEntry.text.length > 0);
this._promptEntryTextChangedId =
this._promptEntry.clutter_text.connect('text-changed',
Lang.bind(this, function() {
this._updateSignInButtonSensitivity(this._promptEntry.text.length > 0);
}));
this._promptEntryActivateId =
this._promptEntry.clutter_text.connect('activate', function() {
hold.release();
});
},
_updateSensitivity: function(sensitive) {
this._promptEntry.reactive = sensitive;
this._promptEntry.clutter_text.editable = sensitive;
this._sessionList.updateSensitivity(sensitive);
this._updateSignInButtonSensitivity(sensitive);
},
_updateSignInButtonSensitivity: function(sensitive) {
if (this._signInButton) {
this._signInButton.reactive = sensitive;
this._signInButton.can_focus = sensitive;
}
},
_hidePrompt: function() {
if (this._promptEntryTextChangedId > 0) {
this._promptEntry.clutter_text.disconnect(this._promptEntryTextChangedId);
this._promptEntryTextChangedId = 0;
}
if (this._promptEntryActivateId > 0) {
this._promptEntry.clutter_text.disconnect(this._promptEntryActivateId);
this._promptEntryActivateId = 0;
}
this._setWorking(false);
this._promptBox.hide();
this._promptLoginHint.hide();
this._promptUser.set_child(null);
this._updateSensitivity(true);
this._promptEntry.set_text('');
this._sessionList.close();
this._promptLoginHint.hide();
this._buttonBox.remove_all_children();
this._signInButton = null;
this._cancelButton = null;
},
_askQuestion: function(verifier, serviceName, question, passwordChar) {
this._promptLabel.set_text(question);
this._updateSensitivity(true);
this._promptEntry.set_text('');
this._promptEntry.clutter_text.set_password_char(passwordChar);
let tasks = [function() {
return this._showPrompt(!!passwordChar);
},
function() {
let text = this._promptEntry.get_text();
this._updateSensitivity(false);
this._setWorking(true);
this._userVerifier.answerQuery(serviceName, text);
}];
let batch = new Batch.ConsecutiveBatch(this, tasks);
return batch.run();
}, },
_showRealmLoginHint: function(realmManager, hint) { _showRealmLoginHint: function(realmManager, hint) {
@ -597,34 +904,34 @@ const LoginDialog = new Lang.Class({
// Translators: this message is shown below the username entry field // Translators: this message is shown below the username entry field
// to clue the user in on how to login to the local network realm // to clue the user in on how to login to the local network realm
this._authPrompt.setMessage(_("(e.g., user or %s)").format(hint), GdmUtil.MessageType.HINT); this._showLoginHint(null, _("(e.g., user or %s)").format(hint));
}, },
_askForUsernameAndBeginVerification: function() { _askForUsernameAndLogIn: function() {
this._authPrompt.setPasswordChar(''); this._promptLabel.set_text(_("Username: "));
this._authPrompt.setQuestion(_("Username: ")); this._promptEntry.set_text('');
this._promptEntry.clutter_text.set_password_char('');
let realmManager = new Realmd.Manager(); let realmManager = new Realmd.Manager();
let realmSignalId = realmManager.connect('login-format-changed', let signalId = realmManager.connect('login-format-changed',
Lang.bind(this, this._showRealmLoginHint)); Lang.bind(this, this._showRealmLoginHint));
this._showRealmLoginHint(realmManager.loginFormat); this._showRealmLoginHint(realmManager.loginFormat);
let nextSignalId = this._authPrompt.connect('next', let tasks = [this._showPrompt,
Lang.bind(this, function() {
this._authPrompt.disconnect(nextSignalId);
this._authPrompt.updateSensitivity(false);
let answer = this._authPrompt.getAnswer();
this._user = this._userManager.get_user(answer);
this._authPrompt.clear();
this._authPrompt.startSpinning();
this._authPrompt.begin({ userName: answer });
this._updateCancelButton();
realmManager.disconnect(realmSignalId) function() {
let userName = this._promptEntry.get_text();
this._promptEntry.reactive = false;
return this._beginVerificationForUser(userName);
},
function() {
realmManager.disconnect(signalId)
realmManager.release(); realmManager.release();
})); }];
this._updateCancelButton();
this._showPrompt(); let batch = new Batch.ConsecutiveBatch(this, tasks);
return batch.run();
}, },
_startSession: function(serviceName) { _startSession: function(serviceName) {
@ -644,16 +951,22 @@ const LoginDialog = new Lang.Class({
onComplete: function() { onComplete: function() {
Mainloop.idle_add(Lang.bind(this, function() { Mainloop.idle_add(Lang.bind(this, function() {
this._greeter.call_start_session_when_ready_sync(serviceName, true, null); this._greeter.call_start_session_when_ready_sync(serviceName, true, null);
return GLib.SOURCE_REMOVE; return false;
})); }));
}, },
onCompleteScope: this }); onCompleteScope: this });
}, },
_onSessionOpened: function(client, serviceName) { _onSessionOpened: function(client, serviceName) {
this._authPrompt.finish(Lang.bind(this, function() { if (!this._userVerifier.hasPendingMessages) {
this._startSession(serviceName);
} else {
let signalId = this._userVerifier.connect('no-more-messages',
Lang.bind(this, function() {
this._userVerifier.disconnect(signalId);
this._startSession(serviceName); this._startSession(serviceName);
})); }));
}
}, },
_waitForItemForUser: function(userName) { _waitForItemForUser: function(userName) {
@ -699,7 +1012,6 @@ const LoginDialog = new Lang.Class({
function() { function() {
this._timedLoginAnimationTime -= _TIMED_LOGIN_IDLE_THRESHOLD; this._timedLoginAnimationTime -= _TIMED_LOGIN_IDLE_THRESHOLD;
hold.release(); hold.release();
return GLib.SOURCE_REMOVE;
}); });
return hold; return hold;
}, },
@ -764,7 +1076,7 @@ const LoginDialog = new Lang.Class({
global.stage.connect('captured-event', global.stage.connect('captured-event',
Lang.bind(this, function(actor, event) { Lang.bind(this, function(actor, event) {
if (this._timedLoginDelay == undefined) if (this._timedLoginDelay == undefined)
return Clutter.EVENT_PROPAGATE; return false;
if (event.type() == Clutter.EventType.KEY_PRESS || if (event.type() == Clutter.EventType.KEY_PRESS ||
event.type() == Clutter.EventType.BUTTON_PRESS) { event.type() == Clutter.EventType.BUTTON_PRESS) {
@ -777,7 +1089,7 @@ const LoginDialog = new Lang.Class({
this._resetTimedLogin(); this._resetTimedLogin();
} }
return Clutter.EVENT_PROPAGATE; return false;
})); }));
}, },
@ -786,41 +1098,38 @@ const LoginDialog = new Lang.Class({
this._userSelectionBox.visible = expanded; this._userSelectionBox.visible = expanded;
}, },
_hideUserList: function() { _hideUserListAndLogIn: function() {
this._setUserListExpanded(false); this._setUserListExpanded(false);
if (this._userSelectionBox.visible)
GdmUtil.cloneAndFadeOutActor(this._userSelectionBox); GdmUtil.cloneAndFadeOutActor(this._userSelectionBox);
}, this._askForUsernameAndLogIn();
_hideUserListAskForUsernameAndBeginVerification: function() {
this._hideUserList();
this._askForUsernameAndBeginVerification();
},
_hideUserListAndBeginVerification: function() {
this._hideUserList();
this._authPrompt.begin();
}, },
_showUserList: function() { _showUserList: function() {
this._authPrompt.hide(); this._hidePrompt();
this._sessionMenuButton.close();
this._setUserListExpanded(true); this._setUserListExpanded(true);
this._notListedButton.show();
this._userList.actor.grab_key_focus(); this._userList.actor.grab_key_focus();
}, },
_beginVerificationForItem: function(item) { _beginVerificationForUser: function(userName) {
this._authPrompt.setUser(item.user);
let userName = item.user.get_user_name();
let hold = new Batch.Hold(); let hold = new Batch.Hold();
this._authPrompt.begin({ userName: userName, this._userVerifier.begin(userName, hold);
hold: hold }); this._verifyingUser = true;
return hold; return hold;
}, },
_beginVerificationForItem: function(item) {
let userWidget = new UserWidget.UserWidget(item.user);
this._promptUser.set_child(userWidget.actor);
let tasks = [function() {
let userName = item.user.get_user_name();
return this._beginVerificationForUser(userName);
}];
let batch = new Batch.ConsecutiveBatch(this, tasks);
return batch.run();
},
_onUserListActivated: function(activatedItem) { _onUserListActivated: function(activatedItem) {
let tasks = [function() { let tasks = [function() {
return GdmUtil.cloneAndFadeOutActor(this._userSelectionBox); return GdmUtil.cloneAndFadeOutActor(this._userSelectionBox);
@ -831,8 +1140,6 @@ const LoginDialog = new Lang.Class({
this._user = activatedItem.user; this._user = activatedItem.user;
this._updateCancelButton();
let batch = new Batch.ConcurrentBatch(this, [new Batch.ConsecutiveBatch(this, tasks), let batch = new Batch.ConcurrentBatch(this, [new Batch.ConsecutiveBatch(this, tasks),
this._beginVerificationForItem(activatedItem)]); this._beginVerificationForItem(activatedItem)]);
batch.run(); batch.run();
@ -872,12 +1179,6 @@ const LoginDialog = new Lang.Class({
{ sortGroup: CtrlAltTab.SortGroup.MIDDLE }); { sortGroup: CtrlAltTab.SortGroup.MIDDLE });
this._userList.actor.grab_key_focus(); this._userList.actor.grab_key_focus();
this.actor.show(); this.actor.show();
this.actor.opacity = 0;
Tweener.addTween(this.actor,
{ opacity: 255,
time: 1,
transition: 'easeInQuad' });
return true; return true;
}, },
@ -886,16 +1187,8 @@ const LoginDialog = new Lang.Class({
Main.ctrlAltTabManager.removeGroup(this.dialogLayout); Main.ctrlAltTabManager.removeGroup(this.dialogLayout);
}, },
cancel: function() {
this._authPrompt.cancel();
},
addCharacter: function(unichar) { addCharacter: function(unichar) {
this._authPrompt.addCharacter(unichar); this._promptEntry.clutter_text.insert_unichar(unichar);
},
finish: function(onComplete) {
this._authPrompt.finish(onComplete);
}, },
}); });
Signals.addSignalMethods(LoginDialog.prototype); Signals.addSignalMethods(LoginDialog.prototype);

View File

@ -1,64 +0,0 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Signals = imports.signals;
const OVirtCredentialsIface = '<node> \
<interface name="org.ovirt.vdsm.Credentials"> \
<signal name="UserAuthenticated"> \
<arg type="s" name="token"/> \
</signal> \
</interface> \
</node>';
const OVirtCredentialsInfo = Gio.DBusInterfaceInfo.new_for_xml(OVirtCredentialsIface);
let _oVirtCredentialsManager = null;
function OVirtCredentials() {
var self = new Gio.DBusProxy({ g_connection: Gio.DBus.system,
g_interface_name: OVirtCredentialsInfo.name,
g_interface_info: OVirtCredentialsInfo,
g_name: 'org.ovirt.vdsm.Credentials',
g_object_path: '/org/ovirt/vdsm/Credentials',
g_flags: (Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
self.init(null);
return self;
}
const OVirtCredentialsManager = new Lang.Class({
Name: 'OVirtCredentialsManager',
_init: function() {
this._token = null;
this._credentials = new OVirtCredentials();
this._credentials.connectSignal('UserAuthenticated',
Lang.bind(this, this._onUserAuthenticated));
},
_onUserAuthenticated: function(proxy, sender, [token]) {
this._token = token;
this.emit('user-authenticated', token);
},
hasToken: function() {
return this._token != null;
},
getToken: function() {
return this._token;
},
resetToken: function() {
this._token = null;
}
});
Signals.addSignalMethods(OVirtCredentialsManager.prototype);
function getOVirtCredentialsManager() {
if (!_oVirtCredentialsManager)
_oVirtCredentialsManager = new OVirtCredentialsManager();
return _oVirtCredentialsManager;
}

129
js/gdm/powerMenu.js Normal file
View File

@ -0,0 +1,129 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/*
* Copyright 2011 Red Hat, Inc
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const LoginManager = imports.misc.loginManager;
const GdmUtil = imports.gdm.util;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const PowerMenuButton = new Lang.Class({
Name: 'PowerMenuButton',
Extends: PanelMenu.SystemStatusButton,
_init: function() {
/* Translators: accessible name of the power menu in the login screen */
this.parent('system-shutdown-symbolic', _("Power"));
this._loginManager = LoginManager.getLoginManager();
this._settings = new Gio.Settings({ schema: GdmUtil.LOGIN_SCREEN_SCHEMA });
this._settings.connect('changed::disable-restart-buttons',
Lang.bind(this, this._updateVisibility));
this._createSubMenu();
// ConsoleKit doesn't send notifications when shutdown/reboot
// are disabled, so we update the menu item each time the menu opens
this.menu.connect('open-state-changed', Lang.bind(this,
function(menu, open) {
if (open) {
this._updateHaveShutdown();
this._updateHaveRestart();
this._updateHaveSuspend();
}
}));
this._updateHaveShutdown();
this._updateHaveRestart();
this._updateHaveSuspend();
},
_updateVisibility: function() {
let shouldBeVisible = (this._haveSuspend || this._haveShutdown || this._haveRestart);
this.actor.visible = shouldBeVisible && !this._settings.get_boolean('disable-restart-buttons');
},
_updateHaveShutdown: function() {
this._loginManager.canPowerOff(Lang.bind(this, function(result) {
this._haveShutdown = result;
this._powerOffItem.actor.visible = this._haveShutdown;
this._updateVisibility();
}));
},
_updateHaveRestart: function() {
this._loginManager.canReboot(Lang.bind(this, function(result) {
this._haveRestart = result;
this._restartItem.actor.visible = this._haveRestart;
this._updateVisibility();
}));
},
_updateHaveSuspend: function() {
this._loginManager.canSuspend(Lang.bind(this, function(result) {
this._haveSuspend = result;
this._suspendItem.actor.visible = this._haveSuspend;
this._updateVisibility();
}));
},
_createSubMenu: function() {
let item;
item = new PopupMenu.PopupMenuItem(_("Suspend"));
item.connect('activate', Lang.bind(this, this._onActivateSuspend));
this.menu.addMenuItem(item);
this._suspendItem = item;
item = new PopupMenu.PopupMenuItem(_("Restart"));
item.connect('activate', Lang.bind(this, this._onActivateRestart));
this.menu.addMenuItem(item);
this._restartItem = item;
item = new PopupMenu.PopupMenuItem(_("Power Off"));
item.connect('activate', Lang.bind(this, this._onActivatePowerOff));
this.menu.addMenuItem(item);
this._powerOffItem = item;
},
_onActivateSuspend: function() {
if (!this._haveSuspend)
return;
this._loginManager.suspend();
},
_onActivateRestart: function() {
if (!this._haveRestart)
return;
this._loginManager.reboot();
},
_onActivatePowerOff: function() {
if (!this._haveShutdown)
return;
this._loginManager.powerOff();
}
});

View File

@ -5,58 +5,52 @@ const Lang = imports.lang;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const Signals = imports.signals; const Signals = imports.signals;
const ProviderIface = '<node> \ const ProviderIface = <interface name='org.freedesktop.realmd.Provider'>
<interface name="org.freedesktop.realmd.Provider"> \ <property name="Name" type="s" access="read"/>
<property name="Name" type="s" access="read"/> \ <property name="Version" type="s" access="read"/>
<property name="Version" type="s" access="read"/> \ <property name="Realms" type="ao" access="read"/>
<property name="Realms" type="ao" access="read"/> \ <method name="Discover">
<method name="Discover"> \ <arg name="string" type="s" direction="in"/>
<arg name="string" type="s" direction="in"/> \ <arg name="options" type="a{sv}" direction="in"/>
<arg name="options" type="a{sv}" direction="in"/> \ <arg name="relevance" type="i" direction="out"/>
<arg name="relevance" type="i" direction="out"/> \ <arg name="realm" type="ao" direction="out"/>
<arg name="realm" type="ao" direction="out"/> \ </method>
</method> \ </interface>;
</interface> \
</node>';
const Provider = Gio.DBusProxy.makeProxyWrapper(ProviderIface); const Provider = Gio.DBusProxy.makeProxyWrapper(ProviderIface);
const ServiceIface = '<node> \ const ServiceIface = <interface name="org.freedesktop.realmd.Service">
<interface name="org.freedesktop.realmd.Service"> \ <method name="Cancel">
<method name="Cancel"> \ <arg name="operation" type="s" direction="in"/>
<arg name="operation" type="s" direction="in"/> \ </method>
</method> \ <method name="Release" />
<method name="Release" /> \ <method name="SetLocale">
<method name="SetLocale"> \ <arg name="locale" type="s" direction="in"/>
<arg name="locale" type="s" direction="in"/> \ </method>
</method> \ <signal name="Diagnostics">
<signal name="Diagnostics"> \ <arg name="data" type="s"/>
<arg name="data" type="s"/> \ <arg name="operation" type="s"/>
<arg name="operation" type="s"/> \ </signal>
</signal> \ </interface>;
</interface> \
</node>';
const Service = Gio.DBusProxy.makeProxyWrapper(ServiceIface); const Service = Gio.DBusProxy.makeProxyWrapper(ServiceIface);
const RealmIface = '<node> \ const RealmIface = <interface name="org.freedesktop.realmd.Realm">
<interface name="org.freedesktop.realmd.Realm"> \ <property name="Name" type="s" access="read"/>
<property name="Name" type="s" access="read"/> \ <property name="Configured" type="s" access="read"/>
<property name="Configured" type="s" access="read"/> \ <property name="Details" type="a(ss)" access="read"/>
<property name="Details" type="a(ss)" access="read"/> \ <property name="LoginFormats" type="as" access="read"/>
<property name="LoginFormats" type="as" access="read"/> \ <property name="LoginPolicy" type="s" access="read"/>
<property name="LoginPolicy" type="s" access="read"/> \ <property name="PermittedLogins" type="as" access="read"/>
<property name="PermittedLogins" type="as" access="read"/> \ <property name="SupportedInterfaces" type="as" access="read"/>
<property name="SupportedInterfaces" type="as" access="read"/> \ <method name="ChangeLoginPolicy">
<method name="ChangeLoginPolicy"> \ <arg name="login_policy" type="s" direction="in"/>
<arg name="login_policy" type="s" direction="in"/> \ <arg name="permitted_add" type="as" direction="in"/>
<arg name="permitted_add" type="as" direction="in"/> \ <arg name="permitted_remove" type="as" direction="in"/>
<arg name="permitted_remove" type="as" direction="in"/> \ <arg name="options" type="a{sv}" direction="in"/>
<arg name="options" type="a{sv}" direction="in"/> \ </method>
</method> \ <method name="Deconfigure">
<method name="Deconfigure"> \ <arg name="options" type="a{sv}" direction="in"/>
<arg name="options" type="a{sv}" direction="in"/> \ </method>
</method> \ </interface>;
</interface> \
</node>';
const Realm = Gio.DBusProxy.makeProxyWrapper(RealmIface); const Realm = Gio.DBusProxy.makeProxyWrapper(RealmIface);
const Manager = new Lang.Class({ const Manager = new Lang.Class({

View File

@ -6,28 +6,20 @@ const GLib = imports.gi.GLib;
const Lang = imports.lang; const Lang = imports.lang;
const Mainloop = imports.mainloop; const Mainloop = imports.mainloop;
const Signals = imports.signals; const Signals = imports.signals;
const St = imports.gi.St;
const Batch = imports.gdm.batch; const Batch = imports.gdm.batch;
const Fprint = imports.gdm.fingerprint; const Fprint = imports.gdm.fingerprint;
const OVirt = imports.gdm.oVirt;
const Main = imports.ui.main; const Main = imports.ui.main;
const Params = imports.misc.params; const Params = imports.misc.params;
const ShellEntry = imports.ui.shellEntry;
const SmartcardManager = imports.misc.smartcardManager;
const Tweener = imports.ui.tweener; const Tweener = imports.ui.tweener;
const PASSWORD_SERVICE_NAME = 'gdm-password'; const PASSWORD_SERVICE_NAME = 'gdm-password';
const FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint'; const FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint';
const SMARTCARD_SERVICE_NAME = 'gdm-smartcard';
const OVIRT_SERVICE_NAME = 'gdm-ovirtcred';
const FADE_ANIMATION_TIME = 0.16; const FADE_ANIMATION_TIME = 0.16;
const CLONE_FADE_ANIMATION_TIME = 0.25; const CLONE_FADE_ANIMATION_TIME = 0.25;
const LOGIN_SCREEN_SCHEMA = 'org.gnome.login-screen'; const LOGIN_SCREEN_SCHEMA = 'org.gnome.login-screen';
const PASSWORD_AUTHENTICATION_KEY = 'enable-password-authentication';
const FINGERPRINT_AUTHENTICATION_KEY = 'enable-fingerprint-authentication'; const FINGERPRINT_AUTHENTICATION_KEY = 'enable-fingerprint-authentication';
const SMARTCARD_AUTHENTICATION_KEY = 'enable-smartcard-authentication';
const BANNER_MESSAGE_KEY = 'banner-message-enable'; const BANNER_MESSAGE_KEY = 'banner-message-enable';
const BANNER_MESSAGE_TEXT_KEY = 'banner-message-text'; const BANNER_MESSAGE_TEXT_KEY = 'banner-message-text';
const ALLOWED_FAILURES_KEY = 'allowed-failures'; const ALLOWED_FAILURES_KEY = 'allowed-failures';
@ -38,13 +30,6 @@ const DISABLE_USER_LIST_KEY = 'disable-user-list';
// Give user 16ms to read each character of a PAM message // Give user 16ms to read each character of a PAM message
const USER_READ_TIME = 16 const USER_READ_TIME = 16
const MessageType = {
NONE: 0,
ERROR: 1,
INFO: 2,
HINT: 3
};
function fadeInActor(actor) { function fadeInActor(actor) {
if (actor.opacity == 255 && actor.visible) if (actor.opacity == 255 && actor.visible)
return null; return null;
@ -129,45 +114,19 @@ const ShellUserVerifier = new Lang.Class({
this._client = client; this._client = client;
this._settings = new Gio.Settings({ schema: LOGIN_SCREEN_SCHEMA }); this._settings = new Gio.Settings({ schema: LOGIN_SCREEN_SCHEMA });
this._settings.connect('changed',
Lang.bind(this, this._updateDefaultService));
this._updateDefaultService();
this._fprintManager = new Fprint.FprintManager(); this._fprintManager = new Fprint.FprintManager();
this._smartcardManager = SmartcardManager.getSmartcardManager();
// We check for smartcards right away, since an inserted smartcard
// at startup should result in immediately initiating authentication.
// This is different than fingeprint readers, where we only check them
// after a user has been picked.
this._checkForSmartcard();
this._smartcardManager.connect('smartcard-inserted',
Lang.bind(this, this._checkForSmartcard));
this._smartcardManager.connect('smartcard-removed',
Lang.bind(this, this._checkForSmartcard));
this._messageQueue = []; this._messageQueue = [];
this._messageQueueTimeoutId = 0; this._messageQueueTimeoutId = 0;
this.hasPendingMessages = false; this.hasPendingMessages = false;
this.reauthenticating = false;
this._failCounter = 0; this._failCounter = 0;
this._oVirtCredentialsManager = OVirt.getOVirtCredentialsManager();
if (this._oVirtCredentialsManager.hasToken())
this._oVirtUserAuthenticated(this._oVirtCredentialsManager.getToken());
this._oVirtCredentialsManager.connect('user-authenticated',
Lang.bind(this, this._oVirtUserAuthenticated));
}, },
begin: function(userName, hold) { begin: function(userName, hold) {
this._cancellable = new Gio.Cancellable(); this._cancellable = new Gio.Cancellable();
this._hold = hold; this._hold = hold;
this._userName = userName; this._userName = userName;
this.reauthenticating = false;
this._checkForFingerprintReader(); this._checkForFingerprintReader();
@ -185,10 +144,8 @@ const ShellUserVerifier = new Lang.Class({
if (this._cancellable) if (this._cancellable)
this._cancellable.cancel(); this._cancellable.cancel();
if (this._userVerifier) { if (this._userVerifier)
this._userVerifier.call_cancel_sync(null); this._userVerifier.call_cancel_sync(null);
this.clear();
}
}, },
clear: function() { clear: function() {
@ -206,12 +163,13 @@ const ShellUserVerifier = new Lang.Class({
}, },
answerQuery: function(serviceName, answer) { answerQuery: function(serviceName, answer) {
if (!this.hasPendingMessages) { if (!this._userVerifier.hasPendingMessages) {
this._clearMessageQueue();
this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null); this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
} else { } else {
let signalId = this.connect('no-more-messages', let signalId = this._userVerifier.connect('no-more-messages',
Lang.bind(this, function() { Lang.bind(this, function() {
this.disconnect(signalId); this._userVerifier.disconnect(signalId);
this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null); this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
})); }));
} }
@ -242,23 +200,21 @@ const ShellUserVerifier = new Lang.Class({
return; return;
let message = this._messageQueue.shift(); let message = this._messageQueue.shift();
this.emit('show-message', message.text, message.iconName);
this.emit('show-message', message.text, message.type);
this._messageQueueTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, this._messageQueueTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
message.interval, message.interval,
Lang.bind(this, function() { Lang.bind(this, function() {
this._messageQueueTimeoutId = 0; this._messageQueueTimeoutId = 0;
this._queueMessageTimeout(); this._queueMessageTimeout();
return GLib.SOURCE_REMOVE;
})); }));
}, },
_queueMessage: function(message, messageType) { _queueMessage: function(message, iconName) {
let interval = this._getIntervalForMessage(message); let interval = this._getIntervalForMessage(message);
this.hasPendingMessages = true; this.hasPendingMessages = true;
this._messageQueue.push({ text: message, type: messageType, interval: interval }); this._messageQueue.push({ text: message, interval: interval, iconName: iconName });
this._queueMessageTimeout(); this._queueMessageTimeout();
}, },
@ -269,57 +225,27 @@ const ShellUserVerifier = new Lang.Class({
GLib.source_remove(this._messageQueueTimeoutId); GLib.source_remove(this._messageQueueTimeoutId);
this._messageQueueTimeoutId = 0; this._messageQueueTimeoutId = 0;
} }
this.emit('show-message', null, MessageType.NONE); this.emit('show-message', null, null);
}, },
_checkForFingerprintReader: function() { _checkForFingerprintReader: function() {
this._haveFingerprintReader = false; this._haveFingerprintReader = false;
if (!this._settings.get_boolean(FINGERPRINT_AUTHENTICATION_KEY)) { if (!this._settings.get_boolean(FINGERPRINT_AUTHENTICATION_KEY))
this._updateDefaultService();
return; return;
}
this._fprintManager.GetDefaultDeviceRemote(Gio.DBusCallFlags.NONE, this._cancellable, Lang.bind(this, this._fprintManager.GetDefaultDeviceRemote(Gio.DBusCallFlags.NONE, this._cancellable, Lang.bind(this,
function(device, error) { function(device, error) {
if (!error && device) if (!error && device)
this._haveFingerprintReader = true; this._haveFingerprintReader = true;
this._updateDefaultService();
})); }));
}, },
_oVirtUserAuthenticated: function(token) {
this._preemptingService = OVIRT_SERVICE_NAME;
this.emit('ovirt-user-authenticated');
},
_checkForSmartcard: function() {
let smartcardDetected;
if (!this._settings.get_boolean(SMARTCARD_AUTHENTICATION_KEY))
smartcardDetected = false;
else if (this.reauthenticating)
smartcardDetected = this._smartcardManager.hasInsertedLoginToken();
else
smartcardDetected = this._smartcardManager.hasInsertedTokens();
if (smartcardDetected != this.smartcardDetected) {
this.smartcardDetected = smartcardDetected;
if (this.smartcardDetected)
this._preemptingService = SMARTCARD_SERVICE_NAME;
else if (this._preemptingService == SMARTCARD_SERVICE_NAME)
this._preemptingService = null;
this.emit('smartcard-status-changed');
}
},
_reportInitError: function(where, error) { _reportInitError: function(where, error) {
logError(error, where); logError(error, where);
this._hold.release(); this._hold.release();
this._queueMessage(_("Authentication error"), MessageType.ERROR); this._queueMessage(_("Authentication error"), 'login-dialog-message-warning');
this._verificationFailed(false); this._verificationFailed(false);
}, },
@ -340,7 +266,6 @@ const ShellUserVerifier = new Lang.Class({
return; return;
} }
this.reauthenticating = true;
this._connectSignals(); this._connectSignals();
this._beginVerification(); this._beginVerification();
this._hold.release(); this._hold.release();
@ -371,34 +296,11 @@ const ShellUserVerifier = new Lang.Class({
this._userVerifier.connect('verification-complete', Lang.bind(this, this._onVerificationComplete)); this._userVerifier.connect('verification-complete', Lang.bind(this, this._onVerificationComplete));
}, },
_getForegroundService: function() { _beginVerification: function() {
if (this._preemptingService)
return this._preemptingService;
return this._defaultService;
},
serviceIsForeground: function(serviceName) {
return serviceName == this._getForegroundService();
},
serviceIsDefault: function(serviceName) {
return serviceName == this._defaultService;
},
_updateDefaultService: function() {
if (this._settings.get_boolean(PASSWORD_AUTHENTICATION_KEY))
this._defaultService = PASSWORD_SERVICE_NAME;
else if (this.smartcardDetected)
this._defaultService = SMARTCARD_SERVICE_NAME;
else if (this._haveFingerprintReader)
this._defaultService = FINGERPRINT_SERVICE_NAME;
},
_startService: function(serviceName) {
this._hold.acquire(); this._hold.acquire();
if (this._userName) { if (this._userName) {
this._userVerifier.call_begin_verification_for_user(serviceName, this._userVerifier.call_begin_verification_for_user(PASSWORD_SERVICE_NAME,
this._userName, this._userName,
this._cancellable, this._cancellable,
Lang.bind(this, function(obj, result) { Lang.bind(this, function(obj, result) {
@ -413,8 +315,28 @@ const ShellUserVerifier = new Lang.Class({
this._hold.release(); this._hold.release();
})); }));
if (this._haveFingerprintReader) {
this._hold.acquire();
this._userVerifier.call_begin_verification_for_user(FINGERPRINT_SERVICE_NAME,
this._userName,
this._cancellable,
Lang.bind(this, function(obj, result) {
try {
obj.call_begin_verification_for_user_finish(result);
} catch(e if e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
return;
} catch(e) {
this._reportInitError('Failed to start fingerprint verification for user', e);
return;
}
this._hold.release();
}));
}
} else { } else {
this._userVerifier.call_begin_verification(serviceName, this._userVerifier.call_begin_verification(PASSWORD_SERVICE_NAME,
this._cancellable, this._cancellable,
Lang.bind(this, function(obj, result) { Lang.bind(this, function(obj, result) {
try { try {
@ -431,59 +353,48 @@ const ShellUserVerifier = new Lang.Class({
} }
}, },
_beginVerification: function() {
this._startService(this._getForegroundService());
if (this._userName && this._haveFingerprintReader && !this.serviceIsForeground(FINGERPRINT_SERVICE_NAME))
this._startService(FINGERPRINT_SERVICE_NAME);
},
_onInfo: function(client, serviceName, info) { _onInfo: function(client, serviceName, info) {
if (this.serviceIsForeground(serviceName)) { // We don't display fingerprint messages, because they
this._queueMessage(info, MessageType.INFO); // have words like UPEK in them. Instead we use the messages
} else if (serviceName == FINGERPRINT_SERVICE_NAME &&
this._haveFingerprintReader) {
// We don't show fingerprint messages directly since it's
// not the main auth service. Instead we use the messages
// as a cue to display our own message. // as a cue to display our own message.
if (serviceName == FINGERPRINT_SERVICE_NAME &&
this._haveFingerprintReader) {
// Translators: this message is shown below the password entry field // Translators: this message is shown below the password entry field
// to indicate the user can swipe their finger instead // to indicate the user can swipe their finger instead
this._queueMessage(_("(or swipe finger)"), MessageType.HINT); this.emit('show-login-hint', _("(or swipe finger)"));
} else if (serviceName == PASSWORD_SERVICE_NAME) {
this._queueMessage(info, 'login-dialog-message-info');
} }
}, },
_onProblem: function(client, serviceName, problem) { _onProblem: function(client, serviceName, problem) {
if (!this.serviceIsForeground(serviceName)) // we don't want to show auth failed messages to
// users who haven't enrolled their fingerprint.
if (serviceName != PASSWORD_SERVICE_NAME)
return; return;
this._queueMessage(problem, 'login-dialog-message-warning');
this._queueMessage(problem, MessageType.ERROR);
}, },
_onInfoQuery: function(client, serviceName, question) { _onInfoQuery: function(client, serviceName, question) {
if (!this.serviceIsForeground(serviceName)) // We only expect questions to come from the main auth service
if (serviceName != PASSWORD_SERVICE_NAME)
return; return;
this.emit('ask-question', serviceName, question, ''); this.emit('ask-question', serviceName, question, '');
}, },
_onSecretInfoQuery: function(client, serviceName, secretQuestion) { _onSecretInfoQuery: function(client, serviceName, secretQuestion) {
if (!this.serviceIsForeground(serviceName)) // We only expect secret requests to come from the main auth service
if (serviceName != PASSWORD_SERVICE_NAME)
return; return;
if (serviceName == OVIRT_SERVICE_NAME) {
// The only question asked by this service is "Token?"
this.answerQuery(serviceName, this._oVirtCredentialsManager.getToken());
return;
}
this.emit('ask-question', serviceName, secretQuestion, '\u25cf'); this.emit('ask-question', serviceName, secretQuestion, '\u25cf');
}, },
_onReset: function() { _onReset: function() {
// Clear previous attempts to authenticate // Clear previous attempts to authenticate
this._failCounter = 0; this._failCounter = 0;
this._updateDefaultService();
this.emit('reset'); this.emit('reset');
}, },
@ -512,22 +423,22 @@ const ShellUserVerifier = new Lang.Class({
this._failCounter < this._settings.get_int(ALLOWED_FAILURES_KEY); this._failCounter < this._settings.get_int(ALLOWED_FAILURES_KEY);
if (canRetry) { if (canRetry) {
if (!this.hasPendingMessages) { if (!this._userVerifier.hasPendingMessages) {
this._retry(); this._retry();
} else { } else {
let signalId = this.connect('no-more-messages', let signalId = this._userVerifier.connect('no-more-messages',
Lang.bind(this, function() { Lang.bind(this, function() {
this.disconnect(signalId); this._userVerifier.disconnect(signalId);
this._retry(); this._retry();
})); }));
} }
} else { } else {
if (!this.hasPendingMessages) { if (!this._userVerifier.hasPendingMessages) {
this._cancelAndReset(); this._cancelAndReset();
} else { } else {
let signalId = this.connect('no-more-messages', let signalId = this._userVerifier.connect('no-more-messages',
Lang.bind(this, function() { Lang.bind(this, function() {
this.disconnect(signalId); this._userVerifier.disconnect(signalId);
this._cancelAndReset(); this._cancelAndReset();
})); }));
} }
@ -537,22 +448,14 @@ const ShellUserVerifier = new Lang.Class({
}, },
_onConversationStopped: function(client, serviceName) { _onConversationStopped: function(client, serviceName) {
// If the login failed with the preauthenticated oVirt credentials
// then discard the credentials and revert to default authentication
// mechanism.
if (this.serviceIsForeground(OVIRT_SERVICE_NAME)) {
this._oVirtCredentialsManager.resetToken();
this._preemptingService = null;
this._verificationFailed(false);
return;
}
// if the password service fails, then cancel everything. // if the password service fails, then cancel everything.
// But if, e.g., fingerprint fails, still give // But if, e.g., fingerprint fails, still give
// password authentication a chance to succeed // password authentication a chance to succeed
if (this.serviceIsForeground(serviceName)) { if (serviceName == PASSWORD_SERVICE_NAME) {
this._verificationFailed(true); this._verificationFailed(true);
} }
this.emit('hide-login-hint');
}, },
}); });
Signals.addSignalMethods(ShellUserVerifier.prototype); Signals.addSignalMethods(ShellUserVerifier.prototype);

View File

@ -1,114 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
<gresource prefix="/org/gnome/shell">
<file>gdm/authPrompt.js</file>
<file>gdm/batch.js</file>
<file>gdm/fingerprint.js</file>
<file>gdm/loginDialog.js</file>
<file>gdm/oVirt.js</file>
<file>gdm/realmd.js</file>
<file>gdm/util.js</file>
<file>extensionPrefs/main.js</file>
<file>misc/config.js</file>
<file>misc/extensionUtils.js</file>
<file>misc/fileUtils.js</file>
<file>misc/gnomeSession.js</file>
<file>misc/hash.js</file>
<file>misc/history.js</file>
<file>misc/jsParse.js</file>
<file>misc/loginManager.js</file>
<file>misc/modemManager.js</file>
<file>misc/objectManager.js</file>
<file>misc/params.js</file>
<file>misc/smartcardManager.js</file>
<file>misc/util.js</file>
<file>perf/core.js</file>
<file>ui/altTab.js</file>
<file>ui/animation.js</file>
<file>ui/appDisplay.js</file>
<file>ui/appFavorites.js</file>
<file>ui/backgroundMenu.js</file>
<file>ui/background.js</file>
<file>ui/boxpointer.js</file>
<file>ui/calendar.js</file>
<file>ui/checkBox.js</file>
<file>ui/ctrlAltTab.js</file>
<file>ui/dash.js</file>
<file>ui/dateMenu.js</file>
<file>ui/dnd.js</file>
<file>ui/endSessionDialog.js</file>
<file>ui/environment.js</file>
<file>ui/extensionDownloader.js</file>
<file>ui/extensionSystem.js</file>
<file>ui/focusCaretTracker.js</file>
<file>ui/grabHelper.js</file>
<file>ui/ibusCandidatePopup.js</file>
<file>ui/iconGrid.js</file>
<file>ui/keyboard.js</file>
<file>ui/layout.js</file>
<file>ui/lightbox.js</file>
<file>ui/lookingGlass.js</file>
<file>ui/magnifier.js</file>
<file>ui/magnifierDBus.js</file>
<file>ui/main.js</file>
<file>ui/messageTray.js</file>
<file>ui/modalDialog.js</file>
<file>ui/notificationDaemon.js</file>
<file>ui/osdWindow.js</file>
<file>ui/overview.js</file>
<file>ui/overviewControls.js</file>
<file>ui/panel.js</file>
<file>ui/panelMenu.js</file>
<file>ui/pointerWatcher.js</file>
<file>ui/popupMenu.js</file>
<file>ui/remoteMenu.js</file>
<file>ui/remoteSearch.js</file>
<file>ui/runDialog.js</file>
<file>ui/screenShield.js</file>
<file>ui/screencast.js</file>
<file>ui/screenshot.js</file>
<file>ui/scripting.js</file>
<file>ui/search.js</file>
<file>ui/separator.js</file>
<file>ui/sessionMode.js</file>
<file>ui/shellDBus.js</file>
<file>ui/shellEntry.js</file>
<file>ui/shellMountOperation.js</file>
<file>ui/slider.js</file>
<file>ui/switcherPopup.js</file>
<file>ui/tweener.js</file>
<file>ui/unlockDialog.js</file>
<file>ui/userWidget.js</file>
<file>ui/viewSelector.js</file>
<file>ui/windowAttentionHandler.js</file>
<file>ui/windowManager.js</file>
<file>ui/workspace.js</file>
<file>ui/workspaceSwitcherPopup.js</file>
<file>ui/workspaceThumbnail.js</file>
<file>ui/workspacesView.js</file>
<file>ui/xdndHandler.js</file>
<file>ui/components/__init__.js</file>
<file>ui/components/autorunManager.js</file>
<file>ui/components/automountManager.js</file>
<file>ui/components/networkAgent.js</file>
<file>ui/components/polkitAgent.js</file>
<file>ui/components/telepathyClient.js</file>
<file>ui/components/keyring.js</file>
<file>ui/status/accessibility.js</file>
<file>ui/status/brightness.js</file>
<file>ui/status/keyboard.js</file>
<file>ui/status/network.js</file>
<file>ui/status/power.js</file>
<file>ui/status/rfkill.js</file>
<file>ui/status/volume.js</file>
<file>ui/status/bluetooth.js</file>
<file>ui/status/screencast.js</file>
<file>ui/status/system.js</file>
</gresource>
</gresources>

View File

@ -174,9 +174,17 @@ const ExtensionFinder = new Lang.Class({
this.emit('extension-found', extension); this.emit('extension-found', extension);
}, },
_extensionsLoaded: function() {
this.emit('extensions-loaded');
},
scanExtensions: function() { scanExtensions: function() {
let perUserDir = Gio.File.new_for_path(global.userdatadir); let perUserDir = Gio.File.new_for_path(global.userdatadir);
FileUtils.collectFromDatadirs('extensions', true, Lang.bind(this, this._loadExtension, perUserDir)); FileUtils.collectFromDatadirsAsync('extensions',
{ processFile: Lang.bind(this, this._loadExtension),
loadedCallback: Lang.bind(this, this._extensionsLoaded),
includeUserDir: true,
data: perUserDir });
} }
}); });
Signals.addSignalMethods(ExtensionFinder.prototype); Signals.addSignalMethods(ExtensionFinder.prototype);

View File

@ -25,27 +25,60 @@ function listDirAsync(file, callback) {
}); });
} }
function collectFromDatadirs(subdir, includeUserDir, processFile) { function _collectFromDirectoryAsync(dir, loadState) {
function done() {
loadState.numLoading--;
if (loadState.loadedCallback &&
loadState.numLoading == 0)
loadState.loadedCallback(loadState.data);
}
dir.query_info_async('standard::type', Gio.FileQueryInfoFlags.NONE,
GLib.PRIORITY_DEFAULT, null, function(object, res) {
try {
object.query_info_finish(res);
} catch (e) {
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND))
log(e.message);
done();
return;
}
listDirAsync(dir, Lang.bind(this, function(infos) {
for (let i = 0; i < infos.length; i++)
loadState.processFile(dir.get_child(infos[i].get_name()),
infos[i], loadState.data);
done();
}));
});
}
function collectFromDatadirsAsync(subdir, params) {
params = Params.parse(params, { includeUserDir: false,
processFile: null,
loadedCallback: null,
data: null });
let loadState = { data: params.data,
numLoading: 0,
loadedCallback: params.loadedCallback,
processFile: params.processFile };
if (params.processFile == null) {
if (params.loadedCallback)
params.loadedCallback(params.data);
return;
}
let dataDirs = GLib.get_system_data_dirs(); let dataDirs = GLib.get_system_data_dirs();
if (includeUserDir) if (params.includeUserDir)
dataDirs.unshift(GLib.get_user_data_dir()); dataDirs.unshift(GLib.get_user_data_dir());
loadState.numLoading = dataDirs.length;
for (let i = 0; i < dataDirs.length; i++) { for (let i = 0; i < dataDirs.length; i++) {
let path = GLib.build_filenamev([dataDirs[i], 'gnome-shell', subdir]); let path = GLib.build_filenamev([dataDirs[i], 'gnome-shell', subdir]);
let dir = Gio.File.new_for_path(path); let dir = Gio.File.new_for_path(path);
let fileEnum; _collectFromDirectoryAsync(dir, loadState);
try {
fileEnum = dir.enumerate_children('standard::name,standard::type',
Gio.FileQueryInfoFlags.NONE, null);
} catch (e) {
fileEnum = null;
}
if (fileEnum != null) {
let info;
while ((info = fileEnum.next_file(null)))
processFile(fileEnum.get_child(info), info);
}
} }
} }
@ -84,6 +117,7 @@ function recursivelyMoveDir(srcDir, destDir) {
let type = info.get_file_type(); let type = info.get_file_type();
let srcChild = srcDir.get_child(info.get_name()); let srcChild = srcDir.get_child(info.get_name());
let destChild = destDir.get_child(info.get_name()); let destChild = destDir.get_child(info.get_name());
log([srcChild.get_path(), destChild.get_path()]);
if (type == Gio.FileType.REGULAR) if (type == Gio.FileType.REGULAR)
srcChild.move(destChild, Gio.FileCopyFlags.NONE, null, null); srcChild.move(destChild, Gio.FileCopyFlags.NONE, null, null);
else if (type == Gio.FileType.DIRECTORY) else if (type == Gio.FileType.DIRECTORY)

View File

@ -4,17 +4,15 @@ const Gio = imports.gi.Gio;
const Lang = imports.lang; const Lang = imports.lang;
const Signals = imports.signals; const Signals = imports.signals;
const PresenceIface = '<node> \ const PresenceIface = <interface name="org.gnome.SessionManager.Presence">
<interface name="org.gnome.SessionManager.Presence"> \ <method name="SetStatus">
<method name="SetStatus"> \ <arg type="u" direction="in"/>
<arg type="u" direction="in"/> \ </method>
</method> \ <property name="status" type="u" access="readwrite"/>
<property name="status" type="u" access="readwrite"/> \ <signal name="StatusChanged">
<signal name="StatusChanged"> \ <arg type="u" direction="out"/>
<arg type="u" direction="out"/> \ </signal>
</signal> \ </interface>;
</interface> \
</node>';
const PresenceStatus = { const PresenceStatus = {
AVAILABLE: 0, AVAILABLE: 0,
@ -32,16 +30,14 @@ function Presence(initCallback, cancellable) {
// Note inhibitors are immutable objects, so they don't // Note inhibitors are immutable objects, so they don't
// change at runtime (changes always come in the form // change at runtime (changes always come in the form
// of new inhibitors) // of new inhibitors)
const InhibitorIface = '<node> \ const InhibitorIface = <interface name="org.gnome.SessionManager.Inhibitor">
<interface name="org.gnome.SessionManager.Inhibitor"> \ <method name="GetAppId">
<method name="GetAppId"> \ <arg type="s" direction="out" />
<arg type="s" direction="out" /> \ </method>
</method> \ <method name="GetReason">
<method name="GetReason"> \ <arg type="s" direction="out" />
<arg type="s" direction="out" /> \ </method>
</method> \ </interface>;
</interface> \
</node>';
var InhibitorProxy = Gio.DBusProxy.makeProxyWrapper(InhibitorIface); var InhibitorProxy = Gio.DBusProxy.makeProxyWrapper(InhibitorIface);
function Inhibitor(objectPath, initCallback, cancellable) { function Inhibitor(objectPath, initCallback, cancellable) {
@ -49,29 +45,27 @@ function Inhibitor(objectPath, initCallback, cancellable) {
} }
// Not the full interface, only the methods we use // Not the full interface, only the methods we use
const SessionManagerIface = '<node> \ const SessionManagerIface = <interface name="org.gnome.SessionManager">
<interface name="org.gnome.SessionManager"> \ <method name="Logout">
<method name="Logout"> \ <arg type="u" direction="in" />
<arg type="u" direction="in" /> \ </method>
</method> \ <method name="Shutdown" />
<method name="Shutdown" /> \ <method name="Reboot" />
<method name="Reboot" /> \ <method name="CanShutdown">
<method name="CanShutdown"> \ <arg type="b" direction="out" />
<arg type="b" direction="out" /> \ </method>
</method> \ <method name="IsInhibited">
<method name="IsInhibited"> \ <arg type="u" direction="in" />
<arg type="u" direction="in" /> \ <arg type="b" direction="out" />
<arg type="b" direction="out" /> \ </method>
</method> \ <property name="SessionIsActive" type="b" access="read"/>
<property name="SessionIsActive" type="b" access="read"/> \ <signal name="InhibitorAdded">
<signal name="InhibitorAdded"> \ <arg type="o" direction="out"/>
<arg type="o" direction="out"/> \ </signal>
</signal> \ <signal name="InhibitorRemoved">
<signal name="InhibitorRemoved"> \ <arg type="o" direction="out"/>
<arg type="o" direction="out"/> \ </signal>
</signal> \ </interface>;
</interface> \
</node>';
var SessionManagerProxy = Gio.DBusProxy.makeProxyWrapper(SessionManagerIface); var SessionManagerProxy = Gio.DBusProxy.makeProxyWrapper(SessionManagerIface);
function SessionManager(initCallback, cancellable) { function SessionManager(initCallback, cancellable) {

View File

@ -89,7 +89,7 @@ const HistoryManager = new Lang.Class({
} else if (symbol == Clutter.KEY_Down) { } else if (symbol == Clutter.KEY_Down) {
return this._setNextItem(entry.get_text()); return this._setNextItem(entry.get_text());
} }
return Clutter.EVENT_PROPAGATE; return false;
}, },
_indexChanged: function() { _indexChanged: function() {

View File

@ -7,66 +7,70 @@ const Mainloop = imports.mainloop;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const Signals = imports.signals; const Signals = imports.signals;
const SystemdLoginManagerIface = '<node> \ const SystemdLoginManagerIface = <interface name='org.freedesktop.login1.Manager'>
<interface name="org.freedesktop.login1.Manager"> \ <method name='PowerOff'>
<method name="Suspend"> \ <arg type='b' direction='in'/>
<arg type="b" direction="in"/> \ </method>
</method> \ <method name='Reboot'>
<method name="CanSuspend"> \ <arg type='b' direction='in'/>
<arg type="s" direction="out"/> \ </method>
</method> \ <method name='Suspend'>
<method name="Inhibit"> \ <arg type='b' direction='in'/>
<arg type="s" direction="in"/> \ </method>
<arg type="s" direction="in"/> \ <method name='CanPowerOff'>
<arg type="s" direction="in"/> \ <arg type='s' direction='out'/>
<arg type="s" direction="in"/> \ </method>
<arg type="h" direction="out"/> \ <method name='CanReboot'>
</method> \ <arg type='s' direction='out'/>
<method name="GetSession"> \ </method>
<arg type="s" direction="in"/> \ <method name='CanSuspend'>
<arg type="o" direction="out"/> \ <arg type='s' direction='out'/>
</method> \ </method>
<method name="ListSessions"> \ <method name='Inhibit'>
<arg name="sessions" type="a(susso)" direction="out"/> \ <arg type='s' direction='in'/>
</method> \ <arg type='s' direction='in'/>
<signal name="PrepareForSleep"> \ <arg type='s' direction='in'/>
<arg type="b" direction="out"/> \ <arg type='s' direction='in'/>
</signal> \ <arg type='h' direction='out'/>
</interface> \ </method>
</node>'; <method name='GetSession'>
<arg type='s' direction='in'/>
<arg type='o' direction='out'/>
</method>
<method name='ListSessions'>
<arg name='sessions' type='a(susso)' direction='out'/>
</method>
<signal name='PrepareForSleep'>
<arg type='b' direction='out'/>
</signal>
</interface>;
const SystemdLoginSessionIface = '<node> \ const SystemdLoginSessionIface = <interface name='org.freedesktop.login1.Session'>
<interface name="org.freedesktop.login1.Session"> \ <signal name='Lock' />
<signal name="Lock" /> \ <signal name='Unlock' />
<signal name="Unlock" /> \ </interface>;
</interface> \
</node>';
const SystemdLoginManager = Gio.DBusProxy.makeProxyWrapper(SystemdLoginManagerIface); const SystemdLoginManager = Gio.DBusProxy.makeProxyWrapper(SystemdLoginManagerIface);
const SystemdLoginSession = Gio.DBusProxy.makeProxyWrapper(SystemdLoginSessionIface); const SystemdLoginSession = Gio.DBusProxy.makeProxyWrapper(SystemdLoginSessionIface);
const ConsoleKitManagerIface = '<node> \ const ConsoleKitManagerIface = <interface name='org.freedesktop.ConsoleKit.Manager'>
<interface name="org.freedesktop.ConsoleKit.Manager"> \ <method name='CanRestart'>
<method name="CanRestart"> \ <arg type='b' direction='out'/>
<arg type="b" direction="out"/> \ </method>
</method> \ <method name='CanStop'>
<method name="CanStop"> \ <arg type='b' direction='out'/>
<arg type="b" direction="out"/> \ </method>
</method> \ <method name='Restart' />
<method name="Restart" /> \ <method name='Stop' />
<method name="Stop" /> \ <method name='GetCurrentSession'>
<method name="GetCurrentSession"> \ <arg type='o' direction='out' />
<arg type="o" direction="out" /> \ </method>
</method> \ </interface>;
</interface> \
</node>';
const ConsoleKitSessionIface = '<node> \ const ConsoleKitSessionIface = <interface name='org.freedesktop.ConsoleKit.Session'>
<interface name="org.freedesktop.ConsoleKit.Session"> \ <signal name='Lock' />
<signal name="Lock" /> \ <signal name='Unlock' />
<signal name="Unlock" /> \ </interface>;
</interface> \
</node>';
const ConsoleKitSession = Gio.DBusProxy.makeProxyWrapper(ConsoleKitSessionIface); const ConsoleKitSession = Gio.DBusProxy.makeProxyWrapper(ConsoleKitSessionIface);
const ConsoleKitManager = Gio.DBusProxy.makeProxyWrapper(ConsoleKitManagerIface); const ConsoleKitManager = Gio.DBusProxy.makeProxyWrapper(ConsoleKitManagerIface);
@ -80,10 +84,8 @@ function versionCompare(required, reference) {
reference = reference.split('.'); reference = reference.split('.');
for (let i = 0; i < required.length; i++) { for (let i = 0; i < required.length; i++) {
let requiredInt = parseInt(required[i]); if (required[i] != reference[i])
let referenceInt = parseInt(reference[i]); return required[i] < reference[i];
if (requiredInt != referenceInt)
return requiredInt < referenceInt;
} }
return true; return true;
@ -157,6 +159,24 @@ const LoginManagerSystemd = new Lang.Class({
})); }));
}, },
canPowerOff: function(asyncCallback) {
this._proxy.CanPowerOffRemote(function(result, error) {
if (error)
asyncCallback(false);
else
asyncCallback(result[0] != 'no');
});
},
canReboot: function(asyncCallback) {
this._proxy.CanRebootRemote(function(result, error) {
if (error)
asyncCallback(false);
else
asyncCallback(result[0] != 'no');
});
},
canSuspend: function(asyncCallback) { canSuspend: function(asyncCallback) {
this._proxy.CanSuspendRemote(function(result, error) { this._proxy.CanSuspendRemote(function(result, error) {
if (error) if (error)
@ -175,6 +195,14 @@ const LoginManagerSystemd = new Lang.Class({
}); });
}, },
powerOff: function() {
this._proxy.PowerOffRemote(true);
},
reboot: function() {
this._proxy.RebootRemote(true);
},
suspend: function() { suspend: function() {
this._proxy.SuspendRemote(true); this._proxy.SuspendRemote(true);
}, },
@ -236,6 +264,24 @@ const LoginManagerConsoleKit = new Lang.Class({
})); }));
}, },
canPowerOff: function(asyncCallback) {
this._proxy.CanStopRemote(function(result, error) {
if (error)
asyncCallback(false);
else
asyncCallback(result[0]);
});
},
canReboot: function(asyncCallback) {
this._proxy.CanRestartRemote(function(result, error) {
if (error)
asyncCallback(false);
else
asyncCallback(result[0]);
});
},
canSuspend: function(asyncCallback) { canSuspend: function(asyncCallback) {
asyncCallback(false); asyncCallback(false);
}, },
@ -244,6 +290,14 @@ const LoginManagerConsoleKit = new Lang.Class({
asyncCallback([]); asyncCallback([]);
}, },
powerOff: function() {
this._proxy.StopRemote();
},
reboot: function() {
this._proxy.RestartRemote();
},
suspend: function() { suspend: function() {
this.emit('prepare-for-sleep', true); this.emit('prepare-for-sleep', true);
this.emit('prepare-for-sleep', false); this.emit('prepare-for-sleep', false);

View File

@ -92,41 +92,37 @@ function _findProviderForSid(sid) {
// The following are not the complete interfaces, just the methods we need // The following are not the complete interfaces, just the methods we need
// (or may need in the future) // (or may need in the future)
const ModemGsmNetworkInterface = '<node> \ const ModemGsmNetworkInterface = <interface name="org.freedesktop.ModemManager.Modem.Gsm.Network">
<interface name="org.freedesktop.ModemManager.Modem.Gsm.Network"> \ <method name="GetRegistrationInfo">
<method name="GetRegistrationInfo"> \ <arg type="(uss)" direction="out" />
<arg type="(uss)" direction="out" /> \ </method>
</method> \ <method name="GetSignalQuality">
<method name="GetSignalQuality"> \ <arg type="u" direction="out" />
<arg type="u" direction="out" /> \ </method>
</method> \ <property name="AccessTechnology" type="u" access="read" />
<property name="AccessTechnology" type="u" access="read" /> \ <signal name="SignalQuality">
<signal name="SignalQuality"> \ <arg type="u" direction="out" />
<arg type="u" direction="out" /> \ </signal>
</signal> \ <signal name="RegistrationInfo">
<signal name="RegistrationInfo"> \ <arg type="u" direction="out" />
<arg type="u" direction="out" /> \ <arg type="s" direction="out" />
<arg type="s" direction="out" /> \ <arg type="s" direction="out" />
<arg type="s" direction="out" /> \ </signal>
</signal> \ </interface>;
</interface> \
</node>';
const ModemGsmNetworkProxy = Gio.DBusProxy.makeProxyWrapper(ModemGsmNetworkInterface); const ModemGsmNetworkProxy = Gio.DBusProxy.makeProxyWrapper(ModemGsmNetworkInterface);
const ModemCdmaInterface = '<node> \ const ModemCdmaInterface = <interface name="org.freedesktop.ModemManager.Modem.Cdma">
<interface name="org.freedesktop.ModemManager.Modem.Cdma"> \ <method name="GetSignalQuality">
<method name="GetSignalQuality"> \ <arg type="u" direction="out" />
<arg type="u" direction="out" /> \ </method>
</method> \ <method name="GetServingSystem">
<method name="GetServingSystem"> \ <arg type="(usu)" direction="out" />
<arg type="(usu)" direction="out" /> \ </method>
</method> \ <signal name="SignalQuality">
<signal name="SignalQuality"> \ <arg type="u" direction="out" />
<arg type="u" direction="out" /> \ </signal>
</signal> \ </interface>;
</interface> \
</node>';
const ModemCdmaProxy = Gio.DBusProxy.makeProxyWrapper(ModemCdmaInterface); const ModemCdmaProxy = Gio.DBusProxy.makeProxyWrapper(ModemCdmaInterface);
@ -222,26 +218,20 @@ Signals.addSignalMethods(ModemCdma.prototype);
// Support for the new ModemManager1 interface (MM >= 0.7) // Support for the new ModemManager1 interface (MM >= 0.7)
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
const BroadbandModemInterface = '<node> \ const BroadbandModemInterface = <interface name="org.freedesktop.ModemManager1.Modem">
<interface name="org.freedesktop.ModemManager1.Modem"> \ <property name="SignalQuality" type="(ub)" access="read" />
<property name="SignalQuality" type="(ub)" access="read" /> \ </interface>;
</interface> \
</node>';
const BroadbandModemProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModemInterface); const BroadbandModemProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModemInterface);
const BroadbandModem3gppInterface = '<node> \ const BroadbandModem3gppInterface = <interface name="org.freedesktop.ModemManager1.Modem.Modem3gpp">
<interface name="org.freedesktop.ModemManager1.Modem.Modem3gpp"> \ <property name="OperatorCode" type="s" access="read" />
<property name="OperatorCode" type="s" access="read" /> \ <property name="OperatorName" type="s" access="read" />
<property name="OperatorName" type="s" access="read" /> \ </interface>;
</interface> \
</node>';
const BroadbandModem3gppProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModem3gppInterface); const BroadbandModem3gppProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModem3gppInterface);
const BroadbandModemCdmaInterface = '<node> \ const BroadbandModemCdmaInterface = <interface name="org.freedesktop.ModemManager1.Modem.ModemCdma">
<interface name="org.freedesktop.ModemManager1.Modem.ModemCdma"> \ <property name="Sid" type="u" access="read" />
<property name="Sid" type="u" access="read" /> \ </interface>;
</interface> \
</node>';
const BroadbandModemCdmaProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModemCdmaInterface); const BroadbandModemCdmaProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModemCdmaInterface);
const BroadbandModem = new Lang.Class({ const BroadbandModem = new Lang.Class({

View File

@ -1,259 +0,0 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Params = imports.misc.params;
const Signals = imports.signals;
// Specified in the D-Bus specification here:
// http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager
const ObjectManagerIface = '<node> \
<interface name="org.freedesktop.DBus.ObjectManager"> \
<method name="GetManagedObjects"> \
<arg name="objects" type="a{oa{sa{sv}}}" direction="out"/> \
</method> \
<signal name="InterfacesAdded"> \
<arg name="objectPath" type="o"/> \
<arg name="interfaces" type="a{sa{sv}}" /> \
</signal> \
<signal name="InterfacesRemoved"> \
<arg name="objectPath" type="o"/> \
<arg name="interfaces" type="as" /> \
</signal> \
</interface> \
</node>';
const ObjectManagerInfo = Gio.DBusInterfaceInfo.new_for_xml(ObjectManagerIface);
const ObjectManager = new Lang.Class({
Name: 'ObjectManager',
_init: function(params) {
params = Params.parse(params, { connection: null,
name: null,
objectPath: null,
knownInterfaces: null,
cancellable: null,
onLoaded: null });
this._connection = params.connection;
this._serviceName = params.name;
this._managerPath = params.objectPath;
this._cancellable = params.cancellable;
this._managerProxy = new Gio.DBusProxy({ g_connection: this._connection,
g_interface_name: ObjectManagerInfo.name,
g_interface_info: ObjectManagerInfo,
g_name: this._serviceName,
g_object_path: this._managerPath,
g_flags: Gio.DBusProxyFlags.NONE });
this._interfaceInfos = {};
this._objects = {};
this._interfaces = {};
this._onLoaded = params.onLoaded;
if (params.knownInterfaces)
this._registerInterfaces(params.knownInterfaces);
// Start out inhibiting load until at least the proxy
// manager is loaded and the remote objects are fetched
this._numLoadInhibitors = 1;
this._managerProxy.init_async(GLib.PRIORITY_DEFAULT,
this._cancellable,
Lang.bind(this, this._onManagerProxyLoaded));
},
_tryToCompleteLoad: function() {
this._numLoadInhibitors--;
if (this._numLoadInhibitors == 0) {
if (this._onLoaded)
this._onLoaded();
}
},
_addInterface: function(objectPath, interfaceName, onFinished) {
let info = this._interfaceInfos[interfaceName];
if (!info) {
if (onFinished)
onFinished();
return;
}
let proxy = new Gio.DBusProxy({ g_connection: this._connection,
g_name: this._serviceName,
g_object_path: objectPath,
g_interface_name: interfaceName,
g_interface_info: info,
g_flags: Gio.DBusProxyFlags.NONE });
proxy.init_async(GLib.PRIORITY_DEFAULT,
this._cancellable,
Lang.bind(this, function(initable, result) {
let error = null;
try {
initable.init_finish(result);
} catch(e) {
logError(e, 'could not initialize proxy for interface ' + interfaceName);
if (onFinished)
onFinished();
return;
}
let isNewObject;
if (!this._objects[objectPath]) {
this._objects[objectPath] = {};
isNewObject = true;
} else {
isNewObject = false;
}
this._objects[objectPath][interfaceName] = proxy;
if (!this._interfaces[interfaceName])
this._interfaces[interfaceName] = [];
this._interfaces[interfaceName].push(proxy);
if (isNewObject)
this.emit('object-added', objectPath);
this.emit('interface-added', interfaceName, proxy);
if (onFinished)
onFinished();
}));
},
_removeInterface: function(objectPath, interfaceName) {
if (!this._objects[objectPath])
return;
let proxy = this._objects[objectPath][interfaceName];
if (this._interfaces[interfaceName]) {
let index = this._interfaces[interfaceName].indexOf(proxy);
if (index >= 0)
this._interfaces[interfaceName].splice(index, 1);
if (this._interfaces[interfaceName].length == 0)
delete this._interfaces[interfaceName];
}
this.emit('interface-removed', interfaceName, proxy);
this._objects[objectPath][interfaceName] = null;
if (Object.keys(this._objects[objectPath]).length == 0) {
delete this._objects[objectPath];
this.emit('object-removed', objectPath);
}
},
_onManagerProxyLoaded: function(initable, result) {
let error = null;
try {
initable.init_finish(result);
} catch(e) {
logError(e, 'could not initialize object manager for object ' + params.name);
this._tryToCompleteLoad();
return;
}
this._managerProxy.connectSignal('InterfacesAdded',
Lang.bind(this, function(objectManager, sender, [objectPath, interfaces]) {
let interfaceNames = Object.keys(interfaces);
for (let i = 0; i < interfaceNames.length; i++)
this._addInterface(objectPath, interfaceNames[i]);
}));
this._managerProxy.connectSignal('InterfacesRemoved',
Lang.bind(this, function(objectManager, sender, [objectPath, interfaceNames]) {
for (let i = 0; i < interfaceNames.length; i++)
this._removeInterface(objectPath, interfaceNames[i]);
}));
if (Object.keys(this._interfaceInfos).length == 0) {
this._tryToCompleteLoad();
return;
}
this._managerProxy.GetManagedObjectsRemote(Lang.bind(this, function(result, error) {
if (!result) {
if (error) {
logError(error, 'could not get remote objects for service ' + this._serviceName + ' path ' + this._managerPath);
}
this._tryToCompleteLoad();
return;
}
let [objects] = result;
let objectPaths = Object.keys(objects);
for (let i = 0; i < objectPaths.length; i++) {
let objectPath = objectPaths[i];
let object = objects[objectPath];
let interfaceNames = Object.getOwnPropertyNames(object);
for (let j = 0; j < interfaceNames.length; j++) {
let interfaceName = interfaceNames[j];
// Prevent load from completing until the interface is loaded
this._numLoadInhibitors++;
this._addInterface(objectPath,
interfaceName,
Lang.bind(this, this._tryToCompleteLoad));
}
}
this._tryToCompleteLoad();
}));
},
_registerInterfaces: function(interfaces) {
for (let i = 0; i < interfaces.length; i++) {
let info = Gio.DBusInterfaceInfo.new_for_xml(interfaces[i]);
this._interfaceInfos[info.name] = info;
}
},
getProxy: function(objectPath, interfaceName) {
let object = this._objects[objectPath];
if (!object)
return null;
return object[interfaceName];
},
getProxiesForInterface: function(interfaceName) {
let proxyList = this._interfaces[interfaceName];
if (!proxyList)
return [];
return proxyList;
},
getAllProxies: function() {
let proxies = [];
let objectPaths = Object.keys(this._objects);
for (let i = 0; i < objectPaths.length; i++) {
let object = this._objects[objectPaths];
let interfaceNames = Object.keys(object);
for (let j = 0; i < interfaceNames.length; i++) {
let interfaceName = interfaceNames[i];
if (object[interfaceName])
proxies.push(object(interfaceName));
}
}
return proxies;
}
});
Signals.addSignalMethods(ObjectManager.prototype);

View File

@ -1,119 +0,0 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const ObjectManager = imports.misc.objectManager;
const SmartcardTokenIface = '<node> \
<interface name="org.gnome.SettingsDaemon.Smartcard.Token"> \
<property name="Name" type="s" access="read"/> \
<property name="Driver" type="o" access="read"/> \
<property name="IsInserted" type="b" access="read"/> \
<property name="UsedToLogin" type="b" access="read"/> \
</interface> \
</node>';
let _smartcardManager = null;
function getSmartcardManager() {
if (_smartcardManager == null)
_smartcardManager = new SmartcardManager();
return _smartcardManager;
}
const SmartcardManager = new Lang.Class({
Name: 'SmartcardManager',
_init: function() {
this._objectManager = new ObjectManager.ObjectManager({ connection: Gio.DBus.session,
name: "org.gnome.SettingsDaemon.Smartcard",
objectPath: '/org/gnome/SettingsDaemon/Smartcard',
knownInterfaces: [ SmartcardTokenIface ],
onLoaded: Lang.bind(this, this._onLoaded) });
this._insertedTokens = {};
this._loginToken = null;
},
_onLoaded: function() {
let tokens = this._objectManager.getProxiesForInterface('org.gnome.SettingsDaemon.Smartcard.Token');
for (let i = 0; i < tokens.length; i++)
this._addToken(tokens[i]);
this._objectManager.connect('interface-added', Lang.bind(this, function(objectManager, interfaceName, proxy) {
if (interfaceName == 'org.gnome.SettingsDaemon.Smartcard.Token')
this._addToken(proxy);
}));
this._objectManager.connect('interface-removed', Lang.bind(this, function(objectManager, interfaceName, proxy) {
if (interfaceName == 'org.gnome.SettingsDaemon.Smartcard.Token')
this._removeToken(proxy);
}));
},
_updateToken: function(token) {
let objectPath = token.get_object_path();
delete this._insertedTokens[objectPath];
if (token.IsInserted)
this._insertedTokens[objectPath] = token;
if (token.UsedToLogin)
this._loginToken = token;
},
_addToken: function(token) {
this._updateToken(token);
token.connect('g-properties-changed',
Lang.bind(this, function(proxy, properties) {
if ('IsInserted' in properties.deep_unpack()) {
this._updateToken(token);
if (token.IsInserted) {
this.emit('smartcard-inserted', token);
} else {
this.emit('smartcard-removed', token);
}
}
}));
// Emit a smartcard-inserted at startup if it's already plugged in
if (token.IsInserted)
this.emit('smartcard-inserted', token);
},
_removeToken: function(token) {
let objectPath = token.get_object_path();
if (this._insertedTokens[objectPath] == token) {
delete this._insertedTokens[objectPath];
this.emit('smartcard-removed', token);
}
if (this._loginToken == token)
this._loginToken = null;
token.disconnectAll();
},
hasInsertedTokens: function() {
return Object.keys(this._insertedTokens).length > 0;
},
hasInsertedLoginToken: function() {
if (!this._loginToken)
return false;
if (!this._loginToken.IsInserted)
return false;
return true;
}
});
Signals.addSignalMethods(SmartcardManager.prototype);

View File

@ -1,9 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib; const GLib = imports.gi.GLib;
const Lang = imports.lang;
const St = imports.gi.St; const St = imports.gi.St;
const Main = imports.ui.main; const Main = imports.ui.main;
@ -80,22 +78,6 @@ function spawnCommandLine(command_line) {
} }
} }
// spawnApp:
// @argv: an argv array
//
// Runs @argv as if it was an application, handling startup notification
function spawnApp(argv) {
try {
let app = Gio.AppInfo.create_from_commandline(argv.join(' '), null,
Gio.AppInfoCreateFlags.SUPPORTS_STARTUP_NOTIFICATION);
let context = global.create_app_launch_context();
app.launch([], context);
} catch(err) {
_handleSpawnError(argv[0], err);
}
}
// trySpawn: // trySpawn:
// @argv: an argv array // @argv: an argv array
// //
@ -157,6 +139,31 @@ function _handleSpawnError(command, err) {
Main.notifyError(title, err.message); Main.notifyError(title, err.message);
} }
// killall:
// @processName: a process name
//
// Kills @processName. If no process with the given name is found,
// this will fail silently.
function killall(processName) {
try {
// pkill is more portable than killall, but on Linux at least
// it won't match if you pass more than 15 characters of the
// process name... However, if you use the '-f' flag to match
// the entire command line, it will work, but we have to be
// careful in that case that we can match
// '/usr/bin/processName' but not 'gedit processName.c' or
// whatever...
let argv = ['pkill', '-f', '^([^ ]*/)?' + processName + '($| )'];
GLib.spawn_sync(null, argv, null, GLib.SpawnFlags.SEARCH_PATH, null);
// It might be useful to return success/failure, but we'd need
// a wrapper around WIFEXITED and WEXITSTATUS. Since none of
// the current callers care, we don't bother.
} catch (e) {
logError(e, 'Failed to kill ' + processName);
}
}
// lowerBound: // lowerBound:
// @array: an array or array-like object, already sorted // @array: an array or array-like object, already sorted
// according to @cmp // according to @cmp
@ -207,57 +214,28 @@ function insertSorted(array, val, cmp) {
return pos; return pos;
} }
const CloseButton = new Lang.Class({ function makeCloseButton() {
Name: 'CloseButton', let closeButton = new St.Button({ style_class: 'notification-close'});
Extends: St.Button,
_init: function(boxpointer) {
this.parent({ style_class: 'notification-close'});
// This is a bit tricky. St.Bin has its own x-align/y-align properties // This is a bit tricky. St.Bin has its own x-align/y-align properties
// that compete with Clutter's properties. This should be fixed for // that compete with Clutter's properties. This should be fixed for
// Clutter 2.0. Since St.Bin doesn't define its own setters, the // Clutter 2.0. Since St.Bin doesn't define its own setters, the
// setters are a workaround to get Clutter's version. // setters are a workaround to get Clutter's version.
this.set_x_align(Clutter.ActorAlign.END); closeButton.set_x_align(Clutter.ActorAlign.END);
this.set_y_align(Clutter.ActorAlign.START); closeButton.set_y_align(Clutter.ActorAlign.START);
// XXX Clutter 2.0 workaround: ClutterBinLayout needs expand // XXX Clutter 2.0 workaround: ClutterBinLayout needs expand
// to respect the alignments. // to respect the alignments.
this.set_x_expand(true); closeButton.set_x_expand(true);
this.set_y_expand(true); closeButton.set_y_expand(true);
this._boxPointer = boxpointer; closeButton.connect('style-changed', function() {
if (boxpointer) let themeNode = closeButton.get_theme_node();
this._boxPointer.connect('arrow-side-changed', Lang.bind(this, this._sync)); closeButton.translation_x = themeNode.get_length('-shell-close-overlap-x');
}, closeButton.translation_y = themeNode.get_length('-shell-close-overlap-y');
_computeBoxPointerOffset: function() {
if (!this._boxPointer || !this._boxPointer.actor.get_stage())
return 0;
let side = this._boxPointer.arrowSide;
if (side == St.Side.TOP)
return this._boxPointer.getArrowHeight();
else
return 0;
},
_sync: function() {
let themeNode = this.get_theme_node();
let offY = this._computeBoxPointerOffset();
this.translation_x = themeNode.get_length('-shell-close-overlap-x')
this.translation_y = themeNode.get_length('-shell-close-overlap-y') + offY;
},
vfunc_style_changed: function() {
this._sync();
this.parent();
},
}); });
function makeCloseButton(boxpointer) { return closeButton;
return new CloseButton(boxpointer);
} }
function ensureActorVisibleInScrollView(scrollView, actor) { function ensureActorVisibleInScrollView(scrollView, actor) {

View File

@ -2,7 +2,6 @@
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio; const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang; const Lang = imports.lang;
const Mainloop = imports.mainloop; const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta; const Meta = imports.gi.Meta;
@ -107,8 +106,6 @@ const AppSwitcherPopup = new Lang.Class({
this._switcherList = new AppSwitcher(apps, this); this._switcherList = new AppSwitcher(apps, this);
this._items = this._switcherList.icons; this._items = this._switcherList.icons;
if (this._items.length == 0)
return false;
return true; return true;
}, },
@ -313,7 +310,7 @@ const AppSwitcherPopup = new Lang.Class({
this._createThumbnails(); this._createThumbnails();
this._thumbnailTimeoutId = 0; this._thumbnailTimeoutId = 0;
this._thumbnailsFocused = false; this._thumbnailsFocused = false;
return GLib.SOURCE_REMOVE; return false;
}, },
_destroyThumbnails : function() { _destroyThumbnails : function() {
@ -358,13 +355,10 @@ const WindowSwitcherPopup = new Lang.Class({
Name: 'WindowSwitcherPopup', Name: 'WindowSwitcherPopup',
Extends: SwitcherPopup.SwitcherPopup, Extends: SwitcherPopup.SwitcherPopup,
_init: function(items) {
this.parent(items);
this._settings = new Gio.Settings({ schema: 'org.gnome.shell.window-switcher' });
},
_getWindowList: function() { _getWindowList: function() {
let workspace = this._settings.get_boolean('current-workspace-only') ? global.screen.get_active_workspace() : null; let settings = new Gio.Settings({ schema: 'org.gnome.shell.window-switcher' });
let workspace = settings.get_boolean('current-workspace-only') ? global.screen.get_active_workspace()
: null;
return global.display.get_tab_list(Meta.TabList.NORMAL, global.screen, workspace); return global.display.get_tab_list(Meta.TabList.NORMAL, global.screen, workspace);
}, },
@ -374,13 +368,9 @@ const WindowSwitcherPopup = new Lang.Class({
if (windows.length == 0) if (windows.length == 0)
return false; return false;
let mode = this._settings.get_enum('app-icon-mode'); this._switcherList = new WindowList(windows);
this._switcherList = new WindowList(windows, mode);
this._items = this._switcherList.icons; this._items = this._switcherList.icons;
if (this._items.length == 0)
return false;
return true; return true;
}, },
@ -446,11 +436,8 @@ const AppSwitcher = new Lang.Class({
this._arrows = []; this._arrows = [];
let windowTracker = Shell.WindowTracker.get_default(); let windowTracker = Shell.WindowTracker.get_default();
let settings = new Gio.Settings({ schema: 'org.gnome.shell.app-switcher' });
let workspace = settings.get_boolean('current-workspace-only') ? global.screen.get_active_workspace()
: null;
let allWindows = global.display.get_tab_list(Meta.TabList.NORMAL, let allWindows = global.display.get_tab_list(Meta.TabList.NORMAL,
global.screen, workspace); global.screen, null);
// Construct the AppIcons, add to the popup // Construct the AppIcons, add to the popup
for (let i = 0; i < apps.length; i++) { for (let i = 0; i < apps.length; i++) {
@ -460,10 +447,8 @@ const AppSwitcher = new Lang.Class({
appIcon.cachedWindows = allWindows.filter(function(w) { appIcon.cachedWindows = allWindows.filter(function(w) {
return windowTracker.get_window_app (w) == appIcon.app; return windowTracker.get_window_app (w) == appIcon.app;
}); });
if (workspace == null || appIcon.cachedWindows.length > 0) {
this._addIcon(appIcon); this._addIcon(appIcon);
} }
}
this._curApp = -1; this._curApp = -1;
this._iconSize = 0; this._iconSize = 0;
@ -548,7 +533,7 @@ const AppSwitcher = new Lang.Class({
Lang.bind(this, function () { Lang.bind(this, function () {
this._enterItem(index); this._enterItem(index);
this._mouseTimeOutId = 0; this._mouseTimeOutId = 0;
return GLib.SOURCE_REMOVE; return false;
})); }));
} else } else
this._itemEntered(index); this._itemEntered(index);
@ -673,7 +658,7 @@ const ThumbnailList = new Lang.Class({
const WindowIcon = new Lang.Class({ const WindowIcon = new Lang.Class({
Name: 'WindowIcon', Name: 'WindowIcon',
_init: function(window, mode) { _init: function(window) {
this.window = window; this.window = window;
this.actor = new St.BoxLayout({ style_class: 'alt-tab-app', this.actor = new St.BoxLayout({ style_class: 'alt-tab-app',
@ -691,7 +676,8 @@ const WindowIcon = new Lang.Class({
this._icon.destroy_all_children(); this._icon.destroy_all_children();
switch (mode) { let settings = new Gio.Settings({ schema: 'org.gnome.shell.window-switcher' });
switch (settings.get_enum('app-icon-mode')) {
case AppIconMode.THUMBNAIL_ONLY: case AppIconMode.THUMBNAIL_ONLY:
size = WINDOW_PREVIEW_SIZE; size = WINDOW_PREVIEW_SIZE;
this._icon.add_actor(_createWindowClone(mutterWindow, WINDOW_PREVIEW_SIZE)); this._icon.add_actor(_createWindowClone(mutterWindow, WINDOW_PREVIEW_SIZE));
@ -729,7 +715,7 @@ const WindowList = new Lang.Class({
Name: 'WindowList', Name: 'WindowList',
Extends: SwitcherPopup.SwitcherList, Extends: SwitcherPopup.SwitcherList,
_init : function(windows, mode) { _init : function(windows) {
this.parent(true); this.parent(true);
this._label = new St.Label({ x_align: Clutter.ActorAlign.CENTER, this._label = new St.Label({ x_align: Clutter.ActorAlign.CENTER,
@ -741,7 +727,7 @@ const WindowList = new Lang.Class({
for (let i = 0; i < windows.length; i++) { for (let i = 0; i < windows.length; i++) {
let win = windows[i]; let win = windows[i];
let icon = new WindowIcon(win, mode); let icon = new WindowIcon(win);
this.addItem(icon.actor, icon.label); this.addItem(icon.actor, icon.label);
this.icons.push(icon); this.icons.push(icon);

View File

@ -1,6 +1,5 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const GLib = imports.gi.GLib;
const Lang = imports.lang; const Lang = imports.lang;
const Mainloop = imports.mainloop; const Mainloop = imports.mainloop;
const St = imports.gi.St; const St = imports.gi.St;
@ -60,7 +59,7 @@ const Animation = new Lang.Class({
_update: function() { _update: function() {
this._showFrame(this._frame + 1); this._showFrame(this._frame + 1);
return GLib.SOURCE_CONTINUE; return true;
}, },
_animationsLoaded: function() { _animationsLoaded: function() {

File diff suppressed because it is too large Load Diff

View File

@ -14,15 +14,15 @@ const AppFavorites = new Lang.Class({
_init: function() { _init: function() {
this._favorites = {}; this._favorites = {};
global.settings.connect('changed::' + this.FAVORITE_APPS_KEY, Lang.bind(this, this._onFavsChanged)); global.settings.connect('changed::' + this.FAVORITE_APPS_KEY, Lang.bind(this, this._onFavsChanged));
this.reload(); this._reload();
}, },
_onFavsChanged: function() { _onFavsChanged: function() {
this.reload(); this._reload();
this.emit('changed'); this.emit('changed');
}, },
reload: function() { _reload: function() {
let ids = global.settings.get_strv(this.FAVORITE_APPS_KEY); let ids = global.settings.get_strv(this.FAVORITE_APPS_KEY);
let appSys = Shell.AppSystem.get_default(); let appSys = Shell.AppSystem.get_default();
let apps = ids.map(function (id) { let apps = ids.map(function (id) {

View File

@ -50,9 +50,11 @@ const BackgroundCache = new Lang.Class({
effects: Meta.BackgroundEffects.NONE }); effects: Meta.BackgroundEffects.NONE });
let content = null; let content = null;
let candidateContent = null; let candidateContent = null;
for (let i = 0; i < this._patterns.length; i++) { for (let i = 0; i < this._patterns.length; i++) {
if (!this._patterns[i])
continue;
if (this._patterns[i].get_shading() != params.shadingType) if (this._patterns[i].get_shading() != params.shadingType)
continue; continue;
@ -86,6 +88,7 @@ const BackgroundCache = new Lang.Class({
} }
this._patterns.push(content); this._patterns.push(content);
return content; return content;
}, },
@ -113,8 +116,8 @@ const BackgroundCache = new Lang.Class({
_removeContent: function(contentList, content) { _removeContent: function(contentList, content) {
let index = contentList.indexOf(content); let index = contentList.indexOf(content);
if (index < 0)
throw new Error("Trying to remove invalid content: " + content); if (index >= 0)
contentList.splice(index, 1); contentList.splice(index, 1);
}, },
@ -125,8 +128,7 @@ const BackgroundCache = new Lang.Class({
removeImageContent: function(content) { removeImageContent: function(content) {
let filename = content.get_filename(); let filename = content.get_filename();
let hasOtherUsers = this._images.some(function(content) { return filename == content.get_filename(); }); if (filename && this._fileMonitors[filename])
if (!hasOtherUsers)
delete this._fileMonitors[filename]; delete this._fileMonitors[filename];
this._removeContent(this._images, content); this._removeContent(this._images, content);
@ -184,17 +186,13 @@ const BackgroundCache = new Lang.Class({
for (let j = 0; j < pendingLoad.callers.length; j++) { for (let j = 0; j < pendingLoad.callers.length; j++) {
if (pendingLoad.callers[j].onFinished) { if (pendingLoad.callers[j].onFinished) {
let newContent;
if (content && pendingLoad.callers[j].shouldCopy) { if (content && pendingLoad.callers[j].shouldCopy) {
newContent = content.copy(pendingLoad.callers[j].monitorIndex, content = object.copy(pendingLoad.callers[j].monitorIndex,
pendingLoad.callers[j].effects); pendingLoad.callers[j].effects);
this._images.push(newContent);
} else {
newContent = content;
} }
pendingLoad.callers[j].onFinished(newContent); pendingLoad.callers[j].onFinished(content);
} }
} }
@ -212,9 +210,11 @@ const BackgroundCache = new Lang.Class({
onFinished: null }); onFinished: null });
let content = null; let content = null;
let candidateContent = null; let candidateContent = null;
for (let i = 0; i < this._images.length; i++) { for (let i = 0; i < this._images.length; i++) {
if (!this._images[i])
continue;
if (this._images[i].get_style() != params.style) if (this._images[i].get_style() != params.style)
continue; continue;
@ -222,7 +222,7 @@ const BackgroundCache = new Lang.Class({
continue; continue;
if (params.style == GDesktopEnums.BackgroundStyle.SPANNED && if (params.style == GDesktopEnums.BackgroundStyle.SPANNED &&
this._images[i].monitor != params.monitorIndex) this._images[i].monitor_index != this._monitorIndex)
continue; continue;
candidateContent = this._images[i]; candidateContent = this._images[i];
@ -262,7 +262,6 @@ const BackgroundCache = new Lang.Class({
if (params.onLoaded) { if (params.onLoaded) {
GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() { GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
params.onLoaded(this._animation); params.onLoaded(this._animation);
return GLib.SOURCE_REMOVE;
})); }));
} }
} }
@ -277,7 +276,6 @@ const BackgroundCache = new Lang.Class({
if (params.onLoaded) { if (params.onLoaded) {
GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() { GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
params.onLoaded(this._animation); params.onLoaded(this._animation);
return GLib.SOURCE_REMOVE;
})); }));
} }
})); }));
@ -297,15 +295,14 @@ const Background = new Lang.Class({
_init: function(params) { _init: function(params) {
params = Params.parse(params, { monitorIndex: 0, params = Params.parse(params, { monitorIndex: 0,
layoutManager: Main.layoutManager, layoutManager: Main.layoutManager,
effects: Meta.BackgroundEffects.NONE, effects: Meta.BackgroundEffects.NONE });
settings: null });
this.actor = new Meta.BackgroundGroup(); this.actor = new Meta.BackgroundGroup();
this.actor._delegate = this; this.actor._delegate = this;
this._destroySignalId = this.actor.connect('destroy', this._destroySignalId = this.actor.connect('destroy',
Lang.bind(this, this._destroy)); Lang.bind(this, this._destroy));
this._settings = params.settings; this._settings = new Gio.Settings({ schema: BACKGROUND_SCHEMA });
this._monitorIndex = params.monitorIndex; this._monitorIndex = params.monitorIndex;
this._layoutManager = params.layoutManager; this._layoutManager = params.layoutManager;
this._effects = params.effects; this._effects = params.effects;
@ -317,10 +314,11 @@ const Background = new Lang.Class({
this._brightness = 1.0; this._brightness = 1.0;
this._vignetteSharpness = 0.2; this._vignetteSharpness = 0.2;
this._saturation = 1.0;
this._cancellable = new Gio.Cancellable(); this._cancellable = new Gio.Cancellable();
this.isLoaded = false; this.isLoaded = false;
this._settingsChangedSignalId = this._settings.connect('changed', Lang.bind(this, function() { this._settings.connect('changed', Lang.bind(this, function() {
this.emit('changed'); this.emit('changed');
})); }));
@ -363,10 +361,6 @@ const Background = new Lang.Class({
this.actor.disconnect(this._destroySignalId); this.actor.disconnect(this._destroySignalId);
this._destroySignalId = 0; this._destroySignalId = 0;
if (this._settingsChangedSignalId != 0)
this._settings.disconnect(this._settingsChangedSignalId);
this._settingsChangedSignalId = 0;
}, },
_setLoaded: function() { _setLoaded: function() {
@ -377,7 +371,7 @@ const Background = new Lang.Class({
GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() { GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
this.emit('loaded'); this.emit('loaded');
return GLib.SOURCE_REMOVE; return false;
})); }));
}, },
@ -416,26 +410,29 @@ const Background = new Lang.Class({
this._fileWatches[filename] = signalId; this._fileWatches[filename] = signalId;
}, },
_ensureImage: function(index) { _addImage: function(content, index, filename) {
if (this._images[index]) content.saturation = this._saturation;
return; content.brightness = this._brightness;
content.vignette_sharpness = this._vignetteSharpness;
let actor = new Meta.BackgroundActor(); let actor = new Meta.BackgroundActor();
actor.content = content;
// The background pattern is the first actor in // The background pattern is the first actor in
// the group, and all images should be above that. // the group, and all images should be above that.
this.actor.insert_child_at_index(actor, index + 1); this.actor.insert_child_at_index(actor, index + 1);
this._images[index] = actor; this._images[index] = actor;
this._watchCacheFile(filename);
}, },
_updateImage: function(index, content, filename) { _updateImage: function(content, index, filename) {
content.saturation = this._saturation;
content.brightness = this._brightness; content.brightness = this._brightness;
content.vignette_sharpness = this._vignetteSharpness; content.vignette_sharpness = this._vignetteSharpness;
let image = this._images[index]; this._cache.removeImageContent(this._images[index].content);
if (image.content) this._images[index].content = content;
this._cache.removeImageContent(content);
image.content = content;
this._watchCacheFile(filename); this._watchCacheFile(filename);
}, },
@ -483,8 +480,11 @@ const Background = new Lang.Class({
return; return;
} }
this._ensureImage(i); if (!this._images[i]) {
this._updateImage(i, content, files[i]); this._addImage(content, i, files[i]);
} else {
this._updateImage(content, i, files[i]);
}
if (numPendingImages == 0) { if (numPendingImages == 0) {
this._setLoaded(); this._setLoaded();
@ -519,7 +519,7 @@ const Background = new Lang.Class({
Lang.bind(this, function() { Lang.bind(this, function() {
this._updateAnimationTimeoutId = 0; this._updateAnimationTimeoutId = 0;
this._updateAnimation(); this._updateAnimation();
return GLib.SOURCE_REMOVE; return false;
})); }));
}, },
@ -539,27 +539,24 @@ const Background = new Lang.Class({
}); });
}, },
_loadImage: function(filename) { _loadFile: function(filename) {
this._cache.getImageContent({ monitorIndex: this._monitorIndex, this._cache.getImageContent({ monitorIndex: this._monitorIndex,
effects: this._effects, effects: this._effects,
style: this._style, style: this._style,
filename: filename, filename: filename,
cancellable: this._cancellable, cancellable: this._cancellable,
onFinished: Lang.bind(this, function(content) { onFinished: Lang.bind(this, function(content) {
if (content) { if (!content) {
this._ensureImage(0); if (!this._cancellable.is_cancelled())
this._updateImage(0, content, filename); this._loadAnimation(filename);
return;
} }
this._addImage(content, 0, filename);
this._setLoaded(); this._setLoaded();
}) })
}); });
},
_loadFile: function(filename) {
if (filename.endsWith('.xml'))
this._loadAnimation(filename);
else
this._loadImage(filename);
}, },
_load: function () { _load: function () {
@ -588,6 +585,24 @@ const Background = new Lang.Class({
this._loadFile(filename); this._loadFile(filename);
}, },
get saturation() {
return this._saturation;
},
set saturation(saturation) {
this._saturation = saturation;
if (this._pattern && this._pattern.content)
this._pattern.content.saturation = saturation;
let keys = Object.keys(this._images);
for (let i = 0; i < keys.length; i++) {
let image = this._images[keys[i]];
if (image && image.content)
image.content.saturation = saturation;
}
},
get brightness() { get brightness() {
return this._brightness; return this._brightness;
}, },
@ -639,13 +654,7 @@ const SystemBackground = new Lang.Class({
this.emit('loaded'); this.emit('loaded');
}) })
}); });
}
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
},
_onDestroy: function() {
this._cache.removeImageContent(this.actor.content);
},
}); });
Signals.addSignalMethods(SystemBackground.prototype); Signals.addSignalMethods(SystemBackground.prototype);
@ -707,10 +716,8 @@ const BackgroundManager = new Lang.Class({
layoutManager: Main.layoutManager, layoutManager: Main.layoutManager,
monitorIndex: null, monitorIndex: null,
effects: Meta.BackgroundEffects.NONE, effects: Meta.BackgroundEffects.NONE,
controlPosition: true, controlPosition: true });
settingsSchema: BACKGROUND_SCHEMA });
this._settings = new Gio.Settings({ schema: params.settingsSchema });
this._container = params.container; this._container = params.container;
this._layoutManager = params.layoutManager; this._layoutManager = params.layoutManager;
this._effects = params.effects; this._effects = params.effects;
@ -733,10 +740,11 @@ const BackgroundManager = new Lang.Class({
} }
}, },
_updateBackground: function(background) { _updateBackground: function(background, monitorIndex) {
let newBackground = this._createBackground(); let newBackground = this._createBackground(monitorIndex);
newBackground.vignetteSharpness = background.vignetteSharpness; newBackground.vignetteSharpness = background.vignetteSharpness;
newBackground.brightness = background.brightness; newBackground.brightness = background.brightness;
newBackground.saturation = background.saturation;
newBackground.visible = background.visible; newBackground.visible = background.visible;
newBackground.loadedSignalId = newBackground.connect('loaded', newBackground.loadedSignalId = newBackground.connect('loaded',
@ -768,8 +776,7 @@ const BackgroundManager = new Lang.Class({
_createBackground: function() { _createBackground: function() {
let background = new Background({ monitorIndex: this._monitorIndex, let background = new Background({ monitorIndex: this._monitorIndex,
layoutManager: this._layoutManager, layoutManager: this._layoutManager,
effects: this._effects, effects: this._effects });
settings: this._settings });
this._container.add_child(background.actor); this._container.add_child(background.actor);
let monitor = this._layoutManager.monitors[this._monitorIndex]; let monitor = this._layoutManager.monitors[this._monitorIndex];
@ -783,7 +790,7 @@ const BackgroundManager = new Lang.Class({
background.changeSignalId = background.connect('changed', Lang.bind(this, function() { background.changeSignalId = background.connect('changed', Lang.bind(this, function() {
background.disconnect(background.changeSignalId); background.disconnect(background.changeSignalId);
background.changeSignalId = 0; background.changeSignalId = 0;
this._updateBackground(background); this._updateBackground(background, this._monitorIndex);
})); }));
background.actor.connect('destroy', Lang.bind(this, function() { background.actor.connect('destroy', Lang.bind(this, function() {

View File

@ -13,7 +13,7 @@ const BackgroundMenu = new Lang.Class({
Name: 'BackgroundMenu', Name: 'BackgroundMenu',
Extends: PopupMenu.PopupMenu, Extends: PopupMenu.PopupMenu,
_init: function(source, layoutManager) { _init: function(source) {
this.parent(source, 0, St.Side.TOP); this.parent(source, 0, St.Side.TOP);
this.addSettingsAction(_("Settings"), 'gnome-control-center.desktop'); this.addSettingsAction(_("Settings"), 'gnome-control-center.desktop');
@ -22,17 +22,17 @@ const BackgroundMenu = new Lang.Class({
this.actor.add_style_class_name('background-menu'); this.actor.add_style_class_name('background-menu');
layoutManager.uiGroup.add_actor(this.actor); Main.uiGroup.add_actor(this.actor);
this.actor.hide(); this.actor.hide();
} }
}); });
function addBackgroundMenu(actor, layoutManager) { function addBackgroundMenu(actor) {
let cursor = new St.Bin({ opacity: 0 }); let cursor = new St.Bin({ opacity: 0 });
layoutManager.uiGroup.add_actor(cursor); Main.uiGroup.add_actor(cursor);
actor.reactive = true; actor.reactive = true;
actor._backgroundMenu = new BackgroundMenu(cursor, layoutManager); actor._backgroundMenu = new BackgroundMenu(cursor);
actor._backgroundManager = new PopupMenu.PopupMenuManager({ actor: actor }); actor._backgroundManager = new PopupMenu.PopupMenuManager({ actor: actor });
actor._backgroundManager.addMenu(actor._backgroundMenu); actor._backgroundManager.addMenu(actor._backgroundMenu);

View File

@ -3,9 +3,8 @@
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const Lang = imports.lang; const Lang = imports.lang;
const Meta = imports.gi.Meta; const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const St = imports.gi.St; const St = imports.gi.St;
const Shell = imports.gi.Shell;
const Main = imports.ui.main; const Main = imports.ui.main;
const Tweener = imports.ui.tweener; const Tweener = imports.ui.tweener;
@ -62,14 +61,10 @@ const BoxPointer = new Lang.Class({
this._muteInput(); this._muteInput();
}, },
get arrowSide() {
return this._arrowSide;
},
_muteInput: function() { _muteInput: function() {
if (this._capturedEventId == 0) if (this._capturedEventId == 0)
this._capturedEventId = this.actor.connect('captured-event', this._capturedEventId = this.actor.connect('captured-event',
function() { return Clutter.EVENT_STOP; }); function() { return true; });
}, },
_unmuteInput: function() { _unmuteInput: function() {
@ -121,9 +116,6 @@ const BoxPointer = new Lang.Class({
}, },
hide: function(animate, onComplete) { hide: function(animate, onComplete) {
if (!this.actor.visible)
return;
let xOffset = 0; let xOffset = 0;
let yOffset = 0; let yOffset = 0;
let themeNode = this.actor.get_theme_node(); let themeNode = this.actor.get_theme_node();
@ -188,9 +180,7 @@ const BoxPointer = new Lang.Class({
}, },
_getPreferredHeight: function(actor, forWidth, alloc) { _getPreferredHeight: function(actor, forWidth, alloc) {
let themeNode = this.actor.get_theme_node(); let [minSize, naturalSize] = this.bin.get_preferred_height(forWidth);
let borderWidth = themeNode.get_length('-arrow-border-width');
let [minSize, naturalSize] = this.bin.get_preferred_height(forWidth - 2 * borderWidth);
alloc.min_size = minSize; alloc.min_size = minSize;
alloc.natural_size = naturalSize; alloc.natural_size = naturalSize;
this._adjustAllocationForArrow(false, alloc); this._adjustAllocationForArrow(false, alloc);
@ -599,12 +589,12 @@ const BoxPointer = new Lang.Class({
return St.Side.TOP; return St.Side.TOP;
break; break;
case St.Side.LEFT: case St.Side.LEFT:
if (sourceAllocation.x2 + boxWidth > monitor.x + monitor.width && if (sourceAllocation.y2 + boxWidth > monitor.x + monitor.width &&
boxWidth < sourceAllocation.x1 - monitor.x) boxWidth < sourceAllocation.x1 - monitor.x)
return St.Side.RIGHT; return St.Side.RIGHT;
break; break;
case St.Side.RIGHT: case St.Side.RIGHT:
if (sourceAllocation.x1 - boxWidth < monitor.x && if (sourceAllocation.y1 - boxWidth < monitor.x &&
boxWidth < monitor.x + monitor.width - sourceAllocation.x2) boxWidth < monitor.x + monitor.width - sourceAllocation.x2)
return St.Side.LEFT; return St.Side.LEFT;
break; break;
@ -622,8 +612,6 @@ const BoxPointer = new Lang.Class({
this._container.queue_relayout(); this._container.queue_relayout();
return false; return false;
})); }));
this.emit('arrow-side-changed');
} }
}, },
@ -651,21 +639,5 @@ const BoxPointer = new Lang.Class({
get opacity() { get opacity() {
return this.actor.opacity; return this.actor.opacity;
},
updateArrowSide: function(side) {
this._arrowSide = side;
this._border.queue_repaint();
this.emit('arrow-side-changed');
},
getPadding: function(side) {
return this.bin.get_theme_node().get_padding(side);
},
getArrowHeight: function() {
return this.actor.get_theme_node().get_length('-arrow-rise');
} }
}); });
Signals.addSignalMethods(BoxPointer.prototype);

View File

@ -17,18 +17,16 @@ const SHOW_WEEKDATE_KEY = 'show-weekdate';
// in org.gnome.desktop.interface // in org.gnome.desktop.interface
const CLOCK_FORMAT_KEY = 'clock-format'; 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) { function _sameYear(dateA, dateB) {
return (dateA.getYear() == dateB.getYear()); return (dateA.getYear() == dateB.getYear());
} }
function _sameMonth(dateA, dateB) {
return _sameYear(dateA, dateB) && (dateA.getMonth() == dateB.getMonth());
}
function _sameDay(dateA, dateB) {
return _sameMonth(dateA, dateB) && (dateA.getDate() == dateB.getDate());
}
/* TODO: maybe needs config - right now we assume that Saturday and /* 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 * Sunday are non-work days (not true in e.g. Israel, it's Sunday and
* Monday there) * Monday there)
@ -192,18 +190,16 @@ const EmptyEventSource = new Lang.Class({
}); });
Signals.addSignalMethods(EmptyEventSource.prototype); Signals.addSignalMethods(EmptyEventSource.prototype);
const CalendarServerIface = '<node> \ const CalendarServerIface = <interface name="org.gnome.Shell.CalendarServer">
<interface name="org.gnome.Shell.CalendarServer"> \ <method name="GetEvents">
<method name="GetEvents"> \ <arg type="x" direction="in" />
<arg type="x" direction="in" /> \ <arg type="x" direction="in" />
<arg type="x" direction="in" /> \ <arg type="b" direction="in" />
<arg type="b" direction="in" /> \ <arg type="a(sssbxxa{sv})" direction="out" />
<arg type="a(sssbxxa{sv})" direction="out" /> \ </method>
</method> \ <property name="HasCalendars" type="b" access="read" />
<property name="HasCalendars" type="b" access="read" /> \ <signal name="Changed" />
<signal name="Changed" /> \ </interface>;
</interface> \
</node>';
const CalendarServerInfo = Gio.DBusInterfaceInfo.new_for_xml(CalendarServerIface); const CalendarServerInfo = Gio.DBusInterfaceInfo.new_for_xml(CalendarServerIface);
@ -331,22 +327,25 @@ const DBusEventSource = new Lang.Class({
return; return;
if (this._curRequestBegin && this._curRequestEnd){ if (this._curRequestBegin && this._curRequestEnd){
let callFlags = Gio.DBusCallFlags.NO_AUTO_START;
if (forceReload)
callFlags = Gio.DBusCallFlags.NONE;
this._dbusProxy.GetEventsRemote(this._curRequestBegin.getTime() / 1000, this._dbusProxy.GetEventsRemote(this._curRequestBegin.getTime() / 1000,
this._curRequestEnd.getTime() / 1000, this._curRequestEnd.getTime() / 1000,
forceReload, forceReload,
Lang.bind(this, this._onEventsReceived), Lang.bind(this, this._onEventsReceived),
Gio.DBusCallFlags.NONE); callFlags);
} }
}, },
requestRange: function(begin, end) { requestRange: function(begin, end, forceReload) {
if (!(_datesEqual(begin, this._lastRequestBegin) && _datesEqual(end, this._lastRequestEnd))) { if (forceReload || !(_datesEqual(begin, this._lastRequestBegin) && _datesEqual(end, this._lastRequestEnd))) {
this.isLoading = true; this.isLoading = true;
this._lastRequestBegin = begin; this._lastRequestBegin = begin;
this._lastRequestEnd = end; this._lastRequestEnd = end;
this._curRequestBegin = begin; this._curRequestBegin = begin;
this._curRequestEnd = end; this._curRequestEnd = end;
this._loadEvents(false); this._loadEvents(forceReload);
} }
}, },
@ -418,19 +417,21 @@ const Calendar = new Lang.Class({
setEventSource: function(eventSource) { setEventSource: function(eventSource) {
this._eventSource = eventSource; this._eventSource = eventSource;
this._eventSource.connect('changed', Lang.bind(this, function() { this._eventSource.connect('changed', Lang.bind(this, function() {
this._update(); this._update(false);
})); }));
this._update(); this._update(true);
}, },
// Sets the calendar to show a specific date // Sets the calendar to show a specific date
setDate: function(date) { setDate: function(date, forceReload) {
if (_sameDay(date, this._selectedDate)) if (!_sameDay(date, this._selectedDate)) {
return;
this._selectedDate = date; this._selectedDate = date;
this._update(); this._update(forceReload);
this.emit('selected-date-changed', new Date(this._selectedDate)); this.emit('selected-date-changed', new Date(this._selectedDate));
} else {
if (forceReload)
this._update(forceReload);
}
}, },
_buildHeader: function() { _buildHeader: function() {
@ -443,17 +444,14 @@ const Calendar = new Lang.Class({
{ row: 0, col: 0, col_span: offsetCols + 7 }); { row: 0, col: 0, col_span: offsetCols + 7 });
this._backButton = new St.Button({ style_class: 'calendar-change-month-back', this._backButton = new St.Button({ style_class: 'calendar-change-month-back',
accessible_name: _("Previous month"),
can_focus: true }); can_focus: true });
this._topBox.add(this._backButton); this._topBox.add(this._backButton);
this._backButton.connect('clicked', Lang.bind(this, this._onPrevMonthButtonClicked)); this._backButton.connect('clicked', Lang.bind(this, this._onPrevMonthButtonClicked));
this._monthLabel = new St.Label({style_class: 'calendar-month-label', this._monthLabel = new St.Label({style_class: 'calendar-month-label'});
can_focus: true });
this._topBox.add(this._monthLabel, { expand: true, x_fill: false, x_align: St.Align.MIDDLE }); this._topBox.add(this._monthLabel, { expand: true, x_fill: false, x_align: St.Align.MIDDLE });
this._forwardButton = new St.Button({ style_class: 'calendar-change-month-forward', this._forwardButton = new St.Button({ style_class: 'calendar-change-month-forward',
accessible_name: _("Next month"),
can_focus: true }); can_focus: true });
this._topBox.add(this._forwardButton); this._topBox.add(this._forwardButton);
this._forwardButton.connect('clicked', Lang.bind(this, this._onNextMonthButtonClicked)); this._forwardButton.connect('clicked', Lang.bind(this, this._onNextMonthButtonClicked));
@ -494,7 +492,6 @@ const Calendar = new Lang.Class({
this._onNextMonthButtonClicked(); this._onNextMonthButtonClicked();
break; break;
} }
return Clutter.EVENT_PROPAGATE;
}, },
_onPrevMonthButtonClicked: function() { _onPrevMonthButtonClicked: function() {
@ -518,7 +515,7 @@ const Calendar = new Lang.Class({
this._backButton.grab_key_focus(); this._backButton.grab_key_focus();
this.setDate(newDate); this.setDate(newDate, false);
}, },
_onNextMonthButtonClicked: function() { _onNextMonthButtonClicked: function() {
@ -542,25 +539,28 @@ const Calendar = new Lang.Class({
this._forwardButton.grab_key_focus(); this._forwardButton.grab_key_focus();
this.setDate(newDate); this.setDate(newDate, false);
}, },
_onSettingsChange: function() { _onSettingsChange: function() {
this._useWeekdate = this._settings.get_boolean(SHOW_WEEKDATE_KEY); this._useWeekdate = this._settings.get_boolean(SHOW_WEEKDATE_KEY);
this._buildHeader(); this._buildHeader();
this._update(); this._update(false);
}, },
_rebuildCalendar: function() { _update: function(forceReload) {
let now = new Date(); 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);
// Remove everything but the topBox and the weekday labels // Remove everything but the topBox and the weekday labels
let children = this.actor.get_children(); let children = this.actor.get_children();
for (let i = this._firstDayIndex; i < children.length; i++) for (let i = this._firstDayIndex; i < children.length; i++)
children[i].destroy(); children[i].destroy();
this._buttons = [];
// Start at the beginning of the week before the start of the month // Start at the beginning of the week before the start of the month
// //
// We want to show always 6 weeks (to keep the calendar menu at the same // We want to show always 6 weeks (to keep the calendar menu at the same
@ -578,13 +578,11 @@ const Calendar = new Lang.Class({
// Actually computing the number of weeks is complex, but we know that the // Actually computing the number of weeks is complex, but we know that the
// problematic categories (2 and 4) always start on week start, and that // problematic categories (2 and 4) always start on week start, and that
// all months at the end have 6 weeks. // all months at the end have 6 weeks.
let beginDate = new Date(this._selectedDate); let beginDate = new Date(this._selectedDate);
beginDate.setDate(1); beginDate.setDate(1);
beginDate.setSeconds(0); beginDate.setSeconds(0);
beginDate.setHours(12); beginDate.setHours(12);
this._calendarBegin = new Date(beginDate);
let year = beginDate.getYear(); let year = beginDate.getYear();
let daysToWeekStart = (7 + beginDate.getDay() - this._weekStart) % 7; let daysToWeekStart = (7 + beginDate.getDay() - this._weekStart) % 7;
@ -605,18 +603,23 @@ const Calendar = new Lang.Class({
if (this._eventSource.isDummy) if (this._eventSource.isDummy)
button.reactive = false; button.reactive = false;
button._date = new Date(iter); let iterStr = iter.toUTCString();
button.connect('clicked', Lang.bind(this, function() { button.connect('clicked', Lang.bind(this, function() {
this.setDate(button._date); this._shouldDateGrabFocus = true;
let newlySelectedDate = new Date(iterStr);
this.setDate(newlySelectedDate, false);
this._shouldDateGrabFocus = false;
})); }));
let hasEvents = this._eventSource.hasEvents(iter); let hasEvents = this._eventSource.hasEvents(iter);
let styleClass = 'calendar-day-base calendar-day'; let styleClass = 'calendar-day-base calendar-day';
if (_isWorkDay(iter)) if (_isWorkDay(iter))
styleClass += ' calendar-work-day'; styleClass += ' calendar-work-day'
else else
styleClass += ' calendar-nonwork-day'; styleClass += ' calendar-nonwork-day'
// Hack used in lieu of border-collapse - see gnome-shell.css // Hack used in lieu of border-collapse - see gnome-shell.css
if (row == 2) if (row == 2)
@ -633,7 +636,7 @@ const Calendar = new Lang.Class({
styleClass += ' calendar-other-month-day'; styleClass += ' calendar-other-month-day';
if (hasEvents) if (hasEvents)
styleClass += ' calendar-day-with-events'; styleClass += ' calendar-day-with-events'
button.style_class = styleClass; button.style_class = styleClass;
@ -641,7 +644,12 @@ const Calendar = new Lang.Class({
this.actor.add(button, this.actor.add(button,
{ row: row, col: offsetCols + (7 + iter.getDay() - this._weekStart) % 7 }); { row: row, col: offsetCols + (7 + iter.getDay() - this._weekStart) % 7 });
this._buttons.push(button); if (_sameDay(this._selectedDate, iter)) {
button.add_style_pseudo_class('active');
if (this._shouldDateGrabFocus)
button.grab_key_focus();
}
if (this._useWeekdate && iter.getDay() == 4) { if (this._useWeekdate && iter.getDay() == 4) {
let label = new St.Label({ text: _getCalendarWeekForDate(iter).toString(), let label = new St.Label({ text: _getCalendarWeekForDate(iter).toString(),
@ -655,29 +663,9 @@ const Calendar = new Lang.Class({
if (iter.getDay() == this._weekStart) if (iter.getDay() == this._weekStart)
row++; row++;
} }
// Signal to the event source that we are interested in events // Signal to the event source that we are interested in events
// only from this date range // only from this date range
this._eventSource.requestRange(beginDate, iter); this._eventSource.requestRange(beginDate, iter, forceReload);
},
_update: function() {
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);
if (!this._calendarBegin || !_sameMonth(this._selectedDate, this._calendarBegin))
this._rebuildCalendar();
this._buttons.forEach(Lang.bind(this, function(button) {
if (_sameDay(button._date, this._selectedDate))
button.add_style_pseudo_class('active');
else
button.remove_style_pseudo_class('active');
}));
} }
}); });

View File

@ -1,40 +1,115 @@
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const Pango = imports.gi.Pango; const Pango = imports.gi.Pango;
const Shell = imports.gi.Shell;
const St = imports.gi.St; const St = imports.gi.St;
const Lang = imports.lang; const Lang = imports.lang;
const CheckBoxContainer = new Lang.Class({
Name: 'CheckBoxContainer',
_init: function() {
this.actor = new Shell.GenericContainer();
this.actor.connect('get-preferred-width',
Lang.bind(this, this._getPreferredWidth));
this.actor.connect('get-preferred-height',
Lang.bind(this, this._getPreferredHeight));
this.actor.connect('allocate',
Lang.bind(this, this._allocate));
this.actor.connect('style-changed', Lang.bind(this,
function() {
let node = this.actor.get_theme_node();
this._spacing = node.get_length('spacing');
}));
this.actor.request_mode = Clutter.RequestMode.HEIGHT_FOR_WIDTH;
this._box = new St.Bin();
this.actor.add_actor(this._box);
this.label = new St.Label();
this.label.clutter_text.set_line_wrap(true);
this.label.clutter_text.set_ellipsize(Pango.EllipsizeMode.NONE);
this.actor.add_actor(this.label);
this._spacing = 0;
},
_getPreferredWidth: function(actor, forHeight, alloc) {
let [minWidth, natWidth] = this._box.get_preferred_width(forHeight);
alloc.min_size = minWidth + this._spacing;
alloc.natural_size = natWidth + this._spacing;
},
_getPreferredHeight: function(actor, forWidth, alloc) {
/* FIXME: StBoxlayout currently does not handle
height-for-width children correctly, so hard-code
two lines for the label until that problem is fixed.
https://bugzilla.gnome.org/show_bug.cgi?id=672543 */
/*
let [minBoxHeight, natBoxHeight] =
this._box.get_preferred_height(forWidth);
let [minLabelHeight, natLabelHeight] =
this.label.get_preferred_height(forWidth);
alloc.min_size = Math.max(minBoxHeight, minLabelHeight);
alloc.natural_size = Math.max(natBoxHeight, natLabelHeight);
*/
let [minBoxHeight, natBoxHeight] =
this._box.get_preferred_height(-1);
let [minLabelHeight, natLabelHeight] =
this.label.get_preferred_height(-1);
alloc.min_size = Math.max(minBoxHeight, 2 * minLabelHeight);
alloc.natural_size = Math.max(natBoxHeight, 2 * natLabelHeight);
},
_allocate: function(actor, box, flags) {
let availWidth = box.x2 - box.x1;
let availHeight = box.y2 - box.y1;
let childBox = new Clutter.ActorBox();
let [minBoxWidth, natBoxWidth] =
this._box.get_preferred_width(-1);
let [minBoxHeight, natBoxHeight] =
this._box.get_preferred_height(-1);
childBox.x1 = box.x1;
childBox.x2 = box.x1 + natBoxWidth;
childBox.y1 = box.y1;
childBox.y2 = box.y1 + natBoxHeight;
this._box.allocate(childBox, flags);
childBox.x1 = box.x1 + natBoxWidth + this._spacing;
childBox.x2 = availWidth - childBox.x1;
childBox.y1 = box.y1;
childBox.y2 = box.y2;
this.label.allocate(childBox, flags);
}
});
const CheckBox = new Lang.Class({ const CheckBox = new Lang.Class({
Name: 'CheckBox', Name: 'CheckBox',
_init: function(label) { _init: function(label) {
let container = new St.BoxLayout();
this.actor = new St.Button({ style_class: 'check-box', this.actor = new St.Button({ style_class: 'check-box',
child: container,
button_mask: St.ButtonMask.ONE, button_mask: St.ButtonMask.ONE,
toggle_mode: true, toggle_mode: true,
can_focus: true, can_focus: true,
x_fill: true, x_fill: true,
y_fill: true }); y_fill: true });
this._container = new CheckBoxContainer();
this._box = new St.Bin(); this.actor.set_child(this._container.actor);
this._box.set_y_align(Clutter.ActorAlign.START);
container.add_actor(this._box);
this._label = new St.Label();
this._label.clutter_text.set_line_wrap(true);
this._label.clutter_text.set_ellipsize(Pango.EllipsizeMode.NONE);
container.add_actor(this._label);
if (label) if (label)
this.setLabel(label); this.setLabel(label);
}, },
setLabel: function(label) { setLabel: function(label) {
this._label.set_text(label); this._container.label.set_text(label);
}, },
getLabelActor: function() { getLabelActor: function() {
return this._label; return this._container.label;
} }
}); });

View File

@ -77,7 +77,7 @@ const AutomountManager = new Lang.Class({
})); }));
this._mountAllId = 0; this._mountAllId = 0;
return GLib.SOURCE_REMOVE; return false;
}, },
_onDriveConnected: function() { _onDriveConnected: function() {
@ -236,7 +236,7 @@ const AutomountManager = new Lang.Class({
_allowAutorunExpire: function(volume) { _allowAutorunExpire: function(volume) {
Mainloop.timeout_add_seconds(AUTORUN_EXPIRE_TIMEOUT_SECS, function() { Mainloop.timeout_add_seconds(AUTORUN_EXPIRE_TIMEOUT_SECS, function() {
volume.allowAutorun = false; volume.allowAutorun = false;
return GLib.SOURCE_REMOVE; return false;
}); });
} }
}); });

View File

@ -31,7 +31,7 @@ function shouldAutorunMount(mount, forTransient) {
if (!volume || (!volume.allowAutorun && forTransient)) if (!volume || (!volume.allowAutorun && forTransient))
return false; return false;
if (root.is_native() && isMountRootHidden(root)) if (!root.is_native() || isMountRootHidden(root))
return false; return false;
return true; return true;
@ -75,14 +75,12 @@ function startAppForMount(app, mount) {
/******************************************/ /******************************************/
const HotplugSnifferIface = '<node> \ const HotplugSnifferIface = <interface name="org.gnome.Shell.HotplugSniffer">
<interface name="org.gnome.Shell.HotplugSniffer"> \ <method name="SniffURI">
<method name="SniffURI"> \ <arg type="s" direction="in" />
<arg type="s" direction="in" /> \ <arg type="as" direction="out" />
<arg type="as" direction="out" /> \ </method>
</method> \ </interface>;
</interface> \
</node>';
const HotplugSnifferProxy = Gio.DBusProxy.makeProxyWrapper(HotplugSnifferIface); const HotplugSnifferProxy = Gio.DBusProxy.makeProxyWrapper(HotplugSnifferIface);
function HotplugSniffer() { function HotplugSniffer() {

View File

@ -13,6 +13,8 @@ const ModalDialog = imports.ui.modalDialog;
const ShellEntry = imports.ui.shellEntry; const ShellEntry = imports.ui.shellEntry;
const CheckBox = imports.ui.checkBox; const CheckBox = imports.ui.checkBox;
let prompter = null;
const KeyringDialog = new Lang.Class({ const KeyringDialog = new Lang.Class({
Name: 'KeyringDialog', Name: 'KeyringDialog',
Extends: ModalDialog.ModalDialog, Extends: ModalDialog.ModalDialog,
@ -45,9 +47,7 @@ const KeyringDialog = new Lang.Class({
this.prompt.bind_property('message', subject, 'text', GObject.BindingFlags.SYNC_CREATE); this.prompt.bind_property('message', subject, 'text', GObject.BindingFlags.SYNC_CREATE);
this._messageBox.add(subject, this._messageBox.add(subject,
{ x_fill: false, { y_fill: false,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.START }); y_align: St.Align.START });
let description = new St.Label({ style_class: 'prompt-dialog-description' }); let description = new St.Label({ style_class: 'prompt-dialog-description' });
@ -80,26 +80,23 @@ const KeyringDialog = new Lang.Class({
}, },
_buildControlTable: function() { _buildControlTable: function() {
let layout = new Clutter.TableLayout(); let table = new St.Table({ style_class: 'keyring-dialog-control-table' });
let table = new St.Widget({ style_class: 'keyring-dialog-control-table',
layout_manager: layout });
layout.hookup_style(table);
let row = 0; let row = 0;
if (this.prompt.password_visible) { if (this.prompt.password_visible) {
let label = new St.Label({ style_class: 'prompt-dialog-password-label' }); let label = new St.Label(({ style_class: 'prompt-dialog-password-label' }));
label.set_text(_("Password:")); label.set_text(_("Password:"));
label.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; table.add(label, { row: row, col: 0,
layout.pack(label, 0, row); x_expand: false, x_fill: true,
layout.child_set(label, { x_expand: false, y_fill: false, x_align: St.Align.START,
x_align: Clutter.TableAlignment.START }); y_fill: false, y_align: St.Align.MIDDLE });
this._passwordEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry', this._passwordEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
text: '', text: '',
can_focus: true}); can_focus: true});
this._passwordEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE this._passwordEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
ShellEntry.addContextMenu(this._passwordEntry, { isPassword: true }); ShellEntry.addContextMenu(this._passwordEntry, { isPassword: true });
this._passwordEntry.clutter_text.connect('activate', Lang.bind(this, this._onPasswordActivate)); this._passwordEntry.clutter_text.connect('activate', Lang.bind(this, this._onPasswordActivate));
layout.pack(this._passwordEntry, 1, row); table.add(this._passwordEntry, { row: row, col: 1, x_expand: true, x_fill: true, x_align: St.Align.START });
row++; row++;
} else { } else {
this._passwordEntry = null; this._passwordEntry = null;
@ -108,16 +105,17 @@ const KeyringDialog = new Lang.Class({
if (this.prompt.confirm_visible) { if (this.prompt.confirm_visible) {
var label = new St.Label(({ style_class: 'prompt-dialog-password-label' })); var label = new St.Label(({ style_class: 'prompt-dialog-password-label' }));
label.set_text(_("Type again:")); label.set_text(_("Type again:"));
layout.pack(label, 0, row); table.add(label, { row: row, col: 0,
layout.child_set(label, { x_expand: false, y_fill: false, x_expand: false, x_fill: true,
x_align: Clutter.TableAlignment.START }); x_align: St.Align.START,
y_fill: false, y_align: St.Align.MIDDLE });
this._confirmEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry', this._confirmEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
text: '', text: '',
can_focus: true}); can_focus: true});
this._confirmEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE this._confirmEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
ShellEntry.addContextMenu(this._confirmEntry, { isPassword: true }); ShellEntry.addContextMenu(this._confirmEntry, { isPassword: true });
this._confirmEntry.clutter_text.connect('activate', Lang.bind(this, this._onConfirmActivate)); this._confirmEntry.clutter_text.connect('activate', Lang.bind(this, this._onConfirmActivate));
layout.pack(this._confirmEntry, 1, row); table.add(this._confirmEntry, { row: row, col: 1, x_expand: true, x_fill: true, x_align: St.Align.START });
row++; row++;
} else { } else {
this._confirmEntry = null; this._confirmEntry = null;
@ -130,15 +128,14 @@ const KeyringDialog = new Lang.Class({
let choice = new CheckBox.CheckBox(); let choice = new CheckBox.CheckBox();
this.prompt.bind_property('choice-label', choice.getLabelActor(), 'text', GObject.BindingFlags.SYNC_CREATE); this.prompt.bind_property('choice-label', choice.getLabelActor(), 'text', GObject.BindingFlags.SYNC_CREATE);
this.prompt.bind_property('choice-chosen', choice.actor, 'checked', GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.BIDIRECTIONAL); this.prompt.bind_property('choice-chosen', choice.actor, 'checked', GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.BIDIRECTIONAL);
layout.pack(choice.actor, 1, row); table.add(choice.actor, { row: row, col: 1, x_expand: false, x_fill: true, x_align: St.Align.START });
row++; row++;
} }
let warning = new St.Label({ style_class: 'prompt-dialog-error-label' }); let warning = new St.Label({ style_class: 'prompt-dialog-error-label' });
warning.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; warning.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
warning.clutter_text.line_wrap = true; warning.clutter_text.line_wrap = true;
layout.pack(warning, 1, row); table.add(warning, { row: row, col: 1, x_expand: false, x_fill: false, x_align: St.Align.START });
layout.child_set(warning, { x_fill: false, x_align: Clutter.TableAlignment.START });
this.prompt.bind_property('warning-visible', warning, 'visible', GObject.BindingFlags.SYNC_CREATE); this.prompt.bind_property('warning-visible', warning, 'visible', GObject.BindingFlags.SYNC_CREATE);
this.prompt.bind_property('warning', warning, 'text', GObject.BindingFlags.SYNC_CREATE); this.prompt.bind_property('warning', warning, 'text', GObject.BindingFlags.SYNC_CREATE);
@ -224,56 +221,27 @@ const KeyringDialog = new Lang.Class({
}, },
}); });
const KeyringDummyDialog = new Lang.Class({
Name: 'KeyringDummyDialog',
_init: function() {
this.prompt = new Shell.KeyringPrompt();
this.prompt.connect('show-password',
Lang.bind(this, this._cancelPrompt));
this.prompt.connect('show-confirm', Lang.bind(this,
this._cancelPrompt));
},
_cancelPrompt: function() {
this.prompt.cancel();
}
});
const KeyringPrompter = new Lang.Class({ const KeyringPrompter = new Lang.Class({
Name: 'KeyringPrompter', Name: 'KeyringPrompter',
_init: function() { _init: function() {
this._prompter = new Gcr.SystemPrompter(); this._prompter = new Gcr.SystemPrompter();
this._prompter.connect('new-prompt', Lang.bind(this, this._prompter.connect('new-prompt', function(prompter) {
function() { let dialog = new KeyringDialog();
let dialog = this._enabled ? new KeyringDialog() return dialog.prompt;
: new KeyringDummyDialog(); });
this._currentPrompt = dialog.prompt;
return this._currentPrompt;
}));
this._dbusId = null; this._dbusId = null;
this._registered = false;
this._enabled = false;
this._currentPrompt = null;
}, },
enable: function() { enable: function() {
if (!this._registered) {
this._prompter.register(Gio.DBus.session); this._prompter.register(Gio.DBus.session);
this._dbusId = Gio.DBus.session.own_name('org.gnome.keyring.SystemPrompter', this._dbusId = Gio.DBus.session.own_name('org.gnome.keyring.SystemPrompter',
Gio.BusNameOwnerFlags.ALLOW_REPLACEMENT, null, null); Gio.BusNameOwnerFlags.ALLOW_REPLACEMENT, null, null);
this._registered = true;
}
this._enabled = true;
}, },
disable: function() { disable: function() {
this._enabled = false; this._prompter.unregister(false);
Gio.DBus.session.unown_name(this._dbusId);
if (this._prompter.prompting)
this._currentPrompt.cancel();
this._currentPrompt = null;
} }
}); });

View File

@ -62,9 +62,14 @@ const NetworkSecretDialog = new Lang.Class({
if (this._content.message != null) { if (this._content.message != null) {
let descriptionLabel = new St.Label({ style_class: 'prompt-dialog-description', let descriptionLabel = new St.Label({ style_class: 'prompt-dialog-description',
text: this._content.message }); text: this._content.message,
// HACK: for reasons unknown to me, the label
// is not asked the correct height for width,
// and thus is underallocated
// place a fixed height to avoid overflowing
style: 'height: 3em'
});
descriptionLabel.clutter_text.line_wrap = true; descriptionLabel.clutter_text.line_wrap = true;
descriptionLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
messageBox.add(descriptionLabel, messageBox.add(descriptionLabel,
{ y_fill: true, { y_fill: true,
@ -72,18 +77,13 @@ const NetworkSecretDialog = new Lang.Class({
expand: true }); expand: true });
} }
let layout = new Clutter.TableLayout(); let secretTable = new St.Table({ style_class: 'network-dialog-secret-table' });
let secretTable = new St.Widget({ style_class: 'network-dialog-secret-table',
layout_manager: layout });
layout.hookup_style(secretTable);
let initialFocusSet = false; let initialFocusSet = false;
let pos = 0; let pos = 0;
for (let i = 0; i < this._content.secrets.length; i++) { for (let i = 0; i < this._content.secrets.length; i++) {
let secret = this._content.secrets[i]; let secret = this._content.secrets[i];
let label = new St.Label({ style_class: 'prompt-dialog-password-label', let label = new St.Label({ style_class: 'prompt-dialog-password-label',
text: secret.label }); text: secret.label });
label.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
let reactive = secret.key != null; let reactive = secret.key != null;
@ -116,10 +116,11 @@ const NetworkSecretDialog = new Lang.Class({
} else } else
secret.valid = true; secret.valid = true;
layout.pack(label, 0, pos); secretTable.add(label, { row: pos, col: 0,
layout.child_set(label, { x_expand: false, y_fill: false, x_expand: false, x_fill: true,
x_align: Clutter.TableAlignment.START }); x_align: St.Align.START,
layout.pack(secret.entry, 1, pos); y_fill: false, y_align: St.Align.MIDDLE });
secretTable.add(secret.entry, { row: pos, col: 1, x_expand: true, x_fill: true, y_align: St.Align.END });
pos++; pos++;
if (secret.password) if (secret.password)
@ -138,8 +139,6 @@ const NetworkSecretDialog = new Lang.Class({
key: Clutter.KEY_Escape, key: Clutter.KEY_Escape,
}, },
this._okButton]); this._okButton]);
this._updateOkButton();
}, },
_updateOkButton: function() { _updateOkButton: function() {
@ -255,7 +254,6 @@ const NetworkSecretDialog = new Lang.Class({
case 'leap': case 'leap':
case 'ttls': case 'ttls':
case 'peap': case 'peap':
case 'fast':
// TTLS and PEAP are actually much more complicated, but this complication // TTLS and PEAP are actually much more complicated, but this complication
// is not visible here since we only care about phase2 authentication // is not visible here since we only care about phase2 authentication
// (and don't even care of which one) // (and don't even care of which one)
@ -387,7 +385,11 @@ const VPNRequestHandler = new Lang.Class({
this._childPid = pid; this._childPid = pid;
this._stdin = new Gio.UnixOutputStream({ fd: stdin, close_fd: true }); this._stdin = new Gio.UnixOutputStream({ fd: stdin, close_fd: true });
this._stdout = new Gio.UnixInputStream({ fd: stdout, close_fd: true }); this._stdout = new Gio.UnixInputStream({ fd: stdout, close_fd: true });
GLib.close(stderr); // We need this one too, even if don't actually care of what the process
// has to say on stderr, because otherwise the fd opened by g_spawn_async_with_pipes
// is kept open indefinitely
let stderrStream = new Gio.UnixInputStream({ fd: stderr, close_fd: true });
stderrStream.close(null);
this._dataStdout = new Gio.DataInputStream({ base_stream: this._stdout }); this._dataStdout = new Gio.DataInputStream({ base_stream: this._stdout });
if (this._newStylePlugin) if (this._newStylePlugin)
@ -435,7 +437,6 @@ const VPNRequestHandler = new Lang.Class({
}, },
_vpnChildFinished: function(pid, status, requestObj) { _vpnChildFinished: function(pid, status, requestObj) {
this._childWatch = 0;
if (this._newStylePlugin) { if (this._newStylePlugin) {
// For new style plugin, all work is done in the async reading functions // For new style plugin, all work is done in the async reading functions
// Just reap the process here // Just reap the process here

View File

@ -54,9 +54,7 @@ const AuthenticationDialog = new Lang.Class({
text: _("Authentication Required") }); text: _("Authentication Required") });
messageBox.add(this._subjectLabel, messageBox.add(this._subjectLabel,
{ x_fill: false, { y_fill: false,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.START }); y_align: St.Align.START });
this._descriptionLabel = new St.Label({ style_class: 'prompt-dialog-description', this._descriptionLabel = new St.Label({ style_class: 'prompt-dialog-description',
@ -65,9 +63,7 @@ const AuthenticationDialog = new Lang.Class({
this._descriptionLabel.clutter_text.line_wrap = true; this._descriptionLabel.clutter_text.line_wrap = true;
messageBox.add(this._descriptionLabel, messageBox.add(this._descriptionLabel,
{ x_fill: false, { y_fill: true,
y_fill: true,
x_align: St.Align.START,
y_align: St.Align.START }); y_align: St.Align.START });
if (userNames.length > 1) { if (userNames.length > 1) {
@ -99,8 +95,7 @@ const AuthenticationDialog = new Lang.Class({
if (userIsRoot) { if (userIsRoot) {
let userLabel = new St.Label(({ style_class: 'polkit-dialog-user-root-label', let userLabel = new St.Label(({ style_class: 'polkit-dialog-user-root-label',
text: userRealName })); text: userRealName }));
messageBox.add(userLabel, { x_fill: false, messageBox.add(userLabel);
x_align: St.Align.START });
} else { } else {
let userBox = new St.BoxLayout({ style_class: 'polkit-dialog-user-layout', let userBox = new St.BoxLayout({ style_class: 'polkit-dialog-user-layout',
vertical: false }); vertical: false });
@ -142,7 +137,7 @@ const AuthenticationDialog = new Lang.Class({
this._errorMessageLabel = new St.Label({ style_class: 'prompt-dialog-error-label' }); this._errorMessageLabel = new St.Label({ style_class: 'prompt-dialog-error-label' });
this._errorMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; this._errorMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
this._errorMessageLabel.clutter_text.line_wrap = true; this._errorMessageLabel.clutter_text.line_wrap = true;
messageBox.add(this._errorMessageLabel, { x_fill: false, x_align: St.Align.START }); messageBox.add(this._errorMessageLabel);
this._errorMessageLabel.hide(); this._errorMessageLabel.hide();
this._infoMessageLabel = new St.Label({ style_class: 'prompt-dialog-info-label' }); this._infoMessageLabel = new St.Label({ style_class: 'prompt-dialog-info-label' });

View File

@ -0,0 +1,61 @@
const Lang = imports.lang;
const Main = imports.ui.main;
const Gio = imports.gi.Gio;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const Recorder = new Lang.Class({
Name: 'Recorder',
_init: function() {
this._recorderSettings = new Gio.Settings({ schema: 'org.gnome.shell.recorder' });
this._desktopLockdownSettings = new Gio.Settings({ schema: 'org.gnome.desktop.lockdown' });
this._bindingSettings = new Gio.Settings({ schema: 'org.gnome.shell.keybindings' });
this._recorder = null;
},
enable: function() {
Main.wm.addKeybinding('toggle-recording',
this._bindingSettings,
Meta.KeyBindingFlags.NONE,
Shell.KeyBindingMode.ALL,
Lang.bind(this, this._toggleRecorder));
},
disable: function() {
Main.wm.removeKeybinding('toggle-recording');
},
_ensureRecorder: function() {
if (this._recorder == null)
this._recorder = new Shell.Recorder({ stage: global.stage });
return this._recorder;
},
_toggleRecorder: function() {
let recorder = this._ensureRecorder();
if (recorder.is_recording()) {
recorder.close();
Meta.enable_unredirect_for_screen(global.screen);
} else if (!this._desktopLockdownSettings.get_boolean('disable-save-to-disk')) {
// read the parameters from GSettings always in case they have changed
recorder.set_framerate(this._recorderSettings.get_int('framerate'));
/* Translators: this is a filename used for screencast recording */
// xgettext:no-c-format
recorder.set_file_template(_("Screencast from %d %t") + '.' + this._recorderSettings.get_string('file-extension'));
let pipeline = this._recorderSettings.get_string('pipeline');
if (!pipeline.match(/^\s*$/))
recorder.set_pipeline(pipeline);
else
recorder.set_pipeline(null);
Meta.disable_unredirect_for_screen(global.screen);
recorder.record();
}
}
});
const Component = Recorder;

View File

@ -13,6 +13,7 @@ const Tp = imports.gi.TelepathyGLib;
const History = imports.misc.history; const History = imports.misc.history;
const Main = imports.ui.main; const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray; const MessageTray = imports.ui.messageTray;
const NotificationDaemon = imports.ui.notificationDaemon;
const Params = imports.misc.params; const Params = imports.misc.params;
const PopupMenu = imports.ui.popupMenu; const PopupMenu = imports.ui.popupMenu;
@ -415,7 +416,7 @@ const TelepathyClient = new Lang.Class({
_ensureAppSource: function() { _ensureAppSource: function() {
if (this._appSource == null) { if (this._appSource == null) {
this._appSource = new MessageTray.Source(_("Chat"), 'empathy'); this._appSource = new MessageTray.Source(_("Chat"), 'empathy');
this._appSource.policy = new MessageTray.NotificationApplicationPolicy('empathy'); this._appSource.policy = new NotificationDaemon.NotificationApplicationPolicy('empathy');
Main.messageTray.add(this._appSource); Main.messageTray.add(this._appSource);
this._appSource.connect('destroy', Lang.bind(this, function () { this._appSource.connect('destroy', Lang.bind(this, function () {
@ -446,7 +447,6 @@ const ChatSource = new Lang.Class({
this._closedId = this._channel.connect('invalidated', Lang.bind(this, this._channelClosed)); this._closedId = this._channel.connect('invalidated', Lang.bind(this, this._channelClosed));
this._notification = new ChatNotification(this); this._notification = new ChatNotification(this);
this._notification.connect('clicked', Lang.bind(this, this.open));
this._notification.setUrgency(MessageTray.Urgency.HIGH); this._notification.setUrgency(MessageTray.Urgency.HIGH);
this._notifyTimeoutId = 0; this._notifyTimeoutId = 0;
@ -488,7 +488,7 @@ const ChatSource = new Lang.Class({
}, },
_createPolicy: function() { _createPolicy: function() {
return new MessageTray.NotificationApplicationPolicy('empathy'); return new NotificationDaemon.NotificationApplicationPolicy('empathy');
}, },
_updateAlias: function() { _updateAlias: function() {
@ -545,13 +545,14 @@ const ChatSource = new Lang.Class({
this._notification.update(this._notification.title, null, { customContent: true }); this._notification.update(this._notification.title, null, { customContent: true });
}, },
open: function() { open: function(notification) {
if (this._client.is_handling_channel(this._channel)) { if (this._client.is_handling_channel(this._channel)) {
// We are handling the channel, try to pass it to Empathy // We are handling the channel, try to pass it to Empathy
this._client.delegate_channels_async([this._channel], this._client.delegate_channels_async([this._channel],
global.get_current_time(), global.get_current_time(),
'org.freedesktop.Telepathy.Client.Empathy.Chat', null); 'org.freedesktop.Telepathy.Client.Empathy.Chat', null);
} else { }
else {
// We are not the handler, just ask to present the channel // We are not the handler, just ask to present the channel
let dbus = Tp.DBusDaemon.dup(); let dbus = Tp.DBusDaemon.dup();
let cd = Tp.ChannelDispatcher.new(dbus); let cd = Tp.ChannelDispatcher.new(dbus);
@ -675,7 +676,7 @@ const ChatSource = new Lang.Class({
this._notifyTimeoutId = 0; this._notifyTimeoutId = 0;
return GLib.SOURCE_REMOVE; return false;
}, },
// This is called for both messages we send from // This is called for both messages we send from
@ -960,8 +961,6 @@ const ChatNotification = new Lang.Class({
}, },
appendTimestamp: function() { appendTimestamp: function() {
this._timestampTimeoutId = 0;
let lastMessageTime = this._history[0].time; let lastMessageTime = this._history[0].time;
let lastMessageDate = new Date(lastMessageTime * 1000); let lastMessageDate = new Date(lastMessageTime * 1000);
@ -975,7 +974,7 @@ const ChatNotification = new Lang.Class({
this._filterMessages(); this._filterMessages();
return GLib.SOURCE_REMOVE; return false;
}, },
appendAliasChange: function(oldAlias, newAlias) { appendAliasChange: function(oldAlias, newAlias) {
@ -1013,7 +1012,7 @@ const ChatNotification = new Lang.Class({
this.source.setChatState(Tp.ChannelChatState.PAUSED); this.source.setChatState(Tp.ChannelChatState.PAUSED);
return GLib.SOURCE_REMOVE; return false;
}, },
_onEntryChanged: function() { _onEntryChanged: function() {
@ -1062,7 +1061,7 @@ const ApproverSource = new Lang.Class({
}, },
_createPolicy: function() { _createPolicy: function() {
return new MessageTray.NotificationApplicationPolicy('empathy'); return new NotificationDaemon.NotificationApplicationPolicy('empathy');
}, },
destroy: function() { destroy: function() {
@ -1097,16 +1096,22 @@ const RoomInviteNotification = new Lang.Class({
* for example. */ * for example. */
this.addBody(_("%s is inviting you to join %s").format(inviter.get_alias(), channel.get_identifier())); this.addBody(_("%s is inviting you to join %s").format(inviter.get_alias(), channel.get_identifier()));
this.addAction(_("Decline"), Lang.bind(this, function() { this.addButton('decline', _("Decline"));
dispatchOp.leave_channels_async(Tp.ChannelGroupChangeReason.NONE, '', function(src, result) { this.addButton('accept', _("Accept"));
src.leave_channels_finish(result);
}); this.connect('action-invoked', Lang.bind(this, function(self, action) {
this.destroy(); switch (action) {
})); case 'decline':
this.addAction(_("Accept"), Lang.bind(this, function() { dispatchOp.leave_channels_async(Tp.ChannelGroupChangeReason.NONE,
dispatchOp.handle_with_time_async('', global.get_current_time(), function(src, result) { '', function(src, result) {
src.handle_with_time_finish(result); src.leave_channels_finish(result)});
}); break;
case 'accept':
dispatchOp.handle_with_time_async('', global.get_current_time(),
function(src, result) {
src.handle_with_time_finish(result)});
break;
}
this.destroy(); this.destroy();
})); }));
} }
@ -1130,19 +1135,23 @@ const AudioVideoNotification = new Lang.Class({
this.parent(source, title, null, { customContent: true }); this.parent(source, title, null, { customContent: true });
this.setResident(true); this.setResident(true);
this.setUrgency(MessageTray.Urgency.CRITICAL); this.addButton('reject', _("Decline"));
this.addAction(_("Decline"), Lang.bind(this, function() {
dispatchOp.leave_channels_async(Tp.ChannelGroupChangeReason.NONE, '', function(src, result) {
src.leave_channels_finish(result);
});
this.destroy();
}));
/* translators: this is a button label (verb), not a noun */ /* translators: this is a button label (verb), not a noun */
this.addAction(_("Answer"), Lang.bind(this, function() { this.addButton('answer', _("Answer"));
dispatchOp.handle_with_time_async('', global.get_current_time(), function(src, result) {
src.handle_with_time_finish(result); this.connect('action-invoked', Lang.bind(this, function(self, action) {
}); switch (action) {
case 'reject':
dispatchOp.leave_channels_async(Tp.ChannelGroupChangeReason.NONE,
'', function(src, result) {
src.leave_channels_finish(result)});
break;
case 'answer':
dispatchOp.handle_with_time_async('', global.get_current_time(),
function(src, result) {
src.handle_with_time_finish(result)});
break;
}
this.destroy(); this.destroy();
})); }));
} }
@ -1166,16 +1175,22 @@ const FileTransferNotification = new Lang.Class({
{ customContent: true }); { customContent: true });
this.setResident(true); this.setResident(true);
this.addAction(_("Decline"), Lang.bind(this, function() { this.addButton('decline', _("Decline"));
dispatchOp.leave_channels_async(Tp.ChannelGroupChangeReason.NONE, '', function(src, result) { this.addButton('accept', _("Accept"));
src.leave_channels_finish(result);
}); this.connect('action-invoked', Lang.bind(this, function(self, action) {
this.destroy(); switch (action) {
})); case 'decline':
this.addAction(_("Accept"), Lang.bind(this, function() { dispatchOp.leave_channels_async(Tp.ChannelGroupChangeReason.NONE,
dispatchOp.handle_with_time_async('', global.get_current_time(), function(src, result) { '', function(src, result) {
src.handle_with_time_finish(result); src.leave_channels_finish(result)});
}); break;
case 'accept':
dispatchOp.handle_with_time_async('', global.get_current_time(),
function(src, result) {
src.handle_with_time_finish(result)});
break;
}
this.destroy(); this.destroy();
})); }));
} }
@ -1223,20 +1238,27 @@ const SubscriptionRequestNotification = new Lang.Class({
this.addActor(layout); this.addActor(layout);
this.addAction(_("Decline"), Lang.bind(this, function() { this.addButton('decline', _("Decline"));
this.addButton('accept', _("Accept"));
this.connect('action-invoked', Lang.bind(this, function(self, action) {
switch (action) {
case 'decline':
contact.remove_async(function(src, result) { contact.remove_async(function(src, result) {
src.remove_finish(result); src.remove_finish(result)});
}); break;
})); case 'accept':
this.addAction(_("Accept"), Lang.bind(this, function() {
// Authorize the contact and request to see his status as well // Authorize the contact and request to see his status as well
contact.authorize_publication_async(function(src, result) { contact.authorize_publication_async(function(src, result) {
src.authorize_publication_finish(result); src.authorize_publication_finish(result)});
});
contact.request_subscription_async('', function(src, result) { contact.request_subscription_async('', function(src, result) {
src.request_subscription_finish(result); src.request_subscription_finish(result)});
}); break;
}
// rely on _subscriptionStatesChangedCb to destroy the
// notification
})); }));
this._changedId = contact.connect('subscription-states-changed', this._changedId = contact.connect('subscription-states-changed',
@ -1335,11 +1357,18 @@ const AccountNotification = new Lang.Class({
this._account = account; this._account = account;
this.addAction(_("View account"), Lang.bind(this, function() { this.addButton('view', _("View account"));
this.connect('action-invoked', Lang.bind(this, function(self, action) {
switch (action) {
case 'view':
let cmd = 'empathy-accounts --select-account=' + let cmd = 'empathy-accounts --select-account=' +
account.get_path_suffix(); account.get_path_suffix();
let app_info = Gio.app_info_create_from_commandline(cmd, null, 0); let app_info = Gio.app_info_create_from_commandline(cmd, null, 0);
app_info.launch([], global.create_app_launch_context()); app_info.launch([], global.create_app_launch_context());
break;
}
this.destroy();
})); }));
this._enabledId = account.connect('notify::enabled', this._enabledId = account.connect('notify::enabled',

View File

@ -58,10 +58,14 @@ const CtrlAltTabManager = new Lang.Class({
}, },
focusGroup: function(item, timestamp) { focusGroup: function(item, timestamp) {
if (item.focusCallback) if (item.focusCallback) {
item.focusCallback(timestamp); item.focusCallback(timestamp);
else } else {
if (global.stage_input_mode == Shell.StageInputMode.NORMAL)
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
item.root.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false); item.root.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
}
}, },
// Sort the items into a consistent order; panel first, tray last, // Sort the items into a consistent order; panel first, tray last,
@ -132,6 +136,8 @@ const CtrlAltTabManager = new Lang.Class({
}, },
_focusWindows: function(timestamp) { _focusWindows: function(timestamp) {
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
global.stage.key_focus = null;
global.screen.focus_default_window(timestamp); global.screen.focus_default_window(timestamp);
} }
}); });

View File

@ -1,7 +1,6 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const Signals = imports.signals; const Signals = imports.signals;
const Lang = imports.lang; const Lang = imports.lang;
const Meta = imports.gi.Meta; const Meta = imports.gi.Meta;
@ -424,10 +423,7 @@ const Dash = new Lang.Class({
this._appSystem = Shell.AppSystem.get_default(); this._appSystem = Shell.AppSystem.get_default();
this._appSystem.connect('installed-changed', Lang.bind(this, function() { this._appSystem.connect('installed-changed', Lang.bind(this, this._queueRedisplay));
AppFavorites.getAppFavorites().reload();
this._queueRedisplay();
}));
AppFavorites.getAppFavorites().connect('changed', Lang.bind(this, this._queueRedisplay)); AppFavorites.getAppFavorites().connect('changed', Lang.bind(this, this._queueRedisplay));
this._appSystem.connect('app-state-changed', Lang.bind(this, this._queueRedisplay)); this._appSystem.connect('app-state-changed', Lang.bind(this, this._queueRedisplay));
@ -506,21 +502,15 @@ const Dash = new Lang.Class({
Main.queueDeferredWork(this._workId); Main.queueDeferredWork(this._workId);
}, },
_hookUpLabel: function(item, appIcon) { _hookUpLabel: function(item) {
item.child.connect('notify::hover', Lang.bind(this, function() { item.child.connect('notify::hover', Lang.bind(this, function() {
this._syncLabel(item, appIcon); this._onHover(item);
})); }));
Main.overview.connect('hiding', Lang.bind(this, function() { Main.overview.connect('hiding', Lang.bind(this, function() {
this._labelShowing = false; this._labelShowing = false;
item.hideLabel(); item.hideLabel();
})); }));
if (appIcon) {
appIcon.connect('sync-tooltip', Lang.bind(this, function() {
this._syncLabel(item, appIcon);
}));
}
}, },
_createAppItem: function(app) { _createAppItem: function(app) {
@ -549,7 +539,7 @@ const Dash = new Lang.Class({
item.setLabelText(app.get_name()); item.setLabelText(app.get_name());
appIcon.icon.setIconSize(this.iconSize); appIcon.icon.setIconSize(this.iconSize);
this._hookUpLabel(item, appIcon); this._hookUpLabel(item);
return item; return item;
}, },
@ -567,18 +557,15 @@ const Dash = new Lang.Class({
} }
}, },
_syncLabel: function (item, appIcon) { _onHover: function (item) {
let shouldShow = appIcon ? appIcon.shouldShowTooltip() : item.child.get_hover(); if (item.child.get_hover()) {
if (shouldShow) {
if (this._showLabelTimeoutId == 0) { if (this._showLabelTimeoutId == 0) {
let timeout = this._labelShowing ? 0 : DASH_ITEM_HOVER_TIMEOUT; let timeout = this._labelShowing ? 0 : DASH_ITEM_HOVER_TIMEOUT;
this._showLabelTimeoutId = Mainloop.timeout_add(timeout, this._showLabelTimeoutId = Mainloop.timeout_add(timeout,
Lang.bind(this, function() { Lang.bind(this, function() {
this._labelShowing = true; this._labelShowing = true;
item.showLabel(); item.showLabel();
this._showLabelTimeoutId = 0; return false;
return GLib.SOURCE_REMOVE;
})); }));
if (this._resetHoverTimeoutId > 0) { if (this._resetHoverTimeoutId > 0) {
Mainloop.source_remove(this._resetHoverTimeoutId); Mainloop.source_remove(this._resetHoverTimeoutId);
@ -594,8 +581,7 @@ const Dash = new Lang.Class({
this._resetHoverTimeoutId = Mainloop.timeout_add(DASH_ITEM_HOVER_TIMEOUT, this._resetHoverTimeoutId = Mainloop.timeout_add(DASH_ITEM_HOVER_TIMEOUT,
Lang.bind(this, function() { Lang.bind(this, function() {
this._labelShowing = false; this._labelShowing = false;
this._resetHoverTimeoutId = 0; return false;
return GLib.SOURCE_REMOVE;
})); }));
} }
} }

View File

@ -49,13 +49,11 @@ const DateMenuButton = new Lang.Class({
menuAlignment = 1.0 - menuAlignment; menuAlignment = 1.0 - menuAlignment;
this.parent(menuAlignment); this.parent(menuAlignment);
this._clockDisplay = new St.Label({ y_align: Clutter.ActorAlign.CENTER }); this._clockDisplay = new St.Label();
this.actor.label_actor = this._clockDisplay;
this.actor.add_actor(this._clockDisplay); this.actor.add_actor(this._clockDisplay);
this.actor.add_style_class_name ('clock-display');
hbox = new St.BoxLayout({name: 'calendarArea' }); hbox = new St.BoxLayout({name: 'calendarArea' });
this.menu.box.add_child(hbox); this.menu.addActor(hbox);
// Fill up the first column // Fill up the first column
@ -63,8 +61,9 @@ const DateMenuButton = new Lang.Class({
hbox.add(vbox); hbox.add(vbox);
// Date // Date
this._date = new St.Label({ style_class: 'datemenu-date-label', this._date = new St.Label();
can_focus: true }); this.actor.label_actor = this._clockDisplay;
this._date.style_class = 'datemenu-date-label';
vbox.add(this._date); vbox.add(this._date);
this._eventList = new Calendar.EventsList(); this._eventList = new Calendar.EventsList();
@ -113,7 +112,22 @@ const DateMenuButton = new Lang.Class({
this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) { this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) {
if (isOpen) { if (isOpen) {
let now = new Date(); let now = new Date();
this._calendar.setDate(now); /* 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
} }
})); }));

View File

@ -1,11 +1,9 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk; const Gtk = imports.gi.Gtk;
const St = imports.gi.St; const St = imports.gi.St;
const Lang = imports.lang; const Lang = imports.lang;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const Signals = imports.signals; const Signals = imports.signals;
const Tweener = imports.ui.tweener; const Tweener = imports.ui.tweener;
@ -28,9 +26,9 @@ const DragMotionResult = {
}; };
const DRAG_CURSOR_MAP = { const DRAG_CURSOR_MAP = {
0: Meta.Cursor.DND_UNSUPPORTED_TARGET, 0: Shell.Cursor.DND_UNSUPPORTED_TARGET,
1: Meta.Cursor.DND_COPY, 1: Shell.Cursor.DND_COPY,
2: Meta.Cursor.DND_MOVE 2: Shell.Cursor.DND_MOVE
}; };
const DragDropResult = { const DragDropResult = {
@ -86,6 +84,11 @@ const _Draggable = new Lang.Class({
this.actor.connect('destroy', Lang.bind(this, function() { this.actor.connect('destroy', Lang.bind(this, function() {
this._actorDestroyed = true; this._actorDestroyed = true;
// If the drag actor is destroyed and we were going to fix
// up its hover state, fix up the parent hover state instead
if (this.actor == this._firstLeaveActor)
this._firstLeaveActor = this._dragOrigParent;
if (this._dragInProgress && this._dragCancellable) if (this._dragInProgress && this._dragCancellable)
this._cancelDrag(global.get_current_time()); this._cancelDrag(global.get_current_time());
this.disconnectAll(); this.disconnectAll();
@ -101,15 +104,21 @@ const _Draggable = new Lang.Class({
this._animationInProgress = false; // The drag is over and the item is in the process of animating to its original position (snapping back or reverting). this._animationInProgress = false; // The drag is over and the item is in the process of animating to its original position (snapping back or reverting).
this._dragCancellable = true; this._dragCancellable = true;
// During the drag, we eat enter/leave events so that actors don't prelight.
// But we remember the actors that we first left/last entered so we can
// fix up the hover state after the drag ends.
this._firstLeaveActor = null;
this._lastEnterActor = null;
this._eventsGrabbed = false; this._eventsGrabbed = false;
}, },
_onButtonPress : function (actor, event) { _onButtonPress : function (actor, event) {
if (event.get_button() != 1) if (event.get_button() != 1)
return Clutter.EVENT_PROPAGATE; return false;
if (Tweener.getTweenCount(actor)) if (Tweener.getTweenCount(actor))
return Clutter.EVENT_PROPAGATE; return false;
this._buttonDown = true; this._buttonDown = true;
this._grabActor(); this._grabActor();
@ -118,7 +127,7 @@ const _Draggable = new Lang.Class({
this._dragStartX = stageX; this._dragStartX = stageX;
this._dragStartY = stageY; this._dragStartY = stageY;
return Clutter.EVENT_PROPAGATE; return false;
}, },
_grabActor: function() { _grabActor: function() {
@ -164,11 +173,11 @@ const _Draggable = new Lang.Class({
} else if (this._dragActor != null && !this._animationInProgress) { } else if (this._dragActor != null && !this._animationInProgress) {
// Drag must have been cancelled with Esc. // Drag must have been cancelled with Esc.
this._dragComplete(); this._dragComplete();
return Clutter.EVENT_STOP; return true;
} else { } else {
// Drag has never started. // Drag has never started.
this._ungrabActor(); this._ungrabActor();
return Clutter.EVENT_PROPAGATE; return false;
} }
// We intercept MOTION event to figure out if the drag has started and to draw // We intercept MOTION event to figure out if the drag has started and to draw
// this._dragActor under the pointer when dragging is in progress // this._dragActor under the pointer when dragging is in progress
@ -184,11 +193,16 @@ const _Draggable = new Lang.Class({
let symbol = event.get_key_symbol(); let symbol = event.get_key_symbol();
if (symbol == Clutter.Escape) { if (symbol == Clutter.Escape) {
this._cancelDrag(event.get_time()); this._cancelDrag(event.get_time());
return Clutter.EVENT_STOP; 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 Clutter.EVENT_PROPAGATE; return false;
}, },
/** /**
@ -229,14 +243,14 @@ const _Draggable = new Lang.Class({
if (this._onEventId) if (this._onEventId)
this._ungrabActor(); this._ungrabActor();
this._grabEvents(); this._grabEvents();
global.screen.set_cursor(Meta.Cursor.DND_IN_DRAG); global.set_cursor(Shell.Cursor.DND_IN_DRAG);
this._dragX = this._dragStartX = stageX; this._dragX = this._dragStartX = stageX;
this._dragY = this._dragStartY = stageY; this._dragY = this._dragStartY = stageY;
if (this.actor._delegate && this.actor._delegate.getDragActor) { if (this.actor._delegate && this.actor._delegate.getDragActor) {
this._dragActor = this.actor._delegate.getDragActor(); this._dragActor = this.actor._delegate.getDragActor();
Main.uiGroup.add_child(this._dragActor); this._dragActor.reparent(Main.uiGroup);
this._dragActor.raise_top(); this._dragActor.raise_top();
Shell.util_set_hidden_from_pick(this._dragActor, true); Shell.util_set_hidden_from_pick(this._dragActor, true);
@ -285,8 +299,7 @@ const _Draggable = new Lang.Class({
this._dragOffsetX = actorStageX - this._dragStartX; this._dragOffsetX = actorStageX - this._dragStartX;
this._dragOffsetY = actorStageY - this._dragStartY; this._dragOffsetY = actorStageY - this._dragStartY;
this._dragOrigParent.remove_actor(this._dragActor); this._dragActor.reparent(Main.uiGroup);
Main.uiGroup.add_child(this._dragActor);
this._dragActor.raise_top(); this._dragActor.raise_top();
Shell.util_set_hidden_from_pick(this._dragActor, true); Shell.util_set_hidden_from_pick(this._dragActor, true);
} }
@ -345,13 +358,25 @@ const _Draggable = new Lang.Class({
return true; return true;
}, },
_updateDragHover : function () { _updateDragPosition : function (event) {
this._updateHoverId = 0; let [stageX, stageY] = event.get_coords();
this._dragX = stageX;
this._dragY = stageY;
// If we are dragging, update the position
if (this._dragActor) {
this._dragActor.set_position(stageX + this._dragOffsetX,
stageY + this._dragOffsetY);
let target = this._dragActor.get_stage().get_actor_at_pos(Clutter.PickMode.ALL, let target = this._dragActor.get_stage().get_actor_at_pos(Clutter.PickMode.ALL,
this._dragX, this._dragY); stageX, stageY);
// We call observers only once per motion with the innermost
// target actor. If necessary, the observer can walk the
// parent itself.
let dragEvent = { let dragEvent = {
x: this._dragX, x: stageX,
y: this._dragY, y: stageY,
dragActor: this._dragActor, dragActor: this._dragActor,
source: this.actor._delegate, source: this.actor._delegate,
targetActor: target targetActor: target
@ -361,15 +386,14 @@ const _Draggable = new Lang.Class({
if (motionFunc) { if (motionFunc) {
let result = motionFunc(dragEvent); let result = motionFunc(dragEvent);
if (result != DragMotionResult.CONTINUE) { if (result != DragMotionResult.CONTINUE) {
global.screen.set_cursor(DRAG_CURSOR_MAP[result]); global.set_cursor(DRAG_CURSOR_MAP[result]);
return GLib.SOURCE_REMOVE; return true;
} }
} }
} }
while (target) { while (target) {
if (target._delegate && target._delegate.handleDragOver) { if (target._delegate && target._delegate.handleDragOver) {
let [r, targX, targY] = target.transform_stage_point(this._dragX, this._dragY); let [r, targX, targY] = target.transform_stage_point(stageX, stageY);
// We currently loop through all parents on drag-over even if one of the children has handled it. // We currently loop through all parents on drag-over even if one of the children has handled it.
// We can check the return value of the function and break the loop if it's true if we don't want // We can check the return value of the function and break the loop if it's true if we don't want
// to continue checking the parents. // to continue checking the parents.
@ -377,34 +401,17 @@ const _Draggable = new Lang.Class({
this._dragActor, this._dragActor,
targX, targX,
targY, targY,
0); event.get_time());
if (result != DragMotionResult.CONTINUE) { if (result != DragMotionResult.CONTINUE) {
global.screen.set_cursor(DRAG_CURSOR_MAP[result]); global.set_cursor(DRAG_CURSOR_MAP[result]);
return GLib.SOURCE_REMOVE; return true;
} }
} }
target = target.get_parent(); target = target.get_parent();
} }
global.screen.set_cursor(Meta.Cursor.DND_IN_DRAG); global.set_cursor(Shell.Cursor.DND_IN_DRAG);
return GLib.SOURCE_REMOVE; }
},
_queueUpdateDragHover: function() {
if (this._updateHoverId)
return;
this._updateHoverId = GLib.idle_add(GLib.PRIORITY_DEFAULT,
Lang.bind(this, this._updateDragHover));
},
_updateDragPosition : function (event) {
let [stageX, stageY] = event.get_coords();
this._dragX = stageX;
this._dragY = stageY;
this._dragActor.set_position(stageX + this._dragOffsetX,
stageY + this._dragOffsetY);
this._queueUpdateDragHover();
return true; return true;
}, },
@ -457,7 +464,7 @@ const _Draggable = new Lang.Class({
} }
this._dragInProgress = false; this._dragInProgress = false;
global.screen.set_cursor(Meta.Cursor.DEFAULT); global.unset_cursor();
this.emit('drag-end', event.get_time(), true); this.emit('drag-end', event.get_time(), true);
this._dragComplete(); this._dragComplete();
return true; return true;
@ -509,7 +516,7 @@ const _Draggable = new Lang.Class({
let [snapBackX, snapBackY, snapBackScale] = this._getRestoreLocation(); let [snapBackX, snapBackY, snapBackScale] = this._getRestoreLocation();
if (this._actorDestroyed) { if (this._actorDestroyed) {
global.screen.set_cursor(Meta.Cursor.DEFAULT); global.unset_cursor();
if (!this._buttonDown) if (!this._buttonDown)
this._dragComplete(); this._dragComplete();
this.emit('drag-end', eventTime, false); this.emit('drag-end', eventTime, false);
@ -557,14 +564,13 @@ const _Draggable = new Lang.Class({
_onAnimationComplete : function (dragActor, eventTime) { _onAnimationComplete : function (dragActor, eventTime) {
if (this._dragOrigParent) { if (this._dragOrigParent) {
Main.uiGroup.remove_child(this._dragActor); dragActor.reparent(this._dragOrigParent);
this._dragOrigParent.add_actor(this._dragActor);
dragActor.set_scale(this._dragOrigScale, this._dragOrigScale); dragActor.set_scale(this._dragOrigScale, this._dragOrigScale);
dragActor.set_position(this._dragOrigX, this._dragOrigY); dragActor.set_position(this._dragOrigX, this._dragOrigY);
} else { } else {
dragActor.destroy(); dragActor.destroy();
} }
global.screen.set_cursor(Meta.Cursor.DEFAULT); global.unset_cursor();
this.emit('drag-end', eventTime, false); this.emit('drag-end', eventTime, false);
this._animationInProgress = false; this._animationInProgress = false;
@ -572,16 +578,32 @@ const _Draggable = new Lang.Class({
this._dragComplete(); 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() { _dragComplete: function() {
if (!this._actorDestroyed) if (!this._actorDestroyed)
Shell.util_set_hidden_from_pick(this._dragActor, false); Shell.util_set_hidden_from_pick(this._dragActor, false);
this._ungrabEvents(); this._ungrabEvents();
global.sync_pointer();
if (this._updateHoverId) { if (this._firstLeaveActor) {
GLib.source_remove(this._updateHoverId); this._syncHover(this._firstLeaveActor);
this._updateHoverId = 0; this._firstLeaveActor = null;
}
if (this._lastEnterActor) {
this._syncHover(this._lastEnterActor);
this._lastEnterActor = null;
} }
this._dragActor = undefined; this._dragActor = undefined;

View File

@ -20,6 +20,7 @@
const Lang = imports.lang; const Lang = imports.lang;
const Mainloop = imports.mainloop; const Mainloop = imports.mainloop;
const Signals = imports.signals;
const AccountsService = imports.gi.AccountsService; const AccountsService = imports.gi.AccountsService;
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
@ -31,7 +32,7 @@ const St = imports.gi.St;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const GnomeSession = imports.misc.gnomeSession; const GnomeSession = imports.misc.gnomeSession;
const LoginManager = imports.misc.loginManager; const Main = imports.ui.main;
const ModalDialog = imports.ui.modalDialog; const ModalDialog = imports.ui.modalDialog;
const Tweener = imports.ui.tweener; const Tweener = imports.ui.tweener;
const UserWidget = imports.ui.userWidget; const UserWidget = imports.ui.userWidget;
@ -43,118 +44,81 @@ const _DIALOG_ICON_SIZE = 32;
const GSM_SESSION_MANAGER_LOGOUT_FORCE = 2; const GSM_SESSION_MANAGER_LOGOUT_FORCE = 2;
const EndSessionDialogIface = '<node> \ const EndSessionDialogIface = <interface name="org.gnome.SessionManager.EndSessionDialog">
<interface name="org.gnome.SessionManager.EndSessionDialog"> \ <method name="Open">
<method name="Open"> \ <arg type="u" direction="in" />
<arg type="u" direction="in" /> \ <arg type="u" direction="in" />
<arg type="u" direction="in" /> \ <arg type="u" direction="in" />
<arg type="u" direction="in" /> \ <arg type="ao" direction="in" />
<arg type="ao" direction="in" /> \ </method>
</method> \ <method name="Close" />
<method name="Close" /> \ <signal name="ConfirmedLogout" />
<signal name="ConfirmedLogout" /> \ <signal name="ConfirmedReboot" />
<signal name="ConfirmedReboot" /> \ <signal name="ConfirmedShutdown" />
<signal name="ConfirmedShutdown" /> \ <signal name="Canceled" />
<signal name="Canceled" /> \ <signal name="Closed" />
<signal name="Closed" /> \ </interface>;
</interface> \
</node>';
const logoutDialogContent = { const logoutDialogContent = {
subjectWithUser: C_("title", "Log Out %s"), subjectWithUser: C_("title", "Log Out %s"),
subject: C_("title", "Log Out"), subject: C_("title", "Log Out"),
descriptionWithUser: function(user, seconds) { inhibitedDescription: _("Click Log Out to quit these applications and log out of the system."),
uninhibitedDescriptionWithUser: function(user, seconds) {
return ngettext("%s will be logged out automatically in %d second.", return ngettext("%s will be logged out automatically in %d second.",
"%s will be logged out automatically in %d seconds.", "%s will be logged out automatically in %d seconds.",
seconds).format(user, seconds); seconds).format(user, seconds);
}, },
description: function(seconds) { uninhibitedDescription: function(seconds) {
return ngettext("You will be logged out automatically in %d second.", return ngettext("You will be logged out automatically in %d second.",
"You will be logged out automatically in %d seconds.", "You will be logged out automatically in %d seconds.",
seconds).format(seconds); seconds).format(seconds);
}, },
endDescription: _("Logging out of the system."),
confirmButtons: [{ signal: 'ConfirmedLogout', confirmButtons: [{ signal: 'ConfirmedLogout',
label: C_("button", "Log Out") }], label: C_("button", "Log Out") }],
iconStyleClass: 'end-session-dialog-logout-icon', iconStyleClass: 'end-session-dialog-logout-icon'
showOtherSessions: false,
}; };
const shutdownDialogContent = { const shutdownDialogContent = {
subject: C_("title", "Power Off"), subject: C_("title", "Power Off"),
description: function(seconds) { inhibitedDescription: _("Click Power Off to quit these applications and power off the system."),
uninhibitedDescription: function(seconds) {
return ngettext("The system will power off automatically in %d second.", return ngettext("The system will power off automatically in %d second.",
"The system will power off automatically in %d seconds.", "The system will power off automatically in %d seconds.",
seconds).format(seconds); seconds).format(seconds);
}, },
endDescription: _("Powering off the system."),
confirmButtons: [{ signal: 'ConfirmedReboot', confirmButtons: [{ signal: 'ConfirmedReboot',
label: C_("button", "Restart") }, label: C_("button", "Restart") },
{ signal: 'ConfirmedShutdown', { signal: 'ConfirmedShutdown',
label: C_("button", "Power Off") }], label: C_("button", "Power Off") }],
iconName: 'system-shutdown-symbolic', iconName: 'system-shutdown-symbolic',
iconStyleClass: 'end-session-dialog-shutdown-icon', iconStyleClass: 'end-session-dialog-shutdown-icon'
showOtherSessions: true,
}; };
const restartDialogContent = { const restartDialogContent = {
subject: C_("title", "Restart"), subject: C_("title", "Restart"),
description: function(seconds) { inhibitedDescription: _("Click Restart to quit these applications and restart the system."),
uninhibitedDescription: function(seconds) {
return ngettext("The system will restart automatically in %d second.", return ngettext("The system will restart automatically in %d second.",
"The system will restart automatically in %d seconds.", "The system will restart automatically in %d seconds.",
seconds).format(seconds); seconds).format(seconds);
}, },
endDescription: _("Restarting the system."),
confirmButtons: [{ signal: 'ConfirmedReboot', confirmButtons: [{ signal: 'ConfirmedReboot',
label: C_("button", "Restart") }], label: C_("button", "Restart") }],
iconName: 'view-refresh-symbolic', iconName: 'view-refresh-symbolic',
iconStyleClass: 'end-session-dialog-shutdown-icon', iconStyleClass: 'end-session-dialog-shutdown-icon'
showOtherSessions: true,
};
const restartInstallDialogContent = {
subject: C_("title", "Restart & Install Updates"),
description: function(seconds) {
return ngettext("The system will automatically restart and install updates in %d second.",
"The system will automatically restart and install updates in %d seconds.",
seconds).format(seconds);
},
confirmButtons: [{ signal: 'ConfirmedReboot',
label: C_("button", "Restart & Install") }],
iconName: 'view-refresh-symbolic',
iconStyleClass: 'end-session-dialog-shutdown-icon',
showOtherSessions: true,
}; };
const DialogContent = { const DialogContent = {
0 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_LOGOUT */: logoutDialogContent, 0 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_LOGOUT */: logoutDialogContent,
1 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_SHUTDOWN */: shutdownDialogContent, 1 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_SHUTDOWN */: shutdownDialogContent,
2 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_RESTART */: restartDialogContent, 2 /* GSM_SHELL_END_SESSION_DIALOG_TYPE_RESTART */: restartDialogContent
3: restartInstallDialogContent
}; };
const MAX_USERS_IN_SESSION_DIALOG = 5;
const LogindSessionIface = '<node> \
<interface name="org.freedesktop.login1.Session"> \
<property name="Id" type="s" access="read"/> \
<property name="Remote" type="b" access="read"/> \
<property name="Class" type="s" access="read"/> \
<property name="Type" type="s" access="read"/> \
<property name="State" type="s" access="read"/> \
</interface> \
</node>';
const LogindSession = Gio.DBusProxy.makeProxyWrapper(LogindSessionIface);
function findAppFromInhibitor(inhibitor) { function findAppFromInhibitor(inhibitor) {
let desktopFile; let [desktopFile] = inhibitor.GetAppIdSync();
try {
[desktopFile] = inhibitor.GetAppIdSync();
} catch(e) {
// XXX -- sometimes JIT inhibitors generated by gnome-session
// get removed too soon. Don't fail in this case.
log('gnome-session gave us a dead inhibitor: %s'.format(inhibitor.get_object_path()));
return null;
}
if (!GLib.str_has_suffix(desktopFile, '.desktop')) if (!GLib.str_has_suffix(desktopFile, '.desktop'))
desktopFile += '.desktop'; desktopFile += '.desktop';
@ -162,6 +126,58 @@ function findAppFromInhibitor(inhibitor) {
return Shell.AppSystem.get_default().lookup_heuristic_basename(desktopFile); return Shell.AppSystem.get_default().lookup_heuristic_basename(desktopFile);
} }
const ListItem = new Lang.Class({
Name: 'ListItem',
_init: function(app, reason) {
this._app = app;
this._reason = reason;
if (this._reason == null)
this._reason = '';
let layout = new St.BoxLayout({ vertical: false});
this.actor = new St.Button({ style_class: 'end-session-dialog-app-list-item',
can_focus: true,
child: layout,
reactive: true,
x_align: St.Align.START,
x_fill: true });
this._icon = this._app.create_icon_texture(_ITEM_ICON_SIZE);
let iconBin = new St.Bin({ style_class: 'end-session-dialog-app-list-item-icon',
child: this._icon });
layout.add(iconBin);
let textLayout = new St.BoxLayout({ style_class: 'end-session-dialog-app-list-item-text-box',
vertical: true });
layout.add(textLayout);
this._nameLabel = new St.Label({ text: this._app.get_name(),
style_class: 'end-session-dialog-app-list-item-name' });
textLayout.add(this._nameLabel,
{ expand: false,
x_fill: true });
this._descriptionLabel = new St.Label({ text: this._reason,
style_class: 'end-session-dialog-app-list-item-description' });
this.actor.label_actor = this._nameLabel;
textLayout.add(this._descriptionLabel,
{ expand: true,
x_fill: true });
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
},
_onClicked: function() {
this.emit('activate');
this._app.activate();
}
});
Signals.addSignalMethods(ListItem.prototype);
// The logout timer only shows updates every 10 seconds // The logout timer only shows updates every 10 seconds
// until the last 10 seconds, then it shows updates every // until the last 10 seconds, then it shows updates every
// second. This function takes a given time and returns // second. This function takes a given time and returns
@ -212,23 +228,22 @@ const EndSessionDialog = new Lang.Class({
this.parent({ styleClass: 'end-session-dialog', this.parent({ styleClass: 'end-session-dialog',
destroyOnClose: false }); destroyOnClose: false });
this._loginManager = LoginManager.getLoginManager(); this._user = AccountsService.UserManager.get_default().get_user(GLib.get_user_name());
this._userManager = AccountsService.UserManager.get_default();
this._user = this._userManager.get_user(GLib.get_user_name());
this._updatesFile = Gio.File.new_for_path('/system-update');
this._secondsLeft = 0; this._secondsLeft = 0;
this._totalSecondsToStayOpen = 0; this._totalSecondsToStayOpen = 0;
this._applications = []; this._inhibitors = [];
this._sessions = [];
this.connect('destroy', this.connect('destroy',
Lang.bind(this, this._onDestroy)); Lang.bind(this, this._onDestroy));
this.connect('opened', this.connect('opened',
Lang.bind(this, this._onOpened)); Lang.bind(this, this._onOpened));
this._userLoadedId = this._user.connect('notify::is_loaded', Lang.bind(this, this._sync)); this._userLoadedId = this._user.connect('notify::is_loaded',
this._userChangedId = this._user.connect('changed', Lang.bind(this, this._sync)); Lang.bind(this, this._updateContent));
this._userChangedId = this._user.connect('changed',
Lang.bind(this, this._updateContent));
let mainContentLayout = new St.BoxLayout({ vertical: false }); let mainContentLayout = new St.BoxLayout({ vertical: false });
this.contentLayout.add(mainContentLayout, this.contentLayout.add(mainContentLayout,
@ -249,9 +264,7 @@ const EndSessionDialog = new Lang.Class({
this._subjectLabel = new St.Label({ style_class: 'end-session-dialog-subject' }); this._subjectLabel = new St.Label({ style_class: 'end-session-dialog-subject' });
messageLayout.add(this._subjectLabel, messageLayout.add(this._subjectLabel,
{ x_fill: false, { y_fill: false,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.START }); y_align: St.Align.START });
this._descriptionLabel = new St.Label({ style_class: 'end-session-dialog-description' }); this._descriptionLabel = new St.Label({ style_class: 'end-session-dialog-description' });
@ -262,30 +275,28 @@ const EndSessionDialog = new Lang.Class({
{ y_fill: true, { y_fill: true,
y_align: St.Align.START }); y_align: St.Align.START });
this._scrollView = new St.ScrollView({ style_class: 'end-session-dialog-list' }); let scrollView = new St.ScrollView({ style_class: 'end-session-dialog-app-list'});
this._scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC); scrollView.set_policy(Gtk.PolicyType.NEVER,
this.contentLayout.add(this._scrollView, Gtk.PolicyType.AUTOMATIC);
this.contentLayout.add(scrollView,
{ x_fill: true, { x_fill: true,
y_fill: true }); y_fill: true });
this._scrollView.hide(); scrollView.hide();
this._inhibitorSection = new St.BoxLayout({ vertical: true, this._applicationList = new St.BoxLayout({ vertical: true });
style_class: 'end-session-dialog-inhibitor-layout' }); scrollView.add_actor(this._applicationList);
this._scrollView.add_actor(this._inhibitorSection);
this._applicationHeader = new St.Label({ style_class: 'end-session-dialog-list-header', this._applicationList.connect('actor-added',
text: _("Some applications are busy or have unsaved work.") }); Lang.bind(this, function() {
this._applicationList = new St.BoxLayout({ style_class: 'end-session-dialog-app-list', if (this._applicationList.get_n_children() == 1)
vertical: true }); scrollView.show();
this._inhibitorSection.add_actor(this._applicationHeader); }));
this._inhibitorSection.add_actor(this._applicationList);
this._sessionHeader = new St.Label({ style_class: 'end-session-dialog-list-header', this._applicationList.connect('actor-removed',
text: _("Other users are logged in.") }); Lang.bind(this, function() {
this._sessionList = new St.BoxLayout({ style_class: 'end-session-dialog-session-list', if (this._applicationList.get_n_children() == 0)
vertical: true }); scrollView.hide();
this._inhibitorSection.add_actor(this._sessionHeader); }));
this._inhibitorSection.add_actor(this._sessionList);
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(EndSessionDialogIface, this); this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(EndSessionDialogIface, this);
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/SessionManager/EndSessionDialog'); this._dbusImpl.export(Gio.DBus.session, '/org/gnome/SessionManager/EndSessionDialog');
@ -296,19 +307,20 @@ const EndSessionDialog = new Lang.Class({
this._user.disconnect(this._userChangedId); this._user.disconnect(this._userChangedId);
}, },
_sync: function() { _updateDescription: function() {
let open = (this.state == ModalDialog.State.OPENING || this.state == ModalDialog.State.OPENED); if (this.state != ModalDialog.State.OPENING &&
if (!open) this.state != ModalDialog.State.OPENED)
return; return;
if (this._type == 2 && this._updatesFile.query_exists(null))
this._type = 3;
let dialogContent = DialogContent[this._type]; let dialogContent = DialogContent[this._type];
let subject = dialogContent.subject; let subject = dialogContent.subject;
let description; let description;
if (this._inhibitors.length > 0) {
this._stopTimer();
description = dialogContent.inhibitedDescription;
} else if (this._secondsLeft > 0 && this._inhibitors.length == 0) {
let displayTime = _roundSecondsToInterval(this._totalSecondsToStayOpen, let displayTime = _roundSecondsToInterval(this._totalSecondsToStayOpen,
this._secondsLeft, this._secondsLeft,
10); 10);
@ -320,18 +332,27 @@ const EndSessionDialog = new Lang.Class({
if (dialogContent.subjectWithUser) if (dialogContent.subjectWithUser)
subject = dialogContent.subjectWithUser.format(realName); subject = dialogContent.subjectWithUser.format(realName);
if (dialogContent.descriptionWithUser) if (dialogContent.uninhibitedDescriptionWithUser)
description = dialogContent.descriptionWithUser(realName, displayTime); description = dialogContent.uninhibitedDescriptionWithUser(realName, displayTime);
else else
description = dialogContent.description(displayTime); description = dialogContent.uninhibitedDescription(displayTime);
} }
} }
if (!description) if (!description)
description = dialogContent.description(displayTime); description = dialogContent.uninhibitedDescription(displayTime);
} else {
description = dialogContent.endDescription;
}
_setLabelText(this._descriptionLabel, description);
_setLabelText(this._subjectLabel, subject); _setLabelText(this._subjectLabel, subject);
_setLabelText(this._descriptionLabel, description);
},
_updateContent: function() {
if (this.state != ModalDialog.State.OPENING &&
this.state != ModalDialog.State.OPENED)
return;
let dialogContent = DialogContent[this._type]; let dialogContent = DialogContent[this._type];
if (dialogContent.iconName) { if (dialogContent.iconName) {
@ -346,11 +367,7 @@ const EndSessionDialog = new Lang.Class({
avatarWidget.update(); avatarWidget.update();
} }
let hasApplications = this._applications.length > 0; this._updateDescription();
let hasSessions = this._sessions.length > 0;
this._scrollView.visible = hasApplications || hasSessions;
this._applicationHeader.visible = hasApplications;
this._sessionHeader.visible = hasSessions;
}, },
_updateButtons: function() { _updateButtons: function() {
@ -396,12 +413,14 @@ const EndSessionDialog = new Lang.Class({
}, },
_onOpened: function() { _onOpened: function() {
this._sync(); if (this._inhibitors.length == 0)
this._startTimer();
}, },
_startTimer: function() { _startTimer: function() {
let startTime = GLib.get_monotonic_time(); let startTime = GLib.get_monotonic_time();
this._secondsLeft = this._totalSecondsToStayOpen; this._secondsLeft = this._totalSecondsToStayOpen;
this._updateDescription();
this._timerId = Mainloop.timeout_add_seconds(1, Lang.bind(this, this._timerId = Mainloop.timeout_add_seconds(1, Lang.bind(this,
function() { function() {
@ -410,21 +429,20 @@ const EndSessionDialog = new Lang.Class({
this._secondsLeft = this._totalSecondsToStayOpen - secondsElapsed; this._secondsLeft = this._totalSecondsToStayOpen - secondsElapsed;
if (this._secondsLeft > 0) { if (this._secondsLeft > 0) {
this._sync(); this._updateDescription();
return GLib.SOURCE_CONTINUE; return true;
} }
let dialogContent = DialogContent[this._type]; let dialogContent = DialogContent[this._type];
let button = dialogContent.confirmButtons[dialogContent.confirmButtons.length - 1]; let button = dialogContent.confirmButtons[dialogContent.confirmButtons.length - 1];
this._confirm(button.signal); this._confirm(button.signal);
this._timerId = 0;
return GLib.SOURCE_REMOVE; return false;
})); }));
}, },
_stopTimer: function() { _stopTimer: function() {
if (this._timerId > 0) { if (this._timerId != 0) {
Mainloop.source_remove(this._timerId); Mainloop.source_remove(this._timerId);
this._timerId = 0; this._timerId = 0;
} }
@ -432,33 +450,8 @@ const EndSessionDialog = new Lang.Class({
this._secondsLeft = 0; this._secondsLeft = 0;
}, },
_constructListItemForApp: function(inhibitor, app) {
let actor = new St.BoxLayout({ style_class: 'end-session-dialog-app-list-item',
can_focus: true });
actor.add(app.create_icon_texture(_ITEM_ICON_SIZE));
let textLayout = new St.BoxLayout({ vertical: true,
y_expand: true,
y_align: Clutter.ActorAlign.CENTER });
actor.add(textLayout);
let nameLabel = new St.Label({ text: app.get_name(),
style_class: 'end-session-dialog-app-list-item-name' });
textLayout.add(nameLabel);
actor.label_actor = nameLabel;
let [reason] = inhibitor.GetReasonSync();
if (reason) {
let reasonLabel = new St.Label({ text: reason,
style_class: 'end-session-dialog-app-list-item-description' });
textLayout.add(reasonLabel);
}
return actor;
},
_onInhibitorLoaded: function(inhibitor) { _onInhibitorLoaded: function(inhibitor) {
if (this._applications.indexOf(inhibitor) < 0) { if (this._inhibitors.indexOf(inhibitor) < 0) {
// Stale inhibitor // Stale inhibitor
return; return;
} }
@ -466,91 +459,28 @@ const EndSessionDialog = new Lang.Class({
let app = findAppFromInhibitor(inhibitor); let app = findAppFromInhibitor(inhibitor);
if (app) { if (app) {
let actor = this._constructListItemForApp(inhibitor, app); let [reason] = inhibitor.GetReasonSync();
this._applicationList.add(actor); let item = new ListItem(app, reason);
item.connect('activate',
Lang.bind(this, function() {
this.close();
}));
this._applicationList.add(item.actor, { x_fill: true });
this._stopTimer();
} else { } else {
// inhibiting app is a service, not an application // inhibiting app is a service, not an application
this._applications.splice(this._applications.indexOf(inhibitor), 1); this._inhibitors.splice(this._inhibitors.indexOf(inhibitor), 1);
} }
this._sync(); this._updateContent();
},
_constructListItemForSession: function(session) {
let avatar = new UserWidget.Avatar(session.user, { iconSize: _ITEM_ICON_SIZE });
avatar.update();
let userName = session.user.get_real_name() ? session.user.get_real_name() : session.username;
let userLabelText;
if (session.remote)
/* Translators: Remote here refers to a remote session, like a ssh login */
userLabelText = _("%s (remote)").format(userName);
else if (session.type == "tty")
/* Translators: Console here refers to a tty like a VT console */
userLabelText = _("%s (console)").format(userName);
else
userLabelText = userName;
let actor = new St.BoxLayout({ style_class: 'end-session-dialog-session-list-item',
can_focus: true });
actor.add(avatar.actor);
let nameLabel = new St.Label({ text: userLabelText,
style_class: 'end-session-dialog-session-list-item-name',
y_expand: true,
y_align: Clutter.ActorAlign.CENTER });
actor.add(nameLabel);
actor.label_actor = nameLabel;
return actor;
},
_loadSessions: function() {
this._loginManager.listSessions(Lang.bind(this, function(result) {
let n = 0;
for (let i = 0; i < result.length; i++) {
let[id, uid, userName, seat, sessionPath] = result[i];
let proxy = new LogindSession(Gio.DBus.system, 'org.freedesktop.login1', sessionPath);
if (proxy.Class != 'user')
continue;
if (proxy.State == 'closing')
continue;
if (proxy.Id == GLib.getenv('XDG_SESSION_ID'))
continue;
let session = { user: this._userManager.get_user(userName),
username: userName,
type: proxy.Type,
remote: proxy.Remote };
this._sessions.push(session);
let actor = this._constructListItemForSession(session);
this._sessionList.add(actor);
// limit the number of entries
n++;
if (n == MAX_USERS_IN_SESSION_DIALOG)
break;
}
this._sync();
}));
}, },
OpenAsync: function(parameters, invocation) { OpenAsync: function(parameters, invocation) {
let [type, timestamp, totalSecondsToStayOpen, inhibitorObjectPaths] = parameters; let [type, timestamp, totalSecondsToStayOpen, inhibitorObjectPaths] = parameters;
this._totalSecondsToStayOpen = totalSecondsToStayOpen; this._totalSecondsToStayOpen = totalSecondsToStayOpen;
this._type = type; this._inhibitors = [];
this._applications = [];
this._applicationList.destroy_all_children(); this._applicationList.destroy_all_children();
this._type = type;
this._sessions = [];
this._sessionList.destroy_all_children();
if (!(this._type in DialogContent)) { if (!(this._type in DialogContent)) {
invocation.return_dbus_error('org.gnome.Shell.ModalDialog.TypeError', invocation.return_dbus_error('org.gnome.Shell.ModalDialog.TypeError',
@ -563,12 +493,9 @@ const EndSessionDialog = new Lang.Class({
this._onInhibitorLoaded(proxy); this._onInhibitorLoaded(proxy);
})); }));
this._applications.push(inhibitor); this._inhibitors.push(inhibitor);
} }
if (DialogContent[type].showOtherSessions)
this._loadSessions();
this._updateButtons(); this._updateButtons();
if (!this.open(timestamp)) { if (!this.open(timestamp)) {
@ -577,8 +504,7 @@ const EndSessionDialog = new Lang.Class({
return; return;
} }
this._startTimer(); this._updateContent();
this._sync();
let signalId = this.connect('opened', let signalId = this.connect('opened',
Lang.bind(this, function() { Lang.bind(this, function() {

View File

@ -10,7 +10,6 @@ const Clutter = imports.gi.Clutter;;
const Gettext = imports.gettext; const Gettext = imports.gettext;
const GLib = imports.gi.GLib; const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk; const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const St = imports.gi.St; const St = imports.gi.St;
@ -40,22 +39,6 @@ function _patchContainerClass(containerClass) {
}; };
} }
function _patchLayoutClass(layoutClass, styleProps) {
if (styleProps)
layoutClass.prototype.hookup_style = function(container) {
container.connect('style-changed', Lang.bind(this, function() {
let node = container.get_theme_node();
for (let prop in styleProps)
this[prop] = node.get_length(styleProps[prop]);
}));
};
layoutClass.prototype.child_set = function(actor, props) {
let meta = this.get_child_meta(actor.get_parent(), actor);
for (let prop in props)
meta[prop] = props[prop];
};
}
function _makeLoggingFunc(func) { function _makeLoggingFunc(func) {
return function() { return function() {
return func([].join.call(arguments, ', ')); return func([].join.call(arguments, ', '));
@ -77,12 +60,6 @@ function init() {
_patchContainerClass(St.BoxLayout); _patchContainerClass(St.BoxLayout);
_patchContainerClass(St.Table); _patchContainerClass(St.Table);
_patchLayoutClass(Clutter.TableLayout, { row_spacing: 'spacing-rows',
column_spacing: 'spacing-columns' });
_patchLayoutClass(Clutter.GridLayout, { row_spacing: 'spacing-rows',
column_spacing: 'spacing-columns' });
_patchLayoutClass(Clutter.BoxLayout, { spacing: 'spacing' });
Clutter.Actor.prototype.toString = function() { Clutter.Actor.prototype.toString = function() {
return St.describe_actor(this); return St.describe_actor(this);
}; };

View File

@ -76,11 +76,7 @@ function disableExtension(uuid) {
theme.unload_stylesheet(extension.stylesheet.get_path()); theme.unload_stylesheet(extension.stylesheet.get_path());
} }
try {
extension.stateObj.disable(); extension.stateObj.disable();
} catch(e) {
logExtensionError(uuid, e);
}
for (let i = 0; i < order.length; i++) { for (let i = 0; i < order.length; i++) {
let uuid = order[i]; let uuid = order[i];
@ -93,11 +89,9 @@ function disableExtension(uuid) {
extensionOrder.splice(orderIdx, 1); extensionOrder.splice(orderIdx, 1);
if ( extension.state != ExtensionState.ERROR ) {
extension.state = ExtensionState.DISABLED; extension.state = ExtensionState.DISABLED;
_signals.emit('extension-state-changed', extension); _signals.emit('extension-state-changed', extension);
} }
}
function enableExtension(uuid) { function enableExtension(uuid) {
let extension = ExtensionUtils.extensions[uuid]; let extension = ExtensionUtils.extensions[uuid];
@ -123,15 +117,10 @@ function enableExtension(uuid) {
} }
} }
try {
extension.stateObj.enable(); extension.stateObj.enable();
extension.state = ExtensionState.ENABLED; extension.state = ExtensionState.ENABLED;
_signals.emit('extension-state-changed', extension); _signals.emit('extension-state-changed', extension);
return;
} catch(e) {
logExtensionError(uuid, e);
return;
}
} }
function logExtensionError(uuid, error) { function logExtensionError(uuid, error) {
@ -161,8 +150,7 @@ function loadExtension(extension) {
} else { } else {
let enabled = enabledExtensions.indexOf(extension.uuid) != -1; let enabled = enabledExtensions.indexOf(extension.uuid) != -1;
if (enabled) { if (enabled) {
if (!initExtension(extension.uuid)) initExtension(extension.uuid);
return;
if (extension.state == ExtensionState.DISABLED) if (extension.state == ExtensionState.DISABLED)
enableExtension(extension.uuid); enableExtension(extension.uuid);
} else { } else {
@ -217,12 +205,7 @@ function initExtension(uuid) {
extensionModule = extension.imports.extension; extensionModule = extension.imports.extension;
if (extensionModule.init) { if (extensionModule.init) {
try {
extensionState = extensionModule.init(extension); extensionState = extensionModule.init(extension);
} catch(e) {
logExtensionError(uuid, e);
return false;
}
} }
if (!extensionState) if (!extensionState)
@ -231,7 +214,6 @@ function initExtension(uuid) {
extension.state = ExtensionState.DISABLED; extension.state = ExtensionState.DISABLED;
_signals.emit('extension-loaded', uuid); _signals.emit('extension-loaded', uuid);
return true;
} }
function getEnabledExtensions() { function getEnabledExtensions() {
@ -253,7 +235,11 @@ function onEnabledExtensionsChanged() {
newEnabledExtensions.filter(function(uuid) { newEnabledExtensions.filter(function(uuid) {
return enabledExtensions.indexOf(uuid) == -1; return enabledExtensions.indexOf(uuid) == -1;
}).forEach(function(uuid) { }).forEach(function(uuid) {
try {
enableExtension(uuid); enableExtension(uuid);
} catch(e) {
logExtensionError(uuid, e);
}
}); });
// Find and disable all the newly disabled extensions: UUIDs found in the // Find and disable all the newly disabled extensions: UUIDs found in the
@ -261,7 +247,11 @@ function onEnabledExtensionsChanged() {
enabledExtensions.filter(function(item) { enabledExtensions.filter(function(item) {
return newEnabledExtensions.indexOf(item) == -1; return newEnabledExtensions.indexOf(item) == -1;
}).forEach(function(uuid) { }).forEach(function(uuid) {
try {
disableExtension(uuid); disableExtension(uuid);
} catch(e) {
logExtensionError(uuid, e);
}
}); });
enabledExtensions = newEnabledExtensions; enabledExtensions = newEnabledExtensions;
@ -272,8 +262,12 @@ function _loadExtensions() {
enabledExtensions = getEnabledExtensions(); enabledExtensions = getEnabledExtensions();
let finder = new ExtensionUtils.ExtensionFinder(); let finder = new ExtensionUtils.ExtensionFinder();
finder.connect('extension-found', function(finder, extension) { finder.connect('extension-found', function(signals, extension) {
try {
loadExtension(extension); loadExtension(extension);
} catch(e) {
logExtensionError(extension.uuid, e);
}
}); });
finder.scanExtensions(); finder.scanExtensions();
} }

View File

@ -1,65 +0,0 @@
/** -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
* Copyright 2012 Inclusive Design Research Centre, OCAD University.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Author:
* Joseph Scheuhammer <clown@alum.mit.edu>
* Contributor:
* Magdalen Berns <m.berns@sms.ed.ac.uk>
*/
const Atspi = imports.gi.Atspi;
const Lang = imports.lang;
const Signals = imports.signals;
const CARETMOVED = 'object:text-caret-moved';
const STATECHANGED = 'object:state-changed';
const FocusCaretTracker = new Lang.Class({
Name: 'FocusCaretTracker',
_init: function() {
Atspi.init();
Atspi.set_timeout(250, 250);
this._atspiListener = Atspi.EventListener.new(Lang.bind(this, this._onChanged));
},
_onChanged: function(event) {
if (event.type.indexOf(STATECHANGED) == 0)
this.emit('focus-changed', event);
else if (event.type == CARETMOVED)
this.emit('caret-moved', event);
},
registerFocusListener: function() {
return this._atspiListener.register(STATECHANGED + ':focused') &&
this._atspiListener.register(STATECHANGED + ':selected');
},
registerCaretListener: function() {
return this._atspiListener.register(CARETMOVED);
},
deregisterFocusListener: function() {
return this._atspiListener.deregister(STATECHANGED + ':focused') &&
this._atspiListener.deregister(STATECHANGED + ':selected');
},
deregisterCaretListener: function() {
return this._atspiListener.deregister(CARETMOVED);
}
});
Signals.addSignalMethods(FocusCaretTracker.prototype);

View File

@ -10,31 +10,6 @@ const St = imports.gi.St;
const Main = imports.ui.main; const Main = imports.ui.main;
const Params = imports.misc.params; const Params = imports.misc.params;
let _capturedEventId = 0;
let _grabHelperStack = [];
function _onCapturedEvent(actor, event) {
let grabHelper = _grabHelperStack[_grabHelperStack.length - 1];
return grabHelper.onCapturedEvent(event);
}
function _pushGrabHelper(grabHelper) {
_grabHelperStack.push(grabHelper);
if (_capturedEventId == 0)
_capturedEventId = global.stage.connect('captured-event', _onCapturedEvent);
}
function _popGrabHelper(grabHelper) {
let poppedHelper = _grabHelperStack.pop();
if (poppedHelper != grabHelper)
throw new Error("incorrect grab helper pop");
if (_grabHelperStack.length == 0) {
global.stage.disconnect(_capturedEventId);
_capturedEventId = 0;
}
}
// GrabHelper: // GrabHelper:
// @owner: the actor that owns the GrabHelper // @owner: the actor that owns the GrabHelper
// @params: optional parameters to pass to Main.pushModal() // @params: optional parameters to pass to Main.pushModal()
@ -56,9 +31,14 @@ const GrabHelper = new Lang.Class({
this._grabStack = []; this._grabStack = [];
this._actors = []; this._actors = [];
this._capturedEventId = 0;
this._keyFocusNotifyId = 0;
this._focusWindowChangedId = 0;
this._ignoreRelease = false; this._ignoreRelease = false;
this._isUngrabbingCount = 0;
this._modalCount = 0; this._modalCount = 0;
this._grabFocusCount = 0;
}, },
// addActor: // addActor:
@ -138,36 +118,38 @@ const GrabHelper = new Lang.Class({
// grab: // grab:
// @params: A bunch of parameters, see below // @params: A bunch of parameters, see below
// //
// The general effect of a "grab" is to ensure that the passed in actor // Grabs the mouse and keyboard, according to the GrabHelper's
// and all actors inside the grab get exclusive control of the mouse and // parameters. If @newFocus is not %null, then the keyboard focus
// keyboard, with the grab automatically being dropped if the user tries // is moved to the first #StWidget:can-focus widget inside it.
// to dismiss it. The actor is passed in through @params.actor.
// //
// grab() can be called multiple times, with the scope of the grab being // The grab will automatically be dropped if:
// changed to a different actor every time. A nested grab does not have // - The user clicks outside the grabbed actors
// to have its grabbed actor inside the parent grab actors. // - The user types Escape
// - The keyboard focus is moved outside the grabbed actors
// - A window is focused
// //
// Grabs can be automatically dropped if the user tries to dismiss it // If @params.actor is not null, then it will be focused as the
// in one of two ways: the user clicking outside the currently grabbed // new actor. If you attempt to grab an already focused actor, the
// actor, or the user typing the Escape key. // request to be focused will be ignored. The actor will not be
// added to the grab stack, so do not call a paired ungrab().
// //
// If the user clicks outside the grabbed actors, and the clicked on // If @params contains { modal: true }, then grab() will push a modal
// actor is part of a previous grab in the stack, grabs will be popped // on the owner of the GrabHelper. As long as there is at least one
// until that grab is active. However, the click event will not be // { modal: true } actor on the grab stack, the grab will be kept.
// replayed to the actor. // When the last { modal: true } actor is ungrabbed, then the modal
// will be dropped. A modal grab can fail if there is already a grab
// in effect from aother application; in this case the function returns
// false and nothing happens. Non-modal grabs can never fail.
// //
// If the user types the Escape key, one grab from the grab stack will // If @params contains { grabFocus: true }, then if you call grab()
// be popped. // while the shell is outside the overview, it will set the stage
// // input mode to %Shell.StageInputMode.FOCUSED, and ungrab() will
// When a grab is popped by user interacting as described above, if you // revert it back, and re-focus the previously-focused window (if
// pass a callback as @params.onUngrab, it will be called with %true. // another window hasn't been explicitly focused before then).
//
// If @params.focus is not null, we'll set the key focus directly
// to that actor instead of navigating in @params.actor. This is for
// use cases like menus, where we want to grab the menu actor, but keep
// focus on the clicked on menu item.
grab: function(params) { grab: function(params) {
params = Params.parse(params, { actor: null, params = Params.parse(params, { actor: null,
modal: false,
grabFocus: false,
focus: null, focus: null,
onUngrab: null }); onUngrab: null });
@ -180,18 +162,24 @@ const GrabHelper = new Lang.Class({
params.savedFocus = focus; params.savedFocus = focus;
if (!this._takeModalGrab()) if (params.modal && !this._takeModalGrab())
return false;
if (params.grabFocus && !this._takeFocusGrab(hadFocus))
return false; return false;
this._grabStack.push(params); this._grabStack.push(params);
if (params.focus) { if (params.focus) {
params.focus.grab_key_focus(); params.focus.grab_key_focus();
} else if (newFocus && hadFocus) { } else if (newFocus && (hadFocus || params.grabFocus)) {
if (!newFocus.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false)) if (!newFocus.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false))
newFocus.grab_key_focus(); newFocus.grab_key_focus();
} }
if ((params.grabFocus || params.modal) && !this._capturedEventId)
this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
return true; return true;
}, },
@ -200,8 +188,6 @@ const GrabHelper = new Lang.Class({
if (firstGrab) { if (firstGrab) {
if (!Main.pushModal(this._owner, this._modalParams)) if (!Main.pushModal(this._owner, this._modalParams))
return false; return false;
_pushGrabHelper(this);
} }
this._modalCount++; this._modalCount++;
@ -213,14 +199,56 @@ const GrabHelper = new Lang.Class({
if (this._modalCount > 0) if (this._modalCount > 0)
return; return;
_popGrabHelper(this);
this._ignoreRelease = false;
Main.popModal(this._owner); Main.popModal(this._owner);
global.sync_pointer(); global.sync_pointer();
}, },
_takeFocusGrab: function(hadFocus) {
let firstGrab = (this._grabFocusCount == 0);
if (firstGrab) {
let metaDisplay = global.screen.get_display();
this._grabbedFromKeynav = hadFocus;
this._preGrabInputMode = global.stage_input_mode;
if (this._preGrabInputMode == Shell.StageInputMode.NORMAL)
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
this._keyFocusNotifyId = global.stage.connect('notify::key-focus', Lang.bind(this, this._onKeyFocusChanged));
this._focusWindowChangedId = metaDisplay.connect('notify::focus-window', Lang.bind(this, this._focusWindowChanged));
}
this._grabFocusCount++;
return true;
},
_releaseFocusGrab: function() {
this._grabFocusCount--;
if (this._grabFocusCount > 0)
return;
if (this._keyFocusNotifyId > 0) {
global.stage.disconnect(this._keyFocusNotifyId);
this._keyFocusNotifyId = 0;
}
if (this._focusWindowChangedId > 0) {
let metaDisplay = global.screen.get_display();
metaDisplay.disconnect(this._focusWindowChangedId);
this._focusWindowChangedId = 0;
}
let prePopInputMode = global.stage_input_mode;
if (this._grabbedFromKeynav) {
if (this._preGrabInputMode == Shell.StageInputMode.FOCUSED &&
prePopInputMode != Shell.StageInputMode.FULLSCREEN)
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
}
global.screen.focus_default_window(global.display.get_current_time_roundtrip());
},
// ignoreRelease: // ignoreRelease:
// //
// Make sure that the next button release event evaluated by the // Make sure that the next button release event evaluated by the
@ -234,14 +262,10 @@ const GrabHelper = new Lang.Class({
// ungrab: // ungrab:
// @params: The parameters for the grab; see below. // @params: The parameters for the grab; see below.
// //
// Pops @params.actor from the grab stack, potentially dropping // Pops an actor from the grab stack, potentially dropping the grab.
// the grab. If the actor is not on the grab stack, this call is
// ignored with no ill effects.
// //
// If the actor is not at the top of the grab stack, grabs are // If the actor that was popped from the grab stack was not the actor
// popped until the grabbed actor is at the top of the grab stack. // That was passed in, this call is ignored.
// The onUngrab callback for every grab is called for every popped
// grab with the parameter %false.
ungrab: function(params) { ungrab: function(params) {
params = Params.parse(params, { actor: this.currentGrab.actor, params = Params.parse(params, { actor: this.currentGrab.actor,
isUser: false }); isUser: false });
@ -250,6 +274,14 @@ const GrabHelper = new Lang.Class({
if (grabStackIndex < 0) if (grabStackIndex < 0)
return; return;
// We may get key focus changes when calling onUngrab, which
// would cause an extra ungrab() on the next actor in the
// stack, which is wrong. Ignore key focus changes during the
// ungrab, and restore the saved key focus ourselves afterwards.
// We use a count as ungrab() may be re-entrant, as onUngrab()
// may ungrab additional actors.
this._isUngrabbingCount++;
let focus = global.stage.key_focus; let focus = global.stage.key_focus;
let hadFocus = focus && this._isWithinGrabbedActor(focus); let hadFocus = focus && this._isWithinGrabbedActor(focus);
@ -264,7 +296,18 @@ const GrabHelper = new Lang.Class({
if (poppedGrab.onUngrab) if (poppedGrab.onUngrab)
poppedGrab.onUngrab(params.isUser); poppedGrab.onUngrab(params.isUser);
if (poppedGrab.modal)
this._releaseModalGrab(); this._releaseModalGrab();
if (poppedGrab.grabFocus)
this._releaseFocusGrab();
}
if (!this.grabbed && this._capturedEventId > 0) {
global.stage.disconnect(this._capturedEventId);
this._capturedEventId = 0;
this._ignoreRelease = false;
} }
if (hadFocus) { if (hadFocus) {
@ -272,15 +315,17 @@ const GrabHelper = new Lang.Class({
if (poppedGrab.savedFocus) if (poppedGrab.savedFocus)
poppedGrab.savedFocus.grab_key_focus(); poppedGrab.savedFocus.grab_key_focus();
} }
this._isUngrabbingCount--;
}, },
onCapturedEvent: function(event) { _onCapturedEvent: function(actor, event) {
let type = event.type(); let type = event.type();
if (type == Clutter.EventType.KEY_PRESS && if (type == Clutter.EventType.KEY_PRESS &&
event.get_key_symbol() == Clutter.KEY_Escape) { event.get_key_symbol() == Clutter.KEY_Escape) {
this.ungrab({ isUser: true }); this.ungrab({ isUser: true });
return Clutter.EVENT_STOP; return true;
} }
let press = type == Clutter.EventType.BUTTON_PRESS; let press = type == Clutter.EventType.BUTTON_PRESS;
@ -289,14 +334,17 @@ const GrabHelper = new Lang.Class({
if (release && this._ignoreRelease) { if (release && this._ignoreRelease) {
this._ignoreRelease = false; this._ignoreRelease = false;
return Clutter.EVENT_STOP; return true;
} }
if (!button && this._modalCount == 0)
return false;
if (this._isWithinGrabbedActor(event.get_source())) if (this._isWithinGrabbedActor(event.get_source()))
return Clutter.EVENT_PROPAGATE; return false;
if (Main.keyboard.shouldTakeEvent(event)) if (Main.keyboard.shouldTakeEvent(event))
return Clutter.EVENT_PROPAGATE; return false;
if (button) { if (button) {
// If we have a press event, ignore the next event, // If we have a press event, ignore the next event,
@ -305,9 +353,24 @@ const GrabHelper = new Lang.Class({
this._ignoreRelease = true; this._ignoreRelease = true;
let i = this._actorInGrabStack(event.get_source()) + 1; let i = this._actorInGrabStack(event.get_source()) + 1;
this.ungrab({ actor: this._grabStack[i].actor, isUser: true }); this.ungrab({ actor: this._grabStack[i].actor, isUser: true });
return Clutter.EVENT_STOP; return true;
} }
return Clutter.EVENT_STOP; return this._modalCount > 0;
}, },
_onKeyFocusChanged: function() {
if (this._isUngrabbingCount > 0)
return;
let focus = global.stage.key_focus;
if (!focus || !this._isWithinGrabbedActor(focus))
this.ungrab({ isUser: true });
},
_focusWindowChanged: function() {
let metaDisplay = global.screen.get_display();
if (metaDisplay.focus_window != null)
this.ungrab({ isUser: true });
}
}); });

View File

@ -32,7 +32,6 @@ const CandidateArea = new Lang.Class({
let j = i; let j = i;
box.connect('button-release-event', Lang.bind(this, function(actor, event) { box.connect('button-release-event', Lang.bind(this, function(actor, event) {
this.emit('candidate-clicked', j, event.get_button(), event.get_state()); this.emit('candidate-clicked', j, event.get_button(), event.get_state());
return Clutter.EVENT_PROPAGATE;
})); }));
} }

View File

@ -1,20 +1,14 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const Gtk = imports.gi.Gtk;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const Signals = imports.signals;
const St = imports.gi.St; const St = imports.gi.St;
const Lang = imports.lang; const Lang = imports.lang;
const Params = imports.misc.params; const Params = imports.misc.params;
const Tweener = imports.ui.tweener;
const ICON_SIZE = 96; const ICON_SIZE = 48;
const MIN_ICON_SIZE = 16;
const EXTRA_SPACE_ANIMATION_TIME = 0.25;
const BaseIcon = new Lang.Class({ const BaseIcon = new Lang.Class({
Name: 'BaseIcon', Name: 'BaseIcon',
@ -23,12 +17,7 @@ const BaseIcon = new Lang.Class({
params = Params.parse(params, { createIcon: null, params = Params.parse(params, { createIcon: null,
setSizeManually: false, setSizeManually: false,
showLabel: true }); showLabel: true });
this.actor = new St.Bin({ style_class: 'overview-icon',
let styleClass = 'overview-icon';
if (params.showLabel)
styleClass += ' overview-icon-with-label';
this.actor = new St.Bin({ style_class: styleClass,
x_fill: true, x_fill: true,
y_fill: true }); y_fill: true });
this.actor._delegate = this; this.actor._delegate = this;
@ -187,31 +176,19 @@ const IconGrid = new Lang.Class({
_init: function(params) { _init: function(params) {
params = Params.parse(params, { rowLimit: null, params = Params.parse(params, { rowLimit: null,
columnLimit: null, columnLimit: null,
minRows: 1,
minColumns: 1,
fillParent: false, fillParent: false,
xAlign: St.Align.MIDDLE, xAlign: St.Align.MIDDLE });
padWithSpacing: false });
this._rowLimit = params.rowLimit; this._rowLimit = params.rowLimit;
this._colLimit = params.columnLimit; this._colLimit = params.columnLimit;
this._minRows = params.minRows;
this._minColumns = params.minColumns;
this._xAlign = params.xAlign; this._xAlign = params.xAlign;
this._fillParent = params.fillParent; this._fillParent = params.fillParent;
this._padWithSpacing = params.padWithSpacing;
this.topPadding = 0;
this.bottomPadding = 0;
this.rightPadding = 0;
this.leftPadding = 0;
this.actor = new St.BoxLayout({ style_class: 'icon-grid', this.actor = new St.BoxLayout({ style_class: 'icon-grid',
vertical: true }); vertical: true });
this._items = [];
// Pulled from CSS, but hardcode some defaults here // Pulled from CSS, but hardcode some defaults here
this._spacing = 0; this._spacing = 0;
this._hItemSize = this._vItemSize = ICON_SIZE; this._hItemSize = this._vItemSize = ICON_SIZE;
this._fixedHItemSize = this._fixedVItemSize = undefined;
this._grid = new Shell.GenericContainer(); this._grid = new Shell.GenericContainer();
this.actor.add(this._grid, { expand: true, y_align: St.Align.START }); this.actor.add(this._grid, { expand: true, y_align: St.Align.START });
this.actor.connect('style-changed', Lang.bind(this, this._onStyleChanged)); this.actor.connect('style-changed', Lang.bind(this, this._onStyleChanged));
@ -227,16 +204,16 @@ const IconGrid = new Lang.Class({
// later we'll allocate as many children as fit the parent // later we'll allocate as many children as fit the parent
return; return;
let nChildren = this._grid.get_n_children(); let children = this._grid.get_children();
let nColumns = this._colLimit ? Math.min(this._colLimit, let nColumns = this._colLimit ? Math.min(this._colLimit,
nChildren) children.length)
: nChildren; : children.length;
let totalSpacing = Math.max(0, nColumns - 1) * this._getSpacing(); let totalSpacing = Math.max(0, nColumns - 1) * this._spacing;
// Kind of a lie, but not really an issue right now. If // Kind of a lie, but not really an issue right now. If
// we wanted to support some sort of hidden/overflow that would // we wanted to support some sort of hidden/overflow that would
// need higher level design // need higher level design
alloc.min_size = this._getHItemSize() + this.leftPadding + this.rightPadding; alloc.min_size = this._hItemSize;
alloc.natural_size = nColumns * this._getHItemSize() + totalSpacing + this.leftPadding + this.rightPadding; alloc.natural_size = nColumns * this._hItemSize + totalSpacing;
}, },
_getVisibleChildren: function() { _getVisibleChildren: function() {
@ -254,11 +231,13 @@ const IconGrid = new Lang.Class({
return; return;
let children = this._getVisibleChildren(); let children = this._getVisibleChildren();
let nColumns; let nColumns, spacing;
if (forWidth < 0) if (forWidth < 0) {
nColumns = children.length; nColumns = children.length;
else spacing = this._spacing;
[nColumns, ] = this._computeLayout(forWidth); } else {
[nColumns, , spacing] = this._computeLayout(forWidth);
}
let nRows; let nRows;
if (nColumns > 0) if (nColumns > 0)
@ -267,8 +246,8 @@ const IconGrid = new Lang.Class({
nRows = 0; nRows = 0;
if (this._rowLimit) if (this._rowLimit)
nRows = Math.min(nRows, this._rowLimit); nRows = Math.min(nRows, this._rowLimit);
let totalSpacing = Math.max(0, nRows - 1) * this._getSpacing(); let totalSpacing = Math.max(0, nRows - 1) * spacing;
let height = nRows * this._getVItemSize() + totalSpacing + this.topPadding + this.bottomPadding; let height = nRows * this._vItemSize + totalSpacing;
alloc.min_size = height; alloc.min_size = height;
alloc.natural_size = height; alloc.natural_size = height;
}, },
@ -284,30 +263,48 @@ const IconGrid = new Lang.Class({
let children = this._getVisibleChildren(); let children = this._getVisibleChildren();
let availWidth = box.x2 - box.x1; let availWidth = box.x2 - box.x1;
let availHeight = box.y2 - box.y1; let availHeight = box.y2 - box.y1;
let spacing = this._getSpacing();
let [nColumns, usedWidth] = this._computeLayout(availWidth);
let leftEmptySpace; let [nColumns, usedWidth, spacing] = this._computeLayout(availWidth);
let leftPadding;
switch(this._xAlign) { switch(this._xAlign) {
case St.Align.START: case St.Align.START:
leftEmptySpace = 0; leftPadding = 0;
break; break;
case St.Align.MIDDLE: case St.Align.MIDDLE:
leftEmptySpace = Math.floor((availWidth - usedWidth) / 2); leftPadding = Math.floor((availWidth - usedWidth) / 2);
break; break;
case St.Align.END: case St.Align.END:
leftEmptySpace = availWidth - usedWidth; leftPadding = availWidth - usedWidth;
} }
let x = box.x1 + leftEmptySpace + this.leftPadding; let x = box.x1 + leftPadding;
let y = box.y1 + this.topPadding; let y = box.y1;
let columnIndex = 0; let columnIndex = 0;
let rowIndex = 0; let rowIndex = 0;
for (let i = 0; i < children.length; i++) { for (let i = 0; i < children.length; i++) {
let childBox = this._calculateChildBox(children[i], x, y, box); let [childMinWidth, childMinHeight, childNaturalWidth, childNaturalHeight]
= children[i].get_preferred_size();
/* Center the item in its allocation horizontally */
let width = Math.min(this._hItemSize, childNaturalWidth);
let childXSpacing = Math.max(0, width - childNaturalWidth) / 2;
let height = Math.min(this._vItemSize, childNaturalHeight);
let childYSpacing = Math.max(0, height - childNaturalHeight) / 2;
let childBox = new Clutter.ActorBox();
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) {
let _x = box.x2 - (x + width);
childBox.x1 = Math.floor(_x - childXSpacing);
} else {
childBox.x1 = Math.floor(x + childXSpacing);
}
childBox.y1 = Math.floor(y + childYSpacing);
childBox.x2 = childBox.x1 + width;
childBox.y2 = childBox.y1 + height;
if (this._rowLimit && rowIndex >= this._rowLimit || if (this._rowLimit && rowIndex >= this._rowLimit ||
this._fillParent && childBox.y2 > availHeight - this.bottomPadding) { this._fillParent && childBox.y2 > availHeight) {
this._grid.set_skip_paint(children[i], true); this._grid.set_skip_paint(children[i], true);
} else { } else {
children[i].allocate(childBox, flags); children[i].allocate(childBox, flags);
@ -321,38 +318,15 @@ const IconGrid = new Lang.Class({
} }
if (columnIndex == 0) { if (columnIndex == 0) {
y += this._getVItemSize() + spacing; y += this._vItemSize + spacing;
x = box.x1 + leftEmptySpace + this.leftPadding; x = box.x1 + leftPadding;
} else { } else {
x += this._getHItemSize() + spacing; x += this._hItemSize + spacing;
} }
} }
}, },
_calculateChildBox: function(child, x, y, box) { childrenInRow: function(rowWidth) {
let [childMinWidth, childMinHeight, childNaturalWidth, childNaturalHeight] =
child.get_preferred_size();
/* Center the item in its allocation horizontally */
let width = Math.min(this._getHItemSize(), childNaturalWidth);
let childXSpacing = Math.max(0, width - childNaturalWidth) / 2;
let height = Math.min(this._getVItemSize(), childNaturalHeight);
let childYSpacing = Math.max(0, height - childNaturalHeight) / 2;
let childBox = new Clutter.ActorBox();
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) {
let _x = box.x2 - (x + width);
childBox.x1 = Math.floor(_x - childXSpacing);
} else {
childBox.x1 = Math.floor(x + childXSpacing);
}
childBox.y1 = Math.floor(y + childYSpacing);
childBox.x2 = childBox.x1 + width;
childBox.y2 = childBox.y1 + height;
return childBox;
},
columnsForWidth: function(rowWidth) {
return this._computeLayout(rowWidth)[0]; return this._computeLayout(rowWidth)[0];
}, },
@ -362,19 +336,26 @@ const IconGrid = new Lang.Class({
_computeLayout: function (forWidth) { _computeLayout: function (forWidth) {
let nColumns = 0; let nColumns = 0;
let usedWidth = this.leftPadding + this.rightPadding; let usedWidth = 0;
let spacing = this._getSpacing(); let spacing = this._spacing;
if (this._colLimit) {
let itemWidth = this._hItemSize * this._colLimit;
let emptyArea = forWidth - itemWidth;
spacing = Math.max(this._spacing, emptyArea / (2 * this._colLimit));
spacing = Math.round(spacing);
}
while ((this._colLimit == null || nColumns < this._colLimit) && while ((this._colLimit == null || nColumns < this._colLimit) &&
(usedWidth + this._getHItemSize() <= forWidth)) { (usedWidth + this._hItemSize <= forWidth)) {
usedWidth += this._getHItemSize() + spacing; usedWidth += this._hItemSize + spacing;
nColumns += 1; nColumns += 1;
} }
if (nColumns > 0) if (nColumns > 0)
usedWidth -= spacing; usedWidth -= spacing;
return [nColumns, usedWidth]; return [nColumns, usedWidth, spacing];
}, },
_onStyleChanged: function() { _onStyleChanged: function() {
@ -385,52 +366,15 @@ const IconGrid = new Lang.Class({
this._grid.queue_relayout(); this._grid.queue_relayout();
}, },
nRows: function(forWidth) {
let children = this._getVisibleChildren();
let nColumns = (forWidth < 0) ? children.length : this._computeLayout(forWidth)[0];
let nRows = (nColumns > 0) ? Math.ceil(children.length / nColumns) : 0;
if (this._rowLimit)
nRows = Math.min(nRows, this._rowLimit);
return nRows;
},
rowsForHeight: function(forHeight) {
return Math.floor((forHeight - (this.topPadding + this.bottomPadding) + this._getSpacing()) / (this._getVItemSize() + this._getSpacing()));
},
usedHeightForNRows: function(nRows) {
return (this._getVItemSize() + this._getSpacing()) * nRows - this._getSpacing() + this.topPadding + this.bottomPadding;
},
usedWidth: function(forWidth) {
return this.usedWidthForNColumns(this.columnsForWidth(forWidth));
},
usedWidthForNColumns: function(columns) {
let usedWidth = columns * (this._getHItemSize() + this._getSpacing());
usedWidth -= this._getSpacing();
return usedWidth + this.leftPadding + this.rightPadding;
},
removeAll: function() { removeAll: function() {
this._items = [];
this._grid.remove_all_children();
},
destroyAll: function() {
this._items = [];
this._grid.destroy_all_children(); this._grid.destroy_all_children();
}, },
addItem: function(item, index) { addItem: function(actor, index) {
if (!item.icon instanceof BaseIcon)
throw new Error('Only items with a BaseIcon icon property can be added to IconGrid');
this._items.push(item);
if (index !== undefined) if (index !== undefined)
this._grid.insert_child_at_index(item.actor, index); this._grid.insert_child_at_index(actor, index);
else else
this._grid.add_actor(item.actor); this._grid.add_actor(actor);
}, },
getItemAtIndex: function(index) { getItemAtIndex: function(index) {
@ -439,311 +383,5 @@ const IconGrid = new Lang.Class({
visibleItemsCount: function() { visibleItemsCount: function() {
return this._grid.get_n_children() - this._grid.get_n_skip_paint(); return this._grid.get_n_children() - this._grid.get_n_skip_paint();
},
setSpacing: function(spacing) {
this._fixedSpacing = spacing;
},
_getSpacing: function() {
return this._fixedSpacing ? this._fixedSpacing : this._spacing;
},
_getHItemSize: function() {
return this._fixedHItemSize ? this._fixedHItemSize : this._hItemSize;
},
_getVItemSize: function() {
return this._fixedVItemSize ? this._fixedVItemSize : this._vItemSize;
},
_updateSpacingForSize: function(availWidth, availHeight) {
let maxEmptyVArea = availHeight - this._minRows * this._getVItemSize();
let maxEmptyHArea = availWidth - this._minColumns * this._getHItemSize();
let maxHSpacing, maxVSpacing;
if (this._padWithSpacing) {
// minRows + 1 because we want to put spacing before the first row, so it is like we have one more row
// to divide the empty space
maxVSpacing = Math.floor(maxEmptyVArea / (this._minRows +1));
maxHSpacing = Math.floor(maxEmptyHArea / (this._minColumns +1));
} else {
if (this._minRows <= 1)
maxVSpacing = maxEmptyVArea;
else
maxVSpacing = Math.floor(maxEmptyVArea / (this._minRows - 1));
if (this._minColumns <= 1)
maxHSpacing = maxEmptyHArea;
else
maxHSpacing = Math.floor(maxEmptyHArea / (this._minColumns - 1));
}
let maxSpacing = Math.min(maxHSpacing, maxVSpacing);
// Limit spacing to the item size
maxSpacing = Math.min(maxSpacing, Math.min(this._getVItemSize(), this._getHItemSize()));
// The minimum spacing, regardless of whether it satisfies the row/columng minima,
// is the spacing we get from CSS.
let spacing = Math.max(this._spacing, maxSpacing);
this.setSpacing(spacing);
if (this._padWithSpacing)
this.topPadding = this.rightPadding = this.bottomPadding = this.leftPadding = spacing;
},
/**
* This function must to be called before iconGrid allocation,
* to know how much spacing can the grid has
*/
adaptToSize: function(availWidth, availHeight) {
this._fixedHItemSize = this._hItemSize;
this._fixedVItemSize = this._vItemSize;
this._updateSpacingForSize(availWidth, availHeight);
let spacing = this._getSpacing();
if (this.columnsForWidth(availWidth) < this._minColumns || this.rowsForHeight(availHeight) < this._minRows) {
let neededWidth = this.usedWidthForNColumns(this._minColumns) - availWidth ;
let neededHeight = this.usedHeightForNRows(this._minRows) - availHeight ;
let neededSpacePerItem = (neededWidth > neededHeight) ? Math.ceil(neededWidth / this._minColumns)
: Math.ceil(neededHeight / this._minRows);
this._fixedHItemSize = Math.max(this._hItemSize - neededSpacePerItem, MIN_ICON_SIZE);
this._fixedVItemSize = Math.max(this._vItemSize - neededSpacePerItem, MIN_ICON_SIZE);
if (this._fixedHItemSize < MIN_ICON_SIZE)
this._fixedHItemSize = MIN_ICON_SIZE;
if (this._fixedVItemSize < MIN_ICON_SIZE)
this._fixedVItemSize = MIN_ICON_SIZE;
this._updateSpacingForSize(availWidth, availHeight);
}
let scale = Math.min(this._fixedHItemSize, this._fixedVItemSize) / Math.max(this._hItemSize, this._vItemSize);
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() { this._updateChildrenScale(scale); }));
},
// Note that this is ICON_SIZE as used by BaseIcon, not elsewhere in IconGrid; it's a bit messed up
_updateChildrenScale: function(scale) {
for (let i in this._items) {
let newIconSize = Math.floor(ICON_SIZE * scale);
this._items[i].icon.setIconSize(newIconSize);
}
} }
}); });
const PaginatedIconGrid = new Lang.Class({
Name: 'PaginatedIconGrid',
Extends: IconGrid,
_init: function(params) {
this.parent(params);
this._nPages = 0;
this._rowsPerPage = 0;
this._spaceBetweenPages = 0;
this._childrenPerPage = 0;
},
_getPreferredHeight: function (grid, forWidth, alloc) {
alloc.min_size = (this._availableHeightPerPageForItems() + this.bottomPadding + this.topPadding) * this._nPages + this._spaceBetweenPages * this._nPages;
alloc.natural_size = (this._availableHeightPerPageForItems() + this.bottomPadding + this.topPadding) * this._nPages + this._spaceBetweenPages * this._nPages;
},
_allocate: function (grid, box, flags) {
if (this._childrenPerPage == 0)
log('computePages() must be called before allocate(); pagination will not work.');
if (this._fillParent) {
// Reset the passed in box to fill the parent
let parentBox = this.actor.get_parent().allocation;
let gridBox = this.actor.get_theme_node().get_content_box(parentBox);
box = this._grid.get_theme_node().get_content_box(gridBox);
}
let children = this._getVisibleChildren();
let availWidth = box.x2 - box.x1;
let availHeight = box.y2 - box.y1;
let spacing = this._getSpacing();
let [nColumns, usedWidth] = this._computeLayout(availWidth);
let leftEmptySpace;
switch(this._xAlign) {
case St.Align.START:
leftEmptySpace = 0;
break;
case St.Align.MIDDLE:
leftEmptySpace = Math.floor((availWidth - usedWidth) / 2);
break;
case St.Align.END:
leftEmptySpace = availWidth - usedWidth;
}
let x = box.x1 + leftEmptySpace + this.leftPadding;
let y = box.y1 + this.topPadding;
let columnIndex = 0;
let rowIndex = 0;
for (let i = 0; i < children.length; i++) {
let childBox = this._calculateChildBox(children[i], x, y, box);
children[i].allocate(childBox, flags);
this._grid.set_skip_paint(children[i], false);
columnIndex++;
if (columnIndex == nColumns) {
columnIndex = 0;
rowIndex++;
}
if (columnIndex == 0) {
y += this._getVItemSize() + spacing;
if ((i + 1) % this._childrenPerPage == 0)
y += this._spaceBetweenPages - spacing + this.bottomPadding + this.topPadding;
x = box.x1 + leftEmptySpace + this.leftPadding;
} else
x += this._getHItemSize() + spacing;
}
},
_computePages: function (availWidthPerPage, availHeightPerPage) {
let [nColumns, usedWidth] = this._computeLayout(availWidthPerPage);
let nRows;
let children = this._getVisibleChildren();
if (nColumns > 0)
nRows = Math.ceil(children.length / nColumns);
else
nRows = 0;
if (this._rowLimit)
nRows = Math.min(nRows, this._rowLimit);
let spacing = this._getSpacing();
// We want to contain the grid inside the parent box with padding
this._rowsPerPage = this.rowsForHeight(availHeightPerPage);
this._nPages = Math.ceil(nRows / this._rowsPerPage);
this._spaceBetweenPages = availHeightPerPage - (this.topPadding + this.bottomPadding) - this._availableHeightPerPageForItems();
this._childrenPerPage = nColumns * this._rowsPerPage;
},
adaptToSize: function(availWidth, availHeight) {
this.parent(availWidth, availHeight);
this._computePages(availWidth, availHeight);
},
_availableHeightPerPageForItems: function() {
return this.usedHeightForNRows(this._rowsPerPage) - (this.topPadding + this.bottomPadding);
},
nPages: function() {
return this._nPages;
},
getPageY: function(pageNumber) {
if (!this._nPages)
return 0;
let firstPageItem = pageNumber * this._childrenPerPage
let childBox = this._getVisibleChildren()[firstPageItem].get_allocation_box();
return childBox.y1 - this.topPadding;
},
getItemPage: function(item) {
let children = this._getVisibleChildren();
let index = children.indexOf(item);
if (index == -1) {
throw new Error('Item not found.');
return 0;
}
return Math.floor(index / this._childrenPerPage);
},
/**
* openExtraSpace:
* @sourceItem: the item for which to create extra space
* @side: where @sourceItem should be located relative to the created space
* @nRows: the amount of space to create
*
* Pan view to create extra space for @nRows above or below @sourceItem.
*/
openExtraSpace: function(sourceItem, side, nRows) {
let children = this._getVisibleChildren();
let index = children.indexOf(sourceItem.actor);
if (index == -1) {
throw new Error('Item not found.');
return;
}
let pageIndex = Math.floor(index / this._childrenPerPage);
let pageOffset = pageIndex * this._childrenPerPage;
let childrenPerRow = this._childrenPerPage / this._rowsPerPage;
let sourceRow = Math.floor((index - pageOffset) / childrenPerRow);
let nRowsAbove = (side == St.Side.TOP) ? sourceRow + 1
: sourceRow;
let nRowsBelow = this._rowsPerPage - nRowsAbove;
let nRowsUp, nRowsDown;
if (side == St.Side.TOP) {
nRowsDown = Math.min(nRowsBelow, nRows);
nRowsUp = nRows - nRowsDown;
} else {
nRowsUp = Math.min(nRowsAbove, nRows);
nRowsDown = nRows - nRowsUp;
}
let childrenDown = children.splice(pageOffset +
nRowsAbove * childrenPerRow,
nRowsBelow * childrenPerRow);
let childrenUp = children.splice(pageOffset,
nRowsAbove * childrenPerRow);
// Special case: On the last row with no rows below the icon,
// there's no need to move any rows either up or down
if (childrenDown.length == 0 && nRowsUp == 0) {
this._translatedChildren = [];
this.emit('space-opened');
} else {
this._translateChildren(childrenUp, Gtk.DirectionType.UP, nRowsUp);
this._translateChildren(childrenDown, Gtk.DirectionType.DOWN, nRowsDown);
this._translatedChildren = childrenUp.concat(childrenDown);
}
},
_translateChildren: function(children, direction, nRows) {
let translationY = nRows * (this._getVItemSize() + this._getSpacing());
if (translationY == 0)
return;
if (direction == Gtk.DirectionType.UP)
translationY *= -1;
for (let i = 0; i < children.length; i++) {
children[i].translation_y = 0;
let params = { translation_y: translationY,
time: EXTRA_SPACE_ANIMATION_TIME,
transition: 'easeInOutQuad'
};
if (i == (children.length - 1))
params.onComplete = Lang.bind(this,
function() {
this.emit('space-opened');
});
Tweener.addTween(children[i], params);
}
},
closeExtraSpace: function() {
if (!this._translatedChildren || !this._translatedChildren.length) {
this.emit('space-closed');
return;
}
for (let i = 0; i < this._translatedChildren.length; i++) {
if (!this._translatedChildren[i].translation_y)
continue;
Tweener.addTween(this._translatedChildren[i],
{ translation_y: 0,
time: EXTRA_SPACE_ANIMATION_TIME,
transition: 'easeInOutQuad',
onComplete: Lang.bind(this,
function() {
this.emit('space-closed');
})
});
}
}
});
Signals.addSignalMethods(PaginatedIconGrid.prototype);

View File

@ -23,29 +23,27 @@ const KEYBOARD_TYPE = 'keyboard-type';
const A11Y_APPLICATIONS_SCHEMA = 'org.gnome.desktop.a11y.applications'; const A11Y_APPLICATIONS_SCHEMA = 'org.gnome.desktop.a11y.applications';
const SHOW_KEYBOARD = 'screen-keyboard-enabled'; const SHOW_KEYBOARD = 'screen-keyboard-enabled';
const CaribouKeyboardIface = '<node> \ const CaribouKeyboardIface = <interface name='org.gnome.Caribou.Keyboard'>
<interface name="org.gnome.Caribou.Keyboard"> \ <method name='Show'>
<method name="Show"> \ <arg type='u' direction='in' />
<arg type="u" direction="in" /> \ </method>
</method> \ <method name='Hide'>
<method name="Hide"> \ <arg type='u' direction='in' />
<arg type="u" direction="in" /> \ </method>
</method> \ <method name='SetCursorLocation'>
<method name="SetCursorLocation"> \ <arg type='i' direction='in' />
<arg type="i" direction="in" /> \ <arg type='i' direction='in' />
<arg type="i" direction="in" /> \ <arg type='i' direction='in' />
<arg type="i" direction="in" /> \ <arg type='i' direction='in' />
<arg type="i" direction="in" /> \ </method>
</method> \ <method name='SetEntryLocation'>
<method name="SetEntryLocation"> \ <arg type='i' direction='in' />
<arg type="i" direction="in" /> \ <arg type='i' direction='in' />
<arg type="i" direction="in" /> \ <arg type='i' direction='in' />
<arg type="i" direction="in" /> \ <arg type='i' direction='in' />
<arg type="i" direction="in" /> \ </method>
</method> \ <property name='Name' access='read' type='s' />
<property name="Name" access="read" type="s" /> \ </interface>;
</interface> \
</node>';
const Key = new Lang.Class({ const Key = new Lang.Class({
Name: 'Key', Name: 'Key',
@ -82,16 +80,8 @@ const Key = new Lang.Class({
style_class: 'keyboard-key' }); style_class: 'keyboard-key' });
button.key_width = this._key.width; button.key_width = this._key.width;
button.connect('button-press-event', Lang.bind(this, button.connect('button-press-event', Lang.bind(this, function () { this._key.press(); }));
function () { button.connect('button-release-event', Lang.bind(this, function () { this._key.release(); }));
this._key.press();
return Clutter.EVENT_PROPAGATE;
}));
button.connect('button-release-event', Lang.bind(this,
function () {
this._key.release();
return Clutter.EVENT_PROPAGATE;
}));
return button; return button;
}, },
@ -114,16 +104,8 @@ const Key = new Lang.Class({
let label = this._getUnichar(extended_key); let label = this._getUnichar(extended_key);
let key = new St.Button({ label: label, style_class: 'keyboard-key' }); let key = new St.Button({ label: label, style_class: 'keyboard-key' });
key.extended_key = extended_key; key.extended_key = extended_key;
key.connect('button-press-event', Lang.bind(this, key.connect('button-press-event', Lang.bind(this, function () { extended_key.press(); }));
function () { key.connect('button-release-event', Lang.bind(this, function () { extended_key.release(); }));
extended_key.press();
return Clutter.EVENT_PROPAGATE;
}));
key.connect('button-release-event', Lang.bind(this,
function () {
extended_key.release();
return Clutter.EVENT_PROPAGATE;
}));
this._extended_keyboard.add(key); this._extended_keyboard.add(key);
} }
this._boxPointer.bin.add_actor(this._extended_keyboard); this._boxPointer.bin.add_actor(this._extended_keyboard);
@ -268,10 +250,7 @@ const Keyboard = new Lang.Class({
if (!this._showIdleId) if (!this._showIdleId)
this._showIdleId = GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, this._showIdleId = GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE,
Lang.bind(this, function() { Lang.bind(this, function() { this.Show(time); }));
this.Show(time);
return GLib.SOURCE_REMOVE;
}));
}, },
_createLayersForGroup: function (gname) { _createLayersForGroup: function (gname) {
@ -313,7 +292,7 @@ const Keyboard = new Lang.Class({
else if (release && this._capturedPress) else if (release && this._capturedPress)
this._hideSubkeys(); this._hideSubkeys();
return Clutter.EVENT_STOP; return true;
}, },
_addRows : function (keys, layout) { _addRows : function (keys, layout) {
@ -457,6 +436,7 @@ const Keyboard = new Lang.Class({
_createSource: function () { _createSource: function () {
if (this._source == null) { if (this._source == null) {
this._source = new KeyboardSource(this); this._source = new KeyboardSource(this);
this._source.setTransient(true);
Main.messageTray.add(this._source); Main.messageTray.add(this._source);
} }
}, },
@ -498,7 +478,6 @@ const Keyboard = new Lang.Class({
Lang.bind(this, function() { Lang.bind(this, function() {
this._clearKeyboardRestTimer(); this._clearKeyboardRestTimer();
this._show(monitor); this._show(monitor);
return GLib.SOURCE_REMOVE;
})); }));
}, },
@ -524,7 +503,6 @@ const Keyboard = new Lang.Class({
Lang.bind(this, function() { Lang.bind(this, function() {
this._clearKeyboardRestTimer(); this._clearKeyboardRestTimer();
this._hide(); this._hide();
return GLib.SOURCE_REMOVE;
})); }));
}, },

View File

@ -227,6 +227,11 @@ const LayoutManager = new Lang.Class({
this._backgroundGroup.lower_bottom(); this._backgroundGroup.lower_bottom();
this._bgManagers = []; this._bgManagers = [];
// This blocks the XDND picks from finding the activities button
// and we never attempt to pick anything from it anyway so make
// it invisible from picks
Shell.util_set_hidden_from_pick(global.top_window_group, true);
// Need to update struts on new workspaces when they are added // Need to update struts on new workspaces when they are added
global.screen.connect('notify::n-workspaces', global.screen.connect('notify::n-workspaces',
Lang.bind(this, this._queueUpdateRegions)); Lang.bind(this, this._queueUpdateRegions));
@ -251,7 +256,7 @@ const LayoutManager = new Lang.Class({
this._inOverview = true; this._inOverview = true;
this._updateVisibility(); this._updateVisibility();
this._updateRegions(); this._queueUpdateRegions();
}, },
hideOverview: function() { hideOverview: function() {
@ -352,26 +357,26 @@ const LayoutManager = new Lang.Class({
this.emit('hot-corners-changed'); this.emit('hot-corners-changed');
}, },
_addBackgroundMenu: function(bgManager) { _createBackground: function(monitorIndex) {
BackgroundMenu.addBackgroundMenu(bgManager.background.actor, this);
},
_createBackgroundManager: function(monitorIndex) {
let bgManager = new Background.BackgroundManager({ container: this._backgroundGroup, let bgManager = new Background.BackgroundManager({ container: this._backgroundGroup,
layoutManager: this, layoutManager: this,
monitorIndex: monitorIndex }); monitorIndex: monitorIndex });
BackgroundMenu.addBackgroundMenu(bgManager.background.actor);
bgManager.connect('changed', Lang.bind(this, this._addBackgroundMenu)); bgManager.connect('changed', Lang.bind(this, function() {
this._addBackgroundMenu(bgManager); BackgroundMenu.addBackgroundMenu(bgManager.background.actor);
}));
return bgManager; this._bgManagers.push(bgManager);
return bgManager.background;
}, },
_showSecondaryBackgrounds: function() { _createSecondaryBackgrounds: function() {
for (let i = 0; i < this.monitors.length; i++) { for (let i = 0; i < this.monitors.length; i++) {
if (i != this.primaryIndex) { if (i != this.primaryIndex) {
let background = this._bgManagers[i].background; let background = this._createBackground(i);
background.actor.show();
background.actor.opacity = 0; background.actor.opacity = 0;
Tweener.addTween(background.actor, Tweener.addTween(background.actor,
{ opacity: 255, { opacity: 255,
@ -381,6 +386,10 @@ const LayoutManager = new Lang.Class({
} }
}, },
_createPrimaryBackground: function() {
this._createBackground(this.primaryIndex);
},
_updateBackgrounds: function() { _updateBackgrounds: function() {
let i; let i;
for (i = 0; i < this._bgManagers.length; i++) for (i = 0; i < this._bgManagers.length; i++)
@ -391,12 +400,11 @@ const LayoutManager = new Lang.Class({
if (Main.sessionMode.isGreeter) if (Main.sessionMode.isGreeter)
return; return;
for (let i = 0; i < this.monitors.length; i++) { if (this._startingUp)
let bgManager = this._createBackgroundManager(i); return;
this._bgManagers.push(bgManager);
if (i != this.primaryIndex && this._startingUp) for (let i = 0; i < this.monitors.length; i++) {
bgManager.background.actor.hide(); this._createBackground(i);
} }
}, },
@ -520,10 +528,17 @@ const LayoutManager = new Lang.Class({
get focusIndex() { get focusIndex() {
let i = Main.layoutManager.primaryIndex; let i = Main.layoutManager.primaryIndex;
if (global.stage.key_focus != null) if (global.stage_input_mode == Shell.StageInputMode.FOCUSED ||
i = this.findIndexForActor(global.stage.key_focus); global.stage_input_mode == Shell.StageInputMode.FULLSCREEN) {
else if (global.display.focus_window != null) let focusActor = global.stage.key_focus;
i = global.display.focus_window.get_monitor(); if (focusActor)
i = this.findIndexForActor(focusActor);
} else {
let focusWindow = global.display.focus_window;
if (focusWindow)
i = focusWindow.get_monitor();
}
return i; return i;
}, },
@ -581,19 +596,13 @@ const LayoutManager = new Lang.Class({
// screen. So, we set no_clear_hint at the end of the animation. // screen. So, we set no_clear_hint at the end of the animation.
_prepareStartupAnimation: function() { _prepareStartupAnimation: function() {
// During the initial transition, add a simple actor to block all events, // Set ourselves to FULLSCREEN input mode while the animation is running
// so they don't get delivered to X11 windows that have been transformed. // so events don't get delivered to X11 windows (which are distorted by the animation)
this._coverPane = new Clutter.Actor({ opacity: 0, global.stage_input_mode = Shell.StageInputMode.FULLSCREEN;
width: global.screen_width,
height: global.screen_height,
reactive: true });
this.addChrome(this._coverPane);
if (Main.sessionMode.isGreeter) { if (Main.sessionMode.isGreeter) {
this.panelBox.translation_y = -this.panelBox.height; this.panelBox.translation_y = -this.panelBox.height;
} else { } else {
this._updateBackgrounds();
// We need to force an update of the regions now before we scale // We need to force an update of the regions now before we scale
// the UI group to get the coorect allocation for the struts. // the UI group to get the coorect allocation for the struts.
this._updateRegions(); this._updateRegions();
@ -607,7 +616,7 @@ const LayoutManager = new Lang.Class({
this.uiGroup.set_pivot_point(x / global.screen_width, this.uiGroup.set_pivot_point(x / global.screen_width,
y / global.screen_height); y / global.screen_height);
this.uiGroup.scale_x = this.uiGroup.scale_y = 0.75; this.uiGroup.scale_x = this.uiGroup.scale_y = 0.5;
this.uiGroup.opacity = 0; this.uiGroup.opacity = 0;
global.window_group.set_clip(monitor.x, monitor.y, monitor.width, monitor.height); global.window_group.set_clip(monitor.x, monitor.y, monitor.width, monitor.height);
} }
@ -622,7 +631,7 @@ const LayoutManager = new Lang.Class({
// when the system is bogged down // when the system is bogged down
GLib.idle_add(GLib.PRIORITY_LOW, Lang.bind(this, function() { GLib.idle_add(GLib.PRIORITY_LOW, Lang.bind(this, function() {
this._startupAnimation(); this._startupAnimation();
return GLib.SOURCE_REMOVE; return false;
})); }));
}, },
@ -643,6 +652,7 @@ const LayoutManager = new Lang.Class({
}, },
_startupAnimationSession: function() { _startupAnimationSession: function() {
this._createPrimaryBackground();
Tweener.addTween(this.uiGroup, Tweener.addTween(this.uiGroup,
{ scale_x: 1, { scale_x: 1,
scale_y: 1, scale_y: 1,
@ -658,8 +668,7 @@ const LayoutManager = new Lang.Class({
// we no longer need to clear the stage // we no longer need to clear the stage
global.stage.no_clear_hint = true; global.stage.no_clear_hint = true;
this._coverPane.destroy(); global.stage_input_mode = Shell.StageInputMode.NORMAL;
this._coverPane = null;
this._systemBackground.actor.destroy(); this._systemBackground.actor.destroy();
this._systemBackground = null; this._systemBackground = null;
@ -670,7 +679,7 @@ const LayoutManager = new Lang.Class({
this.keyboardBox.show(); this.keyboardBox.show();
if (!Main.sessionMode.isGreeter) { if (!Main.sessionMode.isGreeter) {
this._showSecondaryBackgrounds(); this._createSecondaryBackgrounds();
global.window_group.remove_clip(); global.window_group.remove_clip();
} }
@ -1039,7 +1048,7 @@ const LayoutManager = new Lang.Class({
workspace.set_builtin_struts(struts); workspace.set_builtin_struts(struts);
} }
return GLib.SOURCE_REMOVE; return false;
} }
}); });
Signals.addSignalMethods(LayoutManager.prototype); Signals.addSignalMethods(LayoutManager.prototype);
@ -1226,20 +1235,20 @@ const HotCorner = new Lang.Class({
this._entered = true; this._entered = true;
this._toggleOverview(); this._toggleOverview();
} }
return Clutter.EVENT_PROPAGATE; return false;
}, },
_onCornerLeft : function(actor, event) { _onCornerLeft : function(actor, event) {
if (event.get_related() != this.actor) if (event.get_related() != this.actor)
this._entered = false; this._entered = false;
// Consume event, otherwise this will confuse onEnvironsLeft // Consume event, otherwise this will confuse onEnvironsLeft
return Clutter.EVENT_STOP; return true;
}, },
_onEnvironsLeft : function(actor, event) { _onEnvironsLeft : function(actor, event) {
if (event.get_related() != this._corner) if (event.get_related() != this._corner)
this._entered = false; this._entered = false;
return Clutter.EVENT_PROPAGATE; return false;
} }
}); });

View File

@ -42,11 +42,15 @@ const Lightbox = new Lang.Class({
params = Params.parse(params, { inhibitEvents: false, params = Params.parse(params, { inhibitEvents: false,
width: null, width: null,
height: null, height: null,
fadeFactor: DEFAULT_FADE_FACTOR, fadeInTime: null,
fadeOutTime: null,
fadeFactor: DEFAULT_FADE_FACTOR
}); });
this._container = container; this._container = container;
this._children = container.get_children(); this._children = container.get_children();
this._fadeInTime = params.fadeInTime;
this._fadeOutTime = params.fadeOutTime;
this._fadeFactor = params.fadeFactor; this._fadeFactor = params.fadeFactor;
this.actor = new St.Bin({ x: 0, this.actor = new St.Bin({ x: 0,
y: 0, y: 0,
@ -97,16 +101,14 @@ const Lightbox = new Lang.Class({
} }
}, },
show: function(fadeInTime) { show: function() {
fadeInTime = fadeInTime || 0;
Tweener.removeTweens(this.actor); Tweener.removeTweens(this.actor);
if (fadeInTime != 0) { if (this._fadeInTime) {
this.shown = false; this.shown = false;
this.actor.opacity = 0; this.actor.opacity = 0;
Tweener.addTween(this.actor, Tweener.addTween(this.actor,
{ opacity: 255 * this._fadeFactor, { opacity: 255 * this._fadeFactor,
time: fadeInTime, time: this._fadeInTime,
transition: 'easeOutQuad', transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() { onComplete: Lang.bind(this, function() {
this.shown = true; this.shown = true;
@ -121,15 +123,13 @@ const Lightbox = new Lang.Class({
this.actor.show(); this.actor.show();
}, },
hide: function(fadeOutTime) { hide: function() {
fadeOutTime = fadeOutTime || 0;
this.shown = false; this.shown = false;
Tweener.removeTweens(this.actor); Tweener.removeTweens(this.actor);
if (fadeOutTime != 0) { if (this._fadeOutTime) {
Tweener.addTween(this.actor, Tweener.addTween(this.actor,
{ opacity: 0, { opacity: 0,
time: fadeOutTime, time: this._fadeOutTime,
transition: 'easeOutQuad', transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() { onComplete: Lang.bind(this, function() {
this.actor.hide(); this.actor.hide();

View File

@ -109,7 +109,6 @@ const AutoComplete = new Lang.Class({
} }
this._lastTabTime = currTime; this._lastTabTime = currTime;
} }
return Clutter.EVENT_PROPAGATE;
}, },
// Insert characters of text not already included in head at cursor position. i.e., if text="abc" and head="a", // Insert characters of text not already included in head at cursor position. i.e., if text="abc" and head="a",
@ -559,7 +558,7 @@ const Inspector = new Lang.Class({
_onKeyPressEvent: function (actor, event) { _onKeyPressEvent: function (actor, event) {
if (event.get_key_symbol() == Clutter.Escape) if (event.get_key_symbol() == Clutter.Escape)
this._close(); this._close();
return Clutter.EVENT_STOP; return true;
}, },
_onButtonPressEvent: function (actor, event) { _onButtonPressEvent: function (actor, event) {
@ -568,7 +567,7 @@ const Inspector = new Lang.Class({
this.emit('target', this._target, stageX, stageY); this.emit('target', this._target, stageX, stageY);
} }
this._close(); this._close();
return Clutter.EVENT_STOP; return true;
}, },
_onScrollEvent: function (actor, event) { _onScrollEvent: function (actor, event) {
@ -602,12 +601,12 @@ const Inspector = new Lang.Class({
default: default:
break; break;
} }
return Clutter.EVENT_STOP; return true;
}, },
_onMotionEvent: function (actor, event) { _onMotionEvent: function (actor, event) {
this._update(event); this._update(event);
return Clutter.EVENT_STOP; return true;
}, },
_update: function(event) { _update: function(event) {
@ -630,6 +629,55 @@ const Inspector = new Lang.Class({
Signals.addSignalMethods(Inspector.prototype); Signals.addSignalMethods(Inspector.prototype);
const Memory = new Lang.Class({
Name: 'Memory',
_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._last_gc_seconds_ago = new St.Label();
this.actor.add(this._last_gc_seconds_ago);
this._gcbutton = new St.Button({ label: 'Full GC',
style_class: 'lg-obj-inspector-button' });
this._gcbutton.connect('clicked', Lang.bind(this, function () { System.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;
this._last_gc_seconds_ago.text = 'last_gc_seconds_ago: ' + memInfo.last_gc_seconds_ago;
}
});
const Extensions = new Lang.Class({ const Extensions = new Lang.Class({
Name: 'Extensions', Name: 'Extensions',
@ -801,9 +849,8 @@ const LookingGlass = new Lang.Class({
this._updateFont(); this._updateFont();
// We want it to appear to slide out from underneath the panel // We want it to appear to slide out from underneath the panel
Main.uiGroup.add_actor(this.actor); Main.layoutManager.panelBox.add_actor(this.actor);
Main.uiGroup.set_child_below_sibling(this.actor, this.actor.lower_bottom();
Main.layoutManager.panelBox);
Main.layoutManager.panelBox.connect('allocation-changed', Main.layoutManager.panelBox.connect('allocation-changed',
Lang.bind(this, this._queueResize)); Lang.bind(this, this._queueResize));
Main.layoutManager.keyboardBox.connect('allocation-changed', Main.layoutManager.keyboardBox.connect('allocation-changed',
@ -829,22 +876,7 @@ const LookingGlass = new Lang.Class({
global.stage.set_key_focus(this._entry); global.stage.set_key_focus(this._entry);
})); }));
this.actor.hide(); this.actor.hide();
return Clutter.EVENT_STOP; return true;
}));
let gcIcon = new St.Icon({ icon_name: 'gnome-fs-trash-full',
icon_size: 24 });
toolbar.add_actor(gcIcon);
gcIcon.reactive = true;
gcIcon.connect('button-press-event', Lang.bind(this, function () {
gcIcon.icon_name = 'gnome-fs-trash-empty';
System.gc();
this._timeoutId = Mainloop.timeout_add(500, Lang.bind(this, function () {
gcIcon.icon_name = 'gnome-fs-trash-full';
Mainloop.source_remove(this._timeoutId);
return GLib.SOURCE_REMOVE;
}));
return Clutter.EVENT_PROPAGATE;
})); }));
let notebook = new Notebook(); let notebook = new Notebook();
@ -874,6 +906,9 @@ const LookingGlass = new Lang.Class({
this._windowList = new WindowList(this); this._windowList = new WindowList(this);
notebook.appendPage('Windows', this._windowList.actor); notebook.appendPage('Windows', this._windowList.actor);
this._memory = new Memory();
notebook.appendPage('Memory', this._memory.actor);
this._extensions = new Extensions(this); this._extensions = new Extensions(this);
notebook.appendPage('Extensions', this._extensions.actor); notebook.appendPage('Extensions', this._extensions.actor);
@ -884,7 +919,7 @@ const LookingGlass = new Lang.Class({
let text = o.get_text(); let text = o.get_text();
// Ensure we don't get newlines in the command; the history file is // Ensure we don't get newlines in the command; the history file is
// newline-separated. // newline-separated.
text = text.replace('\n', ' '); text.replace('\n', ' ');
// Strip leading and trailing whitespace // Strip leading and trailing whitespace
text = text.replace(/^\s+/g, '').replace(/\s+$/g, ''); text = text.replace(/^\s+/g, '').replace(/\s+$/g, '');
if (text == '') if (text == '')
@ -1036,15 +1071,15 @@ const LookingGlass = new Lang.Class({
let myWidth = primary.width * 0.7; let myWidth = primary.width * 0.7;
let availableHeight = primary.height - Main.layoutManager.keyboardBox.height; let availableHeight = primary.height - Main.layoutManager.keyboardBox.height;
let myHeight = Math.min(primary.height * 0.7, availableHeight * 0.9); let myHeight = Math.min(primary.height * 0.7, availableHeight * 0.9);
this.actor.x = primary.x + (primary.width - myWidth) / 2; this.actor.x = (primary.width - myWidth) / 2;
this._hiddenY = primary.y + Main.layoutManager.panelBox.height - myHeight - 4; // -4 to hide the top corners this._hiddenY = this.actor.get_parent().height - myHeight - 4; // -4 to hide the top corners
this._targetY = this._hiddenY + myHeight; this._targetY = this._hiddenY + myHeight;
this.actor.y = this._hiddenY; this.actor.y = this._hiddenY;
this.actor.width = myWidth; this.actor.width = myWidth;
this.actor.height = myHeight; this.actor.height = myHeight;
this._objInspector.actor.set_size(Math.floor(myWidth * 0.8), Math.floor(myHeight * 0.8)); this._objInspector.actor.set_size(Math.floor(myWidth * 0.8), Math.floor(myHeight * 0.8));
this._objInspector.actor.set_position(this.actor.x + Math.floor(myWidth * 0.1), this._objInspector.actor.set_position(primary.x + this.actor.x + Math.floor(myWidth * 0.1),
this._targetY + Math.floor(myHeight * 0.1)); primary.y + this._targetY + Math.floor(myHeight * 0.1));
}, },
insertObject: function(obj) { insertObject: function(obj) {
@ -1066,7 +1101,7 @@ const LookingGlass = new Lang.Class({
} else { } else {
this.close(); this.close();
} }
return Clutter.EVENT_STOP; return true;
} }
// Ctrl+PgUp and Ctrl+PgDown switches tabs in the notebook view // Ctrl+PgUp and Ctrl+PgDown switches tabs in the notebook view
if (modifierState & Clutter.ModifierType.CONTROL_MASK) { if (modifierState & Clutter.ModifierType.CONTROL_MASK) {
@ -1076,7 +1111,7 @@ const LookingGlass = new Lang.Class({
this._notebook.nextTab(); this._notebook.nextTab();
} }
} }
return Clutter.EVENT_PROPAGATE; return false;
}, },
open : function() { open : function() {
@ -1114,7 +1149,7 @@ const LookingGlass = new Lang.Class({
Main.popModal(this._entry); Main.popModal(this._entry);
Tweener.addTween(this.actor, { time: Math.min(0.5 / St.get_slow_down_factor(), 0.5), Tweener.addTween(this.actor, { time: 0.5 / St.get_slow_down_factor(),
transition: 'easeOutQuad', transition: 'easeOutQuad',
y: this._hiddenY, y: this._hiddenY,
onComplete: Lang.bind(this, function () { onComplete: Lang.bind(this, function () {

View File

@ -1,6 +1,5 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Atspi = imports.gi.Atspi;
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const GDesktopEnums = imports.gi.GDesktopEnums; const GDesktopEnums = imports.gi.GDesktopEnums;
const Gio = imports.gi.Gio; const Gio = imports.gi.Gio;
@ -8,10 +7,8 @@ const Shell = imports.gi.Shell;
const St = imports.gi.St; const St = imports.gi.St;
const Lang = imports.lang; const Lang = imports.lang;
const Mainloop = imports.mainloop; const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
const Signals = imports.signals; const Signals = imports.signals;
const FocusCaretTracker = imports.ui.focusCaretTracker;
const Main = imports.ui.main; const Main = imports.ui.main;
const MagnifierDBus = imports.ui.magnifierDBus; const MagnifierDBus = imports.ui.magnifierDBus;
const Params = imports.misc.params; const Params = imports.misc.params;
@ -39,8 +36,6 @@ const CONTRAST_BLUE_KEY = 'contrast-blue';
const LENS_MODE_KEY = 'lens-mode'; const LENS_MODE_KEY = 'lens-mode';
const CLAMP_MODE_KEY = 'scroll-at-edges'; const CLAMP_MODE_KEY = 'scroll-at-edges';
const MOUSE_TRACKING_KEY = 'mouse-tracking'; const MOUSE_TRACKING_KEY = 'mouse-tracking';
const FOCUS_TRACKING_KEY = 'focus-tracking';
const CARET_TRACKING_KEY = 'caret-tracking';
const SHOW_CROSS_HAIRS_KEY = 'show-cross-hairs'; const SHOW_CROSS_HAIRS_KEY = 'show-cross-hairs';
const CROSS_HAIRS_THICKNESS_KEY = 'cross-hairs-thickness'; const CROSS_HAIRS_THICKNESS_KEY = 'cross-hairs-thickness';
const CROSS_HAIRS_COLOR_KEY = 'cross-hairs-color'; const CROSS_HAIRS_COLOR_KEY = 'cross-hairs-color';
@ -57,24 +52,10 @@ const Magnifier = new Lang.Class({
// Magnifier is a manager of ZoomRegions. // Magnifier is a manager of ZoomRegions.
this._zoomRegions = []; this._zoomRegions = [];
// Export to dbus.
magDBusService = new MagnifierDBus.ShellMagnifier();
let showAtLaunch = this._settingsInit();
this.setActive(showAtLaunch);
},
_initialize: function() {
if (this._initialized)
return;
this._initialized = true;
this._settingsInitLate();
// Create small clutter tree for the magnified mouse. // Create small clutter tree for the magnified mouse.
let cursorTracker = Meta.CursorTracker.get_for_screen(global.screen); let xfixesCursor = Shell.XFixesCursor.get_for_stage(global.stage);
this._mouseSprite = new Clutter.Texture(); this._mouseSprite = new Clutter.Texture();
Shell.util_cursor_tracker_to_clutter(cursorTracker, this._mouseSprite); xfixesCursor.update_texture_image(this._mouseSprite);
this._cursorRoot = new Clutter.Actor(); this._cursorRoot = new Clutter.Actor();
this._cursorRoot.add_actor(this._mouseSprite); this._cursorRoot.add_actor(this._mouseSprite);
@ -86,11 +67,15 @@ const Magnifier = new Lang.Class({
let aZoomRegion = new ZoomRegion(this, this._cursorRoot); let aZoomRegion = new ZoomRegion(this, this._cursorRoot);
this._zoomRegions.push(aZoomRegion); this._zoomRegions.push(aZoomRegion);
this._settingsInitRegion(aZoomRegion); let showAtLaunch = this._settingsInit(aZoomRegion);
aZoomRegion.scrollContentsTo(this.xMouse, this.yMouse); aZoomRegion.scrollContentsTo(this.xMouse, this.yMouse);
cursorTracker.connect('cursor-changed', Lang.bind(this, this._updateMouseSprite)); xfixesCursor.connect('cursor-change', Lang.bind(this, this._updateMouseSprite));
this._cursorTracker = cursorTracker; this._xfixesCursor = xfixesCursor;
// Export to dbus.
magDBusService = new MagnifierDBus.ShellMagnifier();
this.setActive(showAtLaunch);
}, },
/** /**
@ -98,7 +83,7 @@ const Magnifier = new Lang.Class({
* Show the system mouse pointer. * Show the system mouse pointer.
*/ */
showSystemCursor: function() { showSystemCursor: function() {
this._cursorTracker.set_pointer_visible(true); this._xfixesCursor.show();
}, },
/** /**
@ -106,7 +91,7 @@ const Magnifier = new Lang.Class({
* Hide the system mouse pointer. * Hide the system mouse pointer.
*/ */
hideSystemCursor: function() { hideSystemCursor: function() {
this._cursorTracker.set_pointer_visible(false); this._xfixesCursor.hide();
}, },
/** /**
@ -115,12 +100,6 @@ const Magnifier = new Lang.Class({
* @activate: Boolean to activate or de-activate the magnifier. * @activate: Boolean to activate or de-activate the magnifier.
*/ */
setActive: function(activate) { setActive: function(activate) {
if (activate == this.isActive())
return;
if (activate)
this._initialize();
this._zoomRegions.forEach (function(zoomRegion, index, array) { this._zoomRegions.forEach (function(zoomRegion, index, array) {
zoomRegion.setActive(activate); zoomRegion.setActive(activate);
}); });
@ -133,7 +112,7 @@ const Magnifier = new Lang.Class({
// Make sure system mouse pointer is shown when all zoom regions are // Make sure system mouse pointer is shown when all zoom regions are
// invisible. // invisible.
if (!activate) if (!activate)
this._cursorTracker.set_pointer_visible(true); this._xfixesCursor.show();
// Notify interested parties of this change // Notify interested parties of this change
this.emit('active-changed', activate); this.emit('active-changed', activate);
@ -443,12 +422,17 @@ const Magnifier = new Lang.Class({
//// Private methods //// //// Private methods ////
_updateMouseSprite: function() { _updateMouseSprite: function() {
Shell.util_cursor_tracker_to_clutter(this._cursorTracker, this._mouseSprite); this._xfixesCursor.update_texture_image(this._mouseSprite);
let [xHot, yHot] = this._cursorTracker.get_hot(); let xHot = this._xfixesCursor.get_hot_x();
let yHot = this._xfixesCursor.get_hot_y();
this._mouseSprite.set_anchor_point(xHot, yHot); this._mouseSprite.set_anchor_point(xHot, yHot);
}, },
_settingsInitRegion: function(zoomRegion) { _settingsInit: function(zoomRegion) {
this._appSettings = new Gio.Settings({ schema: APPLICATIONS_SCHEMA });
this._settings = new Gio.Settings({ schema: MAGNIFIER_SCHEMA });
if (zoomRegion) {
// Mag factor is accurate to two decimal places. // Mag factor is accurate to two decimal places.
let aPref = parseFloat(this._settings.get_double(MAG_FACTOR_KEY).toFixed(2)); let aPref = parseFloat(this._settings.get_double(MAG_FACTOR_KEY).toFixed(2));
if (aPref != 0.0) if (aPref != 0.0)
@ -465,14 +449,6 @@ const Magnifier = new Lang.Class({
if (aPref) if (aPref)
zoomRegion.setMouseTrackingMode(aPref); zoomRegion.setMouseTrackingMode(aPref);
aPref = this._settings.get_enum(FOCUS_TRACKING_KEY);
if (aPref)
zoomRegion.setFocusTrackingMode(aPref);
aPref = this._settings.get_enum(CARET_TRACKING_KEY);
if (aPref)
zoomRegion.setCaretTrackingMode(aPref);
aPref = this._settings.get_boolean(INVERT_LIGHTNESS_KEY); aPref = this._settings.get_boolean(INVERT_LIGHTNESS_KEY);
if (aPref) if (aPref)
zoomRegion.setInvertLightness(aPref); zoomRegion.setInvertLightness(aPref);
@ -491,25 +467,17 @@ const Magnifier = new Lang.Class({
bc.g = this._settings.get_double(CONTRAST_GREEN_KEY); bc.g = this._settings.get_double(CONTRAST_GREEN_KEY);
bc.b = this._settings.get_double(CONTRAST_BLUE_KEY); bc.b = this._settings.get_double(CONTRAST_BLUE_KEY);
zoomRegion.setContrast(bc); zoomRegion.setContrast(bc);
}, }
_settingsInit: function() {
this._appSettings = new Gio.Settings({ schema: APPLICATIONS_SCHEMA });
this._settings = new Gio.Settings({ schema: MAGNIFIER_SCHEMA });
this._appSettings.connect('changed::' + SHOW_KEY, Lang.bind(this, function() {
let active = this._appSettings.get_boolean(SHOW_KEY);
this.setActive(active);
}));
return this._appSettings.get_boolean(SHOW_KEY);
},
_settingsInitLate: function() {
let showCrosshairs = this._settings.get_boolean(SHOW_CROSS_HAIRS_KEY); let showCrosshairs = this._settings.get_boolean(SHOW_CROSS_HAIRS_KEY);
this.addCrosshairs(); this.addCrosshairs();
this.setCrosshairsVisible(showCrosshairs); this.setCrosshairsVisible(showCrosshairs);
this._appSettings.connect('changed::' + SHOW_KEY,
Lang.bind(this, function() {
this.setActive(this._appSettings.get_boolean(SHOW_KEY));
}));
this._settings.connect('changed::' + SCREEN_POSITION_KEY, this._settings.connect('changed::' + SCREEN_POSITION_KEY,
Lang.bind(this, this._updateScreenPosition)); Lang.bind(this, this._updateScreenPosition));
this._settings.connect('changed::' + MAG_FACTOR_KEY, this._settings.connect('changed::' + MAG_FACTOR_KEY,
@ -520,10 +488,6 @@ const Magnifier = new Lang.Class({
Lang.bind(this, this._updateClampMode)); Lang.bind(this, this._updateClampMode));
this._settings.connect('changed::' + MOUSE_TRACKING_KEY, this._settings.connect('changed::' + MOUSE_TRACKING_KEY,
Lang.bind(this, this._updateMouseTrackingMode)); Lang.bind(this, this._updateMouseTrackingMode));
this._settings.connect('changed::' + FOCUS_TRACKING_KEY,
Lang.bind(this, this._updateFocusTrackingMode));
this._settings.connect('changed::' + CARET_TRACKING_KEY,
Lang.bind(this, this._updateCaretTrackingMode));
this._settings.connect('changed::' + INVERT_LIGHTNESS_KEY, this._settings.connect('changed::' + INVERT_LIGHTNESS_KEY,
Lang.bind(this, this._updateInvertLightness)); Lang.bind(this, this._updateInvertLightness));
@ -573,6 +537,8 @@ const Magnifier = new Lang.Class({
Lang.bind(this, function() { Lang.bind(this, function() {
this.setCrosshairsClip(this._settings.get_boolean(CROSS_HAIRS_CLIP_KEY)); this.setCrosshairsClip(this._settings.get_boolean(CROSS_HAIRS_CLIP_KEY));
})); }));
return this._appSettings.get_boolean(SHOW_KEY);
}, },
_updateScreenPosition: function() { _updateScreenPosition: function() {
@ -619,24 +585,6 @@ const Magnifier = new Lang.Class({
} }
}, },
_updateFocusTrackingMode: function() {
// Applies only to the first zoom region.
if (this._zoomRegions.length) {
this._zoomRegions[0].setFocusTrackingMode(
this._settings.get_enum(FOCUS_TRACKING_KEY)
);
}
},
_updateCaretTrackingMode: function() {
// Applies only to the first zoom region.
if (this._zoomRegions.length) {
this._zoomRegions[0].setCaretTrackingMode(
this._settings.get_enum(CARET_TRACKING_KEY)
);
}
},
_updateInvertLightness: function() { _updateInvertLightness: function() {
// Applies only to the first zoom region. // Applies only to the first zoom region.
if (this._zoomRegions.length) { if (this._zoomRegions.length) {
@ -675,7 +623,7 @@ const Magnifier = new Lang.Class({
contrast.b = this._settings.get_double(CONTRAST_BLUE_KEY); contrast.b = this._settings.get_double(CONTRAST_BLUE_KEY);
this._zoomRegions[0].setContrast(contrast); this._zoomRegions[0].setContrast(contrast);
} }
} },
}); });
Signals.addSignalMethods(Magnifier.prototype); Signals.addSignalMethods(Magnifier.prototype);
@ -684,11 +632,8 @@ const ZoomRegion = new Lang.Class({
_init: function(magnifier, mouseSourceActor) { _init: function(magnifier, mouseSourceActor) {
this._magnifier = magnifier; this._magnifier = magnifier;
this._focusCaretTracker = new FocusCaretTracker.FocusCaretTracker();
this._mouseTrackingMode = GDesktopEnums.MagnifierMouseTrackingMode.NONE; this._mouseTrackingMode = GDesktopEnums.MagnifierMouseTrackingMode.NONE;
this._focusTrackingMode = GDesktopEnums.MagnifierFocusTrackingMode.NONE;
this._caretTrackingMode = GDesktopEnums.MagnifierCaretTrackingMode.NONE;
this._clampScrollingAtEdges = false; this._clampScrollingAtEdges = false;
this._lensMode = false; this._lensMode = false;
this._screenPosition = GDesktopEnums.MagnifierScreenPosition.FULL_SCREEN; this._screenPosition = GDesktopEnums.MagnifierScreenPosition.FULL_SCREEN;
@ -714,35 +659,9 @@ const ZoomRegion = new Lang.Class({
this._xMagFactor = 1; this._xMagFactor = 1;
this._yMagFactor = 1; this._yMagFactor = 1;
this._followingCursor = false; this._followingCursor = false;
this._xFocus = 0;
this._yFocus = 0;
this._xCaret = 0;
this._yCaret = 0;
Main.layoutManager.connect('monitors-changed', Main.layoutManager.connect('monitors-changed',
Lang.bind(this, this._monitorsChanged)); Lang.bind(this, this._monitorsChanged));
this._focusCaretTracker.connect('caret-moved',
Lang.bind(this, this._updateCaret));
this._focusCaretTracker.connect('focus-changed',
Lang.bind(this, this._updateFocus));
},
_updateFocus: function(caller, event) {
let component = event.source.get_component_iface();
if (!component || event.detail1 != 1)
return;
let extents = component.get_extents(Atspi.CoordType.SCREEN);
[this._xFocus, this._yFocus] = [extents.x, extents.y]
this._centerFromFocusPosition();
},
_updateCaret: function(caller, event) {
let text = event.source.get_text_iface();
if (!text)
return;
let extents = text.get_character_extents(text.get_caret_offset(), 0);
[this._xCaret, this._yCaret] = [extents.x, extents.y];
this._centerFromCaretPosition();
}, },
/** /**
@ -750,17 +669,14 @@ const ZoomRegion = new Lang.Class({
* @activate: Boolean to show/hide the ZoomRegion. * @activate: Boolean to show/hide the ZoomRegion.
*/ */
setActive: function(activate) { setActive: function(activate) {
if (activate == this.isActive()) if (activate && !this.isActive()) {
return;
if (activate) {
this._createActors(); this._createActors();
if (this._isMouseOverRegion()) if (this._isMouseOverRegion())
this._magnifier.hideSystemCursor(); this._magnifier.hideSystemCursor();
this._updateMagViewGeometry(); this._updateMagViewGeometry();
this._updateCloneGeometry(); this._updateCloneGeometry();
this._updateMousePosition(); this._updateMousePosition();
} else { } else if (!activate && this.isActive()) {
this._destroyActors(); this._destroyActors();
} }
}, },
@ -816,30 +732,6 @@ const ZoomRegion = new Lang.Class({
return this._mouseTrackingMode; return this._mouseTrackingMode;
}, },
/**
* setFocusTrackingMode
* @mode: One of the enum FocusTrackingMode values.
*/
setFocusTrackingMode: function(mode) {
this._focusTrackingMode = mode;
if (this._focusTrackingMode == GDesktopEnums.MagnifierFocusTrackingMode.NONE)
this._focusCaretTracker.deregisterFocusListener();
else
this._focusCaretTracker.registerFocusListener();
},
/**
* setCaretTrackingMode
* @mode: One of the enum CaretTrackingMode values.
*/
setCaretTrackingMode: function(mode) {
this._caretTrackingMode = mode;
if (this._caretTrackingMode == GDesktopEnums.MagnifierCaretTrackingMode.NONE)
this._focusCaretTracker.deregisterCaretListener();
else
this._focusCaretTracker.registerCaretListener();
},
/** /**
* setViewPort * setViewPort
* Sets the position and size of the ZoomRegion on screen. * Sets the position and size of the ZoomRegion on screen.
@ -1131,6 +1023,20 @@ const ZoomRegion = new Lang.Class({
this._magShaderEffects.setBrightness(this._brightness); this._magShaderEffects.setBrightness(this._brightness);
}, },
/**
* getBrightness:
* Retrive the current brightness of the Zoom Region.
* @return Object containing the brightness change for the red, green,
* and blue channels.
*/
getBrightness: function() {
let brightness = {};
brightness.r = this._brightness.r;
brightness.g = this._brightness.g;
brightness.b = this._brightness.b;
return brightness;
},
/** /**
* setContrast: * setContrast:
* Alter the contrast of the magnified view. * Alter the contrast of the magnified view.
@ -1337,47 +1243,19 @@ const ZoomRegion = new Lang.Class({
let yMouse = this._magnifier.yMouse; let yMouse = this._magnifier.yMouse;
if (this._mouseTrackingMode == GDesktopEnums.MagnifierMouseTrackingMode.PROPORTIONAL) { if (this._mouseTrackingMode == GDesktopEnums.MagnifierMouseTrackingMode.PROPORTIONAL) {
return this._centerFromPointProportional(xMouse, yMouse); return this._centerFromMouseProportional(xMouse, yMouse);
} }
else if (this._mouseTrackingMode == GDesktopEnums.MagnifierMouseTrackingMode.PUSH) { else if (this._mouseTrackingMode == GDesktopEnums.MagnifierMouseTrackingMode.PUSH) {
return this._centerFromPointPush(xMouse, yMouse); return this._centerFromMousePush(xMouse, yMouse);
} }
else if (this._mouseTrackingMode == GDesktopEnums.MagnifierMouseTrackingMode.CENTERED) { else if (this._mouseTrackingMode == GDesktopEnums.MagnifierMouseTrackingMode.CENTERED) {
return this._centerFromPointCentered(xMouse, yMouse); return this._centerFromMouseCentered(xMouse, yMouse);
} }
return null; // Should never be hit return null; // Should never be hit
}, },
_centerFromCaretPosition: function() { _centerFromMousePush: function(xMouse, yMouse) {
let xCaret = this._xCaret;
let yCaret = this._yCaret;
if (this._caretTrackingMode == GDesktopEnums.MagnifierCaretTrackingMode.PROPORTIONAL)
[xCaret, yCaret] = this._centerFromPointProportional(xCaret, yCaret);
else if (this._caretTrackingMode == GDesktopEnums.MagnifierCaretTrackingMode.PUSH)
[xCaret, yCaret] = this._centerFromPointPush(xCaret, yCaret);
else if (this._caretTrackingMode == GDesktopEnums.MagnifierCaretTrackingMode.CENTERED)
[xCaret, yCaret] = this._centerFromPointCentered(xCaret, yCaret);
this.scrollContentsTo(xCaret, yCaret);
},
_centerFromFocusPosition: function() {
let xFocus = this._xFocus;
let yFocus = this._yFocus;
if (this._focusTrackingMode == GDesktopEnums.MagnifierFocusTrackingMode.PROPORTIONAL)
[xFocus, yFocus] = this._centerFromPointProportional(xFocus, yFocus);
else if (this._focusTrackingMode == GDesktopEnums.MagnifierFocusTrackingMode.PUSH)
[xFocus, yFocus] = this._centerFromPointPush(xFocus, yFocus);
else if (this._focusTrackingMode == GDesktopEnums.MagnifierFocusTrackingMode.CENTERED)
[xFocus, yFocus] = this._centerFromPointCentered(xFocus, yFocus);
this.scrollContentsTo(xFocus, yFocus);
},
_centerFromPointPush: function(xPoint, yPoint) {
let [xRoi, yRoi, widthRoi, heightRoi] = this.getROI(); let [xRoi, yRoi, widthRoi, heightRoi] = this.getROI();
let [cursorWidth, cursorHeight] = this._mouseSourceActor.get_size(); let [cursorWidth, cursorHeight] = this._mouseSourceActor.get_size();
let xPos = xRoi + widthRoi / 2; let xPos = xRoi + widthRoi / 2;
@ -1385,20 +1263,20 @@ const ZoomRegion = new Lang.Class({
let xRoiRight = xRoi + widthRoi - cursorWidth; let xRoiRight = xRoi + widthRoi - cursorWidth;
let yRoiBottom = yRoi + heightRoi - cursorHeight; let yRoiBottom = yRoi + heightRoi - cursorHeight;
if (xPoint < xRoi) if (xMouse < xRoi)
xPos -= (xRoi - xPoint); xPos -= (xRoi - xMouse);
else if (xPoint > xRoiRight) else if (xMouse > xRoiRight)
xPos += (xPoint - xRoiRight); xPos += (xMouse - xRoiRight);
if (yPoint < yRoi) if (yMouse < yRoi)
yPos -= (yRoi - yPoint); yPos -= (yRoi - yMouse);
else if (yPoint > yRoiBottom) else if (yMouse > yRoiBottom)
yPos += (yPoint - yRoiBottom); yPos += (yMouse - yRoiBottom);
return [xPos, yPos]; return [xPos, yPos];
}, },
_centerFromPointProportional: function(xPoint, yPoint) { _centerFromMouseProportional: function(xMouse, yMouse) {
let [xRoi, yRoi, widthRoi, heightRoi] = this.getROI(); let [xRoi, yRoi, widthRoi, heightRoi] = this.getROI();
let halfScreenWidth = global.screen_width / 2; let halfScreenWidth = global.screen_width / 2;
let halfScreenHeight = global.screen_height / 2; let halfScreenHeight = global.screen_height / 2;
@ -1407,16 +1285,16 @@ const ZoomRegion = new Lang.Class({
let unscaledPadding = Math.min(this._viewPortWidth, this._viewPortHeight) / 5; let unscaledPadding = Math.min(this._viewPortWidth, this._viewPortHeight) / 5;
let xPadding = unscaledPadding / this._xMagFactor; let xPadding = unscaledPadding / this._xMagFactor;
let yPadding = unscaledPadding / this._yMagFactor; let yPadding = unscaledPadding / this._yMagFactor;
let xProportion = (xPoint - halfScreenWidth) / halfScreenWidth; // -1 ... 1 let xProportion = (xMouse - halfScreenWidth) / halfScreenWidth; // -1 ... 1
let yProportion = (yPoint - halfScreenHeight) / halfScreenHeight; // -1 ... 1 let yProportion = (yMouse - halfScreenHeight) / halfScreenHeight; // -1 ... 1
let xPos = xPoint - xProportion * (widthRoi / 2 - xPadding); let xPos = xMouse - xProportion * (widthRoi / 2 - xPadding);
let yPos = yPoint - yProportion * (heightRoi /2 - yPadding); let yPos = yMouse - yProportion * (heightRoi /2 - yPadding);
return [xPos, yPos]; return [xPos, yPos];
}, },
_centerFromPointCentered: function(xPoint, yPoint) { _centerFromMouseCentered: function(xMouse, yMouse) {
return [xPoint, yPoint]; return [xMouse, yMouse];
}, },
_screenToViewPort: function(screenX, screenY) { _screenToViewPort: function(screenX, screenY) {
@ -1633,6 +1511,15 @@ const Crosshairs = new Lang.Class({
this._vertBottomHair.set_opacity(opacity); this._vertBottomHair.set_opacity(opacity);
}, },
/**
* getOpacity:
* Retriev how opaque the crosshairs are.
* @return: A value between 0 (transparent) and 255 (opaque).
*/
getOpacity: function() {
return this._horizLeftHair.get_opacity();
},
/** /**
* setLength: * setLength:
* Set the length of the vertical and horizontal lines in the crosshairs. * Set the length of the vertical and horizontal lines in the crosshairs.
@ -1676,6 +1563,15 @@ const Crosshairs = new Lang.Class({
} }
}, },
/**
* getClip:
* Get the dimensions of the clip rectangle.
* @return: An array of the form [width, height].
*/
getClip: function() {
return this._clipSize;
},
/** /**
* show: * show:
* Show the crosshairs. * Show the crosshairs.
@ -1771,10 +1667,23 @@ const MagShaderEffects = new Lang.Class({
this._inverse.set_enabled(invertFlag); this._inverse.set_enabled(invertFlag);
}, },
/**
* getInvertLightness:
* Report whether the inversion effect is enabled.
* @return: Boolean.
*/
getInvertLightness: function() {
return this._inverse.get_enabled();
},
setColorSaturation: function(factor) { setColorSaturation: function(factor) {
this._colorDesaturation.set_factor(1.0 - factor); this._colorDesaturation.set_factor(1.0 - factor);
}, },
getColorSaturation: function() {
return 1.0 - this._colorDesaturation.get_factor();
},
/** /**
* setBrightness: * setBrightness:
* Set the brightness of the magnified view. * Set the brightness of the magnified view.
@ -1799,6 +1708,24 @@ const MagShaderEffects = new Lang.Class({
); );
}, },
/**
* getBrightness:
* Retrieve current brightness of the magnified view.
* @return: Object containing the brightness for the red, green,
* and blue channels. Values of 0.0 represent "standard"
* brightness (no change), whereas values less or greater than
* 0.0 indicate decreased or incresaed brightness, respectively.
*/
getBrightness: function() {
let result = {};
let [bRed, bGreen, bBlue] = this._brightnessContrast.get_brightness();
result.r = bRed;
result.g = bGreen;
result.b = bBlue;
return result;
},
/** /**
* Set the contrast of the magnified view. * Set the contrast of the magnified view.
* @contrast: Object containing the contrast for the red, green, * @contrast: Object containing the contrast for the red, green,
@ -1823,4 +1750,21 @@ const MagShaderEffects = new Lang.Class({
bRed != NO_CHANGE || bGreen != NO_CHANGE || bBlue != NO_CHANGE bRed != NO_CHANGE || bGreen != NO_CHANGE || bBlue != NO_CHANGE
); );
}, },
/**
* Retrieve current contrast of the magnified view.
* @return: Object containing the contrast for the red, green,
* and blue channels. Values of 0.0 represent "standard"
* contrast (no change), whereas values less or greater than
* 0.0 indicate decreased or incresaed contrast, respectively.
*/
getContrast: function() {
let resutl = {};
let [cRed, cGreen, cBlue] = this._brightnessContrast.get_contrast();
result.r = cRed;
result.g = cGreen;
result.b = cBlue;
return result;
}
}); });

View File

@ -4,94 +4,92 @@ const Gio = imports.gi.Gio;
const Lang = imports.lang; const Lang = imports.lang;
const Main = imports.ui.main; const Main = imports.ui.main;
const MAG_SERVICE_NAME = 'org.gnome.Magnifier';
const MAG_SERVICE_PATH = '/org/gnome/Magnifier'; const MAG_SERVICE_PATH = '/org/gnome/Magnifier';
const ZOOM_SERVICE_NAME = 'org.gnome.Magnifier.ZoomRegion';
const ZOOM_SERVICE_PATH = '/org/gnome/Magnifier/ZoomRegion'; const ZOOM_SERVICE_PATH = '/org/gnome/Magnifier/ZoomRegion';
// Subset of gnome-mag's Magnifier dbus interface -- to be expanded. See: // Subset of gnome-mag's Magnifier dbus interface -- to be expanded. See:
// http://git.gnome.org/browse/gnome-mag/tree/xml/...Magnifier.xml // http://git.gnome.org/browse/gnome-mag/tree/xml/...Magnifier.xml
const MagnifierIface = '<node> \ const MagnifierIface = <interface name={MAG_SERVICE_NAME}>
<interface name="org.gnome.Magnifier"> \ <method name="setActive">
<method name="setActive"> \ <arg type="b" direction="in" />
<arg type="b" direction="in" /> \ </method>
</method> \ <method name="isActive">
<method name="isActive"> \ <arg type="b" direction="out" />
<arg type="b" direction="out" /> \ </method>
</method> \ <method name="showCursor" />
<method name="showCursor" /> \ <method name="hideCursor" />
<method name="hideCursor" /> \ <method name="createZoomRegion">
<method name="createZoomRegion"> \ <arg type="d" direction="in" />
<arg type="d" direction="in" /> \ <arg type="d" direction="in" />
<arg type="d" direction="in" /> \ <arg type="ai" direction="in" />
<arg type="ai" direction="in" /> \ <arg type="ai" direction="in" />
<arg type="ai" direction="in" /> \ <arg type="o" direction="out" />
<arg type="o" direction="out" /> \ </method>
</method> \ <method name="addZoomRegion">
<method name="addZoomRegion"> \ <arg type="o" direction="in" />
<arg type="o" direction="in" /> \ <arg type="b" direction="out" />
<arg type="b" direction="out" /> \ </method>
</method> \ <method name="getZoomRegions">
<method name="getZoomRegions"> \ <arg type="ao" direction="out" />
<arg type="ao" direction="out" /> \ </method>
</method> \ <method name="clearAllZoomRegions" />
<method name="clearAllZoomRegions" /> \ <method name="fullScreenCapable">
<method name="fullScreenCapable"> \ <arg type="b" direction="out" />
<arg type="b" direction="out" /> \ </method>
</method> \ <method name="setCrosswireSize">
<method name="setCrosswireSize"> \ <arg type="i" direction="in" />
<arg type="i" direction="in" /> \ </method>
</method> \ <method name="getCrosswireSize">
<method name="getCrosswireSize"> \ <arg type="i" direction="out" />
<arg type="i" direction="out" /> \ </method>
</method> \ <method name="setCrosswireLength">
<method name="setCrosswireLength"> \ <arg type="i" direction="in" />
<arg type="i" direction="in" /> \ </method>
</method> \ <method name="getCrosswireLength">
<method name="getCrosswireLength"> \ <arg type="i" direction="out" />
<arg type="i" direction="out" /> \ </method>
</method> \ <method name="setCrosswireClip">
<method name="setCrosswireClip"> \ <arg type="b" direction="in" />
<arg type="b" direction="in" /> \ </method>
</method> \ <method name="getCrosswireClip">
<method name="getCrosswireClip"> \ <arg type="b" direction="out" />
<arg type="b" direction="out" /> \ </method>
</method> \ <method name="setCrosswireColor">
<method name="setCrosswireColor"> \ <arg type="u" direction="in" />
<arg type="u" direction="in" /> \ </method>
</method> \ <method name="getCrosswireColor">
<method name="getCrosswireColor"> \ <arg type="u" direction="out" />
<arg type="u" direction="out" /> \ </method>
</method> \ </interface>;
</interface> \
</node>';
// Subset of gnome-mag's ZoomRegion dbus interface -- to be expanded. See: // Subset of gnome-mag's ZoomRegion dbus interface -- to be expanded. See:
// http://git.gnome.org/browse/gnome-mag/tree/xml/...ZoomRegion.xml // http://git.gnome.org/browse/gnome-mag/tree/xml/...ZoomRegion.xml
const ZoomRegionIface = '<node> \ const ZoomRegionIface = <interface name={ZOOM_SERVICE_NAME}>
<interface name="org.gnome.Magnifier.ZoomRegion"> \ <method name="setMagFactor">
<method name="setMagFactor"> \ <arg type="d" direction="in" />
<arg type="d" direction="in" /> \ <arg type="d" direction="in" />
<arg type="d" direction="in" /> \ </method>
</method> \ <method name="getMagFactor">
<method name="getMagFactor"> \ <arg type="d" direction="out" />
<arg type="d" direction="out" /> \ <arg type="d" direction="out" />
<arg type="d" direction="out" /> \ </method>
</method> \ <method name="setRoi">
<method name="setRoi"> \ <arg type="ai" direction="in" />
<arg type="ai" direction="in" /> \ </method>
</method> \ <method name="getRoi">
<method name="getRoi"> \ <arg type="ai" direction="out" />
<arg type="ai" direction="out" /> \ </method>
</method> \ <method name="shiftContentsTo">
<method name="shiftContentsTo"> \ <arg type="i" direction="in" />
<arg type="i" direction="in" /> \ <arg type="i" direction="in" />
<arg type="i" direction="in" /> \ <arg type="b" direction="out" />
<arg type="b" direction="out" /> \ </method>
</method> \ <method name="moveResize">
<method name="moveResize"> \ <arg type="ai" direction="in" />
<arg type="ai" direction="in" /> \ </method>
</method> \ </interface>;
</interface> \
</node>';
// For making unique ZoomRegion DBus proxy object paths of the form: // For making unique ZoomRegion DBus proxy object paths of the form:
// '/org/gnome/Magnifier/ZoomRegion/zoomer0', // '/org/gnome/Magnifier/ZoomRegion/zoomer0',

View File

@ -28,7 +28,6 @@ const LoginManager = imports.misc.loginManager;
const LookingGlass = imports.ui.lookingGlass; const LookingGlass = imports.ui.lookingGlass;
const NotificationDaemon = imports.ui.notificationDaemon; const NotificationDaemon = imports.ui.notificationDaemon;
const WindowAttentionHandler = imports.ui.windowAttentionHandler; const WindowAttentionHandler = imports.ui.windowAttentionHandler;
const Screencast = imports.ui.screencast;
const ScreenShield = imports.ui.screenShield; const ScreenShield = imports.ui.screenShield;
const Scripting = imports.ui.scripting; const Scripting = imports.ui.scripting;
const SessionMode = imports.ui.sessionMode; const SessionMode = imports.ui.sessionMode;
@ -60,7 +59,6 @@ let sessionMode = null;
let shellDBusService = null; let shellDBusService = null;
let shellMountOpDBusService = null; let shellMountOpDBusService = null;
let screenSaverDBus = null; let screenSaverDBus = null;
let screencastService = null;
let modalCount = 0; let modalCount = 0;
let keybindingMode = Shell.KeyBindingMode.NONE; let keybindingMode = Shell.KeyBindingMode.NONE;
let modalActorFocusStack = []; let modalActorFocusStack = [];
@ -90,28 +88,26 @@ function _sessionUpdated() {
Shell.KeyBindingMode.OVERVIEW, Shell.KeyBindingMode.OVERVIEW,
sessionMode.hasRunDialog ? openRunDialog : null); sessionMode.hasRunDialog ? openRunDialog : null);
if (!sessionMode.hasRunDialog) { if (!sessionMode.hasRunDialog && lookingGlass)
if (runDialog)
runDialog.close();
if (lookingGlass)
lookingGlass.close(); lookingGlass.close();
} }
}
function start() { function start() {
// These are here so we don't break compatibility. // These are here so we don't break compatibility.
global.logError = window.log; global.logError = window.log;
global.log = window.log; global.log = window.log;
if (!Meta.is_wayland_compositor)
Meta.is_wayland_compositor = function () { return false; };
// Chain up async errors reported from C // Chain up async errors reported from C
global.connect('notify-error', function (global, msg, detail) { notifyError(msg, detail); }); global.connect('notify-error', function (global, msg, detail) { notifyError(msg, detail); });
Gio.DesktopAppInfo.set_desktop_env('GNOME'); Gio.DesktopAppInfo.set_desktop_env('GNOME');
sessionMode = new SessionMode.SessionMode(); sessionMode = new SessionMode.SessionMode();
sessionMode.connect('sessions-loaded', _sessionsLoaded);
sessionMode.init();
}
function _sessionsLoaded() {
sessionMode.connect('updated', _sessionUpdated); sessionMode.connect('updated', _sessionUpdated);
_initializePrefs(); _initializePrefs();
_initializeUI(); _initializeUI();
@ -155,7 +151,6 @@ function _initializeUI() {
// working until it's updated. // working until it's updated.
uiGroup = layoutManager.uiGroup; uiGroup = layoutManager.uiGroup;
screencastService = new Screencast.ScreencastService();
xdndHandler = new XdndHandler.XdndHandler(); xdndHandler = new XdndHandler.XdndHandler();
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager(); ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
osdWindow = new OsdWindow.OsdWindow(); osdWindow = new OsdWindow.OsdWindow();
@ -265,7 +260,11 @@ function loadTheme() {
let themeContext = St.ThemeContext.get_for_stage (global.stage); let themeContext = St.ThemeContext.get_for_stage (global.stage);
let previousTheme = themeContext.get_theme(); let previousTheme = themeContext.get_theme();
let theme = new St.Theme ({ application_stylesheet: _cssStylesheet, let cssStylesheet = _defaultCssStylesheet;
if (_cssStylesheet != null)
cssStylesheet = _cssStylesheet;
let theme = new St.Theme ({ application_stylesheet: cssStylesheet,
default_stylesheet: _defaultCssStylesheet }); default_stylesheet: _defaultCssStylesheet });
if (previousTheme) { if (previousTheme) {
@ -357,6 +356,8 @@ function pushModal(actor, params) {
Meta.disable_unredirect_for_screen(global.screen); Meta.disable_unredirect_for_screen(global.screen);
} }
global.set_stage_input_mode(Shell.StageInputMode.FULLSCREEN);
modalCount += 1; modalCount += 1;
let actorDestroyId = actor.connect('destroy', function() { let actorDestroyId = actor.connect('destroy', function() {
let index = _findModal(actor); let index = _findModal(actor);
@ -405,6 +406,7 @@ function popModal(actor, timestamp) {
if (focusIndex < 0) { if (focusIndex < 0) {
global.stage.set_key_focus(null); global.stage.set_key_focus(null);
global.end_modal(timestamp); global.end_modal(timestamp);
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
keybindingMode = Shell.KeyBindingMode.NORMAL; keybindingMode = Shell.KeyBindingMode.NORMAL;
throw new Error('incorrect pop'); throw new Error('incorrect pop');
@ -452,6 +454,7 @@ function popModal(actor, timestamp) {
return; return;
global.end_modal(timestamp); global.end_modal(timestamp);
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
Meta.enable_unredirect_for_screen(global.screen); Meta.enable_unredirect_for_screen(global.screen);
keybindingMode = Shell.KeyBindingMode.NORMAL; keybindingMode = Shell.KeyBindingMode.NORMAL;
} }
@ -609,7 +612,7 @@ function queueDeferredWork(workId) {
_deferredTimeoutId = Mainloop.timeout_add_seconds(DEFERRED_TIMEOUT_SECONDS, function () { _deferredTimeoutId = Mainloop.timeout_add_seconds(DEFERRED_TIMEOUT_SECONDS, function () {
_runAllDeferredWork(); _runAllDeferredWork();
_deferredTimeoutId = 0; _deferredTimeoutId = 0;
return GLib.SOURCE_REMOVE; return false;
}); });
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -79,13 +79,9 @@ const ModalDialog = new Lang.Class({
this.dialogLayout = new St.BoxLayout({ style_class: 'modal-dialog', this.dialogLayout = new St.BoxLayout({ style_class: 'modal-dialog',
vertical: true }); vertical: true });
// modal dialogs are fixed width and grow vertically; set the request if (params.styleClass != null) {
// mode accordingly so wrapped labels are handled correctly during
// size requests.
this.dialogLayout.request_mode = Clutter.RequestMode.HEIGHT_FOR_WIDTH;
if (params.styleClass != null)
this.dialogLayout.add_style_class_name(params.styleClass); this.dialogLayout.add_style_class_name(params.styleClass);
}
if (!this._shellReactive) { if (!this._shellReactive) {
this._lightbox = new Lightbox.Lightbox(this._group, this._lightbox = new Lightbox.Lightbox(this._group,
@ -100,8 +96,7 @@ const ModalDialog = new Lang.Class({
this.contentLayout = new St.BoxLayout({ vertical: true }); this.contentLayout = new St.BoxLayout({ vertical: true });
this.dialogLayout.add(this.contentLayout, this.dialogLayout.add(this.contentLayout,
{ expand: true, { x_fill: true,
x_fill: true,
y_fill: true, y_fill: true,
x_align: St.Align.MIDDLE, x_align: St.Align.MIDDLE,
y_align: St.Align.START }); y_align: St.Align.START });
@ -109,7 +104,8 @@ const ModalDialog = new Lang.Class({
this.buttonLayout = new St.BoxLayout({ style_class: 'modal-dialog-button-box', this.buttonLayout = new St.BoxLayout({ style_class: 'modal-dialog-button-box',
vertical: false }); vertical: false });
this.dialogLayout.add(this.buttonLayout, this.dialogLayout.add(this.buttonLayout,
{ x_align: St.Align.MIDDLE, { expand: true,
x_align: St.Align.MIDDLE,
y_align: St.Align.END }); y_align: St.Align.END });
global.focus_manager.add_group(this.dialogLayout); global.focus_manager.add_group(this.dialogLayout);
@ -229,7 +225,6 @@ const ModalDialog = new Lang.Class({
_onKeyPressEvent: function(object, event) { _onKeyPressEvent: function(object, event) {
this._pressedKey = event.get_key_symbol(); this._pressedKey = event.get_key_symbol();
return Clutter.EVENT_PROPAGATE;
}, },
_onKeyReleaseEvent: function(object, event) { _onKeyReleaseEvent: function(object, event) {
@ -238,21 +233,21 @@ const ModalDialog = new Lang.Class({
let symbol = event.get_key_symbol(); let symbol = event.get_key_symbol();
if (symbol != pressedKey) if (symbol != pressedKey)
return Clutter.EVENT_PROPAGATE; return false;
let buttonInfo = this._buttonKeys[symbol]; let buttonInfo = this._buttonKeys[symbol];
if (!buttonInfo) if (!buttonInfo)
return Clutter.EVENT_PROPAGATE; return false;
let button = buttonInfo['button']; let button = buttonInfo['button'];
let action = buttonInfo['action']; let action = buttonInfo['action'];
if (action && button.reactive) { if (action && button.reactive) {
action(); action();
return Clutter.EVENT_STOP; return true;
} }
return Clutter.EVENT_PROPAGATE; return false;
}, },
_onGroupDestroy: function() { _onGroupDestroy: function() {
@ -362,9 +357,8 @@ const ModalDialog = new Lang.Class({
if (this._savedKeyFocus) { if (this._savedKeyFocus) {
this._savedKeyFocus.grab_key_focus(); this._savedKeyFocus.grab_key_focus();
this._savedKeyFocus = null; this._savedKeyFocus = null;
} else { } else
this._initialKeyFocus.grab_key_focus(); this._initialKeyFocus.grab_key_focus();
}
if (!this._shellReactive) if (!this._shellReactive)
this._eventBlocker.lower_bottom(); this._eventBlocker.lower_bottom();

View File

@ -4,7 +4,6 @@ const Clutter = imports.gi.Clutter;
const GdkPixbuf = imports.gi.GdkPixbuf; const GdkPixbuf = imports.gi.GdkPixbuf;
const Gio = imports.gi.Gio; const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib; const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang; const Lang = imports.lang;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const Mainloop = imports.mainloop; const Mainloop = imports.mainloop;
@ -16,56 +15,54 @@ const MessageTray = imports.ui.messageTray;
const Params = imports.misc.params; const Params = imports.misc.params;
const Util = imports.misc.util; const Util = imports.misc.util;
let nextNotificationId = 1;
// Should really be defined in Gio.js // Should really be defined in Gio.js
const BusIface = '<node> \ const BusIface = <interface name="org.freedesktop.DBus">
<interface name="org.freedesktop.DBus"> \ <method name="GetConnectionUnixProcessID">
<method name="GetConnectionUnixProcessID"> \ <arg type="s" direction="in" />
<arg type="s" direction="in" /> \ <arg type="u" direction="out" />
<arg type="u" direction="out" /> \ </method>
</method> \ </interface>;
</interface> \
</node>';
var BusProxy = Gio.DBusProxy.makeProxyWrapper(BusIface); var BusProxy = Gio.DBusProxy.makeProxyWrapper(BusIface);
function Bus() { function Bus() {
return new BusProxy(Gio.DBus.session, 'org.freedesktop.DBus', '/org/freedesktop/DBus'); return new BusProxy(Gio.DBus.session, 'org.freedesktop.DBus', '/org/freedesktop/DBus');
} }
const FdoNotificationsIface = '<node> \ const NotificationDaemonIface = <interface name="org.freedesktop.Notifications">
<interface name="org.freedesktop.Notifications"> \ <method name="Notify">
<method name="Notify"> \ <arg type="s" direction="in"/>
<arg type="s" direction="in"/> \ <arg type="u" direction="in"/>
<arg type="u" direction="in"/> \ <arg type="s" direction="in"/>
<arg type="s" direction="in"/> \ <arg type="s" direction="in"/>
<arg type="s" direction="in"/> \ <arg type="s" direction="in"/>
<arg type="s" direction="in"/> \ <arg type="as" direction="in"/>
<arg type="as" direction="in"/> \ <arg type="a{sv}" direction="in"/>
<arg type="a{sv}" direction="in"/> \ <arg type="i" direction="in"/>
<arg type="i" direction="in"/> \ <arg type="u" direction="out"/>
<arg type="u" direction="out"/> \ </method>
</method> \ <method name="CloseNotification">
<method name="CloseNotification"> \ <arg type="u" direction="in"/>
<arg type="u" direction="in"/> \ </method>
</method> \ <method name="GetCapabilities">
<method name="GetCapabilities"> \ <arg type="as" direction="out"/>
<arg type="as" direction="out"/> \ </method>
</method> \ <method name="GetServerInformation">
<method name="GetServerInformation"> \ <arg type="s" direction="out"/>
<arg type="s" direction="out"/> \ <arg type="s" direction="out"/>
<arg type="s" direction="out"/> \ <arg type="s" direction="out"/>
<arg type="s" direction="out"/> \ <arg type="s" direction="out"/>
<arg type="s" direction="out"/> \ </method>
</method> \ <signal name="NotificationClosed">
<signal name="NotificationClosed"> \ <arg type="u"/>
<arg type="u"/> \ <arg type="u"/>
<arg type="u"/> \ </signal>
</signal> \ <signal name="ActionInvoked">
<signal name="ActionInvoked"> \ <arg type="u"/>
<arg type="u"/> \ <arg type="s"/>
<arg type="s"/> \ </signal>
</signal> \ </interface>;
</interface> \
</node>';
const NotificationClosedReason = { const NotificationClosedReason = {
EXPIRED: 1, EXPIRED: 1,
@ -106,11 +103,131 @@ const STANDARD_TRAY_ICON_IMPLEMENTATIONS = {
'ibus-ui-gtk': 'keyboard' 'ibus-ui-gtk': 'keyboard'
}; };
const FdoNotificationDaemon = new Lang.Class({ const NotificationGenericPolicy = new Lang.Class({
Name: 'FdoNotificationDaemon', Name: 'NotificationGenericPolicy',
Extends: MessageTray.NotificationPolicy,
_init: function() { _init: function() {
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(FdoNotificationsIface, this); // Don't chain to parent, it would try setting
// our properties to the defaults
this.id = 'generic';
this._masterSettings = new Gio.Settings({ schema: 'org.gnome.desktop.notifications' });
this._masterSettings.connect('changed', Lang.bind(this, this._changed));
},
store: function() { },
destroy: function() {
this._masterSettings.run_dispose();
},
_changed: function(settings, key) {
this.emit('policy-changed', key);
},
get enable() {
return true;
},
get enableSound() {
return true;
},
get showBanners() {
return this._masterSettings.get_boolean('show-banners');
},
get forceExpanded() {
return false;
},
get showInLockScreen() {
return this._masterSettings.get_boolean('show-in-lock-screen');
},
get detailsInLockScreen() {
return false;
}
});
const NotificationApplicationPolicy = new Lang.Class({
Name: 'NotificationApplicationPolicy',
Extends: MessageTray.NotificationPolicy,
_init: function(id) {
// Don't chain to parent, it would try setting
// our properties to the defaults
this.id = id;
this._canonicalId = this._canonicalizeId(id)
this._masterSettings = new Gio.Settings({ schema: 'org.gnome.desktop.notifications' });
this._settings = new Gio.Settings({ schema: 'org.gnome.desktop.notifications.application',
path: '/org/gnome/desktop/notifications/application/' + this._canonicalId + '/' });
this._masterSettings.connect('changed', Lang.bind(this, this._changed));
this._settings.connect('changed', Lang.bind(this, this._changed));
},
store: function() {
this._settings.set_string('application-id', this.id + '.desktop');
let apps = this._masterSettings.get_strv('application-children');
if (apps.indexOf(this._canonicalId) < 0) {
apps.push(this._canonicalId);
this._masterSettings.set_strv('application-children', apps);
}
},
destroy: function() {
this._masterSettings.run_dispose();
this._settings.run_dispose();
},
_changed: function(settings, key) {
this.emit('policy-changed', key);
},
_canonicalizeId: function(id) {
// Keys are restricted to lowercase alphanumeric characters and dash,
// and two dashes cannot be in succession
return id.toLowerCase().replace(/[^a-z0-9\-]/g, '-').replace(/--+/g, '-');
},
get enable() {
return this._settings.get_boolean('enable');
},
get enableSound() {
return this._settings.get_boolean('enable-sound-alerts');
},
get showBanners() {
return this._masterSettings.get_boolean('show-banners') &&
this._settings.get_boolean('show-banners');
},
get forceExpanded() {
return this._settings.get_boolean('force-expanded');
},
get showInLockScreen() {
return this._masterSettings.get_boolean('show-in-lock-screen') &&
this._settings.get_boolean('show-in-lock-screen');
},
get detailsInLockScreen() {
return this._settings.get_boolean('details-in-lock-screen');
}
});
const NotificationDaemon = new Lang.Class({
Name: 'NotificationDaemon',
_init: function() {
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(NotificationDaemonIface, this);
this._dbusImpl.export(Gio.DBus.session, '/org/freedesktop/Notifications'); this._dbusImpl.export(Gio.DBus.session, '/org/freedesktop/Notifications');
this._sources = []; this._sources = [];
@ -118,8 +235,6 @@ const FdoNotificationDaemon = new Lang.Class({
this._notifications = {}; this._notifications = {};
this._busProxy = new Bus(); this._busProxy = new Bus();
this._nextNotificationId = 1;
this._trayManager = new Shell.TrayManager(); this._trayManager = new Shell.TrayManager();
this._trayIconAddedId = this._trayManager.connect('tray-icon-added', Lang.bind(this, this._onTrayIconAdded)); this._trayIconAddedId = this._trayManager.connect('tray-icon-added', Lang.bind(this, this._onTrayIconAdded));
this._trayIconRemovedId = this._trayManager.connect('tray-icon-removed', Lang.bind(this, this._onTrayIconRemoved)); this._trayIconRemovedId = this._trayManager.connect('tray-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
@ -181,10 +296,12 @@ const FdoNotificationDaemon = new Lang.Class({
}, },
// Returns the source associated with ndata.notification if it is set. // Returns the source associated with ndata.notification if it is set.
// If the existing or requested source is associated with a tray icon // Otherwise, returns the source associated with the title and pid if
// and passed in pid matches a pid of an existing source, the title // such source is stored in this._sources and the notification is not
// match is ignored to enable representing a tray icon and notifications // transient. If the existing or requested source is associated with
// from the same application with a single source. // a tray icon and passed in pid matches a pid of an existing source,
// the title match is ignored to enable representing a tray icon and
// notifications from the same application with a single source.
// //
// If no existing source is found, a new source is created as long as // If no existing source is found, a new source is created as long as
// pid is provided. // pid is provided.
@ -202,20 +319,32 @@ const FdoNotificationDaemon = new Lang.Class({
if (ndata && ndata.notification) if (ndata && ndata.notification)
return ndata.notification.source; 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) {
let source = this._lookupSource(title, pid, trayIcon); let source = this._lookupSource(title, pid, trayIcon);
if (source) { if (source) {
source.setTitle(title); source.setTitle(title);
return source; return source;
} }
}
let source = new FdoNotificationDaemonSource(title, pid, sender, trayIcon, ndata ? ndata.hints['desktop-entry'] : null); let source = new Source(title, pid, sender, trayIcon, ndata ? ndata.hints['desktop-entry'] : null);
source.setTransient(isForTransientNotification);
if (!isForTransientNotification) {
this._sources.push(source); this._sources.push(source);
source.connect('destroy', Lang.bind(this, function() { source.connect('destroy', Lang.bind(this,
function() {
let index = this._sources.indexOf(source); let index = this._sources.indexOf(source);
if (index >= 0) if (index >= 0)
this._sources.splice(index, 1); this._sources.splice(index, 1);
})); }));
}
Main.messageTray.add(source); Main.messageTray.add(source);
return source; return source;
@ -243,11 +372,11 @@ const FdoNotificationDaemon = new Lang.Class({
hints['category'] == 'presence.offline')) { hints['category'] == 'presence.offline')) {
// Ignore replacesId since we already sent back a // Ignore replacesId since we already sent back a
// NotificationClosed for that id. // NotificationClosed for that id.
id = this._nextNotificationId++; id = nextNotificationId++;
Mainloop.idle_add(Lang.bind(this, Mainloop.idle_add(Lang.bind(this,
function () { function () {
this._emitNotificationClosed(id, NotificationClosedReason.DISMISSED); this._emitNotificationClosed(id, NotificationClosedReason.DISMISSED);
return GLib.SOURCE_REMOVE; return false;
})); }));
return invocation.return_value(GLib.Variant.new('(u)', [id])); return invocation.return_value(GLib.Variant.new('(u)', [id]));
} }
@ -267,13 +396,12 @@ const FdoNotificationDaemon = new Lang.Class({
if (!hints['image-path'] && hints['image_path']) if (!hints['image-path'] && hints['image_path'])
hints['image-path'] = hints['image_path']; // version 1.1 of the spec hints['image-path'] = hints['image_path']; // version 1.1 of the spec
if (!hints['image-data']) { if (!hints['image-data'])
if (hints['image_data']) if (hints['image_data'])
hints['image-data'] = hints['image_data']; // version 1.1 of the spec hints['image-data'] = hints['image_data']; // version 1.1 of the spec
else if (hints['icon_data'] && !hints['image-path']) else if (hints['icon_data'] && !hints['image-path'])
// early versions of the spec; 'icon_data' should only be used if 'image-path' is not available // early versions of the spec; 'icon_data' should only be used if 'image-path' is not available
hints['image-data'] = hints['icon_data']; hints['image-data'] = hints['icon_data'];
}
let ndata = { appName: appName, let ndata = { appName: appName,
icon: icon, icon: icon,
@ -287,7 +415,7 @@ const FdoNotificationDaemon = new Lang.Class({
ndata.notification = this._notifications[replacesId].notification; ndata.notification = this._notifications[replacesId].notification;
} else { } else {
replacesId = 0; replacesId = 0;
ndata.id = id = this._nextNotificationId++; ndata.id = id = nextNotificationId++;
} }
this._notifications[id] = ndata; this._notifications[id] = ndata;
@ -322,29 +450,26 @@ const FdoNotificationDaemon = new Lang.Class({
let [pid] = result; let [pid] = result;
source = this._getSource(appName, pid, ndata, sender, null); source = this._getSource(appName, pid, ndata, sender, null);
// 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; this._senderToPid[sender] = pid;
source.connect('destroy', Lang.bind(this, function() { source.connect('destroy', Lang.bind(this, function() {
delete this._senderToPid[sender]; delete this._senderToPid[sender];
})); }));
}
this._notifyForSource(source, ndata); this._notifyForSource(source, ndata);
})); }));
return invocation.return_value(GLib.Variant.new('(u)', [id])); return invocation.return_value(GLib.Variant.new('(u)', [id]));
}, },
_makeButton: function(id, label, useActionIcons) {
let button = new St.Button({ can_focus: true });
let iconName = id.endsWith('-symbolic') ? id : id + '-symbolic';
if (useActionIcons && Gtk.IconTheme.get_default().has_icon(iconName)) {
button.add_style_class_name('notification-icon-button');
button.child = new St.Icon({ icon_name: iconName });
} else {
button.add_style_class_name('notification-button');
button.label = label;
}
return button;
},
_notifyForSource: function(source, ndata) { _notifyForSource: function(source, ndata) {
let [id, icon, summary, body, actions, hints, notification] = let [id, icon, summary, body, actions, hints, notification] =
[ndata.id, ndata.icon, ndata.summary, ndata.body, [ndata.id, ndata.icon, ndata.summary, ndata.body,
@ -370,6 +495,10 @@ const FdoNotificationDaemon = new Lang.Class({
} }
this._emitNotificationClosed(ndata.id, notificationClosedReason); this._emitNotificationClosed(ndata.id, notificationClosedReason);
})); }));
notification.connect('action-invoked', Lang.bind(this,
function(n, actionId) {
this._emitActionInvoked(ndata.id, actionId);
}));
} }
// Mark music notifications so they can be shown in the screen shield // Mark music notifications so they can be shown in the screen shield
@ -403,33 +532,18 @@ const FdoNotificationDaemon = new Lang.Class({
soundName: hints['sound-name'] }); soundName: hints['sound-name'] });
notification.setImage(image); notification.setImage(image);
let hasDefaultAction = false;
if (actions.length) { if (actions.length) {
let useActionIcons = (hints['action-icons'] == true); notification.setUseActionIcons(hints['action-icons'] == true);
for (let i = 0; i < actions.length - 1; i += 2) { for (let i = 0; i < actions.length - 1; i += 2) {
let [actionId, label] = [actions[i], actions[i+1]]; if (actions[i] == 'default')
if (actionId == 'default') { notification.connect('clicked', Lang.bind(this,
hasDefaultAction = true; function() {
} else { this._emitActionInvoked(ndata.id, "default");
notification.addButton(this._makeButton(actionId, label, useActionIcons), Lang.bind(this, function() {
this._emitActionInvoked(ndata.id, actionId);
})); }));
else
notification.addButton(actions[i], actions[i + 1]);
} }
} }
}
if (hasDefaultAction) {
notification.connect('clicked', Lang.bind(this, function() {
this._emitActionInvoked(ndata.id, 'default');
}));
} else {
notification.connect('clicked', Lang.bind(this, function() {
source.open();
}));
}
switch (hints.urgency) { switch (hints.urgency) {
case Urgency.LOW: case Urgency.LOW:
notification.setUrgency(MessageTray.Urgency.LOW); notification.setUrgency(MessageTray.Urgency.LOW);
@ -522,8 +636,8 @@ const FdoNotificationDaemon = new Lang.Class({
} }
}); });
const FdoNotificationDaemonSource = new Lang.Class({ const Source = new Lang.Class({
Name: 'FdoNotificationDaemonSource', Name: 'NotificationDaemonSource',
Extends: MessageTray.Source, Extends: MessageTray.Source,
_init: function(title, pid, sender, trayIcon, appId) { _init: function(title, pid, sender, trayIcon, appId) {
@ -558,11 +672,11 @@ const FdoNotificationDaemonSource = new Lang.Class({
}, },
_createPolicy: function() { _createPolicy: function() {
if (this.app && this.app.get_app_info()) { if (this.app) {
let id = this.app.get_id().replace(/\.desktop$/,''); let id = this.app.get_id().replace(/\.desktop$/,'');
return new MessageTray.NotificationApplicationPolicy(id); return new NotificationApplicationPolicy(id);
} else { } else {
return new MessageTray.NotificationGenericPolicy(); return new NotificationGenericPolicy();
} }
}, },
@ -603,8 +717,8 @@ const FdoNotificationDaemonSource = new Lang.Class({
this.notifications.length > 0) this.notifications.length > 0)
return false; return false;
let id = global.stage.connect('deactivate', Lang.bind(this, function () { let id = global.connect('notify::stage-input-mode', Lang.bind(this, function () {
global.stage.disconnect(id); global.disconnect(id);
this.trayIcon.click(event); this.trayIcon.click(event);
})); }));
@ -620,11 +734,7 @@ const FdoNotificationDaemonSource = new Lang.Class({
return app; return app;
if (this.trayIcon) { if (this.trayIcon) {
app = Shell.AppSystem.get_default().lookup_startup_wmclass(this.trayIcon.wm_class); app = Shell.AppSystem.get_default().lookup_wmclass(this.trayIcon.wm_class);
if (app != null)
return app;
app = Shell.AppSystem.get_default().lookup_desktop_wmclass(this.trayIcon.wm_class);
if (app != null) if (app != null)
return app; return app;
} }
@ -638,6 +748,22 @@ const FdoNotificationDaemonSource = new Lang.Class({
return null; return null;
}, },
_setApp: function(appId) {
if (this.app)
return;
this.app = this._getApp(appId);
if (!this.app)
return;
// Only override the icon if we were previously using
// notification-based icons (ie, not a trayicon) or if it was unset before
if (!this.trayIcon) {
this.useNotificationIcon = false;
this.iconUpdated();
}
},
setTitle: function(title) { setTitle: function(title) {
// Do nothing if .app is set, we don't want to override the // Do nothing if .app is set, we don't want to override the
// app name with whatever is provided through libnotify (usually // app name with whatever is provided through libnotify (usually
@ -648,9 +774,9 @@ const FdoNotificationDaemonSource = new Lang.Class({
this.parent(title); this.parent(title);
}, },
open: function() { open: function(notification) {
this.openApp();
this.destroyNonResidentNotifications(); this.destroyNonResidentNotifications();
this.openApp();
}, },
_lastNotificationRemoved: function() { _lastNotificationRemoved: function() {
@ -662,8 +788,11 @@ const FdoNotificationDaemonSource = new Lang.Class({
if (this.app == null) if (this.app == null)
return; return;
this.app.activate(); let windows = this.app.get_windows();
Main.overview.hide(); if (windows.length > 0) {
let mostRecentWindow = windows[0];
Main.activateWindow(mostRecentWindow);
}
}, },
destroy: function() { destroy: function() {
@ -690,276 +819,3 @@ const FdoNotificationDaemonSource = new Lang.Class({
} }
} }
}); });
const GtkNotificationDaemonNotification = new Lang.Class({
Name: 'GtkNotificationDaemonNotification',
Extends: MessageTray.Notification,
_init: function(source, notification) {
this.parent(source);
this._serialized = GLib.Variant.new('a{sv}', notification);
let { "title": title,
"body": body,
"icon": gicon,
"urgent": urgent,
"buttons": buttons,
"default-action": defaultAction,
"default-action-target": defaultActionTarget } = notification;
this.setUrgency(urgent.unpack() ? MessageTray.Urgency.CRITICAL
: MessageTray.Urgency.NORMAL);
if (buttons) {
buttons.deep_unpack().forEach(Lang.bind(this, function(button) {
this.addAction(button.label.unpack(),
Lang.bind(this, this._onButtonClicked, button));
}));
}
this._defaultAction = defaultAction ? defaultAction.unpack() : null;
this._defaultActionTarget = defaultActionTarget;
this.update(title.unpack(), body ? body.unpack() : null,
{ gicon: gicon ? Gio.icon_deserialize(gicon) : null });
},
_activateAction: function(namespacedActionId, target) {
if (namespacedActionId) {
if (namespacedActionId.startsWith('app.')) {
let actionId = namespacedActionId.slice('app.'.length);
this.source.activateAction(actionId, target);
}
} else {
this.source.open();
}
},
_onButtonClicked: function(button) {
let { 'action': action, 'target': actionTarget } = button;
this._activateAction(action.unpack(), actionTarget);
},
_onClicked: function() {
this._activateAction(this._defaultAction, this._defaultActionTarget);
this.parent();
},
serialize: function() {
return this._serialized;
},
});
const FdoApplicationIface = '<node> \
<interface name="org.freedesktop.Application"> \
<method name="ActivateAction"> \
<arg type="s" direction="in" /> \
<arg type="av" direction="in" /> \
<arg type="a{sv}" direction="in" /> \
</method> \
<method name="Activate"> \
<arg type="a{sv}" direction="in" /> \
</method> \
</interface> \
</node>';
const FdoApplicationProxy = Gio.DBusProxy.makeProxyWrapper(FdoApplicationIface);
function objectPathFromAppId(appId) {
return '/' + appId.replace(/\./g, '/');
}
function getPlatformData() {
let startupId = GLib.Variant.new('s', '_TIME' + global.get_current_time());
return { "desktop-startup-id": startupId };
}
function InvalidAppError() {}
const GtkNotificationDaemonAppSource = new Lang.Class({
Name: 'GtkNotificationDaemonAppSource',
Extends: MessageTray.Source,
_init: function(appId) {
this._appId = appId;
this._objectPath = objectPathFromAppId(appId);
this._app = Shell.AppSystem.get_default().lookup_app(appId + '.desktop');
if (!this._app)
throw new InvalidAppError();
this._notifications = {};
this.parent(this._app.get_name());
},
createIcon: function(size) {
return this._app.create_icon_texture(size);
},
_createPolicy: function() {
return new MessageTray.NotificationApplicationPolicy(this._appId);
},
_createApp: function() {
return new FdoApplicationProxy(Gio.DBus.session, this._appId, this._objectPath);
},
activateAction: function(actionId, target) {
let app = this._createApp();
app.ActivateActionRemote(actionId, target ? [target] : [], getPlatformData());
},
open: function() {
let app = this._createApp();
app.ActivateRemote(getPlatformData());
},
addNotification: function(notificationId, notificationParams, showBanner) {
if (this._notifications[notificationId])
this._notifications[notificationId].destroy();
let notification = new GtkNotificationDaemonNotification(this, notificationParams);
notification.connect('destroy', Lang.bind(this, function() {
delete this._notifications[notificationId];
}));
this._notifications[notificationId] = notification;
if (showBanner)
this.notify(notification);
else
this.pushNotification(notification);
},
removeNotification: function(notificationId) {
if (this._notifications[notificationId])
this._notifications[notificationId].destroy(MessageTray.NotificationDestroyedReason.SOURCE_CLOSED);
},
serialize: function() {
let notifications = [];
for (let notificationId in this._notifications) {
let notification = this._notifications[notificationId];
notifications.push([notificationId, notification.serialize()]);
}
return [this._appId, notifications];
},
});
const GtkNotificationsIface = '<node> \
<interface name="org.gtk.Notifications"> \
<method name="AddNotification"> \
<arg type="s" direction="in" /> \
<arg type="s" direction="in" /> \
<arg type="a{sv}" direction="in" /> \
</method> \
<method name="RemoveNotification"> \
<arg type="s" direction="in" /> \
<arg type="s" direction="in" /> \
</method> \
</interface> \
</node>';
const GtkNotificationDaemon = new Lang.Class({
Name: 'GtkNotificationDaemon',
_init: function() {
this._sources = {};
this._loadNotifications();
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(GtkNotificationsIface, this);
this._dbusImpl.export(Gio.DBus.session, '/org/gtk/Notifications');
Gio.DBus.session.own_name('org.gtk.Notifications', Gio.BusNameOwnerFlags.REPLACE, null, null);
},
_ensureAppSource: function(appId) {
if (this._sources[appId])
return this._sources[appId];
let source = new GtkNotificationDaemonAppSource(appId);
source.connect('destroy', Lang.bind(this, function() {
delete this._sources[appId];
this._saveNotifications();
}));
source.connect('count-updated', Lang.bind(this, this._saveNotifications));
Main.messageTray.add(source);
this._sources[appId] = source;
return source;
},
_loadNotifications: function() {
this._isLoading = true;
let value = global.get_persistent_state('a(sa(sv))', 'notifications');
if (value) {
let sources = value.deep_unpack();
sources.forEach(Lang.bind(this, function([appId, notifications]) {
if (notifications.length == 0)
return;
let source;
try {
source = this._ensureAppSource(appId);
} catch(e if e instanceof InvalidAppError) {
return;
}
notifications.forEach(function([notificationId, notification]) {
source.addNotification(notificationId, notification.deep_unpack(), false);
});
}));
}
this._isLoading = false;
},
_saveNotifications: function() {
if (this._isLoading)
return;
let sources = [];
for (let appId in this._sources) {
let source = this._sources[appId];
sources.push(source.serialize());
}
global.set_persistent_state('notifications', new GLib.Variant('a(sa(sv))', sources));
},
AddNotificationAsync: function(params, invocation) {
let [appId, notificationId, notification] = params;
let source;
try {
source = this._ensureAppSource(appId);
} catch(e if e instanceof InvalidAppError) {
invocation.return_dbus_error('org.gtk.Notifications.InvalidApp', 'The app by ID "%s" could not be found'.format(appId));
return;
}
source.addNotification(notificationId, notification, true);
invocation.return_value(null);
},
RemoveNotificationAsync: function(params, invocation) {
let [appId, notificationId] = params;
let source = this._sources[appId];
if (source)
source.removeNotification(notificationId);
invocation.return_value(null);
},
});
const NotificationDaemon = new Lang.Class({
Name: 'NotificationDaemon',
_init: function() {
this._fdoNotificationDaemon = new FdoNotificationDaemon();
this._gtkNotificationDaemon = new GtkNotificationDaemon();
},
});

View File

@ -1,7 +1,6 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const St = imports.gi.St; const St = imports.gi.St;
const Lang = imports.lang; const Lang = imports.lang;
@ -78,8 +77,7 @@ const OsdWindow = new Lang.Class({
y_expand: true, y_expand: true,
x_align: Clutter.ActorAlign.CENTER, x_align: Clutter.ActorAlign.CENTER,
y_align: Clutter.ActorAlign.CENTER }); y_align: Clutter.ActorAlign.CENTER });
this._currentMonitor = undefined; this.actor.add_constraint(new Layout.MonitorConstraint({ primary: true }));
this.setMonitor (-1);
this._box = new St.BoxLayout({ style_class: 'osd-window', this._box = new St.BoxLayout({ style_class: 'osd-window',
vertical: true }); vertical: true });
this.actor.add_actor(this._box); this.actor.add_actor(this._box);
@ -174,7 +172,6 @@ const OsdWindow = new Lang.Class({
Meta.enable_unredirect_for_screen(global.screen); Meta.enable_unredirect_for_screen(global.screen);
}) })
}); });
return GLib.SOURCE_REMOVE;
}, },
_reset: function() { _reset: function() {
@ -185,13 +182,7 @@ const OsdWindow = new Lang.Class({
_monitorsChanged: function() { _monitorsChanged: function() {
/* assume 110x110 on a 640x480 display and scale from there */ /* assume 110x110 on a 640x480 display and scale from there */
let monitor; let monitor = Main.layoutManager.primaryMonitor;
if (this._currentMonitor >= 0)
monitor = Main.layoutManager.monitors[this._currentMonitor];
else
monitor = Main.layoutManager.primaryMonitor;
let scalew = monitor.width / 640.0; let scalew = monitor.width / 640.0;
let scaleh = monitor.height / 480.0; let scaleh = monitor.height / 480.0;
let scale = Math.min(scalew, scaleh); let scale = Math.min(scalew, scaleh);
@ -215,23 +206,5 @@ const OsdWindow = new Lang.Class({
let minHeight = this._popupSize - horizontalPadding - topBorder - bottomBorder; let minHeight = this._popupSize - horizontalPadding - topBorder - bottomBorder;
this._box.style = 'min-height: %dpx;'.format(Math.max(minWidth, minHeight)); this._box.style = 'min-height: %dpx;'.format(Math.max(minWidth, minHeight));
},
setMonitor: function(index) {
let constraint;
if (index < 0)
index = -1;
if (this._currentMonitor == index)
return;
if (index < 0)
constraint = new Layout.MonitorConstraint({ primary: true });
else
constraint = new Layout.MonitorConstraint({ index: index });
this.actor.clear_constraints();
this.actor.add_constraint(constraint);
this._currentMonitor = index;
} }
}); });

View File

@ -1,7 +1,6 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk; const Gtk = imports.gi.Gtk;
const Meta = imports.gi.Meta; const Meta = imports.gi.Meta;
const Mainloop = imports.mainloop; const Mainloop = imports.mainloop;
@ -79,8 +78,10 @@ const ShellInfo = new Lang.Class({
} }
this._undoCallback = undoCallback; this._undoCallback = undoCallback;
if (undoCallback) if (undoCallback) {
notification.addAction(_("Undo"), Lang.bind(this, this._onUndoClicked)); notification.addButton('system-undo', _("Undo"));
notification.connect('action-invoked', Lang.bind(this, this._onUndoClicked));
}
this._source.notify(notification); this._source.notify(notification);
} }
@ -113,6 +114,9 @@ const Overview = new Lang.Class({
// rendering options without duplicating the texture data. // rendering options without duplicating the texture data.
let monitor = Main.layoutManager.primaryMonitor; let monitor = Main.layoutManager.primaryMonitor;
this._desktopFade = new St.Bin();
Main.layoutManager.overviewGroup.add_child(this._desktopFade);
let layout = new Clutter.BinLayout(); let layout = new Clutter.BinLayout();
this._stack = new Clutter.Actor({ layout_manager: layout }); this._stack = new Clutter.Actor({ layout_manager: layout });
this._stack.add_constraint(new LayoutManager.MonitorConstraint({ primary: true })); this._stack.add_constraint(new LayoutManager.MonitorConstraint({ primary: true }));
@ -131,9 +135,6 @@ const Overview = new Lang.Class({
Main.layoutManager.overviewGroup.add_child(this._backgroundGroup); Main.layoutManager.overviewGroup.add_child(this._backgroundGroup);
this._bgManagers = []; this._bgManagers = [];
this._desktopFade = new St.Bin();
Main.layoutManager.overviewGroup.add_child(this._desktopFade);
this._activationTime = 0; this._activationTime = 0;
this.visible = false; // animating to overview, in overview, animating out this.visible = false; // animating to overview, in overview, animating out
@ -147,8 +148,8 @@ const Overview = new Lang.Class({
// Dash elements, or mouseover handlers in the workspaces. // Dash elements, or mouseover handlers in the workspaces.
this._coverPane = new Clutter.Actor({ opacity: 0, this._coverPane = new Clutter.Actor({ opacity: 0,
reactive: true }); reactive: true });
Main.layoutManager.overviewGroup.add_child(this._coverPane); this._overview.add_actor(this._coverPane);
this._coverPane.connect('event', Lang.bind(this, function (actor, event) { return Clutter.EVENT_STOP; })); this._coverPane.connect('event', Lang.bind(this, function (actor, event) { return true; }));
this._stack.add_actor(this._overview); this._stack.add_actor(this._overview);
Main.layoutManager.overviewGroup.add_child(this._stack); Main.layoutManager.overviewGroup.add_child(this._stack);
@ -264,7 +265,7 @@ const Overview = new Lang.Class({
// Create controls // Create controls
this._controls = new OverviewControls.ControlsManager(this._searchEntry); this._controls = new OverviewControls.ControlsManager(this._searchEntry);
this._dash = this._controls.dash; this._dash = this._controls.dash;
this.viewSelector = this._controls.viewSelector; this._viewSelector = this._controls.viewSelector;
// Add our same-line elements after the search entry // Add our same-line elements after the search entry
this._overview.add(this._controls.actor, { y_fill: true, expand: true }); this._overview.add(this._controls.actor, { y_fill: true, expand: true });
@ -284,11 +285,11 @@ const Overview = new Lang.Class({
}, },
addSearchProvider: function(provider) { addSearchProvider: function(provider) {
this.viewSelector.addSearchProvider(provider); this._viewSelector.addSearchProvider(provider);
}, },
removeSearchProvider: function(provider) { removeSearchProvider: function(provider) {
this.viewSelector.removeSearchProvider(provider); this._viewSelector.removeSearchProvider(provider);
}, },
// //
@ -364,13 +365,11 @@ const Overview = new Lang.Class({
this._lastHoveredWindow = dragEvent.targetActor._delegate.metaWindow; this._lastHoveredWindow = dragEvent.targetActor._delegate.metaWindow;
this._windowSwitchTimeoutId = Mainloop.timeout_add(DND_WINDOW_SWITCH_TIMEOUT, this._windowSwitchTimeoutId = Mainloop.timeout_add(DND_WINDOW_SWITCH_TIMEOUT,
Lang.bind(this, function() { Lang.bind(this, function() {
this._windowSwitchTimeoutId = 0;
this._needsFakePointerEvent = true; this._needsFakePointerEvent = true;
Main.activateWindow(dragEvent.targetActor._delegate.metaWindow, Main.activateWindow(dragEvent.targetActor._delegate.metaWindow,
this._windowSwitchTimestamp); this._windowSwitchTimestamp);
this.hide(); this.hide();
this._lastHoveredWindow = null; this._lastHoveredWindow = null;
return GLib.SOURCE_REMOVE;
})); }));
} }
@ -379,7 +378,6 @@ const Overview = new Lang.Class({
_onScrollEvent: function(actor, event) { _onScrollEvent: function(actor, event) {
this.emit('scroll-event', event); this.emit('scroll-event', event);
return Clutter.EVENT_PROPAGATE;
}, },
addAction: function(action) { addAction: function(action) {
@ -447,17 +445,17 @@ const Overview = new Lang.Class({
this._inDrag = false; this._inDrag = false;
}, },
beginWindowDrag: function(clone) { beginWindowDrag: function(source) {
this.emit('window-drag-begin', clone); this.emit('window-drag-begin');
this._inDrag = true; this._inDrag = true;
}, },
cancelledWindowDrag: function(clone) { cancelledWindowDrag: function(source) {
this.emit('window-drag-cancelled', clone); this.emit('window-drag-cancelled');
}, },
endWindowDrag: function(clone) { endWindowDrag: function(source) {
this.emit('window-drag-end', clone); this.emit('window-drag-end');
this._inDrag = false; this._inDrag = false;
}, },
@ -514,8 +512,17 @@ const Overview = new Lang.Class({
this.visibleTarget = true; this.visibleTarget = true;
this._activationTime = Date.now() / 1000; this._activationTime = Date.now() / 1000;
// All the the actors in the window group are completely obscured,
// hiding the group holding them while the Overview is displayed greatly
// increases performance of the Overview especially when there are many
// windows visible.
//
// If we switched to displaying the actors in the Overview rather than
// clones of them, this would obviously no longer be necessary.
//
// Disable unredirection while in the overview
Meta.disable_unredirect_for_screen(global.screen); Meta.disable_unredirect_for_screen(global.screen);
this.viewSelector.show(); this._viewSelector.show();
this._stack.opacity = 0; this._stack.opacity = 0;
Tweener.addTween(this._stack, Tweener.addTween(this._stack,
@ -622,7 +629,7 @@ const Overview = new Lang.Class({
this.animationInProgress = true; this.animationInProgress = true;
this.visibleTarget = false; this.visibleTarget = false;
this.viewSelector.zoomFromOverview(); this._viewSelector.zoomFromOverview();
// Make other elements fade out. // Make other elements fade out.
Tweener.addTween(this._stack, Tweener.addTween(this._stack,
@ -657,7 +664,7 @@ const Overview = new Lang.Class({
// Re-enable unredirection // Re-enable unredirection
Meta.enable_unredirect_for_screen(global.screen); Meta.enable_unredirect_for_screen(global.screen);
this.viewSelector.hide(); this._viewSelector.hide();
this._desktopFade.hide(); this._desktopFade.hide();
this._coverPane.hide(); this._coverPane.hide();

View File

@ -36,7 +36,6 @@ const SlideLayout = new Lang.Class({
_init: function(params) { _init: function(params) {
this._slideX = 1; this._slideX = 1;
this._translationX = 0;
this._direction = SlideDirection.LEFT; this._direction = SlideDirection.LEFT;
this.parent(params); this.parent(params);
@ -56,21 +55,18 @@ const SlideLayout = new Lang.Class({
vfunc_allocate: function(container, box, flags) { vfunc_allocate: function(container, box, flags) {
let child = container.get_first_child(); let child = container.get_first_child();
let [, , natWidth, natHeight] = child.get_preferred_size();
let availWidth = Math.round(box.x2 - box.x1); let availWidth = Math.round(box.x2 - box.x1);
let availHeight = Math.round(box.y2 - box.y1); let availHeight = Math.round(box.y2 - box.y1);
let [, natWidth] = child.get_preferred_width(availHeight);
// Align the actor inside the clipped box, as the actor's alignment
// flags only determine what to do if the allocated box is bigger
// than the actor's box.
let realDirection = getRtlSlideDirection(this._direction, child); let realDirection = getRtlSlideDirection(this._direction, child);
let alignX = (realDirection == SlideDirection.LEFT) ? (availWidth - natWidth) : 0; let translationX = (realDirection == SlideDirection.LEFT) ?
(availWidth - natWidth) : (natWidth - availWidth);
let actorBox = new Clutter.ActorBox(); let actorBox = new Clutter.ActorBox({ x1: translationX,
actorBox.x1 = box.x1 + alignX + this._translationX; y1: 0,
actorBox.x2 = actorBox.x1 + (child.x_expand ? availWidth : natWidth); x2: child.x_expand ? availWidth : natWidth,
actorBox.y1 = box.y1; y2: child.y_expand ? availHeight : natHeight });
actorBox.y2 = actorBox.y1 + availHeight;
child.allocate(actorBox, flags); child.allocate(actorBox, flags);
}, },
@ -91,16 +87,7 @@ const SlideLayout = new Lang.Class({
get slideDirection() { get slideDirection() {
return this._direction; return this._direction;
}, }
set translationX(value) {
this._translationX = value;
this.layout_changed();
},
get translationX() {
return this._translationX;
},
}); });
const SlidingControl = new Lang.Class({ const SlidingControl = new Lang.Class({
@ -109,8 +96,8 @@ const SlidingControl = new Lang.Class({
_init: function(params) { _init: function(params) {
params = Params.parse(params, { slideDirection: SlideDirection.LEFT }); params = Params.parse(params, { slideDirection: SlideDirection.LEFT });
this._visible = true; this.visible = true;
this._inDrag = false; this.inDrag = false;
this.layout = new SlideLayout(); this.layout = new SlideLayout();
this.layout.slideDirection = params.slideDirection; this.layout.slideDirection = params.slideDirection;
@ -119,7 +106,6 @@ const SlidingControl = new Lang.Class({
clip_to_allocation: true }); clip_to_allocation: true });
Main.overview.connect('showing', Lang.bind(this, this._onOverviewShowing)); Main.overview.connect('showing', Lang.bind(this, this._onOverviewShowing));
Main.overview.connect('hiding', Lang.bind(this, this._onOverviewHiding));
Main.overview.connect('item-drag-begin', Lang.bind(this, this._onDragBegin)); 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-end', Lang.bind(this, this._onDragEnd));
@ -130,12 +116,12 @@ const SlidingControl = new Lang.Class({
Main.overview.connect('window-drag-end', Lang.bind(this, this._onWindowDragEnd)); Main.overview.connect('window-drag-end', Lang.bind(this, this._onWindowDragEnd));
}, },
_getSlide: function() { getSlide: function() {
throw new Error('getSlide() must be overridden'); throw new Error('getSlide() must be overridden');
}, },
_updateSlide: function() { updateSlide: function() {
Tweener.addTween(this.layout, { slideX: this._getSlide(), Tweener.addTween(this.layout, { slideX: this.getSlide(),
time: SIDE_CONTROLS_ANIMATION_TIME, time: SIDE_CONTROLS_ANIMATION_TIME,
transition: 'easeOutQuad' }); transition: 'easeOutQuad' });
}, },
@ -162,30 +148,28 @@ const SlidingControl = new Lang.Class({
let translationEnd = 0; let translationEnd = 0;
let translation = this._getTranslation(); let translation = this._getTranslation();
if (this._visible) { if (this.visible) {
translationStart = translation; translationStart = translation;
} else { } else {
translationEnd = translation; translationEnd = translation;
} }
if (this.layout.translationX == translationEnd) if (this.actor.translation_x == translationEnd)
return; return;
this.layout.translationX = translationStart; this.actor.translation_x = translationStart;
Tweener.addTween(this.layout, { translationX: translationEnd, Tweener.addTween(this.actor, { translation_x: translationEnd,
time: SIDE_CONTROLS_ANIMATION_TIME, time: SIDE_CONTROLS_ANIMATION_TIME,
transition: 'easeOutQuad' }); transition: 'easeOutQuad'
});
}, },
_onOverviewShowing: function() { _onOverviewShowing: function() {
this._visible = true; // reset any translation and make sure the actor is visible when
this.layout.slideX = this._getSlide(); // entering the overview
this.layout.translationX = this._getTranslation(); this.visible = true;
this.slideIn(); this.layout.slideX = this.getSlide();
}, this.actor.translation_x = 0;
_onOverviewHiding: function() {
this.slideOut();
}, },
_onWindowDragBegin: function() { _onWindowDragBegin: function() {
@ -197,14 +181,14 @@ const SlidingControl = new Lang.Class({
}, },
_onDragBegin: function() { _onDragBegin: function() {
this._inDrag = true; this.inDrag = true;
this.layout.translationX = 0; this.actor.translation_x = 0;
this._updateSlide(); this.updateSlide();
}, },
_onDragEnd: function() { _onDragEnd: function() {
this._inDrag = false; this.inDrag = false;
this._updateSlide(); this.updateSlide();
}, },
fadeIn: function() { fadeIn: function() {
@ -222,13 +206,12 @@ const SlidingControl = new Lang.Class({
}, },
slideIn: function() { slideIn: function() {
this._visible = true; this.visible = true;
this._updateTranslation();
// we will update slideX and the translation from pageEmpty // we will update slideX and the translation from pageEmpty
}, },
slideOut: function() { slideOut: function() {
this._visible = false; this.visible = false;
this._updateTranslation(); this._updateTranslation();
// we will update slideX from pageEmpty // we will update slideX from pageEmpty
}, },
@ -238,7 +221,7 @@ const SlidingControl = new Lang.Class({
// selector; this means we can now safely set the full slide for // selector; this means we can now safely set the full slide for
// the next page, since slideIn or slideOut might have been called, // the next page, since slideIn or slideOut might have been called,
// changing the visiblity // changing the visiblity
this.layout.slideX = this._getSlide(); this.layout.slideX = this.getSlide();
this._updateTranslation(); this._updateTranslation();
} }
}); });
@ -252,20 +235,25 @@ const ThumbnailsSlider = new Lang.Class({
this._thumbnailsBox = thumbnailsBox; this._thumbnailsBox = thumbnailsBox;
// SlideLayout reads the actor's expand flags to decide
// whether to allocate the natural size to its child, or the whole
// available allocation
this._thumbnailsBox.actor.y_expand = true;
this.actor.request_mode = Clutter.RequestMode.WIDTH_FOR_HEIGHT; this.actor.request_mode = Clutter.RequestMode.WIDTH_FOR_HEIGHT;
this.actor.reactive = true; this.actor.reactive = true;
this.actor.track_hover = true; this.actor.track_hover = true;
this.actor.add_actor(this._thumbnailsBox.actor); this.actor.add_actor(this._thumbnailsBox.actor);
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._updateSlide)); Main.layoutManager.connect('monitors-changed', Lang.bind(this, this.updateSlide));
this.actor.connect('notify::hover', Lang.bind(this, this._updateSlide)); this.actor.connect('notify::hover', Lang.bind(this, this.updateSlide));
this._thumbnailsBox.actor.bind_property('visible', this.actor, 'visible', GObject.BindingFlags.SYNC_CREATE); this._thumbnailsBox.actor.bind_property('visible', this.actor, 'visible', GObject.BindingFlags.SYNC_CREATE);
}, },
_getAlwaysZoomOut: function() { _getAlwaysZoomOut: function() {
// Always show the pager when hover, during a drag, or if workspaces are // Always show the pager when hover, during a drag, or if workspaces are
// actually used, e.g. there are windows on more than one // actually used, e.g. there are windows on more than one
let alwaysZoomOut = this.actor.hover || this._inDrag || !Meta.prefs_get_dynamic_workspaces() || global.screen.n_workspaces > 2; let alwaysZoomOut = this.actor.hover || this.inDrag || !Meta.prefs_get_dynamic_workspaces() || global.screen.n_workspaces > 2;
if (!alwaysZoomOut) { if (!alwaysZoomOut) {
let monitors = Main.layoutManager.monitors; let monitors = Main.layoutManager.monitors;
@ -290,8 +278,8 @@ const ThumbnailsSlider = new Lang.Class({
return child.get_theme_node().get_length('visible-width'); return child.get_theme_node().get_length('visible-width');
}, },
_getSlide: function() { getSlide: function() {
if (!this._visible) if (!this.visible)
return 0; return 0;
let alwaysZoomOut = this._getAlwaysZoomOut(); let alwaysZoomOut = this._getAlwaysZoomOut();
@ -327,18 +315,18 @@ const DashSlider = new Lang.Class({
// whether to allocate the natural size to its child, or the whole // whether to allocate the natural size to its child, or the whole
// available allocation // available allocation
this._dash.actor.x_expand = true; this._dash.actor.x_expand = true;
this._dash.actor.y_expand = true;
this.actor.x_expand = true;
this.actor.x_align = Clutter.ActorAlign.START; this.actor.x_align = Clutter.ActorAlign.START;
this.actor.y_expand = true; this.actor.y_expand = true;
this.actor.add_actor(this._dash.actor); this.actor.add_actor(this._dash.actor);
this._dash.connect('icon-size-changed', Lang.bind(this, this._updateSlide)); this._dash.connect('icon-size-changed', Lang.bind(this, this.updateSlide));
}, },
_getSlide: function() { getSlide: function() {
if (this._visible || this._inDrag) if (this.visible || this.inDrag)
return 1; return 1;
else else
return 0; return 0;
@ -460,6 +448,9 @@ const MessagesIndicator = new Lang.Class({
if (source.trayIcon) if (source.trayIcon)
return; return;
if (source.isTransient)
return;
source.connect('count-updated', Lang.bind(this, this._updateCount)); source.connect('count-updated', Lang.bind(this, this._updateCount));
this._sources.push(source); this._sources.push(source);
this._updateCount(); this._updateCount();
@ -496,17 +487,6 @@ const MessagesIndicator = new Lang.Class({
} }
}); });
const ControlsLayout = new Lang.Class({
Name: 'ControlsLayout',
Extends: Clutter.BinLayout,
Signals: { 'allocation-changed': { flags: GObject.SignalFlags.RUN_LAST } },
vfunc_allocate: function(container, box, flags) {
this.parent(container, box, flags);
this.emit('allocation-changed');
}
});
const ControlsManager = new Lang.Class({ const ControlsManager = new Lang.Class({
Name: 'ControlsManager', Name: 'ControlsManager',
@ -527,8 +507,7 @@ const ControlsManager = new Lang.Class({
this._indicator = new MessagesIndicator(this.viewSelector); this._indicator = new MessagesIndicator(this.viewSelector);
this.indicatorActor = this._indicator.actor; this.indicatorActor = this._indicator.actor;
let layout = new ControlsLayout(); this.actor = new St.Widget({ layout_manager: new Clutter.BinLayout(),
this.actor = new St.Widget({ layout_manager: layout,
reactive: true, reactive: true,
x_expand: true, y_expand: true, x_expand: true, y_expand: true,
clip_to_allocation: true }); clip_to_allocation: true });
@ -543,7 +522,7 @@ const ControlsManager = new Lang.Class({
expand: true }); expand: true });
this._group.add_actor(this._thumbnailsSlider.actor); this._group.add_actor(this._thumbnailsSlider.actor);
layout.connect('allocation-changed', Lang.bind(this, this._updateWorkspacesGeometry)); this._group.connect('notify::allocation', Lang.bind(this, this._updateWorkspacesGeometry));
Main.overview.connect('showing', Lang.bind(this, this._updateSpacerVisibility)); Main.overview.connect('showing', Lang.bind(this, this._updateSpacerVisibility));
Main.overview.connect('item-drag-begin', Lang.bind(this, Main.overview.connect('item-drag-begin', Lang.bind(this,

View File

@ -189,6 +189,7 @@ const AppMenuButton = new Lang.Class({
this.actor.bind_property("reactive", this.actor, "can-focus", 0); this.actor.bind_property("reactive", this.actor, "can-focus", 0);
this.actor.reactive = false; this.actor.reactive = false;
this._targetIsCurrent = false;
this._container = new Shell.GenericContainer(); this._container = new Shell.GenericContainer();
bin.set_child(this._container); bin.set_child(this._container);
@ -206,23 +207,20 @@ const AppMenuButton = new Lang.Class({
this._iconBox.connect('notify::allocation', this._iconBox.connect('notify::allocation',
Lang.bind(this, this._updateIconBoxClip)); Lang.bind(this, this._updateIconBoxClip));
this._container.add_actor(this._iconBox); this._container.add_actor(this._iconBox);
this._hbox = new St.BoxLayout({ style_class: 'panel-status-menu-box' });
this._container.add_actor(this._hbox);
this._label = new TextShadower(); this._label = new TextShadower();
this._label.actor.y_align = Clutter.ActorAlign.CENTER; this._container.add_actor(this._label.actor);
this._hbox.add_actor(this._label.actor);
this._arrow = PopupMenu.unicodeArrow(St.Side.BOTTOM);
this._hbox.add_actor(this._arrow);
this._iconBottomClip = 0; this._iconBottomClip = 0;
this._visible = !Main.overview.visible; this._visible = !Main.overview.visible;
if (!this._visible) if (!this._visible)
this.actor.hide(); this.actor.hide();
this._overviewHidingId = Main.overview.connect('hiding', Lang.bind(this, this._sync)); this._overviewHidingId = Main.overview.connect('hiding', Lang.bind(this, function () {
this._overviewShowingId = Main.overview.connect('showing', Lang.bind(this, this._sync)); this.show();
}));
this._overviewShowingId = Main.overview.connect('showing', Lang.bind(this, function () {
this.hide();
}));
this._stop = true; this._stop = true;
@ -245,8 +243,13 @@ const AppMenuButton = new Lang.Class({
return; return;
this._visible = true; this._visible = true;
this.actor.reactive = true;
this.actor.show(); this.actor.show();
if (!this._targetIsCurrent)
return;
this.actor.reactive = true;
Tweener.removeTweens(this.actor); Tweener.removeTweens(this.actor);
Tweener.addTween(this.actor, Tweener.addTween(this.actor,
{ opacity: 255, { opacity: 255,
@ -260,6 +263,11 @@ const AppMenuButton = new Lang.Class({
this._visible = false; this._visible = false;
this.actor.reactive = false; this.actor.reactive = false;
if (!this._targetIsCurrent) {
this.actor.hide();
return;
}
Tweener.removeTweens(this.actor); Tweener.removeTweens(this.actor);
Tweener.addTween(this.actor, Tweener.addTween(this.actor,
{ opacity: 0, { opacity: 0,
@ -278,8 +286,9 @@ const AppMenuButton = new Lang.Class({
return; return;
this._spinnerIcon = icon; this._spinnerIcon = icon;
this._spinner = new Animation.AnimatedIcon(this._spinnerIcon, PANEL_ICON_SIZE); this._spinner = new Animation.AnimatedIcon(this._spinnerIcon, PANEL_ICON_SIZE);
this._hbox.add_actor(this._spinner.actor); this._container.add_actor(this._spinner.actor);
this._spinner.actor.hide(); this._spinner.actor.hide();
this._spinner.actor.lower_bottom();
}, },
_onIconBoxStyleChanged: function() { _onIconBoxStyleChanged: function() {
@ -288,19 +297,13 @@ const AppMenuButton = new Lang.Class({
this._updateIconBoxClip(); this._updateIconBoxClip();
}, },
_syncIcon: function() {
if (!this._targetApp)
return;
let icon = this._targetApp.get_faded_icon(2 * PANEL_ICON_SIZE, this._iconBox.text_direction);
this._iconBox.set_child(icon);
},
_onIconThemeChanged: function() { _onIconThemeChanged: function() {
if (this._iconBox.child == null) if (this._iconBox.child == null)
return; return;
this._syncIcon(); this._iconBox.child.destroy();
let icon = this._targetApp.get_faded_icon(2 * PANEL_ICON_SIZE);
this._iconBox.set_child(icon);
}, },
_updateIconBoxClip: function() { _updateIconBoxClip: function() {
@ -318,6 +321,7 @@ const AppMenuButton = new Lang.Class({
return; return;
this._stop = true; this._stop = true;
this.actor.reactive = true;
if (this._spinner == null) if (this._spinner == null)
return; return;
@ -337,6 +341,7 @@ const AppMenuButton = new Lang.Class({
startAnimation: function() { startAnimation: function() {
this._stop = false; this._stop = false;
this.actor.reactive = false;
if (this._spinner == null) if (this._spinner == null)
return; return;
@ -349,7 +354,7 @@ const AppMenuButton = new Lang.Class({
let [minSize, naturalSize] = this._iconBox.get_preferred_width(forHeight); let [minSize, naturalSize] = this._iconBox.get_preferred_width(forHeight);
alloc.min_size = minSize; alloc.min_size = minSize;
alloc.natural_size = naturalSize; alloc.natural_size = naturalSize;
[minSize, naturalSize] = this._hbox.get_preferred_width(forHeight); [minSize, naturalSize] = this._label.actor.get_preferred_width(forHeight);
alloc.min_size = alloc.min_size + Math.max(0, minSize - Math.floor(alloc.min_size / 2)); alloc.min_size = alloc.min_size + Math.max(0, minSize - Math.floor(alloc.min_size / 2));
alloc.natural_size = alloc.natural_size + Math.max(0, naturalSize - Math.floor(alloc.natural_size / 2)); alloc.natural_size = alloc.natural_size + Math.max(0, naturalSize - Math.floor(alloc.natural_size / 2));
}, },
@ -358,7 +363,7 @@ const AppMenuButton = new Lang.Class({
let [minSize, naturalSize] = this._iconBox.get_preferred_height(forWidth); let [minSize, naturalSize] = this._iconBox.get_preferred_height(forWidth);
alloc.min_size = minSize; alloc.min_size = minSize;
alloc.natural_size = naturalSize; alloc.natural_size = naturalSize;
[minSize, naturalSize] = this._hbox.get_preferred_height(forWidth); [minSize, naturalSize] = this._label.actor.get_preferred_height(forWidth);
if (minSize > alloc.min_size) if (minSize > alloc.min_size)
alloc.min_size = minSize; alloc.min_size = minSize;
if (naturalSize > alloc.natural_size) if (naturalSize > alloc.natural_size)
@ -388,10 +393,11 @@ const AppMenuButton = new Lang.Class({
let iconWidth = childBox.x2 - childBox.x1; let iconWidth = childBox.x2 - childBox.x1;
[minWidth, naturalWidth] = this._hbox.get_preferred_width(-1); [minWidth, minHeight, naturalWidth, naturalHeight] = this._label.actor.get_preferred_size();
childBox.y1 = 0; yPadding = Math.floor(Math.max(0, allocHeight - naturalHeight) / 2);
childBox.y2 = allocHeight; childBox.y1 = yPadding;
childBox.y2 = childBox.y1 + Math.min(naturalHeight, allocHeight);
if (direction == Clutter.TextDirection.LTR) { if (direction == Clutter.TextDirection.LTR) {
childBox.x1 = Math.floor(iconWidth / 2); childBox.x1 = Math.floor(iconWidth / 2);
@ -400,7 +406,24 @@ const AppMenuButton = new Lang.Class({
childBox.x2 = allocWidth - Math.floor(iconWidth / 2); childBox.x2 = allocWidth - Math.floor(iconWidth / 2);
childBox.x1 = Math.max(0, childBox.x2 - naturalWidth); childBox.x1 = Math.max(0, childBox.x2 - naturalWidth);
} }
this._hbox.allocate(childBox, flags); this._label.actor.allocate(childBox, flags);
if (this._spinner == null)
return;
if (direction == Clutter.TextDirection.LTR) {
childBox.x1 = Math.floor(iconWidth / 2) + this._label.actor.width;
childBox.x2 = childBox.x1 + this._spinner.actor.width;
childBox.y1 = box.y1;
childBox.y2 = box.y2 - 1;
this._spinner.actor.allocate(childBox, flags);
} else {
childBox.x1 = -this._spinner.actor.width;
childBox.x2 = childBox.x1 + this._spinner.actor.width;
childBox.y1 = box.y1;
childBox.y2 = box.y2 - 1;
this._spinner.actor.allocate(childBox, flags);
}
}, },
_onAppStateChanged: function(appSys, app) { _onAppStateChanged: function(appSys, app) {
@ -426,76 +449,104 @@ const AppMenuButton = new Lang.Class({
// If the app has just lost focus to the panel, pretend // If the app has just lost focus to the panel, pretend
// nothing happened; otherwise you can't keynav to the // nothing happened; otherwise you can't keynav to the
// app menu. // app menu.
if (global.stage.key_focus != null) if (global.stage_input_mode == Shell.StageInputMode.FOCUSED)
return; return;
} }
this._sync(); this._sync();
}, },
_findTargetApp: function() { _sync: function() {
let workspace = global.screen.get_active_workspace();
let tracker = Shell.WindowTracker.get_default(); let tracker = Shell.WindowTracker.get_default();
let focusedApp = tracker.focus_app; let focusedApp = tracker.focus_app;
if (focusedApp && focusedApp.is_on_workspace(workspace)) let lastStartedApp = null;
return focusedApp; let workspace = global.screen.get_active_workspace();
for (let i = 0; i < this._startingApps.length; i++) for (let i = 0; i < this._startingApps.length; i++)
if (this._startingApps[i].is_on_workspace(workspace)) if (this._startingApps[i].is_on_workspace(workspace))
return this._startingApps[i]; lastStartedApp = this._startingApps[i];
return null; let targetApp = focusedApp != null ? focusedApp : lastStartedApp;
},
_sync: function() { if (targetApp == null) {
let targetApp = this._findTargetApp(); if (!this._targetIsCurrent)
return;
if (this._targetApp != targetApp) { this.actor.reactive = false;
if (this._appMenuNotifyId) { this._targetIsCurrent = false;
this._targetApp.disconnect(this._appMenuNotifyId);
this._appMenuNotifyId = 0; Tweener.removeTweens(this.actor);
Tweener.addTween(this.actor, { opacity: 0,
time: Overview.ANIMATION_TIME,
transition: 'easeOutQuad' });
return;
} }
if (this._actionGroupNotifyId) {
if (!targetApp.is_on_workspace(workspace))
return;
if (!this._targetIsCurrent) {
this.actor.reactive = true;
this._targetIsCurrent = true;
Tweener.removeTweens(this.actor);
Tweener.addTween(this.actor, { opacity: 255,
time: Overview.ANIMATION_TIME,
transition: 'easeOutQuad' });
}
if (targetApp == this._targetApp) {
if (targetApp &&
targetApp.get_state() != Shell.AppState.STARTING &&
targetApp.get_state() != Shell.AppState.BUSY) {
this.stopAnimation();
this._maybeSetMenu();
} else if (targetApp &&
targetApp.get_state() == Shell.AppState.BUSY) {
this.startAnimation();
}
return;
}
if (this._spinner)
this._spinner.actor.hide();
if (this._iconBox.child != null)
this._iconBox.child.destroy();
this._iconBox.hide();
this._label.setText('');
if (this._appMenuNotifyId)
this._targetApp.disconnect(this._appMenuNotifyId);
if (this._actionGroupNotifyId)
this._targetApp.disconnect(this._actionGroupNotifyId); this._targetApp.disconnect(this._actionGroupNotifyId);
if (targetApp) {
this._appMenuNotifyId = targetApp.connect('notify::menu', Lang.bind(this, this._sync));
this._actionGroupNotifyId = targetApp.connect('notify::action-group', Lang.bind(this, this._sync));
} else {
this._appMenuNotifyId = 0;
this._actionGroupNotifyId = 0; this._actionGroupNotifyId = 0;
} }
this._targetApp = targetApp; this._targetApp = targetApp;
let icon = targetApp.get_faded_icon(2 * PANEL_ICON_SIZE);
if (this._targetApp) { this._label.setText(targetApp.get_name());
this._appMenuNotifyId = this._targetApp.connect('notify::menu', Lang.bind(this, this._sync)); this.setName(targetApp.get_name());
this._actionGroupNotifyId = this._targetApp.connect('notify::action-group', Lang.bind(this, this._sync));
this._label.setText(this._targetApp.get_name());
this.actor.set_accessible_name(this._targetApp.get_name());
}
}
let visible = (this._targetApp != null && !Main.overview.visibleTarget); this._iconBox.set_child(icon);
if (visible) this._iconBox.show();
this.show();
else
this.hide();
let isBusy = (this._targetApp != null && if (targetApp.get_state() == Shell.AppState.STARTING ||
(this._targetApp.get_state() == Shell.AppState.STARTING || targetApp.get_state() == Shell.AppState.BUSY)
this._targetApp.get_state() == Shell.AppState.BUSY));
if (isBusy)
this.startAnimation(); this.startAnimation();
else else
this.stopAnimation();
this.actor.reactive = (visible && !isBusy);
this._syncIcon();
this._maybeSetMenu(); this._maybeSetMenu();
this.emit('changed'); this.emit('changed');
}, },
_maybeSetMenu: function() { _maybeSetMenu: function() {
let menu; let menu;
if (this._targetApp == null) { if (this._targetApp.action_group && this._targetApp.menu) {
menu = null;
} else if (this._targetApp.action_group && this._targetApp.menu) {
if (this.menu instanceof RemoteMenu.RemoteMenu && if (this.menu instanceof RemoteMenu.RemoteMenu &&
this.menu.actionGroup == this._targetApp.action_group) this.menu.actionGroup == this._targetApp.action_group)
return; return;
@ -507,7 +558,7 @@ const AppMenuButton = new Lang.Class({
})); }));
} else { } else {
if (this.menu && this.menu.isDummyQuitMenu) if (this.menu.isDummyQuitMenu)
return; return;
// fallback to older menu // fallback to older menu
@ -519,7 +570,6 @@ const AppMenuButton = new Lang.Class({
} }
this.setMenu(menu); this.setMenu(menu);
if (menu)
this._menuManager.addMenu(menu); this._menuManager.addMenu(menu);
}, },
@ -565,8 +615,7 @@ const ActivitiesButton = new Lang.Class({
/* Translators: If there is no suitable word for "Activities" /* Translators: If there is no suitable word for "Activities"
in your language, you can use the word for "Overview". */ in your language, you can use the word for "Overview". */
this._label = new St.Label({ text: _("Activities"), this._label = new St.Label({ text: _("Activities") });
y_align: Clutter.ActorAlign.CENTER });
this.actor.add_actor(this._label); this.actor.add_actor(this._label);
this.actor.label_actor = this._label; this.actor.label_actor = this._label;
@ -602,15 +651,13 @@ const ActivitiesButton = new Lang.Class({
_onCapturedEvent: function(actor, event) { _onCapturedEvent: function(actor, event) {
if (event.type() == Clutter.EventType.BUTTON_PRESS) { if (event.type() == Clutter.EventType.BUTTON_PRESS) {
if (!Main.overview.shouldToggleByCornerOrButton()) if (!Main.overview.shouldToggleByCornerOrButton())
return Clutter.EVENT_STOP; return true;
} }
return Clutter.EVENT_PROPAGATE; return false;
}, },
_onButtonRelease: function() { _onButtonRelease: function() {
Main.overview.toggle(); Main.overview.toggle();
this.menu.close();
return Clutter.EVENT_PROPAGATE;
}, },
_onKeyRelease: function(actor, event) { _onKeyRelease: function(actor, event) {
@ -618,7 +665,6 @@ const ActivitiesButton = new Lang.Class({
if (symbol == Clutter.KEY_Return || symbol == Clutter.KEY_space) { if (symbol == Clutter.KEY_Return || symbol == Clutter.KEY_space) {
Main.overview.toggle(); Main.overview.toggle();
} }
return Clutter.EVENT_PROPAGATE;
}, },
_xdndToggleOverview: function(actor) { _xdndToggleOverview: function(actor) {
@ -630,7 +676,6 @@ const ActivitiesButton = new Lang.Class({
Mainloop.source_remove(this._xdndTimeOut); Mainloop.source_remove(this._xdndTimeOut);
this._xdndTimeOut = 0; this._xdndTimeOut = 0;
return GLib.SOURCE_REMOVE;
} }
}); });
@ -801,65 +846,31 @@ const PanelCorner = new Lang.Class({
} }
}); });
const AggregateMenu = new Lang.Class({
Name: 'AggregateMenu',
Extends: PanelMenu.Button,
_init: function() {
this.parent(0.0, _("Settings"), false);
this.menu.actor.add_style_class_name('aggregate-menu');
this._indicators = new St.BoxLayout({ style_class: 'panel-status-indicators-box' });
this.actor.add_child(this._indicators);
this._network = new imports.ui.status.network.NMApplet();
if (Config.HAVE_BLUETOOTH) {
this._bluetooth = new imports.ui.status.bluetooth.Indicator();
} else {
this._bluetooth = null;
}
this._power = new imports.ui.status.power.Indicator();
this._rfkill = new imports.ui.status.rfkill.Indicator();
this._volume = new imports.ui.status.volume.Indicator();
this._brightness = new imports.ui.status.brightness.Indicator();
this._system = new imports.ui.status.system.Indicator();
this._screencast = new imports.ui.status.screencast.Indicator();
this._indicators.add_child(this._screencast.indicators);
this._indicators.add_child(this._network.indicators);
if (this._bluetooth) {
this._indicators.add_child(this._bluetooth.indicators);
}
this._indicators.add_child(this._rfkill.indicators);
this._indicators.add_child(this._volume.indicators);
this._indicators.add_child(this._power.indicators);
this._indicators.add_child(PopupMenu.unicodeArrow(St.Side.BOTTOM));
this.menu.addMenuItem(this._volume.menu);
this.menu.addMenuItem(this._brightness.menu);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addMenuItem(this._network.menu);
if (this._bluetooth) {
this.menu.addMenuItem(this._bluetooth.menu);
}
this.menu.addMenuItem(this._rfkill.menu);
this.menu.addMenuItem(this._power.menu);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addMenuItem(this._system.menu);
},
});
const PANEL_ITEM_IMPLEMENTATIONS = { const PANEL_ITEM_IMPLEMENTATIONS = {
'activities': ActivitiesButton, 'activities': ActivitiesButton,
'aggregateMenu': AggregateMenu,
'appMenu': AppMenuButton, 'appMenu': AppMenuButton,
'dateMenu': imports.ui.dateMenu.DateMenuButton, 'dateMenu': imports.ui.dateMenu.DateMenuButton,
'a11y': imports.ui.status.accessibility.ATIndicator, 'a11y': imports.ui.status.accessibility.ATIndicator,
'a11yGreeter': imports.ui.status.accessibility.ATGreeterIndicator, 'a11yGreeter': imports.ui.status.accessibility.ATGreeterIndicator,
'volume': imports.ui.status.volume.Indicator,
'battery': imports.ui.status.power.Indicator,
'lockScreen': imports.ui.status.lockScreenMenu.Indicator,
'keyboard': imports.ui.status.keyboard.InputSourceIndicator, 'keyboard': imports.ui.status.keyboard.InputSourceIndicator,
'powerMenu': imports.gdm.powerMenu.PowerMenuButton,
'userMenu': imports.ui.userMenu.UserMenuButton
}; };
if (Config.HAVE_BLUETOOTH)
PANEL_ITEM_IMPLEMENTATIONS['bluetooth'] =
imports.ui.status.bluetooth.Indicator;
try {
PANEL_ITEM_IMPLEMENTATIONS['network'] =
imports.ui.status.network.NMApplet;
} catch(e) {
log('NMApplet is not supported. It is possible that your NetworkManager version is too old');
}
const Panel = new Lang.Class({ const Panel = new Lang.Class({
Name: 'Panel', Name: 'Panel',
@ -986,23 +997,23 @@ const Panel = new Lang.Class({
_onButtonPress: function(actor, event) { _onButtonPress: function(actor, event) {
if (Main.modalCount > 0) if (Main.modalCount > 0)
return Clutter.EVENT_PROPAGATE; return false;
if (event.get_source() != actor) if (event.get_source() != actor)
return Clutter.EVENT_PROPAGATE; return false;
let button = event.get_button(); let button = event.get_button();
if (button != 1) if (button != 1)
return Clutter.EVENT_PROPAGATE; return false;
let focusWindow = global.display.focus_window; let focusWindow = global.display.focus_window;
if (!focusWindow) if (!focusWindow)
return Clutter.EVENT_PROPAGATE; return false;
let dragWindow = focusWindow.is_attached_dialog() ? focusWindow.get_transient_for() let dragWindow = focusWindow.is_attached_dialog() ? focusWindow.get_transient_for()
: focusWindow; : focusWindow;
if (!dragWindow) if (!dragWindow)
return Clutter.EVENT_PROPAGATE; return false;
let rect = dragWindow.get_outer_rect(); let rect = dragWindow.get_outer_rect();
let [stageX, stageY] = event.get_coords(); let [stageX, stageY] = event.get_coords();
@ -1011,7 +1022,7 @@ const Panel = new Lang.Class({
stageX > rect.x && stageX < rect.x + rect.width; stageX > rect.x && stageX < rect.x + rect.width;
if (!allowDrag) if (!allowDrag)
return Clutter.EVENT_PROPAGATE; return false;
global.display.begin_grab_op(global.screen, global.display.begin_grab_op(global.screen,
dragWindow, dragWindow,
@ -1023,7 +1034,7 @@ const Panel = new Lang.Class({
event.get_time(), event.get_time(),
stageX, stageY); stageX, stageY);
return Clutter.EVENT_STOP; return true;
}, },
toggleAppMenu: function() { toggleAppMenu: function() {

View File

@ -86,8 +86,13 @@ const ButtonBox = new Lang.Class({
childBox.x2 = availWidth - this._minHPadding; childBox.x2 = availWidth - this._minHPadding;
} }
if (natHeight <= availHeight) {
childBox.y1 = Math.floor((availHeight - natHeight) / 2);
childBox.y2 = childBox.y1 + natHeight;
} else {
childBox.y1 = 0; childBox.y1 = 0;
childBox.y2 = availHeight; childBox.y2 = availHeight;
}
child.allocate(childBox, flags); child.allocate(childBox, flags);
}, },
@ -101,17 +106,17 @@ const Button = new Lang.Class({
this.parent({ reactive: true, this.parent({ reactive: true,
can_focus: true, can_focus: true,
track_hover: true, track_hover: true,
accessible_name: nameText ? nameText : "",
accessible_role: Atk.Role.MENU }); accessible_role: Atk.Role.MENU });
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress)); this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
this.actor.connect('key-press-event', Lang.bind(this, this._onSourceKeyPress)); this.actor.connect('key-press-event', Lang.bind(this, this._onSourceKeyPress));
this.actor.connect('notify::visible', Lang.bind(this, this._onVisibilityChanged));
if (dontCreateMenu) if (dontCreateMenu)
this.menu = new PopupMenu.PopupDummyMenu(this.actor); this.menu = new PopupMenu.PopupDummyMenu(this.actor);
else else
this.setMenu(new PopupMenu.PopupMenu(this.actor, menuAlignment, St.Side.TOP, 0)); this.setMenu(new PopupMenu.PopupMenu(this.actor, menuAlignment, St.Side.TOP, 0));
this.setName(nameText);
}, },
setSensitive: function(sensitive) { setSensitive: function(sensitive) {
@ -120,6 +125,22 @@ const Button = new Lang.Class({
this.actor.track_hover = sensitive; this.actor.track_hover = sensitive;
}, },
setName: function(text) {
if (text != null) {
// This is the easiest way to provide a accessible name to
// this widget. The label could be also used for other
// purposes in the future.
if (!this.label) {
this.label = new St.Label({ text: text });
this.actor.label_actor = this.label;
} else
this.label.text = text;
} else {
this.label = null;
this.actor.label_actor = null;
}
},
setMenu: function(menu) { setMenu: function(menu) {
if (this.menu) if (this.menu)
this.menu.destroy(); this.menu.destroy();
@ -137,43 +158,34 @@ const Button = new Lang.Class({
_onButtonPress: function(actor, event) { _onButtonPress: function(actor, event) {
if (!this.menu) if (!this.menu)
return Clutter.EVENT_PROPAGATE; return;
this.menu.toggle(); this.menu.toggle();
return Clutter.EVENT_PROPAGATE;
}, },
_onSourceKeyPress: function(actor, event) { _onSourceKeyPress: function(actor, event) {
if (!this.menu) if (!this.menu)
return Clutter.EVENT_PROPAGATE; return false;
let symbol = event.get_key_symbol(); let symbol = event.get_key_symbol();
if (symbol == Clutter.KEY_space || symbol == Clutter.KEY_Return) { if (symbol == Clutter.KEY_space || symbol == Clutter.KEY_Return) {
this.menu.toggle(); this.menu.toggle();
return Clutter.EVENT_STOP; return true;
} else if (symbol == Clutter.KEY_Escape && this.menu.isOpen) { } else if (symbol == Clutter.KEY_Escape && this.menu.isOpen) {
this.menu.close(); this.menu.close();
return Clutter.EVENT_STOP; return true;
} else if (symbol == Clutter.KEY_Down) { } else if (symbol == Clutter.KEY_Down) {
if (!this.menu.isOpen) if (!this.menu.isOpen)
this.menu.toggle(); this.menu.toggle();
this.menu.actor.navigate_focus(this.actor, Gtk.DirectionType.DOWN, false); this.menu.actor.navigate_focus(this.actor, Gtk.DirectionType.DOWN, false);
return Clutter.EVENT_STOP; return true;
} else } else
return Clutter.EVENT_PROPAGATE; return false;
},
_onVisibilityChanged: function() {
if (!this.menu)
return;
if (!this.actor.visible)
this.menu.close();
}, },
_onMenuKeyPress: function(actor, event) { _onMenuKeyPress: function(actor, event) {
if (global.focus_manager.navigate_from_event(event)) if (global.focus_manager.navigate_from_event(event))
return Clutter.EVENT_STOP; return true;
let symbol = event.get_key_symbol(); let symbol = event.get_key_symbol();
if (symbol == Clutter.KEY_Left || symbol == Clutter.KEY_Right) { if (symbol == Clutter.KEY_Left || symbol == Clutter.KEY_Right) {
@ -181,10 +193,10 @@ const Button = new Lang.Class({
if (group) { if (group) {
let direction = (symbol == Clutter.KEY_Left) ? Gtk.DirectionType.LEFT : Gtk.DirectionType.RIGHT; let direction = (symbol == Clutter.KEY_Left) ? Gtk.DirectionType.LEFT : Gtk.DirectionType.RIGHT;
group.navigate_focus(this.actor, direction, false); group.navigate_focus(this.actor, direction, false);
return Clutter.EVENT_STOP; return true;
} }
} }
return Clutter.EVENT_PROPAGATE; return false;
}, },
_onOpenStateChanged: function(menu, open) { _onOpenStateChanged: function(menu, open) {
@ -212,35 +224,51 @@ const Button = new Lang.Class({
}); });
Signals.addSignalMethods(Button.prototype); Signals.addSignalMethods(Button.prototype);
/* SystemIndicator: /* SystemStatusButton:
* *
* This class manages one system indicator, which are the icons * This class manages one System Status indicator (network, keyboard,
* that you see at the top right. A system indicator is composed * volume, bluetooth...), which is just a PanelMenuButton with an
* of an icon and a menu section, which will be composed into the * icon.
* aggregate menu.
*/ */
const SystemIndicator = new Lang.Class({ const SystemStatusButton = new Lang.Class({
Name: 'SystemIndicator', Name: 'SystemStatusButton',
Extends: Button,
_init: function() { _init: function(iconName, nameText) {
this.indicators = new St.BoxLayout({ style_class: 'panel-status-indicators-box', this.parent(0.0, nameText);
reactive: true }); this.actor.add_style_class_name('panel-status-button');
this.indicators.hide();
this.menu = new PopupMenu.PopupMenuSection(); this._box = new St.BoxLayout({ style_class: 'panel-status-button-box' });
this.actor.add_actor(this._box);
if (iconName)
this.setIcon(iconName);
}, },
_syncIndicatorsVisible: function() { get icons() {
this.indicators.visible = this.indicators.get_children().some(function(actor) { return this._box.get_children();
return actor.visible;
});
}, },
_addIndicator: function() { addIcon: function(gicon) {
let icon = new St.Icon({ style_class: 'system-status-icon' }); let icon = new St.Icon({ gicon: gicon,
this.indicators.add_actor(icon); style_class: 'system-status-icon' });
icon.connect('notify::visible', Lang.bind(this, this._syncIndicatorsVisible)); this._box.add_actor(icon);
this._syncIndicatorsVisible();
this.emit('icons-changed');
return icon; return icon;
},
setIcon: function(iconName) {
if (!this.mainIcon)
this.mainIcon = this.addIcon(null);
this.mainIcon.icon_name = iconName;
},
setGIcon: function(gicon) {
if (this.mainIcon)
this.mainIcon.gicon = gicon;
else
this.mainIcon = this.addIcon(gicon);
} }
}); });
Signals.addSignalMethods(SystemIndicator.prototype);

View File

@ -1,9 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const GLib = imports.gi.GLib;
const Lang = imports.lang; const Lang = imports.lang;
const Mainloop = imports.mainloop; const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
const GnomeDesktop = imports.gi.GnomeDesktop; const GnomeDesktop = imports.gi.GnomeDesktop;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
@ -43,7 +41,7 @@ const PointerWatcher = new Lang.Class({
Name: 'PointerWatcher', Name: 'PointerWatcher',
_init: function() { _init: function() {
this._idleMonitor = Meta.IdleMonitor.get_core(); this._idleMonitor = new GnomeDesktop.IdleMonitor();
this._idleMonitor.add_idle_watch(IDLE_TIME, Lang.bind(this, this._onIdleMonitorBecameIdle)); this._idleMonitor.add_idle_watch(IDLE_TIME, Lang.bind(this, this._onIdleMonitorBecameIdle));
this._idle = this._idleMonitor.get_idletime() > IDLE_TIME; this._idle = this._idleMonitor.get_idletime() > IDLE_TIME;
this._watches = []; this._watches = [];
@ -111,7 +109,7 @@ const PointerWatcher = new Lang.Class({
_onTimeout: function() { _onTimeout: function() {
this._updatePointer(); this._updatePointer();
return GLib.SOURCE_CONTINUE; return true;
}, },
_updatePointer: function() { _updatePointer: function() {

File diff suppressed because it is too large Load Diff

View File

@ -120,7 +120,7 @@ const RemoteMenuItemMapper = new Lang.Class({
this.menuItem = new PopupMenu.PopupBaseMenuItem(); this.menuItem = new PopupMenu.PopupBaseMenuItem();
this._label = new St.Label(); this._label = new St.Label();
this.menuItem.actor.add_child(this._label); this.menuItem.addActor(this._label);
this.menuItem.actor.label_actor = this._label; this.menuItem.actor.label_actor = this._label;
this.menuItem.connect('activate', Lang.bind(this, function() { this.menuItem.connect('activate', Lang.bind(this, function() {

View File

@ -12,62 +12,65 @@ const Search = imports.ui.search;
const KEY_FILE_GROUP = 'Shell Search Provider'; const KEY_FILE_GROUP = 'Shell Search Provider';
const SearchProviderIface = '<node> \ const SearchProviderIface = <interface name="org.gnome.Shell.SearchProvider">
<interface name="org.gnome.Shell.SearchProvider"> \ <method name="GetInitialResultSet">
<method name="GetInitialResultSet"> \ <arg type="as" direction="in" />
<arg type="as" direction="in" /> \ <arg type="as" direction="out" />
<arg type="as" direction="out" /> \ </method>
</method> \ <method name="GetSubsearchResultSet">
<method name="GetSubsearchResultSet"> \ <arg type="as" direction="in" />
<arg type="as" direction="in" /> \ <arg type="as" direction="in" />
<arg type="as" direction="in" /> \ <arg type="as" direction="out" />
<arg type="as" direction="out" /> \ </method>
</method> \ <method name="GetResultMetas">
<method name="GetResultMetas"> \ <arg type="as" direction="in" />
<arg type="as" direction="in" /> \ <arg type="aa{sv}" direction="out" />
<arg type="aa{sv}" direction="out" /> \ </method>
</method> \ <method name="ActivateResult">
<method name="ActivateResult"> \ <arg type="s" direction="in" />
<arg type="s" direction="in" /> \ </method>
</method> \ </interface>;
</interface> \
</node>';
const SearchProvider2Iface = '<node> \ const SearchProvider2Iface = <interface name="org.gnome.Shell.SearchProvider2">
<interface name="org.gnome.Shell.SearchProvider2"> \ <method name="GetInitialResultSet">
<method name="GetInitialResultSet"> \ <arg type="as" direction="in" />
<arg type="as" direction="in" /> \ <arg type="as" direction="out" />
<arg type="as" direction="out" /> \ </method>
</method> \ <method name="GetSubsearchResultSet">
<method name="GetSubsearchResultSet"> \ <arg type="as" direction="in" />
<arg type="as" direction="in" /> \ <arg type="as" direction="in" />
<arg type="as" direction="in" /> \ <arg type="as" direction="out" />
<arg type="as" direction="out" /> \ </method>
</method> \ <method name="GetResultMetas">
<method name="GetResultMetas"> \ <arg type="as" direction="in" />
<arg type="as" direction="in" /> \ <arg type="aa{sv}" direction="out" />
<arg type="aa{sv}" direction="out" /> \ </method>
</method> \ <method name="ActivateResult">
<method name="ActivateResult"> \ <arg type="s" direction="in" />
<arg type="s" direction="in" /> \ <arg type="as" direction="in" />
<arg type="as" direction="in" /> \ <arg type="u" direction="in" />
<arg type="u" direction="in" /> \ </method>
</method> \ <method name="LaunchSearch">
<method name="LaunchSearch"> \ <arg type="as" direction="in" />
<arg type="as" direction="in" /> \ <arg type="u" direction="in" />
<arg type="u" direction="in" /> \ </method>
</method> \ </interface>;
</interface> \
</node>';
var SearchProviderProxyInfo = Gio.DBusInterfaceInfo.new_for_xml(SearchProviderIface); var SearchProviderProxy = Gio.DBusProxy.makeProxyWrapper(SearchProviderIface);
var SearchProvider2ProxyInfo = Gio.DBusInterfaceInfo.new_for_xml(SearchProvider2Iface); var SearchProvider2Proxy = Gio.DBusProxy.makeProxyWrapper(SearchProvider2Iface);
function loadRemoteSearchProviders(callback) { function loadRemoteSearchProviders(addProviderCallback) {
let objectPaths = {}; let data = { loadedProviders: [],
let loadedProviders = []; objectPaths: {},
addProviderCallback: addProviderCallback };
FileUtils.collectFromDatadirsAsync('search-providers',
{ loadedCallback: remoteProvidersLoaded,
processFile: loadRemoteSearchProvider,
data: data
});
}
function loadRemoteSearchProvider(file) { function loadRemoteSearchProvider(file, info, data) {
let keyfile = new GLib.KeyFile(); let keyfile = new GLib.KeyFile();
let path = file.get_path(); let path = file.get_path();
@ -86,7 +89,7 @@ function loadRemoteSearchProviders(callback) {
let busName = keyfile.get_string(group, 'BusName'); let busName = keyfile.get_string(group, 'BusName');
let objectPath = keyfile.get_string(group, 'ObjectPath'); let objectPath = keyfile.get_string(group, 'ObjectPath');
if (objectPaths[objectPath]) if (data.objectPaths[objectPath])
return; return;
let appInfo = null; let appInfo = null;
@ -110,33 +113,22 @@ function loadRemoteSearchProviders(callback) {
else else
remoteProvider = new RemoteSearchProvider(appInfo, busName, objectPath); remoteProvider = new RemoteSearchProvider(appInfo, busName, objectPath);
objectPaths[objectPath] = remoteProvider; data.objectPaths[objectPath] = remoteProvider;
loadedProviders.push(remoteProvider); data.loadedProviders.push(remoteProvider);
} catch(e) { } catch(e) {
log('Failed to add search provider %s: %s'.format(path, e.toString())); log('Failed to add search provider %s: %s'.format(path, e.toString()));
} }
} }
function remoteProvidersLoaded(loadState) {
let searchSettings = new Gio.Settings({ schema: Search.SEARCH_PROVIDERS_SCHEMA }); let searchSettings = new Gio.Settings({ schema: Search.SEARCH_PROVIDERS_SCHEMA });
if (searchSettings.get_boolean('disable-external')) {
callback([]);
return;
}
FileUtils.collectFromDatadirs('search-providers', false, loadRemoteSearchProvider);
let sortOrder = searchSettings.get_strv('sort-order'); let sortOrder = searchSettings.get_strv('sort-order');
// Special case gnome-control-center to be always active and always first // Special case gnome-control-center to be always active and always first
sortOrder.unshift('gnome-control-center.desktop'); sortOrder.unshift('gnome-control-center.desktop');
loadedProviders = loadedProviders.filter(function(provider) { loadState.loadedProviders.sort(
let appId = provider.appInfo.get_id(); function(providerA, providerB) {
let disabled = searchSettings.get_strv('disabled');
return disabled.indexOf(appId) == -1;
});
loadedProviders.sort(function(providerA, providerB) {
let idxA, idxB; let idxA, idxB;
let appIdA, appIdB; let appIdA, appIdB;
@ -166,34 +158,35 @@ function loadRemoteSearchProviders(callback) {
return (idxA - idxB); return (idxA - idxB);
}); });
callback(loadedProviders); loadState.loadedProviders.forEach(
function(provider) {
loadState.addProviderCallback(provider);
});
} }
const RemoteSearchProvider = new Lang.Class({ const RemoteSearchProvider = new Lang.Class({
Name: 'RemoteSearchProvider', Name: 'RemoteSearchProvider',
_init: function(appInfo, dbusName, dbusPath, proxyInfo) { _init: function(appInfo, dbusName, dbusPath, proxyType) {
if (!proxyInfo) if (!proxyType)
proxyInfo = SearchProviderProxyInfo; proxyType = SearchProviderProxy;
this.proxy = new Gio.DBusProxy({ g_bus_type: Gio.BusType.SESSION, this.proxy = new proxyType(Gio.DBus.session,
g_name: dbusName, dbusName, dbusPath, Lang.bind(this, this._onProxyConstructed));
g_object_path: dbusPath,
g_interface_info: proxyInfo,
g_interface_name: proxyInfo.name,
g_flags: (Gio.DBusProxyFlags.DO_NOT_AUTO_START_AT_CONSTRUCTION |
Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
this.proxy.init_async(GLib.PRIORITY_DEFAULT, null, null);
this.appInfo = appInfo; this.appInfo = appInfo;
this.id = appInfo.get_id(); this.id = appInfo.get_id();
this.isRemoteProvider = true; this.isRemoteProvider = true;
this._cancellable = new Gio.Cancellable();
},
_onProxyConstructed: function(proxy) {
// Do nothing
}, },
createIcon: function(size, meta) { createIcon: function(size, meta) {
let gicon = null; let gicon;
let icon = null;
if (meta['icon']) { if (meta['icon']) {
gicon = Gio.icon_deserialize(meta['icon']); gicon = Gio.icon_deserialize(meta['icon']);
} else if (meta['gicon']) { } else if (meta['gicon']) {
@ -205,49 +198,44 @@ const RemoteSearchProvider = new Lang.Class({
bitsPerSample, width, height, rowStride); bitsPerSample, width, height, rowStride);
} }
if (gicon) return new St.Icon({ gicon: gicon,
icon = new St.Icon({ gicon: gicon,
icon_size: size }); icon_size: size });
return icon;
}, },
filterResults: function(results, maxNumber) { _getResultsFinished: function(results, error) {
if (results.length <= maxNumber) if (error)
return results;
let regularResults = results.filter(function(r) { return !r.startsWith('special:'); });
let specialResults = results.filter(function(r) { return r.startsWith('special:'); });
return regularResults.slice(0, maxNumber).concat(specialResults.slice(0, maxNumber));
},
_getResultsFinished: function(results, error, callback) {
if (error) {
if (!error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
log('Received error from DBus search provider %s: %s'.format(this.id, String(error)));
callback([]);
return; return;
} this.searchSystem.setResults(this, results[0]);
callback(results[0]);
}, },
getInitialResultSet: function(terms, callback, cancellable) { getInitialResultSet: function(terms) {
this._cancellable.cancel();
this._cancellable.reset();
try {
this.proxy.GetInitialResultSetRemote(terms, this.proxy.GetInitialResultSetRemote(terms,
Lang.bind(this, this._getResultsFinished, callback), Lang.bind(this, this._getResultsFinished),
cancellable); this._cancellable);
} catch(e) {
log('Error calling GetInitialResultSet for provider %s: %s'.format(this.id, e.toString()));
this.searchSystem.setResults(this, []);
}
}, },
getSubsearchResultSet: function(previousResults, newTerms, callback, cancellable) { getSubsearchResultSet: function(previousResults, newTerms) {
this._cancellable.cancel();
this._cancellable.reset();
try {
this.proxy.GetSubsearchResultSetRemote(previousResults, newTerms, this.proxy.GetSubsearchResultSetRemote(previousResults, newTerms,
Lang.bind(this, this._getResultsFinished, callback), Lang.bind(this, this._getResultsFinished),
cancellable); this._cancellable);
} catch(e) {
log('Error calling GetSubsearchResultSet for provider %s: %s'.format(this.id, e.toString()));
this.searchSystem.setResults(this, []);
}
}, },
_getResultMetasFinished: function(results, error, callback) { _getResultMetasFinished: function(results, error, callback) {
if (error) { if (error) {
if (!error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
log('Received error from DBus search provider %s during GetResultMetas: %s'.format(this.id, String(error)));
callback([]); callback([]);
return; return;
} }
@ -269,10 +257,17 @@ const RemoteSearchProvider = new Lang.Class({
callback(resultMetas); callback(resultMetas);
}, },
getResultMetas: function(ids, callback, cancellable) { getResultMetas: function(ids, callback) {
this._cancellable.cancel();
this._cancellable.reset();
try {
this.proxy.GetResultMetasRemote(ids, this.proxy.GetResultMetasRemote(ids,
Lang.bind(this, this._getResultMetasFinished, callback), Lang.bind(this, this._getResultMetasFinished, callback),
cancellable); this._cancellable);
} catch(e) {
log('Error calling GetResultMetas for provider %s: %s'.format(this.id, e.toString()));
callback([]);
}
}, },
activateResult: function(id) { activateResult: function(id) {
@ -292,7 +287,7 @@ const RemoteSearchProvider2 = new Lang.Class({
Extends: RemoteSearchProvider, Extends: RemoteSearchProvider,
_init: function(appInfo, dbusName, dbusPath) { _init: function(appInfo, dbusName, dbusPath) {
this.parent(appInfo, dbusName, dbusPath, SearchProvider2ProxyInfo); this.parent(appInfo, dbusName, dbusPath, SearchProvider2Proxy);
this.canLaunchSearch = true; this.canLaunchSearch = true;
}, },

View File

@ -73,9 +73,7 @@ const RunDialog = new Lang.Class({
let label = new St.Label({ style_class: 'run-dialog-label', let label = new St.Label({ style_class: 'run-dialog-label',
text: _("Enter a Command") }); text: _("Enter a Command") });
this.contentLayout.add(label, { x_fill: false, this.contentLayout.add(label, { y_align: St.Align.START });
x_align: St.Align.START,
y_align: St.Align.START });
let entry = new St.Entry({ style_class: 'run-dialog-entry', let entry = new St.Entry({ style_class: 'run-dialog-entry',
can_focus: true }); can_focus: true });
@ -103,8 +101,6 @@ const RunDialog = new Lang.Class({
this._errorMessage.clutter_text.line_wrap = true; this._errorMessage.clutter_text.line_wrap = true;
this._errorBox.add(this._errorMessage, { expand: true, this._errorBox.add(this._errorMessage, { expand: true,
x_align: St.Align.START,
x_fill: false,
y_align: St.Align.MIDDLE, y_align: St.Align.MIDDLE,
y_fill: false }); y_fill: false });
@ -128,7 +124,7 @@ const RunDialog = new Lang.Class({
!this.pushModal()) !this.pushModal())
this.close(); this.close();
return Clutter.EVENT_STOP; return true;
} }
if (symbol == Clutter.Tab) { if (symbol == Clutter.Tab) {
let text = o.get_text(); let text = o.get_text();
@ -142,9 +138,9 @@ const RunDialog = new Lang.Class({
o.insert_text(postfix, -1); o.insert_text(postfix, -1);
o.set_cursor_position(text.length + postfix.length); o.set_cursor_position(text.length + postfix.length);
} }
return Clutter.EVENT_STOP; return true;
} }
return Clutter.EVENT_PROPAGATE; return false;
})); }));
}, },

View File

@ -1,12 +1,10 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const AccountsService = imports.gi.AccountsService;
const Cairo = imports.cairo; const Cairo = imports.cairo;
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio; const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib; const GLib = imports.gi.GLib;
const GnomeDesktop = imports.gi.GnomeDesktop; const GnomeDesktop = imports.gi.GnomeDesktop;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang; const Lang = imports.lang;
const Mainloop = imports.mainloop; const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta; const Meta = imports.gi.Meta;
@ -19,14 +17,12 @@ const Background = imports.ui.background;
const GnomeSession = imports.misc.gnomeSession; const GnomeSession = imports.misc.gnomeSession;
const Hash = imports.misc.hash; const Hash = imports.misc.hash;
const Layout = imports.ui.layout; const Layout = imports.ui.layout;
const OVirt = imports.gdm.oVirt;
const LoginManager = imports.misc.loginManager; const LoginManager = imports.misc.loginManager;
const Lightbox = imports.ui.lightbox; const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main; const Main = imports.ui.main;
const Overview = imports.ui.overview; const Overview = imports.ui.overview;
const MessageTray = imports.ui.messageTray; const MessageTray = imports.ui.messageTray;
const ShellDBus = imports.ui.shellDBus; const ShellDBus = imports.ui.shellDBus;
const SmartcardManager = imports.misc.smartcardManager;
const Tweener = imports.ui.tweener; const Tweener = imports.ui.tweener;
const Util = imports.misc.util; const Util = imports.misc.util;
@ -53,10 +49,12 @@ const SUMMARY_ICON_SIZE = 48;
// or when cancelling the dialog // or when cancelling the dialog
// - BACKGROUND_FADE_TIME is used when the background changes to crossfade to new background // - BACKGROUND_FADE_TIME is used when the background changes to crossfade to new background
// - CURTAIN_SLIDE_TIME is used when raising the shield before unlocking // - CURTAIN_SLIDE_TIME is used when raising the shield before unlocking
// - INITIAL_FADE_IN_TIME is used for the initial fade in at startup
const STANDARD_FADE_TIME = 10; const STANDARD_FADE_TIME = 10;
const MANUAL_FADE_TIME = 0.3; const MANUAL_FADE_TIME = 0.3;
const BACKGROUND_FADE_TIME = 1.0; const BACKGROUND_FADE_TIME = 1.0;
const CURTAIN_SLIDE_TIME = 0.3; const CURTAIN_SLIDE_TIME = 0.3;
const INITIAL_FADE_IN_TIME = 0.25;
const Clock = new Lang.Class({ const Clock = new Lang.Class({
Name: 'ScreenShieldClock', Name: 'ScreenShieldClock',
@ -106,14 +104,13 @@ const NotificationsBox = new Lang.Class({
this._musicBin = new St.Bin({ style_class: 'screen-shield-notifications-box', this._musicBin = new St.Bin({ style_class: 'screen-shield-notifications-box',
visible: false }); visible: false });
this._scrollView = new St.ScrollView({ x_fill: false, x_align: St.Align.START, let scrollView = new St.ScrollView({ x_fill: false, x_align: St.Align.START });
hscrollbar_policy: Gtk.PolicyType.NEVER });
this._notificationBox = new St.BoxLayout({ vertical: true, this._notificationBox = new St.BoxLayout({ vertical: true,
style_class: 'screen-shield-notifications-box' }); style_class: 'screen-shield-notifications-box' });
this._scrollView.add_actor(this._notificationBox); scrollView.add_actor(this._notificationBox);
this.actor.add(this._musicBin); this.actor.add(this._musicBin);
this.actor.add(this._scrollView, { x_fill: true, x_align: St.Align.START }); this.actor.add(scrollView, { x_fill: true, x_align: St.Align.START });
this._sources = new Hash.Map(); this._sources = new Hash.Map();
Main.messageTray.getSources().forEach(Lang.bind(this, function(source) { Main.messageTray.getSources().forEach(Lang.bind(this, function(source) {
@ -198,8 +195,8 @@ const NotificationsBox = new Lang.Class({
let body = ''; let body = '';
if (n.bannerBodyText) { if (n.bannerBodyText) {
body = n.bannerBodyMarkup ? n.bannerBodyText body = n.bannerBodyMarkup ? n.bannerBodyText :
: GLib.markup_escape_text(n.bannerBodyText, -1); GLib.markup_escape_text(n.bannerBodyMarkup, -1);
} }
let label = new St.Label({ style_class: 'screen-shield-notification-count-text' }); let label = new St.Label({ style_class: 'screen-shield-notification-count-text' });
@ -239,7 +236,11 @@ const NotificationsBox = new Lang.Class({
(source.unseenCount > (musicNotification ? 1 : 0)); (source.unseenCount > (musicNotification ? 1 : 0));
}, },
_sourceAdded: function(tray, source, initial) { _sourceAdded: function(tray, source, dontUpdateVisibility) {
// Ignore transient sources
if (source.isTransient)
return;
let obj = { let obj = {
visible: source.policy.showInLockScreen, visible: source.policy.showInLockScreen,
detailed: source.policy.detailsInLockScreen, detailed: source.policy.detailsInLockScreen,
@ -282,29 +283,8 @@ const NotificationsBox = new Lang.Class({
this._sources.set(source, obj); this._sources.set(source, obj);
if (!initial) { if (!dontUpdateVisibility)
// block scrollbars while animating, if they're not needed now
let boxHeight = this._notificationBox.height;
if (this._scrollView.height >= boxHeight)
this._scrollView.vscrollbar_policy = Gtk.PolicyType.NEVER;
let widget = obj.sourceBox;
let [, natHeight] = widget.get_preferred_height(-1);
widget.height = 0;
Tweener.addTween(widget,
{ height: natHeight,
transition: 'easeOutQuad',
time: 0.25,
onComplete: function() {
this._scrollView.vscrollbar_policy = Gtk.PolicyType.AUTOMATIC;
widget.set_height(-1);
},
onCompleteScope: this
});
this._updateVisibility(); this._updateVisibility();
Shell.util_wake_up_screen();
}
}, },
_titleChanged: function(source, obj) { _titleChanged: function(source, obj) {
@ -326,10 +306,7 @@ const NotificationsBox = new Lang.Class({
obj.sourceBox.visible = obj.visible && obj.sourceBox.visible = obj.visible &&
(source.unseenCount > (obj.musicNotification ? 1 : 0)); (source.unseenCount > (obj.musicNotification ? 1 : 0));
this._updateVisibility(); this._updateVisibility();
if (obj.sourceBox.visible)
Shell.util_wake_up_screen();
}, },
_visibleChanged: function(source, obj) { _visibleChanged: function(source, obj) {
@ -343,8 +320,6 @@ const NotificationsBox = new Lang.Class({
source.unseenCount > (obj.musicNotification ? 1 : 0); source.unseenCount > (obj.musicNotification ? 1 : 0);
this._updateVisibility(); this._updateVisibility();
if (obj.sourceBox.visible)
Shell.util_wake_up_screen();
}, },
_detailedChanged: function(source, obj) { _detailedChanged: function(source, obj) {
@ -514,10 +489,16 @@ const ScreenShield = new Lang.Class({
this._lockDialogGroup = new St.Widget({ x_expand: true, this._lockDialogGroup = new St.Widget({ x_expand: true,
y_expand: true, y_expand: true,
reactive: true, opacity: 0,
pivot_point: new Clutter.Point({ x: 0.5, y: 0.5 }), pivot_point: new Clutter.Point({ x: 0.5, y: 0.5 }),
name: 'lockDialogGroup' }); name: 'lockDialogGroup' });
Tweener.addTween(this._lockDialogGroup,
{ opacity: 255,
time: INITIAL_FADE_IN_TIME,
transition: 'easeInQuad',
});
this.actor.add_actor(this._lockDialogGroup); this.actor.add_actor(this._lockDialogGroup);
this.actor.add_actor(this._lockScreenGroup); this.actor.add_actor(this._lockScreenGroup);
@ -535,20 +516,6 @@ const ScreenShield = new Lang.Class({
this._screenSaverDBus = new ShellDBus.ScreenSaverDBus(this); this._screenSaverDBus = new ShellDBus.ScreenSaverDBus(this);
this._smartcardManager = SmartcardManager.getSmartcardManager();
this._smartcardManager.connect('smartcard-inserted',
Lang.bind(this, function(token) {
if (this._isLocked && token.UsedToLogin)
this._liftShield(true, 0);
}));
this._oVirtCredentialsManager = OVirt.getOVirtCredentialsManager();
this._oVirtCredentialsManager.connect('user-authenticated',
Lang.bind(this, function() {
if (this._isLocked)
this._liftShield(true, 0);
}));
this._inhibitor = null; this._inhibitor = null;
this._aboutToSuspend = false; this._aboutToSuspend = false;
this._loginManager = LoginManager.getLoginManager(); this._loginManager = LoginManager.getLoginManager();
@ -575,20 +542,13 @@ const ScreenShield = new Lang.Class({
this._becameActiveId = 0; this._becameActiveId = 0;
this._lockTimeoutId = 0; this._lockTimeoutId = 0;
// The "long" lightbox is used for the longer (20 seconds) fade from session this._lightbox = new Lightbox.Lightbox(Main.uiGroup,
// to idle status, the "short" is used for quickly fading to black when locking
// manually
this._longLightbox = new Lightbox.Lightbox(Main.uiGroup,
{ inhibitEvents: true, { inhibitEvents: true,
fadeInTime: STANDARD_FADE_TIME,
fadeFactor: 1 }); fadeFactor: 1 });
this._longLightbox.connect('shown', Lang.bind(this, this._onLongLightboxShown)); this._lightbox.connect('shown', Lang.bind(this, this._onLightboxShown));
this._shortLightbox = new Lightbox.Lightbox(Main.uiGroup,
{ inhibitEvents: true,
fadeFactor: 1 });
this._shortLightbox.connect('shown', Lang.bind(this, this._onShortLightboxShown));
this.idleMonitor = Meta.IdleMonitor.get_core(); this.idleMonitor = new GnomeDesktop.IdleMonitor();
this._cursorTracker = Meta.CursorTracker.get_for_screen(global.screen);
}, },
_createBackground: function(monitorIndex) { _createBackground: function(monitorIndex) {
@ -601,8 +561,7 @@ const ScreenShield = new Lang.Class({
let bgManager = new Background.BackgroundManager({ container: widget, let bgManager = new Background.BackgroundManager({ container: widget,
monitorIndex: monitorIndex, monitorIndex: monitorIndex,
controlPosition: false, controlPosition: false });
settingsSchema: SCREENSAVER_SCHEMA });
this._bgManagers.push(bgManager); this._bgManagers.push(bgManager);
@ -622,28 +581,13 @@ const ScreenShield = new Lang.Class({
_liftShield: function(onPrimary, velocity) { _liftShield: function(onPrimary, velocity) {
if (this._isLocked) { if (this._isLocked) {
if (this._ensureUnlockDialog(onPrimary, true /* allowCancel */)) this._ensureUnlockDialog(onPrimary, true /* allowCancel */);
this._hideLockScreen(true /* animate */, velocity); this._hideLockScreen(true /* animate */, velocity);
} else { } else {
this.deactivate(true /* animate */); this.deactivate(true /* animate */);
} }
}, },
_maybeCancelDialog: function() {
if (!this._dialog)
return;
this._dialog.cancel();
if (this._isGreeter) {
// LoginDialog.cancel() will grab the key focus
// on its own, so ensure it stays on lock screen
// instead
this._lockScreenGroup.grab_key_focus();
} else {
this._dialog = null;
}
},
_becomeModal: function() { _becomeModal: function() {
if (this._isModal) if (this._isModal)
return true; return true;
@ -669,24 +613,24 @@ const ScreenShield = new Lang.Class({
// down after cancel. // down after cancel.
if (this._lockScreenState != MessageTray.State.SHOWN) if (this._lockScreenState != MessageTray.State.SHOWN)
return Clutter.EVENT_PROPAGATE; return false;
let isEnter = (symbol == Clutter.KEY_Return || symbol == Clutter.KEY_KP_Enter); let isEnter = (symbol == Clutter.KEY_Return || symbol == Clutter.KEY_KP_Enter);
if (!isEnter && !(GLib.unichar_isprint(unichar) || symbol == Clutter.KEY_Escape)) if (!isEnter && !(GLib.unichar_isprint(unichar) || symbol == Clutter.KEY_Escape))
return Clutter.EVENT_PROPAGATE; return false;
if (this._isLocked && this._ensureUnlockDialog(true, true);
this._ensureUnlockDialog(true, true) &&
GLib.unichar_isgraph(unichar)) if (GLib.unichar_isgraph(unichar))
this._dialog.addCharacter(unichar); this._dialog.addCharacter(unichar);
this._liftShield(true, 0); this._liftShield(true, 0);
return Clutter.EVENT_STOP; return true;
}, },
_onLockScreenScroll: function(actor, event) { _onLockScreenScroll: function(actor, event) {
if (this._lockScreenState != MessageTray.State.SHOWN) if (this._lockScreenState != MessageTray.State.SHOWN)
return Clutter.EVENT_PROPAGATE; return false;
let delta = 0; let delta = 0;
if (event.get_scroll_direction() == Clutter.ScrollDirection.UP) if (event.get_scroll_direction() == Clutter.ScrollDirection.UP)
@ -701,7 +645,7 @@ const ScreenShield = new Lang.Class({
this._liftShield(true, 0); this._liftShield(true, 0);
} }
return Clutter.EVENT_STOP; return true;
}, },
_inhibitSuspend: function() { _inhibitSuspend: function() {
@ -728,8 +672,6 @@ const ScreenShield = new Lang.Class({
this.lock(true); this.lock(true);
} else { } else {
this._inhibitSuspend(); this._inhibitSuspend();
this._onUserBecameActive();
} }
}, },
@ -752,7 +694,7 @@ const ScreenShield = new Lang.Class({
}); });
} }
return GLib.SOURCE_CONTINUE; return true;
}, },
_onDragBegin: function() { _onDragBegin: function() {
@ -778,8 +720,6 @@ const ScreenShield = new Lang.Class({
}, },
_onDragEnd: function(action, actor, eventX, eventY, modifiers) { _onDragEnd: function(action, actor, eventX, eventY, modifiers) {
if (this._lockScreenState != MessageTray.State.HIDING)
return;
if (this._lockScreenGroup.y < -(ARROW_DRAG_THRESHOLD * global.stage.height)) { if (this._lockScreenGroup.y < -(ARROW_DRAG_THRESHOLD * global.stage.height)) {
// Complete motion automatically // Complete motion automatically
let [velocity, velocityX, velocityY] = this._dragAction.get_velocity(0); let [velocity, velocityX, velocityY] = this._dragAction.get_velocity(0);
@ -801,7 +741,13 @@ const ScreenShield = new Lang.Class({
onCompleteScope: this, onCompleteScope: this,
}); });
this._maybeCancelDialog(); // If we have a unlock dialog, cancel it
if (this._dialog) {
this._dialog.cancel();
if (!this._isGreeter) {
this._dialog = null;
}
}
} }
}, },
@ -809,19 +755,11 @@ const ScreenShield = new Lang.Class({
if (status != GnomeSession.PresenceStatus.IDLE) if (status != GnomeSession.PresenceStatus.IDLE)
return; return;
this._maybeCancelDialog(); if (this._dialog) {
this._dialog.cancel();
if (this._longLightbox.actor.visible || if (!this._isGreeter) {
this._isActive) { this._dialog = null;
// We're either shown and active, or in the process of }
// showing.
// The latter is a very unlikely condition (it requires
// idle-delay < 20), but in any case we have nothing
// to do at this point: either isActive is true, or
// it will soon be.
// isActive can also be true if the lightbox is hidden,
// in case the shield is down and the user hasn't unlocked yet
return;
} }
if (!this._becomeModal()) { if (!this._becomeModal()) {
@ -837,9 +775,26 @@ const ScreenShield = new Lang.Class({
return; return;
} }
if (this._lightbox.actor.visible ||
this._isActive) {
// We're either shown and active, or in the process of
// showing.
// The latter is a very unlikely condition (it requires
// idle-delay < 20), but in any case we have nothing
// to do at this point: either isActive is true, or
// it will soon be.
// isActive can also be true if the lightbox is hidden,
// in case the shield is down and the user hasn't unlocked yet
return;
}
this._lightbox.show();
if (this._activationTime == 0) if (this._activationTime == 0)
this._activationTime = GLib.get_monotonic_time(); this._activationTime = GLib.get_monotonic_time();
this._becameActiveId = this.idleMonitor.add_user_active_watch(Lang.bind(this, this._onUserBecameActive));
let shouldLock = this._settings.get_boolean(LOCK_ENABLED_KEY) && !this._isLocked; let shouldLock = this._settings.get_boolean(LOCK_ENABLED_KEY) && !this._isLocked;
if (shouldLock) { if (shouldLock) {
@ -847,59 +802,48 @@ const ScreenShield = new Lang.Class({
this._lockTimeoutId = Mainloop.timeout_add(lockTimeout * 1000, this._lockTimeoutId = Mainloop.timeout_add(lockTimeout * 1000,
Lang.bind(this, function() { Lang.bind(this, function() {
this._lockTimeoutId = 0; this._lockTimeoutId = 0;
this.lock(false); this.lock(true);
return GLib.SOURCE_REMOVE; return false;
})); }));
} }
this._activateFade(this._longLightbox, STANDARD_FADE_TIME);
},
_activateFade: function(lightbox, time) {
lightbox.show(time);
if (this._becameActiveId == 0)
this._becameActiveId = this.idleMonitor.add_user_active_watch(Lang.bind(this, this._onUserBecameActive));
}, },
_onUserBecameActive: function() { _onUserBecameActive: function() {
// This function gets called here when the user becomes active // This function gets called here when the user becomes active
// after we activated a lightbox // after gnome-session changed the status to IDLE
// There are two possibilities here: // There are four possibilities here:
// - we're called when already locked/active; isLocked or isActive is true, // - we're called when already locked; isActive and isLocked are true,
// we just go back to the lock screen curtain // we just go back to the lock screen curtain
// (isActive == isLocked == true: normal case // - we're called before the lightbox is fully shown; at this point
// isActive == false, isLocked == true: during the fade for manual locking // isActive is false, so we just hide the ligthbox, reset the activationTime
// isActive == true, isLocked == false: after session idle, before lock-delay) // and go back to the unlocked desktop
// - we're called because the session is IDLE but before the lightbox // - we're called after showing the lightbox, but before the lock
// is fully shown; at this point isActive is false, so we just hide // delay; this is mostly like the case above, but isActive is true now
// the lightbox, reset the activationTime and go back to the unlocked // so we need to notify gnome-settings-daemon to go back to the normal
// desktop // policies for blanking
// using deactivate() is a little of overkill, but it ensures we // (they're handled by the same code, and we emit one extra ActiveChanged
// don't forget of some bit like modal, DBus properties or idle watches // signal in the case above)
// // - we're called after showing the lightbox and after lock-delay; the
// Note: if the (long) lightbox is shown then we're necessarily // session is effectivelly locked now, it's time to build and show
// active, because we call activate() without animation. // the lock screen
this.idleMonitor.remove_watch(this._becameActiveId); this.idleMonitor.remove_watch(this._becameActiveId);
this._becameActiveId = 0; this._becameActiveId = 0;
if (this._isActive || this._isLocked) { let lightboxWasShown = this._lightbox.shown;
this._longLightbox.hide(); this._lightbox.hide();
this._shortLightbox.hide();
} else { // Shortcircuit in case the mouse was moved before the fade completed
if (!lightboxWasShown) {
this.deactivate(false); this.deactivate(false);
return;
} }
}, },
_onLongLightboxShown: function() { _onLightboxShown: function() {
this.activate(false); this.activate(false);
}, },
_onShortLightboxShown: function() {
this._completeLockScreenShown();
},
showDialog: function() { showDialog: function() {
// Ensure that the stage window is mapped, before taking a grab // Ensure that the stage window is mapped, before taking a grab
// otherwise X errors out // otherwise X errors out
@ -916,7 +860,7 @@ const ScreenShield = new Lang.Class({
this.actor.show(); this.actor.show();
this._isGreeter = Main.sessionMode.isGreeter; this._isGreeter = Main.sessionMode.isGreeter;
this._isLocked = true; this._isLocked = true;
if (this._ensureUnlockDialog(true, true)) this._ensureUnlockDialog(true, true);
this._hideLockScreen(false, 0); this._hideLockScreen(false, 0);
}, },
@ -934,8 +878,6 @@ const ScreenShield = new Lang.Class({
this._lockScreenState = MessageTray.State.HIDING; this._lockScreenState = MessageTray.State.HIDING;
Tweener.removeTweens(this._lockScreenGroup);
if (animate) { if (animate) {
// Tween the lock screen out of screen // Tween the lock screen out of screen
// if velocity is not specified (i.e. we come here from pressing ESC), // if velocity is not specified (i.e. we come here from pressing ESC),
@ -948,6 +890,7 @@ const ScreenShield = new Lang.Class({
velocity = Math.max(min_velocity, velocity); velocity = Math.max(min_velocity, velocity);
let time = (delta / velocity) / 1000; let time = (delta / velocity) / 1000;
Tweener.removeTweens(this._lockScreenGroup);
Tweener.addTween(this._lockScreenGroup, Tweener.addTween(this._lockScreenGroup,
{ y: -h, { y: -h,
time: time, time: time,
@ -958,7 +901,7 @@ const ScreenShield = new Lang.Class({
this._hideLockScreenComplete(); this._hideLockScreenComplete();
} }
this._cursorTracker.set_pointer_visible(true); global.stage.show_cursor();
}, },
_ensureUnlockDialog: function(onPrimary, allowCancel) { _ensureUnlockDialog: function(onPrimary, allowCancel) {
@ -967,7 +910,7 @@ const ScreenShield = new Lang.Class({
if (!constructor) { if (!constructor) {
// This session mode has no locking capabilities // This session mode has no locking capabilities
this.deactivate(true); this.deactivate(true);
return false; return;
} }
this._dialog = new constructor(this._lockDialogGroup); this._dialog = new constructor(this._lockDialogGroup);
@ -979,22 +922,24 @@ const ScreenShield = new Lang.Class({
// by the time we reach this... // by the time we reach this...
log('Could not open login dialog: failed to acquire grab'); log('Could not open login dialog: failed to acquire grab');
this.deactivate(true); this.deactivate(true);
return false;
} }
this._dialog.connect('failed', Lang.bind(this, this._onUnlockFailed)); this._dialog.connect('failed', Lang.bind(this, this._onUnlockFailed));
this._dialog.connect('unlocked', Lang.bind(this, this._onUnlockSucceded));
} }
this._dialog.allowCancel = allowCancel; this._dialog.allowCancel = allowCancel;
return true;
}, },
_onUnlockFailed: function() { _onUnlockFailed: function() {
this._resetLockScreen({ animateLockScreen: true, this._resetLockScreen(true, false);
fadeToBlack: false });
}, },
_resetLockScreen: function(params) { _onUnlockSucceded: function() {
this.deactivate(true);
},
_resetLockScreen: function(animateLockScreen, animateLockDialog) {
// Don't reset the lock screen unless it is completely hidden // Don't reset the lock screen unless it is completely hidden
// This prevents the shield going down if the lock-delay timeout // This prevents the shield going down if the lock-delay timeout
// fires while the user is dragging (which has the potential // fires while the user is dragging (which has the potential
@ -1009,9 +954,7 @@ const ScreenShield = new Lang.Class({
this._lockScreenGroup.show(); this._lockScreenGroup.show();
this._lockScreenState = MessageTray.State.SHOWING; this._lockScreenState = MessageTray.State.SHOWING;
let fadeToBlack = params.fadeToBlack; if (animateLockScreen) {
if (params.animateLockScreen) {
this._lockScreenGroup.y = -global.screen_height; this._lockScreenGroup.y = -global.screen_height;
Tweener.removeTweens(this._lockScreenGroup); Tweener.removeTweens(this._lockScreenGroup);
Tweener.addTween(this._lockScreenGroup, Tweener.addTween(this._lockScreenGroup,
@ -1019,15 +962,24 @@ const ScreenShield = new Lang.Class({
time: MANUAL_FADE_TIME, time: MANUAL_FADE_TIME,
transition: 'easeOutQuad', transition: 'easeOutQuad',
onComplete: function() { onComplete: function() {
this._lockScreenShown({ fadeToBlack: fadeToBlack, this._lockScreenShown();
animateFade: true });
}, },
onCompleteScope: this onCompleteScope: this
}); });
} else { } else {
this._lockScreenGroup.fixed_position_set = false; this._lockScreenGroup.fixed_position_set = false;
this._lockScreenShown({ fadeToBlack: fadeToBlack, this._lockScreenShown();
animateFade: false }); }
if (animateLockDialog) {
this._lockDialogGroup.opacity = 0;
Tweener.removeTweens(this._lockDialogGroup);
Tweener.addTween(this._lockDialogGroup,
{ opacity: 255,
time: MANUAL_FADE_TIME,
transition: 'easeOutQuad' });
} else {
this._lockDialogGroup.opacity = 255;
} }
this._lockScreenGroup.grab_key_focus(); this._lockScreenGroup.grab_key_focus();
@ -1083,7 +1035,7 @@ const ScreenShield = new Lang.Class({
this._pauseArrowAnimation(); this._pauseArrowAnimation();
}, },
_lockScreenShown: function(params) { _lockScreenShown: function() {
if (this._dialog && !this._isGreeter) { if (this._dialog && !this._isGreeter) {
this._dialog.destroy(); this._dialog.destroy();
this._dialog = null; this._dialog = null;
@ -1091,36 +1043,20 @@ const ScreenShield = new Lang.Class({
this._checkArrowAnimation(); this._checkArrowAnimation();
let motionId = global.stage.connect('captured-event', Lang.bind(this, function(stage, event) { let motionId = global.stage.connect('captured-event', function(stage, event) {
if (event.type() == Clutter.EventType.MOTION) { if (event.type() == Clutter.EventType.MOTION) {
this._cursorTracker.set_pointer_visible(true); global.stage.show_cursor();
global.stage.disconnect(motionId); global.stage.disconnect(motionId);
} }
return Clutter.EVENT_PROPAGATE; return false;
})); });
this._cursorTracker.set_pointer_visible(false); global.stage.hide_cursor();
this._lockScreenState = MessageTray.State.SHOWN; this._lockScreenState = MessageTray.State.SHOWN;
this._lockScreenGroup.fixed_position_set = false; this._lockScreenGroup.fixed_position_set = false;
this._lockScreenScrollCounter = 0; this._lockScreenScrollCounter = 0;
if (params.fadeToBlack && params.animateFade) {
// Take a beat
Mainloop.timeout_add(1000 * MANUAL_FADE_TIME, Lang.bind(this, function() {
this._activateFade(this._shortLightbox, MANUAL_FADE_TIME);
return GLib.SOURCE_REMOVE;
}));
} else {
if (params.fadeToBlack)
this._activateFade(this._shortLightbox, 0);
this._completeLockScreenShown();
}
},
_completeLockScreenShown: function() {
let prevIsActive = this._isActive; let prevIsActive = this._isActive;
this._isActive = true; this._isActive = true;
@ -1188,15 +1124,6 @@ const ScreenShield = new Lang.Class({
}, },
deactivate: function(animate) { deactivate: function(animate) {
if (this._dialog)
this._dialog.finish(Lang.bind(this, function() {
this._continueDeactivate(animate);
}));
else
this._continueDeactivate(animate);
},
_continueDeactivate: function(animate) {
this._hideLockScreen(animate, 0); this._hideLockScreen(animate, 0);
if (this._hasLockScreen) if (this._hasLockScreen)
@ -1207,20 +1134,6 @@ const ScreenShield = new Lang.Class({
if (Main.sessionMode.currentMode == 'unlock-dialog') if (Main.sessionMode.currentMode == 'unlock-dialog')
Main.sessionMode.popMode('unlock-dialog'); Main.sessionMode.popMode('unlock-dialog');
if (this._isGreeter) {
// We don't want to "deactivate" any more than
// this. In particular, we don't want to drop
// the modal, hide ourselves or destroy the dialog
// But we do want to set isActive to false, so that
// gnome-session will reset the idle counter, and
// gnome-settings-daemon will stop blanking the screen
this._activationTime = 0;
this._isActive = false;
this.emit('active-changed');
return;
}
if (this._dialog && !this._isGreeter) if (this._dialog && !this._isGreeter)
this._dialog.popModal(); this._dialog.popModal();
@ -1240,13 +1153,12 @@ const ScreenShield = new Lang.Class({
}, },
_completeDeactivate: function() { _completeDeactivate: function() {
if (this._dialog) { if (this._dialog && !this._isGreeter) {
this._dialog.destroy(); this._dialog.destroy();
this._dialog = null; this._dialog = null;
} }
this._longLightbox.hide(); this._lightbox.hide();
this._shortLightbox.hide();
this.actor.hide(); this.actor.hide();
if (this._becameActiveId != 0) { if (this._becameActiveId != 0) {
@ -1280,8 +1192,7 @@ const ScreenShield = new Lang.Class({
Main.sessionMode.pushMode('unlock-dialog'); Main.sessionMode.pushMode('unlock-dialog');
} }
this._resetLockScreen({ animateLockScreen: animate, this._resetLockScreen(animate, animate);
fadeToBlack: true });
global.set_runtime_state(LOCKED_STATE_STR, GLib.Variant.new('b', true)); global.set_runtime_state(LOCKED_STATE_STR, GLib.Variant.new('b', true));
// We used to set isActive and emit active-changed here, // We used to set isActive and emit active-changed here,
@ -1310,14 +1221,7 @@ const ScreenShield = new Lang.Class({
St.Clipboard.get_default().set_text(St.ClipboardType.CLIPBOARD, ''); St.Clipboard.get_default().set_text(St.ClipboardType.CLIPBOARD, '');
St.Clipboard.get_default().set_text(St.ClipboardType.PRIMARY, ''); St.Clipboard.get_default().set_text(St.ClipboardType.PRIMARY, '');
let userManager = AccountsService.UserManager.get_default();
let user = userManager.get_user(GLib.get_user_name());
if (this._isGreeter)
this._isLocked = true; this._isLocked = true;
else
this._isLocked = user.password_mode != AccountsService.UserPasswordMode.NONE;
this.activate(animate); this.activate(animate);
this.emit('locked-changed'); this.emit('locked-changed');

View File

@ -4,34 +4,31 @@ const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib; const GLib = imports.gi.GLib;
const Lang = imports.lang; const Lang = imports.lang;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const Signals = imports.signals;
const Hash = imports.misc.hash; const Hash = imports.misc.hash;
const Main = imports.ui.main; const Main = imports.ui.main;
const ScreencastIface = '<node> \ const ScreencastIface = <interface name="org.gnome.Shell.Screencast">
<interface name="org.gnome.Shell.Screencast"> \ <method name="Screencast">
<method name="Screencast"> \ <arg type="s" direction="in" name="file_template"/>
<arg type="s" direction="in" name="file_template"/> \ <arg type="a{sv}" direction="in" name="options"/>
<arg type="a{sv}" direction="in" name="options"/> \ <arg type="b" direction="out" name="success"/>
<arg type="b" direction="out" name="success"/> \ <arg type="s" direction="out" name="filename_used"/>
<arg type="s" direction="out" name="filename_used"/> \ </method>
</method> \ <method name="ScreencastArea">
<method name="ScreencastArea"> \ <arg type="i" direction="in" name="x"/>
<arg type="i" direction="in" name="x"/> \ <arg type="i" direction="in" name="y"/>
<arg type="i" direction="in" name="y"/> \ <arg type="i" direction="in" name="width"/>
<arg type="i" direction="in" name="width"/> \ <arg type="i" direction="in" name="height"/>
<arg type="i" direction="in" name="height"/> \ <arg type="s" direction="in" name="file_template"/>
<arg type="s" direction="in" name="file_template"/> \ <arg type="a{sv}" direction="in" name="options"/>
<arg type="a{sv}" direction="in" name="options"/> \ <arg type="b" direction="out" name="success"/>
<arg type="b" direction="out" name="success"/> \ <arg type="s" direction="out" name="filename_used"/>
<arg type="s" direction="out" name="filename_used"/> \ </method>
</method> \ <method name="StopScreencast">
<method name="StopScreencast"> \ <arg type="b" direction="out" name="success"/>
<arg type="b" direction="out" name="success"/> \ </method>
</method> \ </interface>;
</interface> \
</node>';
const ScreencastService = new Lang.Class({ const ScreencastService = new Lang.Class({
Name: 'ScreencastService', Name: 'ScreencastService',
@ -44,34 +41,28 @@ const ScreencastService = new Lang.Class({
this._recorders = new Hash.Map(); this._recorders = new Hash.Map();
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated)); Main.sessionMode.connect('updated',
}, Lang.bind(this, this._sessionModeChanged));
get isRecording() {
return this._recorders.size() > 0;
}, },
_ensureRecorderForSender: function(sender) { _ensureRecorderForSender: function(sender) {
let recorder = this._recorders.get(sender); let recorder = this._recorders.get(sender);
if (!recorder) { if (!recorder) {
recorder = new Shell.Recorder({ stage: global.stage, recorder = new Shell.Recorder({ stage: global.stage });
screen: global.screen });
recorder._watchNameId = recorder._watchNameId =
Gio.bus_watch_name(Gio.BusType.SESSION, sender, 0, null, Gio.bus_watch_name(Gio.BusType.SESSION, sender, 0, null,
Lang.bind(this, this._onNameVanished)); Lang.bind(this, this._onNameVanished));
this._recorders.set(sender, recorder); this._recorders.set(sender, recorder);
this.emit('updated');
} }
return recorder; return recorder;
}, },
_sessionUpdated: function() { _sessionModeChanged: function() {
if (Main.sessionMode.allowScreencast) if (Main.sessionMode.allowScreencast)
return; return;
for (let sender in this._recorders.keys()) for (let sender in this._recorders.keys())
this._recorders.delete(sender); this._recorders.delete(sender);
this.emit('updated');
}, },
_onNameVanished: function(connection, name) { _onNameVanished: function(connection, name) {
@ -86,7 +77,6 @@ const ScreencastService = new Lang.Class({
Gio.bus_unwatch_name(recorder._watchNameId); Gio.bus_unwatch_name(recorder._watchNameId);
recorder.close(); recorder.close();
this._recorders.delete(sender); this._recorders.delete(sender);
this.emit('updated');
return true; return true;
}, },
@ -105,10 +95,8 @@ const ScreencastService = new Lang.Class({
ScreencastAsync: function(params, invocation) { ScreencastAsync: function(params, invocation) {
let returnValue = [false, '']; let returnValue = [false, ''];
if (!Main.sessionMode.allowScreencast) { if (!Main.sessionMode.allowScreencast)
invocation.return_value(GLib.Variant.new('(bs)', returnValue)); invocation.return_value(GLib.Variant.new('(bs)', returnValue));
return;
}
let sender = invocation.get_sender(); let sender = invocation.get_sender();
let recorder = this._ensureRecorderForSender(sender); let recorder = this._ensureRecorderForSender(sender);
@ -126,10 +114,8 @@ const ScreencastService = new Lang.Class({
ScreencastAreaAsync: function(params, invocation) { ScreencastAreaAsync: function(params, invocation) {
let returnValue = [false, '']; let returnValue = [false, ''];
if (!Main.sessionMode.allowScreencast) { if (!Main.sessionMode.allowScreencast)
invocation.return_value(GLib.Variant.new('(bs)', returnValue)); invocation.return_value(GLib.Variant.new('(bs)', returnValue));
return;
}
let sender = invocation.get_sender(); let sender = invocation.get_sender();
let recorder = this._ensureRecorderForSender(sender); let recorder = this._ensureRecorderForSender(sender);
@ -137,16 +123,6 @@ const ScreencastService = new Lang.Class({
if (!recorder.is_recording()) { if (!recorder.is_recording()) {
let [x, y, width, height, fileTemplate, options] = params; let [x, y, width, height, fileTemplate, options] = params;
if (x < 0 || y < 0 ||
width <= 0 || height <= 0 ||
x + width > global.screen_width ||
y + height > global.screen_height) {
invocation.return_error_literal(Gio.IOErrorEnum,
Gio.IOErrorEnum.CANCELLED,
"Invalid params");
return;
}
recorder.set_file_template(fileTemplate); recorder.set_file_template(fileTemplate);
recorder.set_area(x, y, width, height); recorder.set_area(x, y, width, height);
this._applyOptionalParameters(recorder, options); this._applyOptionalParameters(recorder, options);
@ -162,4 +138,3 @@ const ScreencastService = new Lang.Class({
invocation.return_value(GLib.Variant.new('(b)', [success])); invocation.return_value(GLib.Variant.new('(b)', [success]));
} }
}); });
Signals.addSignalMethods(ScreencastService.prototype);

View File

@ -6,7 +6,6 @@ const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib; const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk; const Gtk = imports.gi.Gtk;
const Lang = imports.lang; const Lang = imports.lang;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const Signals = imports.signals; const Signals = imports.signals;
const St = imports.gi.St; const St = imports.gi.St;
@ -15,47 +14,45 @@ const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main; const Main = imports.ui.main;
const Tweener = imports.ui.tweener; const Tweener = imports.ui.tweener;
const ScreenshotIface = '<node> \ const ScreenshotIface = <interface name="org.gnome.Shell.Screenshot">
<interface name="org.gnome.Shell.Screenshot"> \ <method name="ScreenshotArea">
<method name="ScreenshotArea"> \ <arg type="i" direction="in" name="x"/>
<arg type="i" direction="in" name="x"/> \ <arg type="i" direction="in" name="y"/>
<arg type="i" direction="in" name="y"/> \ <arg type="i" direction="in" name="width"/>
<arg type="i" direction="in" name="width"/> \ <arg type="i" direction="in" name="height"/>
<arg type="i" direction="in" name="height"/> \ <arg type="b" direction="in" name="flash"/>
<arg type="b" direction="in" name="flash"/> \ <arg type="s" direction="in" name="filename"/>
<arg type="s" direction="in" name="filename"/> \ <arg type="b" direction="out" name="success"/>
<arg type="b" direction="out" name="success"/> \ <arg type="s" direction="out" name="filename_used"/>
<arg type="s" direction="out" name="filename_used"/> \ </method>
</method> \ <method name="ScreenshotWindow">
<method name="ScreenshotWindow"> \ <arg type="b" direction="in" name="include_frame"/>
<arg type="b" direction="in" name="include_frame"/> \ <arg type="b" direction="in" name="include_cursor"/>
<arg type="b" direction="in" name="include_cursor"/> \ <arg type="b" direction="in" name="flash"/>
<arg type="b" direction="in" name="flash"/> \ <arg type="s" direction="in" name="filename"/>
<arg type="s" direction="in" name="filename"/> \ <arg type="b" direction="out" name="success"/>
<arg type="b" direction="out" name="success"/> \ <arg type="s" direction="out" name="filename_used"/>
<arg type="s" direction="out" name="filename_used"/> \ </method>
</method> \ <method name="Screenshot">
<method name="Screenshot"> \ <arg type="b" direction="in" name="include_cursor"/>
<arg type="b" direction="in" name="include_cursor"/> \ <arg type="b" direction="in" name="flash"/>
<arg type="b" direction="in" name="flash"/> \ <arg type="s" direction="in" name="filename"/>
<arg type="s" direction="in" name="filename"/> \ <arg type="b" direction="out" name="success"/>
<arg type="b" direction="out" name="success"/> \ <arg type="s" direction="out" name="filename_used"/>
<arg type="s" direction="out" name="filename_used"/> \ </method>
</method> \ <method name="SelectArea">
<method name="SelectArea"> \ <arg type="i" direction="out" name="x"/>
<arg type="i" direction="out" name="x"/> \ <arg type="i" direction="out" name="y"/>
<arg type="i" direction="out" name="y"/> \ <arg type="i" direction="out" name="width"/>
<arg type="i" direction="out" name="width"/> \ <arg type="i" direction="out" name="height"/>
<arg type="i" direction="out" name="height"/> \ </method>
</method> \ <method name="FlashArea">
<method name="FlashArea"> \ <arg type="i" direction="in" name="x"/>
<arg type="i" direction="in" name="x"/> \ <arg type="i" direction="in" name="y"/>
<arg type="i" direction="in" name="y"/> \ <arg type="i" direction="in" name="width"/>
<arg type="i" direction="in" name="width"/> \ <arg type="i" direction="in" name="height"/>
<arg type="i" direction="in" name="height"/> \ </method>
</method> \ </interface>;
</interface> \
</node>';
const ScreenshotService = new Lang.Class({ const ScreenshotService = new Lang.Class({
Name: 'ScreenshotService', Name: 'ScreenshotService',
@ -79,9 +76,7 @@ const ScreenshotService = new Lang.Class({
ScreenshotAreaAsync : function (params, invocation) { ScreenshotAreaAsync : function (params, invocation) {
let [x, y, width, height, flash, filename, callback] = params; let [x, y, width, height, flash, filename, callback] = params;
if (x < 0 || y < 0 || if (height <= 0 || width <= 0) {
width <= 0 || height <= 0 ||
x + width > global.screen_width || y + height > global.screen_height) {
invocation.return_error_literal(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED, invocation.return_error_literal(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED,
"Invalid params"); "Invalid params");
return; return;
@ -172,7 +167,7 @@ const SelectArea = new Lang.Class({
if (!Main.pushModal(this._group) || this._group.visible) if (!Main.pushModal(this._group) || this._group.visible)
return; return;
global.screen.set_cursor(Meta.Cursor.CROSSHAIR); global.set_cursor(Shell.Cursor.CROSSHAIR);
this._group.visible = true; this._group.visible = true;
}, },
@ -206,12 +201,12 @@ const SelectArea = new Lang.Class({
if (event.get_key_symbol() == Clutter.Escape) if (event.get_key_symbol() == Clutter.Escape)
this._destroy(null, false); this._destroy(null, false);
return Clutter.EVENT_PROPAGATE; return;
}, },
_onMotionEvent: function(actor, event) { _onMotionEvent: function(actor, event) {
if (this._startX == -1 || this._startY == -1) if (this._startX == -1 || this._startY == -1)
return Clutter.EVENT_PROPAGATE; return false;
[this._lastX, this._lastY] = event.get_coords(); [this._lastX, this._lastY] = event.get_coords();
let geometry = this._getGeometry(); let geometry = this._getGeometry();
@ -219,19 +214,19 @@ const SelectArea = new Lang.Class({
this._rubberband.set_position(geometry.x, geometry.y); this._rubberband.set_position(geometry.x, geometry.y);
this._rubberband.set_size(geometry.width, geometry.height); this._rubberband.set_size(geometry.width, geometry.height);
return Clutter.EVENT_PROPAGATE; return false;
}, },
_onButtonPress: function(actor, event) { _onButtonPress: function(actor, event) {
[this._startX, this._startY] = event.get_coords(); [this._startX, this._startY] = event.get_coords();
this._rubberband.set_position(this._startX, this._startY); this._rubberband.set_position(this._startX, this._startY);
return Clutter.EVENT_PROPAGATE; return false;
}, },
_onButtonRelease: function(actor, event) { _onButtonRelease: function(actor, event) {
this._destroy(this._getGeometry(), true); this._destroy(this._getGeometry(), true);
return Clutter.EVENT_PROPAGATE; return false;
}, },
_destroy: function(geometry, fade) { _destroy: function(geometry, fade) {
@ -243,7 +238,7 @@ const SelectArea = new Lang.Class({
function() { function() {
Main.popModal(this._group); Main.popModal(this._group);
this._group.destroy(); this._group.destroy();
global.screen.set_cursor(Meta.Cursor.DEFAULT); global.unset_cursor();
this.emit('finished', geometry); this.emit('finished', geometry);
}) })

View File

@ -1,7 +1,6 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Gio = imports.gi.Gio; const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Mainloop = imports.mainloop; const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta; const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
@ -42,7 +41,7 @@ function sleep(milliseconds) {
Mainloop.timeout_add(milliseconds, function() { Mainloop.timeout_add(milliseconds, function() {
if (cb) if (cb)
cb(); cb();
return GLib.SOURCE_REMOVE; return false;
}); });
return function(callback) { return function(callback) {
@ -70,18 +69,16 @@ function waitLeisure() {
}; };
} }
const PerfHelperIface = '<node> \ const PerfHelperIface = <interface name="org.gnome.Shell.PerfHelper">
<interface name="org.gnome.Shell.PerfHelper"> \ <method name="CreateWindow">
<method name="CreateWindow"> \ <arg type="i" direction="in" />
<arg type="i" direction="in" /> \ <arg type="i" direction="in" />
<arg type="i" direction="in" /> \ <arg type="b" direction="in" />
<arg type="b" direction="in" /> \ <arg type="b" direction="in" />
<arg type="b" direction="in" /> \ </method>
</method> \ <method name="WaitWindows" />
<method name="WaitWindows" /> \ <method name="DestroyWindows" />
<method name="DestroyWindows" /> \ </interface>;
</interface> \
</node>';
var PerfHelperProxy = Gio.DBusProxy.makeProxyWrapper(PerfHelperIface); var PerfHelperProxy = Gio.DBusProxy.makeProxyWrapper(PerfHelperIface);
function PerfHelper() { function PerfHelper() {

View File

@ -1,706 +1,105 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Lang = imports.lang; const Lang = imports.lang;
const Gio = imports.gi.Gio;
const Gtk = imports.gi.Gtk;
const Meta = imports.gi.Meta;
const Signals = imports.signals; const Signals = imports.signals;
const St = imports.gi.St;
const Atk = imports.gi.Atk;
const AppDisplay = imports.ui.appDisplay;
const DND = imports.ui.dnd;
const IconGrid = imports.ui.iconGrid;
const Main = imports.ui.main;
const Overview = imports.ui.overview;
const RemoteSearch = imports.ui.remoteSearch;
const Separator = imports.ui.separator;
const Util = imports.misc.util;
const SEARCH_PROVIDERS_SCHEMA = 'org.gnome.desktop.search-providers'; const SEARCH_PROVIDERS_SCHEMA = 'org.gnome.desktop.search-providers';
const MAX_LIST_SEARCH_RESULTS_ROWS = 3;
const MAX_GRID_SEARCH_RESULTS_ROWS = 1;
const SearchSystem = new Lang.Class({ const SearchSystem = new Lang.Class({
Name: 'SearchSystem', Name: 'SearchSystem',
_init: function() { _init: function() {
this._providers = []; this._providers = [];
this._remoteProviders = [];
this._registerProvider(new AppDisplay.AppSearchProvider()); this.reset();
this._searchSettings = new Gio.Settings({ schema: SEARCH_PROVIDERS_SCHEMA });
this._searchSettings.connect('changed::disabled', Lang.bind(this, this._reloadRemoteProviders));
this._searchSettings.connect('changed::disable-external', Lang.bind(this, this._reloadRemoteProviders));
this._searchSettings.connect('changed::sort-order', Lang.bind(this, this._reloadRemoteProviders));
this._reloadRemoteProviders();
this._cancellable = new Gio.Cancellable();
}, },
addProvider: function(provider) { registerProvider: function (provider) {
provider.searchSystem = this;
this._providers.push(provider); this._providers.push(provider);
this.emit('providers-changed');
if (provider.isRemoteProvider)
this._remoteProviders.push(provider);
}, },
_reloadRemoteProviders: function() { unregisterProvider: function (provider) {
let remoteProviders = this._providers.filter(function(provider) {
return provider.isRemoteProvider;
});
remoteProviders.forEach(Lang.bind(this, function(provider) {
this._unregisterProvider(provider);
}));
RemoteSearch.loadRemoteSearchProviders(Lang.bind(this, function(providers) {
providers.forEach(Lang.bind(this, this._registerProvider));
}));
this.emit('providers-changed');
},
_registerProvider: function (provider) {
this._providers.push(provider);
},
_unregisterProvider: function (provider) {
let index = this._providers.indexOf(provider); let index = this._providers.indexOf(provider);
if (index == -1)
return;
provider.searchSystem = null;
this._providers.splice(index, 1); this._providers.splice(index, 1);
let remoteIndex = this._remoteProviders.indexOf(provider);
if (remoteIndex != -1)
this._remoteProviders.splice(index, 1);
}, },
getProviders: function() { getProviders: function() {
return this._providers; return this._providers;
}, },
getRemoteProviders: function() {
return this._remoteProviders;
},
getTerms: function() { getTerms: function() {
return this._terms; return this._previousTerms;
}, },
reset: function() { reset: function() {
this._terms = []; this._previousTerms = [];
this._results = {}; this._previousResults = [];
}, },
_gotResults: function(results, provider) { setResults: function(provider, results) {
this._results[provider.id] = results; let i = this._providers.indexOf(provider);
this.emit('search-updated', provider, results); if (i == -1)
return;
this._previousResults[i] = [provider, results];
this.emit('search-updated', this._previousResults[i]);
}, },
setTerms: function(terms) { updateSearchResults: function(terms) {
this._cancellable.cancel();
this._cancellable.reset();
let previousResults = this._results;
let previousTerms = this._terms;
this.reset();
if (!terms) if (!terms)
return; return;
let searchString = terms.join(' '); let searchString = terms.join(' ');
let previousSearchString = previousTerms.join(' '); let previousSearchString = this._previousTerms.join(' ');
if (searchString == previousSearchString) if (searchString == previousSearchString)
return; return;
let isSubSearch = false; let isSubSearch = false;
if (previousTerms.length > 0) if (this._previousTerms.length > 0)
isSubSearch = searchString.indexOf(previousSearchString) == 0; isSubSearch = searchString.indexOf(previousSearchString) == 0;
this._terms = terms; let previousResultsArr = this._previousResults;
this._providers.forEach(Lang.bind(this, function(provider) { let results = [];
let previousProviderResults = previousResults[provider.id]; this._previousTerms = terms;
if (isSubSearch && previousProviderResults) this._previousResults = results;
provider.getSubsearchResultSet(previousProviderResults, terms, Lang.bind(this, this._gotResults, provider), this._cancellable);
else if (isSubSearch) {
provider.getInitialResultSet(terms, Lang.bind(this, this._gotResults, provider), this._cancellable); for (let i = 0; i < this._providers.length; i++) {
})); let [provider, previousResults] = previousResultsArr[i];
try {
results.push([provider, []]);
provider.getSubsearchResultSet(previousResults, terms);
} catch (error) {
log('A ' + error.name + ' has occured in ' + provider.id + ': ' + error.message);
}
}
} else {
for (let i = 0; i < this._providers.length; i++) {
let provider = this._providers[i];
try {
results.push([provider, []]);
provider.getInitialResultSet(terms);
} catch (error) {
log('A ' + error.name + ' has occured in ' + provider.id + ': ' + error.message);
}
}
}
} }
}); });
Signals.addSignalMethods(SearchSystem.prototype); Signals.addSignalMethods(SearchSystem.prototype);
const MaxWidthBin = new Lang.Class({
Name: 'MaxWidthBin',
Extends: St.Bin,
vfunc_allocate: function(box, flags) {
let themeNode = this.get_theme_node();
let maxWidth = themeNode.get_max_width();
let availWidth = box.x2 - box.x1;
let adjustedBox = box;
if (availWidth > maxWidth) {
let excessWidth = availWidth - maxWidth;
adjustedBox.x1 += Math.floor(excessWidth / 2);
adjustedBox.x2 -= Math.floor(excessWidth / 2);
}
this.parent(adjustedBox, flags);
}
});
const SearchResult = new Lang.Class({
Name: 'SearchResult',
_init: function(provider, metaInfo) {
this.provider = provider;
this.metaInfo = metaInfo;
this.actor = new St.Button({ reactive: true,
can_focus: true,
track_hover: true,
x_align: St.Align.START,
y_fill: true });
this.actor._delegate = this;
this.actor.connect('clicked', Lang.bind(this, this.activate));
},
activate: function() {
this.emit('activate', this.metaInfo.id);
},
setSelected: function(selected) {
if (selected)
this.actor.add_style_pseudo_class('selected');
else
this.actor.remove_style_pseudo_class('selected');
}
});
Signals.addSignalMethods(SearchResult.prototype);
const ListSearchResult = new Lang.Class({
Name: 'ListSearchResult',
Extends: SearchResult,
ICON_SIZE: 64,
_init: function(provider, metaInfo) {
this.parent(provider, metaInfo);
this.actor.style_class = 'list-search-result';
this.actor.x_fill = true;
let content = new St.BoxLayout({ style_class: 'list-search-result-content',
vertical: false });
this.actor.set_child(content);
// An icon for, or thumbnail of, content
let icon = this.metaInfo['createIcon'](this.ICON_SIZE);
if (icon) {
content.add(icon);
}
let details = new St.BoxLayout({ vertical: true });
content.add(details, { x_fill: true,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.MIDDLE });
let title = new St.Label({ style_class: 'list-search-result-title',
text: this.metaInfo['name'] })
details.add(title, { x_fill: false,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.START });
this.actor.label_actor = title;
if (this.metaInfo['description']) {
let description = new St.Label({ style_class: 'list-search-result-description' });
description.clutter_text.set_markup(this.metaInfo['description']);
details.add(description, { x_fill: false,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.END });
}
}
});
const GridSearchResult = new Lang.Class({
Name: 'GridSearchResult',
Extends: SearchResult,
_init: function(provider, metaInfo) {
this.parent(provider, metaInfo);
this.actor.style_class = 'grid-search-result';
let content = provider.createResultObject(metaInfo);
let dragSource = null;
if (content == null) {
let actor = new St.Bin();
let icon = new IconGrid.BaseIcon(this.metaInfo['name'],
{ createIcon: this.metaInfo['createIcon'] });
actor.set_child(icon.actor);
actor.label_actor = icon.label;
dragSource = icon.icon;
content = { actor: actor, icon: icon };
} else {
if (content._delegate && content._delegate.getDragActorSource)
dragSource = content._delegate.getDragActorSource();
}
this.actor.set_child(content.actor);
this.actor.label_actor = content.actor.label_actor;
this.icon = content.icon;
let draggable = DND.makeDraggable(this.actor);
draggable.connect('drag-begin',
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);
}));
if (!dragSource)
// not exactly right, but alignment problems are hard to notice
dragSource = content;
this._dragActorSource = dragSource;
},
getDragActorSource: function() {
return this._dragActorSource;
},
getDragActor: function() {
return this.metaInfo['createIcon'](Main.overview.dashIconSize);
},
shellWorkspaceLaunch: function(params) {
if (this.provider.dragActivateResult)
this.provider.dragActivateResult(this.metaInfo.id, params);
else
this.provider.activateResult(this.metaInfo.id, this.terms);
}
});
const SearchResultsBase = new Lang.Class({
Name: 'SearchResultsBase',
_init: function(provider) {
this.provider = provider;
this._terms = [];
this.actor = new St.BoxLayout({ style_class: 'search-section',
vertical: true });
this._resultDisplayBin = new St.Bin({ x_fill: true,
y_fill: true });
this.actor.add(this._resultDisplayBin, { expand: true });
let separator = new Separator.HorizontalSeparator({ style_class: 'search-section-separator' });
this.actor.add(separator.actor);
this._resultDisplays = {};
this._cancellable = new Gio.Cancellable();
},
destroy: function() {
this.actor.destroy();
this._terms = [];
},
_clearResultDisplay: function() {
},
clear: function() {
this._resultDisplays = {};
this._clearResultDisplay();
this.actor.hide();
},
_keyFocusIn: function(actor) {
this.emit('key-focus-in', actor);
},
_activateResult: function(result, id) {
this.provider.activateResult(id, this._terms);
Main.overview.toggle();
},
_setMoreIconVisible: function(visible) {
},
_ensureResultActors: function(results, callback) {
let metasNeeded = results.filter(Lang.bind(this, function(resultId) {
return this._resultDisplays[resultId] === undefined;
}));
if (metasNeeded.length === 0) {
callback();
} else {
this._cancellable.cancel();
this._cancellable.reset();
this.provider.getResultMetas(metasNeeded, Lang.bind(this, function(metas) {
metasNeeded.forEach(Lang.bind(this, function(resultId, i) {
let meta = metas[i];
let display = this._createResultDisplay(meta);
display.connect('activate', Lang.bind(this, this._activateResult));
display.actor.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
this._resultDisplays[resultId] = display;
}));
callback();
}), this._cancellable);
}
},
updateSearch: function(providerResults, terms, callback) {
this._terms = terms;
if (providerResults.length == 0) {
this._clearResultDisplay();
this.actor.hide();
callback();
} else {
let maxResults = this._getMaxDisplayedResults();
let results = this.provider.filterResults(providerResults, maxResults);
let hasMoreResults = results.length < providerResults.length;
this._ensureResultActors(results, Lang.bind(this, function() {
this._clearResultDisplay();
// To avoid CSS transitions causing flickering when
// the first search result stays the same, we hide the
// content while filling in the results.
this.actor.hide();
this._clearResultDisplay();
results.forEach(Lang.bind(this, function(resultId) {
this._addItem(this._resultDisplays[resultId]);
}));
this._setMoreIconVisible(hasMoreResults && this.provider.canLaunchSearch);
this.actor.show();
callback();
}));
}
}
});
const ListSearchResults = new Lang.Class({
Name: 'ListSearchResults',
Extends: SearchResultsBase,
_init: function(provider) {
this.parent(provider);
this._container = new St.BoxLayout({ style_class: 'search-section-content' });
this.providerIcon = new ProviderIcon(provider);
this.providerIcon.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
this.providerIcon.connect('clicked', Lang.bind(this,
function() {
provider.launchSearch(this._terms);
Main.overview.toggle();
}));
this._container.add(this.providerIcon, { x_fill: false,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.START });
this._content = new St.BoxLayout({ style_class: 'list-search-results',
vertical: true });
this._container.add(this._content, { expand: true });
this._resultDisplayBin.set_child(this._container);
},
_setMoreIconVisible: function(visible) {
this.providerIcon.moreIcon.visible = true;
},
_getMaxDisplayedResults: function() {
return MAX_LIST_SEARCH_RESULTS_ROWS;
},
_clearResultDisplay: function () {
this._content.remove_all_children();
},
_createResultDisplay: function(meta) {
return new ListSearchResult(this.provider, meta);
},
_addItem: function(display) {
this._content.add_actor(display.actor);
},
getFirstResult: function() {
if (this._content.get_n_children() > 0)
return this._content.get_child_at_index(0)._delegate;
else
return null;
}
});
Signals.addSignalMethods(ListSearchResults.prototype);
const GridSearchResults = new Lang.Class({
Name: 'GridSearchResults',
Extends: SearchResultsBase,
_init: function(provider) {
this.parent(provider);
this._grid = new IconGrid.IconGrid({ rowLimit: MAX_GRID_SEARCH_RESULTS_ROWS,
xAlign: St.Align.START });
this._bin = new St.Bin({ x_align: St.Align.MIDDLE });
this._bin.set_child(this._grid.actor);
this._resultDisplayBin.set_child(this._bin);
},
_getMaxDisplayedResults: function() {
return this._grid.columnsForWidth(this._bin.width) * this._grid.getRowLimit();
},
_renderResults: function(metas) {
for (let i = 0; i < metas.length; i++) {
let display = new GridSearchResult(this.provider, metas[i]);
display.connect('activate', Lang.bind(this, this._activateResult));
display.actor.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
this._grid.addItem(display);
}
},
_clearResultDisplay: function () {
this._grid.removeAll();
},
_createResultDisplay: function(meta) {
return new GridSearchResult(this.provider, meta);
},
_addItem: function(display) {
this._grid.addItem(display);
},
getFirstResult: function() {
if (this._grid.visibleItemsCount() > 0)
return this._grid.getItemAtIndex(0)._delegate;
else
return null;
}
});
Signals.addSignalMethods(GridSearchResults.prototype);
const SearchResults = new Lang.Class({
Name: 'SearchResults',
_init: function() {
this.actor = new St.BoxLayout({ name: 'searchResults',
vertical: true });
this._content = new St.BoxLayout({ name: 'searchResultsContent',
vertical: true });
this._contentBin = new MaxWidthBin({ name: 'searchResultsBin',
x_fill: true,
y_fill: true,
child: this._content });
let scrollChild = new St.BoxLayout();
scrollChild.add(this._contentBin, { expand: true });
this._scrollView = new St.ScrollView({ x_fill: true,
y_fill: false,
overlay_scrollbars: true,
style_class: 'search-display vfade' });
this._scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
this._scrollView.add_actor(scrollChild);
let action = new Clutter.PanAction({ interpolate: true });
action.connect('pan', Lang.bind(this, this._onPan));
this._scrollView.add_action(action);
this.actor.add(this._scrollView, { x_fill: true,
y_fill: true,
expand: true,
x_align: St.Align.START,
y_align: St.Align.START });
this._statusText = new St.Label({ style_class: 'search-statustext' });
this._statusBin = new St.Bin({ x_align: St.Align.MIDDLE,
y_align: St.Align.MIDDLE });
this._content.add(this._statusBin, { expand: true });
this._statusBin.add_actor(this._statusText);
this._highlightDefault = false;
this._defaultResult = null;
this._searchSystem = new SearchSystem();
this._searchSystem.connect('search-updated', Lang.bind(this, this._updateResults));
this._searchSystem.connect('providers-changed', Lang.bind(this, this._updateProviderDisplays));
this._updateProviderDisplays();
},
_onPan: function(action) {
let [dist, dx, dy] = action.get_motion_delta(0);
let adjustment = this._scrollView.vscroll.adjustment;
adjustment.value -= (dy / this.actor.height) * adjustment.page_size;
return false;
},
_keyFocusIn: function(provider, actor) {
Util.ensureActorVisibleInScrollView(this._scrollView, actor);
},
_ensureProviderDisplay: function(provider) {
if (provider.display)
return;
let providerDisplay;
if (provider.appInfo)
providerDisplay = new ListSearchResults(provider);
else
providerDisplay = new GridSearchResults(provider);
providerDisplay.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
this._content.add(providerDisplay.actor);
provider.display = providerDisplay;
},
_updateProviderDisplays: function() {
this._searchSystem.getProviders().forEach(Lang.bind(this, this._ensureProviderDisplay));
},
_clearDisplay: function() {
this._searchSystem.getProviders().forEach(function(provider) {
provider.display.clear();
});
},
reset: function() {
this._searchSystem.reset();
this._statusBin.hide();
this._clearDisplay();
this._defaultResult = null;
},
startingSearch: function() {
this.reset();
this._statusText.set_text(_("Searching…"));
this._statusBin.show();
},
setTerms: function(terms) {
this._searchSystem.setTerms(terms);
},
_maybeSetInitialSelection: function() {
let newDefaultResult = null;
let providers = this._searchSystem.getProviders();
for (let i = 0; i < providers.length; i++) {
let provider = providers[i];
let display = provider.display;
if (!display.actor.visible)
continue;
let firstResult = display.getFirstResult();
if (firstResult) {
newDefaultResult = firstResult;
break; // select this one!
}
}
if (newDefaultResult != this._defaultResult) {
if (this._defaultResult)
this._defaultResult.setSelected(false);
if (newDefaultResult)
newDefaultResult.setSelected(this._highlightDefault);
this._defaultResult = newDefaultResult;
}
},
_updateStatusText: function () {
let haveResults = this._searchSystem.getProviders().some(function(provider) {
let display = provider.display;
return (display.getFirstResult() != null);
});
if (!haveResults) {
this._statusText.set_text(_("No results."));
this._statusBin.show();
} else {
this._statusBin.hide();
}
},
_updateResults: function(searchSystem, provider, results) {
let terms = searchSystem.getTerms();
let display = provider.display;
display.updateSearch(results, terms, Lang.bind(this, function() {
this._maybeSetInitialSelection();
this._updateStatusText();
}));
},
activateDefault: function() {
if (this._defaultResult)
this._defaultResult.activate();
},
highlightDefault: function(highlight) {
this._highlightDefault = highlight;
if (this._defaultResult)
this._defaultResult.setSelected(highlight);
},
navigateFocus: function(direction) {
let rtl = this.actor.get_text_direction() == Clutter.TextDirection.RTL;
if (direction == Gtk.DirectionType.TAB_BACKWARD ||
direction == (rtl ? Gtk.DirectionType.RIGHT
: Gtk.DirectionType.LEFT) ||
direction == Gtk.DirectionType.UP) {
this.actor.navigate_focus(null, direction, false);
return;
}
let from = this._defaultResult ? this._defaultResult.actor : null;
this.actor.navigate_focus(from, direction, false);
}
});
const ProviderIcon = new Lang.Class({
Name: 'ProviderIcon',
Extends: St.Button,
PROVIDER_ICON_SIZE: 48,
_init: function(provider) {
this.provider = provider;
this.parent({ style_class: 'search-provider-icon',
reactive: true,
can_focus: true,
accessible_name: provider.appInfo.get_name(),
track_hover: true });
this._content = new St.Widget({ layout_manager: new Clutter.BinLayout() });
this.set_child(this._content);
let rtl = (this.get_text_direction() == Clutter.TextDirection.RTL);
this.moreIcon = new St.Widget({ style_class: 'search-provider-icon-more',
visible: false,
x_align: rtl ? Clutter.ActorAlign.START : Clutter.ActorAlign.END,
y_align: Clutter.ActorAlign.END,
x_expand: true,
y_expand: true });
let icon = new St.Icon({ icon_size: this.PROVIDER_ICON_SIZE,
gicon: provider.appInfo.get_icon() });
this._content.add_actor(icon);
this._content.add_actor(this.moreIcon);
}
});

564
js/ui/searchDisplay.js Normal file
View File

@ -0,0 +1,564 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Lang = imports.lang;
const Gtk = imports.gi.Gtk;
const Meta = imports.gi.Meta;
const Signals = imports.signals;
const St = imports.gi.St;
const Atk = imports.gi.Atk;
const DND = imports.ui.dnd;
const IconGrid = imports.ui.iconGrid;
const Main = imports.ui.main;
const Overview = imports.ui.overview;
const Separator = imports.ui.separator;
const Search = imports.ui.search;
const Util = imports.misc.util;
const MAX_LIST_SEARCH_RESULTS_ROWS = 3;
const MAX_GRID_SEARCH_RESULTS_ROWS = 1;
const MaxWidthBin = new Lang.Class({
Name: 'MaxWidthBin',
Extends: St.Bin,
vfunc_allocate: function(box, flags) {
let themeNode = this.get_theme_node();
let maxWidth = themeNode.get_max_width();
let availWidth = box.x2 - box.x1;
let adjustedBox = box;
if (availWidth > maxWidth) {
let excessWidth = availWidth - maxWidth;
adjustedBox.x1 += Math.floor(excessWidth / 2);
adjustedBox.x2 -= Math.floor(excessWidth / 2);
}
this.parent(adjustedBox, flags);
}
});
const SearchResult = new Lang.Class({
Name: 'SearchResult',
_init: function(provider, metaInfo, terms) {
this.provider = provider;
this.metaInfo = metaInfo;
this.terms = terms;
this.actor = new St.Button({ reactive: true,
can_focus: true,
track_hover: true,
x_align: St.Align.START,
y_fill: true });
this.actor._delegate = this;
this.actor.connect('clicked', Lang.bind(this, this.activate));
},
activate: function() {
this.provider.activateResult(this.metaInfo.id, this.terms);
Main.overview.toggle();
},
setSelected: function(selected) {
if (selected)
this.actor.add_style_pseudo_class('selected');
else
this.actor.remove_style_pseudo_class('selected');
}
});
const ListSearchResult = new Lang.Class({
Name: 'ListSearchResult',
Extends: SearchResult,
ICON_SIZE: 64,
_init: function(provider, metaInfo, terms) {
this.parent(provider, metaInfo, terms);
this.actor.style_class = 'list-search-result';
this.actor.x_fill = true;
let content = new St.BoxLayout({ style_class: 'list-search-result-content',
vertical: false });
this.actor.set_child(content);
// An icon for, or thumbnail of, content
let icon = this.metaInfo['createIcon'](this.ICON_SIZE);
if (icon) {
content.add(icon);
}
let details = new St.BoxLayout({ vertical: true });
content.add(details, { x_fill: true,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.MIDDLE });
let title = new St.Label({ style_class: 'list-search-result-title',
text: this.metaInfo['name'] })
details.add(title, { x_fill: false,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.START });
this.actor.label_actor = title;
if (this.metaInfo['description']) {
let description = new St.Label({ style_class: 'list-search-result-description' });
description.clutter_text.set_markup(this.metaInfo['description']);
details.add(description, { x_fill: false,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.END });
}
}
});
const GridSearchResult = new Lang.Class({
Name: 'GridSearchResult',
Extends: SearchResult,
_init: function(provider, metaInfo, terms) {
this.parent(provider, metaInfo, terms);
this.actor.style_class = 'grid-search-result';
let content = provider.createResultActor(metaInfo, terms);
let dragSource = null;
if (content == null) {
content = new St.Bin();
let icon = new IconGrid.BaseIcon(this.metaInfo['name'],
{ createIcon: this.metaInfo['createIcon'] });
content.set_child(icon.actor);
content.label_actor = icon.label;
dragSource = icon.icon;
} else {
if (content._delegate && content._delegate.getDragActorSource)
dragSource = content._delegate.getDragActorSource();
}
this.actor.set_child(content);
this.actor.label_actor = content.label_actor;
let draggable = DND.makeDraggable(this.actor);
draggable.connect('drag-begin',
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);
}));
if (!dragSource)
// not exactly right, but alignment problems are hard to notice
dragSource = content;
this._dragActorSource = dragSource;
},
getDragActorSource: function() {
return this._dragActorSource;
},
getDragActor: function() {
return this.metaInfo['createIcon'](Main.overview.dashIconSize);
},
shellWorkspaceLaunch: function(params) {
if (this.provider.dragActivateResult)
this.provider.dragActivateResult(this.metaInfo.id, params);
else
this.provider.activateResult(this.metaInfo.id, this.terms);
}
});
const SearchResultsBase = new Lang.Class({
Name: 'SearchResultsBase',
_init: function(provider) {
this.provider = provider;
this._terms = [];
this.actor = new St.BoxLayout({ style_class: 'search-section',
vertical: true });
this._resultDisplayBin = new St.Bin({ x_fill: true,
y_fill: true });
this.actor.add(this._resultDisplayBin, { expand: true });
let separator = new Separator.HorizontalSeparator({ style_class: 'search-section-separator' });
this.actor.add(separator.actor);
},
destroy: function() {
this.actor.destroy();
this._terms = [];
},
_clearResultDisplay: function() {
},
clear: function() {
this._clearResultDisplay();
this.actor.hide();
},
_keyFocusIn: function(icon) {
this.emit('key-focus-in', icon);
},
_setMoreIconVisible: function(visible) {
},
updateSearch: function(providerResults, terms, callback) {
this._terms = terms;
if (providerResults.length == 0) {
this._clearResultDisplay();
this.actor.hide();
callback();
} else {
let maxResults = this._getMaxDisplayedResults();
let results = providerResults.slice(0, maxResults);
let hasMoreResults = results.length < providerResults.length;
this.provider.getResultMetas(results, Lang.bind(this, function(metas) {
this.clear();
// To avoid CSS transitions causing flickering when
// the first search result stays the same, we hide the
// content while filling in the results.
this.actor.hide();
this._clearResultDisplay();
this._renderResults(metas);
this._setMoreIconVisible(hasMoreResults && this.provider.canLaunchSearch);
this.actor.show();
callback();
}));
}
}
});
const ListSearchResults = new Lang.Class({
Name: 'ListSearchResults',
Extends: SearchResultsBase,
_init: function(provider) {
this.parent(provider);
this._container = new St.BoxLayout({ style_class: 'search-section-content' });
this.providerIcon = new ProviderIcon(provider);
this.providerIcon.connect('clicked', Lang.bind(this,
function() {
provider.launchSearch(this._terms);
Main.overview.toggle();
}));
this._container.add(this.providerIcon, { x_fill: false,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.START });
this._content = new St.BoxLayout({ style_class: 'list-search-results',
vertical: true });
this._container.add(this._content, { expand: true });
this._resultDisplayBin.set_child(this._container);
},
_setMoreIconVisible: function(visible) {
this.providerIcon.moreIcon.visible = true;
},
_getMaxDisplayedResults: function() {
return MAX_LIST_SEARCH_RESULTS_ROWS;
},
_renderResults: function(metas) {
for (let i = 0; i < metas.length; i++) {
let display = new ListSearchResult(this.provider, metas[i], this._terms);
display.actor.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
this._content.add_actor(display.actor);
}
},
_clearResultDisplay: function () {
this._content.destroy_all_children();
},
getFirstResult: function() {
if (this._content.get_n_children() > 0)
return this._content.get_child_at_index(0)._delegate;
else
return null;
}
});
Signals.addSignalMethods(ListSearchResults.prototype);
const GridSearchResults = new Lang.Class({
Name: 'GridSearchResults',
Extends: SearchResultsBase,
_init: function(provider) {
this.parent(provider);
this._grid = new IconGrid.IconGrid({ rowLimit: MAX_GRID_SEARCH_RESULTS_ROWS,
xAlign: St.Align.START });
this._bin = new St.Bin({ x_align: St.Align.MIDDLE });
this._bin.set_child(this._grid.actor);
this._resultDisplayBin.set_child(this._bin);
},
_getMaxDisplayedResults: function() {
return this._grid.childrenInRow(this._bin.width) * this._grid.getRowLimit();
},
_renderResults: function(metas) {
for (let i = 0; i < metas.length; i++) {
let display = new GridSearchResult(this.provider, metas[i], this._terms);
display.actor.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
this._grid.addItem(display.actor);
}
},
_clearResultDisplay: function () {
this._grid.removeAll();
},
getFirstResult: function() {
if (this._grid.visibleItemsCount() > 0)
return this._grid.getItemAtIndex(0)._delegate;
else
return null;
}
});
Signals.addSignalMethods(GridSearchResults.prototype);
const SearchResults = new Lang.Class({
Name: 'SearchResults',
_init: function(searchSystem) {
this._searchSystem = searchSystem;
this._searchSystem.connect('search-updated', Lang.bind(this, this._updateResults));
this.actor = new St.BoxLayout({ name: 'searchResults',
vertical: true });
this._content = new St.BoxLayout({ name: 'searchResultsContent',
vertical: true });
this._contentBin = new MaxWidthBin({ name: 'searchResultsBin',
x_fill: true,
y_fill: true,
child: this._content });
let scrollChild = new St.BoxLayout();
scrollChild.add(this._contentBin, { expand: true });
this._scrollView = new St.ScrollView({ x_fill: true,
y_fill: false,
overlay_scrollbars: true,
style_class: 'search-display vfade' });
this._scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
this._scrollView.add_actor(scrollChild);
let action = new Clutter.PanAction({ interpolate: true });
action.connect('pan', Lang.bind(this, this._onPan));
this._scrollView.add_action(action);
this.actor.add(this._scrollView, { x_fill: true,
y_fill: true,
expand: true,
x_align: St.Align.START,
y_align: St.Align.START });
this._statusText = new St.Label({ style_class: 'search-statustext' });
this._statusBin = new St.Bin({ x_align: St.Align.MIDDLE,
y_align: St.Align.MIDDLE });
this._content.add(this._statusBin, { expand: true });
this._statusBin.add_actor(this._statusText);
this._providers = this._searchSystem.getProviders();
this._providerDisplays = {};
for (let i = 0; i < this._providers.length; i++) {
this.createProviderDisplay(this._providers[i]);
}
this._highlightDefault = false;
this._defaultResult = null;
},
_onPan: function(action) {
let [dist, dx, dy] = action.get_motion_delta(0);
let adjustment = this._scrollView.vscroll.adjustment;
adjustment.value -= (dy / this.actor.height) * adjustment.page_size;
return false;
},
_keyFocusIn: function(provider, icon) {
Util.ensureActorVisibleInScrollView(this._scrollView, icon);
},
createProviderDisplay: function(provider) {
let providerDisplay = null;
if (provider.appInfo) {
providerDisplay = new ListSearchResults(provider);
} else {
providerDisplay = new GridSearchResults(provider);
}
providerDisplay.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
this._providerDisplays[provider.id] = providerDisplay;
this._content.add(providerDisplay.actor);
},
destroyProviderDisplay: function(provider) {
this._providerDisplays[provider.id].destroy();
delete this._providerDisplays[provider.id];
},
_clearDisplay: function() {
for (let i = 0; i < this._providers.length; i++) {
let provider = this._providers[i];
let providerDisplay = this._providerDisplays[provider.id];
providerDisplay.clear();
}
},
reset: function() {
this._searchSystem.reset();
this._statusBin.hide();
this._clearDisplay();
this._defaultResult = null;
},
startingSearch: function() {
this.reset();
this._statusText.set_text(_("Searching…"));
this._statusBin.show();
},
_maybeSetInitialSelection: function() {
let newDefaultResult = null;
for (let i = 0; i < this._providers.length; i++) {
let provider = this._providers[i];
let display = this._providerDisplays[provider.id];
if (!display.actor.visible)
continue;
let firstResult = display.getFirstResult();
if (firstResult) {
newDefaultResult = firstResult;
break; // select this one!
}
}
if (newDefaultResult != this._defaultResult) {
if (this._defaultResult)
this._defaultResult.setSelected(false);
if (newDefaultResult)
newDefaultResult.setSelected(this._highlightDefault);
this._defaultResult = newDefaultResult;
}
},
_updateStatusText: function () {
let haveResults = false;
for (let i = 0; i < this._providers.length; i++) {
let provider = this._providers[i];
let display = this._providerDisplays[provider.id];
if (display.getFirstResult()) {
haveResults = true;
break;
}
}
if (!haveResults) {
this._statusText.set_text(_("No results."));
this._statusBin.show();
} else {
this._statusBin.hide();
}
},
_updateResults: function(searchSystem, results) {
let terms = searchSystem.getTerms();
let [provider, providerResults] = results;
let display = this._providerDisplays[provider.id];
display.updateSearch(providerResults, terms, Lang.bind(this, function() {
this._maybeSetInitialSelection();
this._updateStatusText();
}));
},
activateDefault: function() {
if (this._defaultResult)
this._defaultResult.activate();
},
highlightDefault: function(highlight) {
this._highlightDefault = highlight;
if (this._defaultResult)
this._defaultResult.setSelected(highlight);
},
navigateFocus: function(direction) {
let rtl = this.actor.get_text_direction() == Clutter.TextDirection.RTL;
if (direction == Gtk.DirectionType.TAB_BACKWARD ||
direction == (rtl ? Gtk.DirectionType.RIGHT
: Gtk.DirectionType.LEFT) ||
direction == Gtk.DirectionType.UP) {
this.actor.navigate_focus(null, direction, false);
return;
}
let from = this._defaultResult ? this._defaultResult.actor : null;
this.actor.navigate_focus(from, direction, false);
}
});
const ProviderIcon = new Lang.Class({
Name: 'ProviderIcon',
Extends: St.Button,
PROVIDER_ICON_SIZE: 48,
_init: function(provider) {
this.provider = provider;
this.parent({ style_class: 'search-provider-icon',
reactive: true,
can_focus: true,
accessible_name: provider.appInfo.get_name(),
track_hover: true });
this._content = new St.Widget({ layout_manager: new Clutter.BinLayout() });
this.set_child(this._content);
let rtl = (this.get_text_direction() == Clutter.TextDirection.RTL);
this.moreIcon = new St.Widget({ style_class: 'search-provider-icon-more',
visible: false,
x_align: rtl ? Clutter.ActorAlign.START : Clutter.ActorAlign.END,
y_align: Clutter.ActorAlign.END,
x_expand: true,
y_expand: true });
let icon = new St.Icon({ icon_size: this.PROVIDER_ICON_SIZE,
gicon: provider.appInfo.get_icon() });
this._content.add_actor(icon);
this._content.add_actor(this.moreIcon);
}
});

View File

@ -21,7 +21,6 @@ const _modes = {
showCalendarEvents: false, showCalendarEvents: false,
allowSettings: false, allowSettings: false,
allowExtensions: false, allowExtensions: false,
allowScreencast: false,
enabledExtensions: [], enabledExtensions: [],
hasRunDialog: false, hasRunDialog: false,
hasWorkspaces: false, hasWorkspaces: false,
@ -49,7 +48,8 @@ const _modes = {
panel: { panel: {
left: [], left: [],
center: ['dateMenu'], center: ['dateMenu'],
right: ['a11yGreeter', 'keyboard', 'aggregateMenu'], right: ['a11yGreeter', 'display', 'keyboard',
'volume', 'battery', 'powerMenu']
}, },
panelStyle: 'login-screen' panelStyle: 'login-screen'
}, },
@ -60,9 +60,9 @@ const _modes = {
unlockDialog: undefined, unlockDialog: undefined,
components: ['polkitAgent', 'telepathyClient'], components: ['polkitAgent', 'telepathyClient'],
panel: { panel: {
left: [], left: ['userMenu'],
center: [], center: [],
right: ['aggregateMenu'] right: ['lockScreen']
}, },
panelStyle: 'lock-screen' panelStyle: 'lock-screen'
}, },
@ -72,9 +72,9 @@ const _modes = {
unlockDialog: undefined, unlockDialog: undefined,
components: ['polkitAgent', 'telepathyClient'], components: ['polkitAgent', 'telepathyClient'],
panel: { panel: {
left: [], left: ['userMenu'],
center: [], center: [],
right: ['a11y', 'keyboard', 'aggregateMenu'] right: ['a11y', 'keyboard', 'lockScreen']
}, },
panelStyle: 'unlock-screen' panelStyle: 'unlock-screen'
}, },
@ -84,7 +84,6 @@ const _modes = {
showCalendarEvents: true, showCalendarEvents: true,
allowSettings: true, allowSettings: true,
allowExtensions: true, allowExtensions: true,
allowScreencast: true,
hasRunDialog: true, hasRunDialog: true,
hasWorkspaces: true, hasWorkspaces: true,
hasWindows: true, hasWindows: true,
@ -93,21 +92,29 @@ const _modes = {
isPrimary: true, isPrimary: true,
unlockDialog: imports.ui.unlockDialog.UnlockDialog, unlockDialog: imports.ui.unlockDialog.UnlockDialog,
components: ['networkAgent', 'polkitAgent', 'telepathyClient', components: ['networkAgent', 'polkitAgent', 'telepathyClient',
'keyring', 'autorunManager', 'automountManager'], 'keyring', 'recorder', 'autorunManager', 'automountManager'],
panel: { panel: {
left: ['activities', 'appMenu'], left: ['activities', 'appMenu'],
center: ['dateMenu'], center: ['dateMenu'],
right: ['a11y', 'keyboard', 'aggregateMenu'] right: ['a11y', 'keyboard', 'volume', 'bluetooth',
'network', 'battery', 'userMenu']
} }
} }
}; };
function _loadMode(file, info) { function _getModes(modesLoadedCallback) {
FileUtils.collectFromDatadirsAsync('modes',
{ processFile: _loadMode,
loadedCallback: modesLoadedCallback,
data: _modes });
}
function _loadMode(file, info, loadedData) {
let name = info.get_name(); let name = info.get_name();
let suffix = name.indexOf('.json'); let suffix = name.indexOf('.json');
let modeName = suffix == -1 ? name : name.slice(name, suffix); let modeName = suffix == -1 ? name : name.slice(name, suffix);
if (_modes.hasOwnProperty(modeName)) if (loadedData.hasOwnProperty(modeName))
return; return;
let fileContent, success, tag, newMode; let fileContent, success, tag, newMode;
@ -118,24 +125,19 @@ function _loadMode(file, info) {
return; return;
} }
_modes[modeName] = {}; loadedData[modeName] = {};
let propBlacklist = ['unlockDialog']; let propBlacklist = ['unlockDialog'];
for (let prop in _modes[DEFAULT_MODE]) { for (let prop in loadedData[DEFAULT_MODE]) {
if (newMode[prop] !== undefined && if (newMode[prop] !== undefined &&
propBlacklist.indexOf(prop) == -1) propBlacklist.indexOf(prop) == -1)
_modes[modeName][prop] = newMode[prop]; loadedData[modeName][prop]= newMode[prop];
} }
_modes[modeName]['isPrimary'] = true; loadedData[modeName]['isPrimary'] = true;
}
function _loadModes() {
FileUtils.collectFromDatadirs('modes', false, _loadMode);
} }
function listModes() { function listModes() {
_loadModes(); _getModes(function(modes) {
Mainloop.idle_add(function() { let names = Object.getOwnPropertyNames(modes);
let names = Object.getOwnPropertyNames(_modes);
for (let i = 0; i < names.length; i++) for (let i = 0; i < names.length; i++)
if (_modes[names[i]].isPrimary) if (_modes[names[i]].isPrimary)
print(names[i]); print(names[i]);
@ -147,13 +149,17 @@ function listModes() {
const SessionMode = new Lang.Class({ const SessionMode = new Lang.Class({
Name: 'SessionMode', Name: 'SessionMode',
_init: function() { init: function() {
_loadModes(); _getModes(Lang.bind(this, function(modes) {
let isPrimary = (_modes[global.session_mode] && this._modes = modes;
_modes[global.session_mode].isPrimary); let primary = modes[global.session_mode] &&
let mode = isPrimary ? global.session_mode : 'user'; modes[global.session_mode].isPrimary;
let mode = primary ? global.session_mode : 'user';
this._modeStack = [mode]; this._modeStack = [mode];
this._sync(); this._sync();
this.emit('sessions-loaded');
}));
}, },
pushMode: function(mode) { pushMode: function(mode) {
@ -179,14 +185,18 @@ const SessionMode = new Lang.Class({
return this._modeStack[this._modeStack.length - 1]; return this._modeStack[this._modeStack.length - 1];
}, },
get allowScreencast() {
return this.components.indexOf('recorder') != -1;
},
_sync: function() { _sync: function() {
let params = _modes[this.currentMode]; let params = this._modes[this.currentMode];
let defaults; let defaults;
if (params.parentMode) if (params.parentMode)
defaults = Params.parse(_modes[params.parentMode], defaults = Params.parse(this._modes[params.parentMode],
_modes[DEFAULT_MODE]); this._modes[DEFAULT_MODE]);
else else
defaults = _modes[DEFAULT_MODE]; defaults = this._modes[DEFAULT_MODE];
params = Params.parse(params, defaults); params = Params.parse(params, defaults);
// A simplified version of Lang.copyProperties, handles // A simplified version of Lang.copyProperties, handles

View File

@ -12,66 +12,57 @@ const ExtensionDownloader = imports.ui.extensionDownloader;
const ExtensionUtils = imports.misc.extensionUtils; const ExtensionUtils = imports.misc.extensionUtils;
const Hash = imports.misc.hash; const Hash = imports.misc.hash;
const Main = imports.ui.main; const Main = imports.ui.main;
const Screencast = imports.ui.screencast;
const Screenshot = imports.ui.screenshot; const Screenshot = imports.ui.screenshot;
const ViewSelector = imports.ui.viewSelector;
const GnomeShellIface = '<node> \ const GnomeShellIface = <interface name="org.gnome.Shell">
<interface name="org.gnome.Shell"> \ <method name="Eval">
<method name="Eval"> \ <arg type="s" direction="in" name="script" />
<arg type="s" direction="in" name="script" /> \ <arg type="b" direction="out" name="success" />
<arg type="b" direction="out" name="success" /> \ <arg type="s" direction="out" name="result" />
<arg type="s" direction="out" name="result" /> \ </method>
</method> \ <method name="FocusSearch"/>
<method name="FocusSearch"/> \ <method name="ShowOSD">
<method name="ShowOSD"> \ <arg type="a{sv}" direction="in" name="params"/>
<arg type="a{sv}" direction="in" name="params"/> \ </method>
</method> \ <method name="GrabAccelerator">
<method name="FocusApp"> \ <arg type="s" direction="in" name="accelerator"/>
<arg type="s" direction="in" name="id"/> \ <arg type="u" direction="in" name="flags"/>
</method> \ <arg type="u" direction="out" name="action"/>
<method name="ShowApplications" /> \ </method>
<method name="GrabAccelerator"> \ <method name="GrabAccelerators">
<arg type="s" direction="in" name="accelerator"/> \ <arg type="a(su)" direction="in" name="accelerators"/>
<arg type="u" direction="in" name="flags"/> \ <arg type="au" direction="out" name="actions"/>
<arg type="u" direction="out" name="action"/> \ </method>
</method> \ <method name="UngrabAccelerator">
<method name="GrabAccelerators"> \ <arg type="u" direction="in" name="action"/>
<arg type="a(su)" direction="in" name="accelerators"/> \ <arg type="b" direction="out" name="success"/>
<arg type="au" direction="out" name="actions"/> \ </method>
</method> \ <signal name="AcceleratorActivated">
<method name="UngrabAccelerator"> \ <arg name="action" type="u" />
<arg type="u" direction="in" name="action"/> \ <arg name="deviceid" type="u" />
<arg type="b" direction="out" name="success"/> \ </signal>
</method> \ <property name="Mode" type="s" access="read" />
<signal name="AcceleratorActivated"> \ <property name="OverviewActive" type="b" access="readwrite" />
<arg name="action" type="u" /> \ <property name="ShellVersion" type="s" access="read" />
<arg name="deviceid" type="u" /> \ </interface>;
<arg name="timestamp" type="u" /> \
</signal> \
<property name="Mode" type="s" access="read" /> \
<property name="OverviewActive" type="b" access="readwrite" /> \
<property name="ShellVersion" type="s" access="read" /> \
</interface> \
</node>';
const ScreenSaverIface = '<node> \ const ScreenSaverIface = <interface name="org.gnome.ScreenSaver">
<interface name="org.gnome.ScreenSaver"> \ <method name="Lock">
<method name="Lock"> \ </method>
</method> \ <method name="GetActive">
<method name="GetActive"> \ <arg name="active" direction="out" type="b" />
<arg name="active" direction="out" type="b" /> \ </method>
</method> \ <method name="SetActive">
<method name="SetActive"> \ <arg name="value" direction="in" type="b" />
<arg name="value" direction="in" type="b" /> \ </method>
</method> \ <method name="GetActiveTime">
<method name="GetActiveTime"> \ <arg name="value" direction="out" type="u" />
<arg name="value" direction="out" type="u" /> \ </method>
</method> \ <signal name="ActiveChanged">
<signal name="ActiveChanged"> \ <arg name="new_value" type="b" />
<arg name="new_value" type="b" /> \ </signal>
</signal> \ </interface>;
</interface> \
</node>';
const GnomeShell = new Lang.Class({ const GnomeShell = new Lang.Class({
Name: 'GnomeShellDBus', Name: 'GnomeShellDBus',
@ -81,14 +72,15 @@ const GnomeShell = new Lang.Class({
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell'); this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell');
this._extensionsService = new GnomeShellExtensions(); this._extensionsService = new GnomeShellExtensions();
this._screencastService = new Screencast.ScreencastService();
this._screenshotService = new Screenshot.ScreenshotService(); this._screenshotService = new Screenshot.ScreenshotService();
this._grabbedAccelerators = new Hash.Map(); this._grabbedAccelerators = new Hash.Map();
this._grabbers = new Hash.Map(); this._grabbers = new Hash.Map();
global.display.connect('accelerator-activated', Lang.bind(this, global.display.connect('accelerator-activated', Lang.bind(this,
function(display, action, deviceid, timestamp) { function(display, action, deviceid) {
this._emitAcceleratorActivated(action, deviceid, timestamp); this._emitAcceleratorActivated(action, deviceid);
})); }));
}, },
@ -119,7 +111,7 @@ const GnomeShell = new Lang.Class({
returnValue = ''; returnValue = '';
success = true; success = true;
} catch (e) { } catch (e) {
returnValue = '' + e; returnValue = JSON.stringify(e);
success = false; success = false;
} }
return [success, returnValue]; return [success, returnValue];
@ -133,31 +125,17 @@ const GnomeShell = new Lang.Class({
for (let param in params) for (let param in params)
params[param] = params[param].deep_unpack(); params[param] = params[param].deep_unpack();
let monitorIndex = -1;
if (params['monitor'])
monitorIndex = params['monitor'];
let icon = null; let icon = null;
if (params['icon']) if (params['icon'])
icon = Gio.Icon.new_for_string(params['icon']); icon = Gio.Icon.new_for_string(params['icon']);
Main.osdWindow.setIcon(icon); Main.osdWindow.setIcon(icon);
Main.osdWindow.setMonitor (monitorIndex);
Main.osdWindow.setLabel(params['label']); Main.osdWindow.setLabel(params['label']);
Main.osdWindow.setLevel(params['level']); Main.osdWindow.setLevel(params['level']);
Main.osdWindow.show(); Main.osdWindow.show();
}, },
FocusApp: function(id) {
this.ShowApplications();
Main.overview.viewSelector.appDisplay.selectApp(id);
},
ShowApplications: function() {
Main.overview.viewSelector.showApps();
},
GrabAcceleratorAsync: function(params, invocation) { GrabAcceleratorAsync: function(params, invocation) {
let [accel, flags] = params; let [accel, flags] = params;
let sender = invocation.get_sender(); let sender = invocation.get_sender();
@ -188,7 +166,7 @@ const GnomeShell = new Lang.Class({
return invocation.return_value(GLib.Variant.new('(b)', [ungrabSucceeded])); return invocation.return_value(GLib.Variant.new('(b)', [ungrabSucceeded]));
}, },
_emitAcceleratorActivated: function(action, deviceid, timestamp) { _emitAcceleratorActivated: function(action, deviceid) {
let destination = this._grabbedAccelerators.get(action); let destination = this._grabbedAccelerators.get(action);
if (!destination) if (!destination)
return; return;
@ -199,7 +177,7 @@ const GnomeShell = new Lang.Class({
this._dbusImpl.get_object_path(), this._dbusImpl.get_object_path(),
info ? info.name : null, info ? info.name : null,
'AcceleratorActivated', 'AcceleratorActivated',
GLib.Variant.new('(uuu)', [action, deviceid, timestamp])); GLib.Variant.new('(uu)', [action, deviceid]));
}, },
_grabAcceleratorForSender: function(accelerator, flags, sender) { _grabAcceleratorForSender: function(accelerator, flags, sender) {
@ -255,43 +233,41 @@ const GnomeShell = new Lang.Class({
ShellVersion: Config.PACKAGE_VERSION ShellVersion: Config.PACKAGE_VERSION
}); });
const GnomeShellExtensionsIface = '<node> \ const GnomeShellExtensionsIface = <interface name="org.gnome.Shell.Extensions">
<interface name="org.gnome.Shell.Extensions"> \ <method name="ListExtensions">
<method name="ListExtensions"> \ <arg type="a{sa{sv}}" direction="out" name="extensions" />
<arg type="a{sa{sv}}" direction="out" name="extensions" /> \ </method>
</method> \ <method name="GetExtensionInfo">
<method name="GetExtensionInfo"> \ <arg type="s" direction="in" name="extension" />
<arg type="s" direction="in" name="extension" /> \ <arg type="a{sv}" direction="out" name="info" />
<arg type="a{sv}" direction="out" name="info" /> \ </method>
</method> \ <method name="GetExtensionErrors">
<method name="GetExtensionErrors"> \ <arg type="s" direction="in" name="extension" />
<arg type="s" direction="in" name="extension" /> \ <arg type="as" direction="out" name="errors" />
<arg type="as" direction="out" name="errors" /> \ </method>
</method> \ <signal name="ExtensionStatusChanged">
<signal name="ExtensionStatusChanged"> \ <arg type="s" name="uuid"/>
<arg type="s" name="uuid"/> \ <arg type="i" name="state"/>
<arg type="i" name="state"/> \ <arg type="s" name="error"/>
<arg type="s" name="error"/> \ </signal>
</signal> \ <method name="InstallRemoteExtension">
<method name="InstallRemoteExtension"> \ <arg type="s" direction="in" name="uuid"/>
<arg type="s" direction="in" name="uuid"/> \ <arg type="s" direction="out" name="result"/>
<arg type="s" direction="out" name="result"/> \ </method>
</method> \ <method name="UninstallExtension">
<method name="UninstallExtension"> \ <arg type="s" direction="in" name="uuid"/>
<arg type="s" direction="in" name="uuid"/> \ <arg type="b" direction="out" name="success"/>
<arg type="b" direction="out" name="success"/> \ </method>
</method> \ <method name="LaunchExtensionPrefs">
<method name="LaunchExtensionPrefs"> \ <arg type="s" direction="in" name="uuid"/>
<arg type="s" direction="in" name="uuid"/> \ </method>
</method> \ <method name="ReloadExtension">
<method name="ReloadExtension"> \ <arg type="s" direction="in" name="uuid"/>
<arg type="s" direction="in" name="uuid"/> \ </method>
</method> \ <method name="CheckForUpdates">
<method name="CheckForUpdates"> \ </method>
</method> \ <property name="ShellVersion" type="s" access="read" />
<property name="ShellVersion" type="s" access="read" /> \ </interface>;
</interface> \
</node>';
const GnomeShellExtensions = new Lang.Class({ const GnomeShellExtensions = new Lang.Class({
Name: 'GnomeShellExtensionsDBus', Name: 'GnomeShellExtensionsDBus',

View File

@ -132,14 +132,14 @@ function _setMenuAlignment(entry, stageX) {
function _onButtonPressEvent(actor, event, entry) { function _onButtonPressEvent(actor, event, entry) {
if (entry.menu.isOpen) { if (entry.menu.isOpen) {
entry.menu.close(BoxPointer.PopupAnimation.FULL); entry.menu.close(BoxPointer.PopupAnimation.FULL);
return Clutter.EVENT_STOP; return true;
} else if (event.get_button() == 3) { } else if (event.get_button() == 3) {
let [stageX, stageY] = event.get_coords(); let [stageX, stageY] = event.get_coords();
_setMenuAlignment(entry, stageX); _setMenuAlignment(entry, stageX);
entry.menu.open(BoxPointer.PopupAnimation.FULL); entry.menu.open(BoxPointer.PopupAnimation.FULL);
return Clutter.EVENT_STOP; return true;
} }
return Clutter.EVENT_PROPAGATE; return false;
}; };
function _onPopup(actor, entry) { function _onPopup(actor, entry) {

View File

@ -521,38 +521,36 @@ const ShellProcessesDialog = new Lang.Class({
}); });
Signals.addSignalMethods(ShellProcessesDialog.prototype); Signals.addSignalMethods(ShellProcessesDialog.prototype);
const GnomeShellMountOpIface = '<node> \ const GnomeShellMountOpIface = <interface name="org.Gtk.MountOperationHandler">
<interface name="org.Gtk.MountOperationHandler"> \ <method name="AskPassword">
<method name="AskPassword"> \ <arg type="s" direction="in" name="object_id"/>
<arg type="s" direction="in" name="object_id"/> \ <arg type="s" direction="in" name="message"/>
<arg type="s" direction="in" name="message"/> \ <arg type="s" direction="in" name="icon_name"/>
<arg type="s" direction="in" name="icon_name"/> \ <arg type="s" direction="in" name="default_user"/>
<arg type="s" direction="in" name="default_user"/> \ <arg type="s" direction="in" name="default_domain"/>
<arg type="s" direction="in" name="default_domain"/> \ <arg type="u" direction="in" name="flags"/>
<arg type="u" direction="in" name="flags"/> \ <arg type="u" direction="out" name="response"/>
<arg type="u" direction="out" name="response"/> \ <arg type="a{sv}" direction="out" name="response_details"/>
<arg type="a{sv}" direction="out" name="response_details"/> \ </method>
</method> \ <method name="AskQuestion">
<method name="AskQuestion"> \ <arg type="s" direction="in" name="object_id"/>
<arg type="s" direction="in" name="object_id"/> \ <arg type="s" direction="in" name="message"/>
<arg type="s" direction="in" name="message"/> \ <arg type="s" direction="in" name="icon_name"/>
<arg type="s" direction="in" name="icon_name"/> \ <arg type="as" direction="in" name="choices"/>
<arg type="as" direction="in" name="choices"/> \ <arg type="u" direction="out" name="response"/>
<arg type="u" direction="out" name="response"/> \ <arg type="a{sv}" direction="out" name="response_details"/>
<arg type="a{sv}" direction="out" name="response_details"/> \ </method>
</method> \ <method name="ShowProcesses">
<method name="ShowProcesses"> \ <arg type="s" direction="in" name="object_id"/>
<arg type="s" direction="in" name="object_id"/> \ <arg type="s" direction="in" name="message"/>
<arg type="s" direction="in" name="message"/> \ <arg type="s" direction="in" name="icon_name"/>
<arg type="s" direction="in" name="icon_name"/> \ <arg type="ai" direction="in" name="application_pids"/>
<arg type="ai" direction="in" name="application_pids"/> \ <arg type="as" direction="in" name="choices"/>
<arg type="as" direction="in" name="choices"/> \ <arg type="u" direction="out" name="response"/>
<arg type="u" direction="out" name="response"/> \ <arg type="a{sv}" direction="out" name="response_details"/>
<arg type="a{sv}" direction="out" name="response_details"/> \ </method>
</method> \ <method name="Close"/>
<method name="Close"/> \ </interface>;
</interface> \
</node>';
const ShellMountOperationType = { const ShellMountOperationType = {
NONE: 0, NONE: 0,

View File

@ -5,7 +5,6 @@ const Clutter = imports.gi.Clutter;
const Lang = imports.lang; const Lang = imports.lang;
const St = imports.gi.St; const St = imports.gi.St;
const Signals = imports.signals; const Signals = imports.signals;
const Atk = imports.gi.Atk;
const SLIDER_SCROLL_STEP = 0.05; /* Slider scrolling step in % */ const SLIDER_SCROLL_STEP = 0.05; /* Slider scrolling step in % */
@ -20,26 +19,14 @@ const Slider = new Lang.Class({
this.actor = new St.DrawingArea({ style_class: 'slider', this.actor = new St.DrawingArea({ style_class: 'slider',
can_focus: true, can_focus: true,
reactive: true, reactive: true });
accessible_role: Atk.Role.SLIDER });
this.actor.connect('repaint', Lang.bind(this, this._sliderRepaint)); this.actor.connect('repaint', Lang.bind(this, this._sliderRepaint));
this.actor.connect('button-press-event', Lang.bind(this, this._startDragging)); this.actor.connect('button-press-event', Lang.bind(this, this._startDragging));
this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent)); this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
this.actor.connect('key-press-event', Lang.bind(this, this.onKeyPressEvent)); this.actor.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
this._releaseId = this._motionId = 0; this._releaseId = this._motionId = 0;
this._dragging = false; this._dragging = false;
this._customAccessible = St.GenericAccessible.new_for_actor(this.actor);
this.actor.set_accessible(this._customAccessible);
this._customAccessible.connect('get-current-value', Lang.bind(this, this._getCurrentValue));
this._customAccessible.connect('get-minimum-value', Lang.bind(this, this._getMinimumValue));
this._customAccessible.connect('get-maximum-value', Lang.bind(this, this._getMaximumValue));
this._customAccessible.connect('get-minimum-increment', Lang.bind(this, this._getMinimumIncrement));
this._customAccessible.connect('set-current-value', Lang.bind(this, this._setCurrentValue));
this.connect('value-changed', Lang.bind(this, this._valueChanged));
}, },
setValue: function(value) { setValue: function(value) {
@ -111,25 +98,22 @@ const Slider = new Lang.Class({
}, },
_startDragging: function(actor, event) { _startDragging: function(actor, event) {
return this.startDragging(event); if (this._dragging) // don't allow two drags at the same time
}, return false;
startDragging: function(event) {
if (this._dragging)
return Clutter.EVENT_PROPAGATE;
this._dragging = true; this._dragging = true;
let device = event.get_device(); // FIXME: we should only grab the specific device that originated
device.grab(this.actor); // the event, but for some weird reason events are still delivered
this._grabbedDevice = device; // outside the slider if using clutter_grab_pointer_for_device
Clutter.grab_pointer(this.actor);
this._releaseId = this.actor.connect('button-release-event', Lang.bind(this, this._endDragging)); this._releaseId = this.actor.connect('button-release-event', Lang.bind(this, this._endDragging));
this._motionId = this.actor.connect('motion-event', Lang.bind(this, this._motionEvent)); this._motionId = this.actor.connect('motion-event', Lang.bind(this, this._motionEvent));
let absX, absY; let absX, absY;
[absX, absY] = event.get_coords(); [absX, absY] = event.get_coords();
this._moveHandle(absX, absY); this._moveHandle(absX, absY);
return Clutter.EVENT_STOP;
return true;
}, },
_endDragging: function() { _endDragging: function() {
@ -137,13 +121,12 @@ const Slider = new Lang.Class({
this.actor.disconnect(this._releaseId); this.actor.disconnect(this._releaseId);
this.actor.disconnect(this._motionId); this.actor.disconnect(this._motionId);
this._grabbedDevice.ungrab(); Clutter.ungrab_pointer();
this._grabbedDevice = null;
this._dragging = false; this._dragging = false;
this.emit('drag-end'); this.emit('drag-end');
} }
return Clutter.EVENT_STOP; return true;
}, },
scroll: function(event) { scroll: function(event) {
@ -151,7 +134,7 @@ const Slider = new Lang.Class({
let delta; let delta;
if (event.is_pointer_emulated()) if (event.is_pointer_emulated())
return Clutter.EVENT_PROPAGATE; return;
if (direction == Clutter.ScrollDirection.DOWN) { if (direction == Clutter.ScrollDirection.DOWN) {
delta = -SLIDER_SCROLL_STEP; delta = -SLIDER_SCROLL_STEP;
@ -168,31 +151,30 @@ const Slider = new Lang.Class({
this.actor.queue_repaint(); this.actor.queue_repaint();
this.emit('value-changed', this._value); this.emit('value-changed', this._value);
return Clutter.EVENT_STOP;
}, },
_onScrollEvent: function(actor, event) { _onScrollEvent: function(actor, event) {
return this.scroll(event); this.scroll(event);
}, },
_motionEvent: function(actor, event) { _motionEvent: function(actor, event) {
let absX, absY; let absX, absY;
[absX, absY] = event.get_coords(); [absX, absY] = event.get_coords();
this._moveHandle(absX, absY); this._moveHandle(absX, absY);
return Clutter.EVENT_STOP; return true;
}, },
onKeyPressEvent: function (actor, event) { _onKeyPressEvent: function (actor, event) {
let key = event.get_key_symbol(); let key = event.get_key_symbol();
if (key == Clutter.KEY_Right || key == Clutter.KEY_Left) { if (key == Clutter.KEY_Right || key == Clutter.KEY_Left) {
let delta = key == Clutter.KEY_Right ? 0.1 : -0.1; let delta = key == Clutter.KEY_Right ? 0.1 : -0.1;
this._value = Math.max(0, Math.min(this._value + delta, 1)); this._value = Math.max(0, Math.min(this._value + delta, 1));
this.actor.queue_repaint(); this._slider.queue_repaint();
this.emit('value-changed', this._value); this.emit('value-changed', this._value);
this.emit('drag-end'); this.emit('drag-end');
return Clutter.EVENT_STOP; return true;
} }
return Clutter.EVENT_PROPAGATE; return false;
}, },
_moveHandle: function(absX, absY) { _moveHandle: function(absX, absY) {
@ -216,30 +198,6 @@ const Slider = new Lang.Class({
this.emit('value-changed', this._value); this.emit('value-changed', this._value);
}, },
_getCurrentValue: function (actor) {
return this._value;
},
_getMinimumValue: function (actor) {
return 0;
},
_getMaximumValue: function (actor) {
return 1;
},
_getMinimumIncrement: function (actor) {
return 0.1;
},
_setCurrentValue: function (actor, value) {
this._value = value;
},
_valueChanged: function (slider, value, property) {
this._customAccessible.notify ("accessible-value");
},
get value() { get value() {
return this._value; return this._value;
} }

View File

@ -1,19 +1,13 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio; const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang; const Lang = imports.lang;
const Mainloop = imports.mainloop; const Mainloop = imports.mainloop;
const St = imports.gi.St;
const PanelMenu = imports.ui.panelMenu; const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu; const PopupMenu = imports.ui.popupMenu;
const A11Y_SCHEMA = 'org.gnome.desktop.a11y'; const A11Y_SCHEMA = 'org.gnome.desktop.a11y.keyboard';
const KEY_ALWAYS_SHOW = 'always-show-universal-access-status';
const A11Y_KEYBOARD_SCHEMA = 'org.gnome.desktop.a11y.keyboard';
const KEY_STICKY_KEYS_ENABLED = 'stickykeys-enable'; const KEY_STICKY_KEYS_ENABLED = 'stickykeys-enable';
const KEY_BOUNCE_KEYS_ENABLED = 'bouncekeys-enable'; const KEY_BOUNCE_KEYS_ENABLED = 'bouncekeys-enable';
const KEY_SLOW_KEYS_ENABLED = 'slowkeys-enable'; const KEY_SLOW_KEYS_ENABLED = 'slowkeys-enable';
@ -36,20 +30,10 @@ const HIGH_CONTRAST_THEME = 'HighContrast';
const ATIndicator = new Lang.Class({ const ATIndicator = new Lang.Class({
Name: 'ATIndicator', Name: 'ATIndicator',
Extends: PanelMenu.Button, Extends: PanelMenu.SystemStatusButton,
_init: function() { _init: function() {
this.parent(0.0, _("Accessibility")); this.parent('preferences-desktop-accessibility-symbolic', _("Accessibility"));
this._hbox = new St.BoxLayout({ style_class: 'panel-status-menu-box' });
this._hbox.add_child(new St.Icon({ style_class: 'system-status-icon',
icon_name: 'preferences-desktop-accessibility-symbolic' }));
this._hbox.add_child(PopupMenu.unicodeArrow(St.Side.BOTTOM));
this.actor.add_child(this._hbox);
this._a11ySettings = new Gio.Settings({ schema: A11Y_SCHEMA });
this._a11ySettings.connect('changed::' + KEY_ALWAYS_SHOW, Lang.bind(this, this._queueSyncMenuVisibility));
let highContrast = this._buildHCItem(); let highContrast = this._buildHCItem();
this.menu.addMenuItem(highContrast); this.menu.addMenuItem(highContrast);
@ -72,30 +56,32 @@ const ATIndicator = new Lang.Class({
let visualBell = this._buildItem(_("Visual Alerts"), WM_SCHEMA, KEY_VISUAL_BELL); let visualBell = this._buildItem(_("Visual Alerts"), WM_SCHEMA, KEY_VISUAL_BELL);
this.menu.addMenuItem(visualBell); this.menu.addMenuItem(visualBell);
let stickyKeys = this._buildItem(_("Sticky Keys"), A11Y_KEYBOARD_SCHEMA, KEY_STICKY_KEYS_ENABLED); let stickyKeys = this._buildItem(_("Sticky Keys"), A11Y_SCHEMA, KEY_STICKY_KEYS_ENABLED);
this.menu.addMenuItem(stickyKeys); this.menu.addMenuItem(stickyKeys);
let slowKeys = this._buildItem(_("Slow Keys"), A11Y_KEYBOARD_SCHEMA, KEY_SLOW_KEYS_ENABLED); let slowKeys = this._buildItem(_("Slow Keys"), A11Y_SCHEMA, KEY_SLOW_KEYS_ENABLED);
this.menu.addMenuItem(slowKeys); this.menu.addMenuItem(slowKeys);
let bounceKeys = this._buildItem(_("Bounce Keys"), A11Y_KEYBOARD_SCHEMA, KEY_BOUNCE_KEYS_ENABLED); let bounceKeys = this._buildItem(_("Bounce Keys"), A11Y_SCHEMA, KEY_BOUNCE_KEYS_ENABLED);
this.menu.addMenuItem(bounceKeys); this.menu.addMenuItem(bounceKeys);
let mouseKeys = this._buildItem(_("Mouse Keys"), A11Y_KEYBOARD_SCHEMA, KEY_MOUSE_KEYS_ENABLED); let mouseKeys = this._buildItem(_("Mouse Keys"), A11Y_SCHEMA, KEY_MOUSE_KEYS_ENABLED);
this.menu.addMenuItem(mouseKeys); this.menu.addMenuItem(mouseKeys);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addSettingsAction(_("Universal Access Settings"), 'gnome-universal-access-panel.desktop');
this._syncMenuVisibility(); this._syncMenuVisibility();
}, },
_syncMenuVisibility: function() { _syncMenuVisibility: function() {
this._syncMenuVisibilityIdle = 0; this._syncMenuVisibilityIdle = 0;
let alwaysShow = this._a11ySettings.get_boolean(KEY_ALWAYS_SHOW);
let items = this.menu._getMenuItems(); let items = this.menu._getMenuItems();
this.actor.visible = alwaysShow || items.some(function(f) { return !!f.state; }); this.actor.visible = items.some(function(f) { return !!f.state; });
return GLib.SOURCE_REMOVE; return false;
}, },
_queueSyncMenuVisibility: function() { _queueSyncMenuVisibility: function() {

View File

@ -2,99 +2,485 @@
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib; const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio; const GnomeBluetoothApplet = imports.gi.GnomeBluetoothApplet;
const GnomeBluetooth = imports.gi.GnomeBluetooth; const GnomeBluetooth = imports.gi.GnomeBluetooth;
const Lang = imports.lang; const Lang = imports.lang;
const St = imports.gi.St; const St = imports.gi.St;
const Main = imports.ui.main; const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const NotificationDaemon = imports.ui.notificationDaemon;
const PanelMenu = imports.ui.panelMenu; const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu; const PopupMenu = imports.ui.popupMenu;
const BUS_NAME = 'org.gnome.SettingsDaemon.Rfkill'; const ConnectionState = {
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Rfkill'; DISCONNECTED: 0,
CONNECTED: 1,
const RfkillManagerInterface = '<node> \ DISCONNECTING: 2,
<interface name="org.gnome.SettingsDaemon.Rfkill"> \ CONNECTING: 3
<property name="BluetoothAirplaneMode" type="b" access="readwrite" /> \ }
</interface> \
</node>';
const RfkillManagerProxy = Gio.DBusProxy.makeProxyWrapper(RfkillManagerInterface);
const Indicator = new Lang.Class({ const Indicator = new Lang.Class({
Name: 'BTIndicator', Name: 'BTIndicator',
Extends: PanelMenu.SystemIndicator, Extends: PanelMenu.SystemStatusButton,
_init: function() { _init: function() {
this.parent(); this.parent('bluetooth-disabled-symbolic', _("Bluetooth"));
this._indicator = this._addIndicator(); this._applet = new GnomeBluetoothApplet.Applet();
this._indicator.icon_name = 'bluetooth-active-symbolic';
this._proxy = new RfkillManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH, this._killswitch = new PopupMenu.PopupSwitchMenuItem(_("Bluetooth"), false);
Lang.bind(this, function(proxy, error) { this._applet.connect('notify::killswitch-state', Lang.bind(this, this._updateKillswitch));
if (error) { this._killswitch.connect('toggled', Lang.bind(this, function() {
log(error.message); let current_state = this._applet.killswitch_state;
if (current_state != GnomeBluetooth.KillswitchState.HARD_BLOCKED &&
current_state != GnomeBluetooth.KillswitchState.NO_ADAPTER) {
this._applet.killswitch_state = this._killswitch.state ?
GnomeBluetooth.KillswitchState.UNBLOCKED:
GnomeBluetooth.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._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.addSettingsAction(_("Bluetooth Settings"), 'gnome-bluetooth-panel.desktop');
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('auth-service-request', Lang.bind(this, this._authServiceRequest));
this._applet.connect('cancel-request', Lang.bind(this, this._cancelRequest));
},
_updateKillswitch: function() {
let current_state = this._applet.killswitch_state;
let on = current_state == GnomeBluetooth.KillswitchState.UNBLOCKED;
let has_adapter = current_state != GnomeBluetooth.KillswitchState.NO_ADAPTER;
let can_toggle = current_state != GnomeBluetooth.KillswitchState.NO_ADAPTER &&
current_state != GnomeBluetooth.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"));
this.actor.visible = has_adapter;
if (on) {
this._discoverable.actor.show();
this.setIcon('bluetooth-active-symbolic');
} else {
this._discoverable.actor.hide();
this.setIcon('bluetooth-disabled-symbolic');
}
},
_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;
}
}
},
_updateDeviceItem: function(item, device) {
if (!device.can_connect && device.capabilities == GnomeBluetoothApplet.Capabilities.NONE) {
item.destroy();
return; return;
} }
}));
// The Bluetooth menu only appears when Bluetooth is in use, let prevDevice = item._device;
// so just statically build it with a "Turn Off" menu item. let prevCapabilities = prevDevice.capabilities;
this._item = new PopupMenu.PopupSubMenuMenuItem(_("Bluetooth"), true); let prevCanConnect = prevDevice.can_connect;
this._item.icon.icon_name = 'bluetooth-active-symbolic';
this._item.menu.addAction(_("Turn Off"), Lang.bind(this, function() {
this._proxy.BluetoothAirplaneMode = true;
}));
this._item.menu.addSettingsAction(_("Bluetooth Settings"), 'gnome-bluetooth-panel.desktop');
this.menu.addMenuItem(this._item);
this._client = new GnomeBluetooth.Client(); // adopt the new device object
this._model = this._client.get_model(); item._device = device;
this._model.connect('row-changed', Lang.bind(this, this._sync)); device._item = item;
this._model.connect('row-deleted', Lang.bind(this, this._sync));
this._model.connect('row-inserted', Lang.bind(this, this._sync)); // update properties
this._sync(); 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);
}, },
_getDefaultAdapter: function() { _createDeviceItem: function(device) {
let [ret, iter] = this._model.get_iter_first(); if (!device.can_connect && device.capabilities == GnomeBluetoothApplet.Capabilities.NONE)
while (ret) {
let isDefault = this._model.get_value(iter,
GnomeBluetooth.Column.DEFAULT);
if (isDefault)
return iter;
ret = this._model.iter_next(iter);
}
return null; 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;
}, },
_getNConnectedDevices: function() { _buildDeviceSubMenu: function(item, device) {
let adapter = this._getDefaultAdapter(); if (device.can_connect) {
if (!adapter) let menuitem = new PopupMenu.PopupSwitchMenuItem(_("Connection"), device.connected);
return 0; item._connected = device.connected;
item._connectedMenuItem = menuitem;
let nDevices = 0; menuitem.connect('toggled', Lang.bind(this, function() {
let [ret, iter] = this._model.iter_children(adapter); if (item._connected > ConnectionState.CONNECTED) {
while (ret) { // operation already in progress, revert
let isConnected = this._model.get_value(iter, // (should not happen anyway)
GnomeBluetooth.Column.CONNECTED); menuitem.setToggleState(menuitem.state);
if (isConnected) }
nDevices++; if (item._connected) {
ret = this._model.iter_next(iter); 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(menuitem);
}
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);
}));
}
switch (device.type) {
case GnomeBluetoothApplet.Type.KEYBOARD:
item.menu.addSettingsAction(_("Keyboard Settings"), 'gnome-keyboard-panel.desktop');
break;
case GnomeBluetoothApplet.Type.MOUSE:
item.menu.addSettingsAction(_("Mouse Settings"), 'gnome-mouse-panel.desktop');
break;
case GnomeBluetoothApplet.Type.HEADSET:
case GnomeBluetoothApplet.Type.HEADPHONES:
case GnomeBluetoothApplet.Type.OTHER_AUDIO:
item.menu.addSettingsAction(_("Sound Settings"), 'gnome-sound-panel.desktop');
break;
default:
break;
}
},
_updateFullMenu: function() {
if (this._applet.show_full_menu) {
this._showAll(this._fullMenuItems);
if (this._hasDevices)
this._showAll(this._deviceItems);
} 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 MessageTray.Source(_("Bluetooth"), 'bluetooth-active');
this._source.policy = new NotificationDaemon.NotificationApplicationPolicy('gnome-bluetooth-panel');
Main.messageTray.add(this._source);
}
},
_authRequest: function(applet, device_path, name, long_name) {
this._ensureSource();
this._source.notify(new AuthNotification(this._source, this._applet, device_path, name, long_name));
},
_authServiceRequest: function(applet, device_path, name, long_name, uuid) {
this._ensureSource();
this._source.notify(new AuthServiceNotification(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();
}
});
const AuthNotification = new Lang.Class({
Name: 'AuthNotification',
Extends: MessageTray.Notification,
_init: function(source, applet, device_path, name, long_name) {
this.parent(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 to pair with this computer").format(long_name));
this.addButton('allow', _("Allow"));
this.addButton('deny', _("Deny"));
this.connect('action-invoked', Lang.bind(this, function(self, action) {
if (action == 'allow')
this._applet.agent_reply_confirm(this._devicePath, true);
else
this._applet.agent_reply_confirm(this._devicePath, false);
this.destroy();
}));
}
});
const AuthServiceNotification = new Lang.Class({
Name: 'AuthServiceNotification',
Extends: MessageTray.Notification,
_init: function(source, applet, device_path, name, long_name, uuid) {
this.parent(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_service(this._devicePath, true, true);
break;
case 'grant':
this._applet.agent_reply_auth_service(this._devicePath, true, false);
break;
case 'reject':
default:
this._applet.agent_reply_auth_service(this._devicePath, false, false);
}
this.destroy();
}));
}
});
const ConfirmNotification = new Lang.Class({
Name: 'ConfirmNotification',
Extends: MessageTray.Notification,
_init: function(source, applet, device_path, name, long_name, pin) {
this.parent(source,
_("Bluetooth"),
/* Translators: argument is the device short name */
_("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 Passkey '%06d' matches the one on the device.").format(pin));
/* Translators: this is the verb, not the noun */
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();
}));
}
});
const PinNotification = new Lang.Class({
Name: 'PinNotification',
Extends: MessageTray.Notification,
_init: function(source, applet, device_path, name, long_name, numeric) {
this.parent(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) {
if (this._canActivateOkButton())
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.setButtonSensitive('ok', this._canActivateOkButton());
this._entry.clutter_text.connect('text-changed', Lang.bind(this,
function() {
this.setButtonSensitive('ok', this._canActivateOkButton());
}));
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();
}));
},
_canActivateOkButton: function() {
// PINs have a fixed length of 6
if (this._numeric)
return this._entry.clutter_text.text.length == 6;
else
return true;
} }
return nDevices;
},
_sync: function() {
let nDevices = this._getNConnectedDevices();
let on = nDevices > 0;
this._indicator.visible = on;
this._item.actor.visible = on;
if (on)
this._item.status.text = ngettext("%d Connected Device", "%d Connected Devices", nDevices).format(nDevices);
},
}); });

View File

@ -1,70 +0,0 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Lang = imports.lang;
const Gio = imports.gi.Gio;
const St = imports.gi.St;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const Slider = imports.ui.slider;
const BUS_NAME = 'org.gnome.SettingsDaemon.Power';
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Power';
const BrightnessInterface = '<node> \
<interface name="org.gnome.SettingsDaemon.Power.Screen"> \
<property name="Brightness" type="i" access="readwrite"/> \
</interface> \
</node>';
const BrightnessProxy = Gio.DBusProxy.makeProxyWrapper(BrightnessInterface);
const Indicator = new Lang.Class({
Name: 'BrightnessIndicator',
Extends: PanelMenu.SystemIndicator,
_init: function() {
this.parent('display-brightness-symbolic');
this._proxy = new BrightnessProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH,
Lang.bind(this, function(proxy, error) {
if (error) {
log(error.message);
return;
}
this._proxy.connect('g-properties-changed', Lang.bind(this, this._sync));
this._sync();
}));
this._item = new PopupMenu.PopupBaseMenuItem({ activate: false });
this.menu.addMenuItem(this._item);
this._slider = new Slider.Slider(0);
this._slider.connect('value-changed', Lang.bind(this, this._sliderChanged));
this._slider.actor.accessible_name = _("Brightness");
let icon = new St.Icon({ icon_name: 'display-brightness-symbolic',
style_class: 'popup-menu-icon' });
this._item.actor.add(icon);
this._item.actor.add(this._slider.actor, { expand: true });
this._item.actor.connect('button-press-event', Lang.bind(this, function(actor, event) {
return this._slider.startDragging(event);
}));
this._item.actor.connect('key-press-event', Lang.bind(this, function(actor, event) {
return this._slider.onKeyPressEvent(actor, event);
}));
},
_sliderChanged: function(slider, value) {
let percent = value * 100;
this._proxy.Brightness = percent;
},
_sync: function() {
let visible = this._proxy.Brightness >= 0;
this._item.actor.visible = visible;
if (visible)
this._slider.setValue(this._proxy.Brightness / 100.0);
},
});

View File

@ -9,7 +9,6 @@ const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const Signals = imports.signals; const Signals = imports.signals;
const St = imports.gi.St; const St = imports.gi.St;
const Gettext = imports.gettext;
try { try {
var IBus = imports.gi.IBus; var IBus = imports.gi.IBus;
@ -41,13 +40,12 @@ const MAX_INPUT_SOURCE_ACTIVATION_TIME = 4000; // ms
const BUS_NAME = 'org.gnome.SettingsDaemon.Keyboard'; const BUS_NAME = 'org.gnome.SettingsDaemon.Keyboard';
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Keyboard'; const OBJECT_PATH = '/org/gnome/SettingsDaemon/Keyboard';
const KeyboardManagerInterface = '<node> \ const KeyboardManagerInterface =
<interface name="org.gnome.SettingsDaemon.Keyboard"> \ <interface name="org.gnome.SettingsDaemon.Keyboard">
<method name="SetInputSource"> \ <method name="SetInputSource">
<arg type="u" direction="in" /> \ <arg type="u" direction="in" />
</method> \ </method>
</interface> \ </interface>;
</node>';
const KeyboardManagerProxy = Gio.DBusProxy.makeProxyWrapper(KeyboardManagerInterface); const KeyboardManagerProxy = Gio.DBusProxy.makeProxyWrapper(KeyboardManagerInterface);
@ -204,8 +202,8 @@ const LayoutMenuItem = new Lang.Class({
this.label = new St.Label({ text: displayName }); this.label = new St.Label({ text: displayName });
this.indicator = new St.Label({ text: shortName }); this.indicator = new St.Label({ text: shortName });
this.actor.add(this.label, { expand: true }); this.addActor(this.label);
this.actor.add(this.indicator); this.addActor(this.indicator);
this.actor.label_actor = this.label; this.actor.label_actor = this.label;
} }
}); });
@ -338,12 +336,7 @@ const InputSourceIndicator = new Lang.Class({
this._container.connect('get-preferred-width', Lang.bind(this, this._containerGetPreferredWidth)); this._container.connect('get-preferred-width', Lang.bind(this, this._containerGetPreferredWidth));
this._container.connect('get-preferred-height', Lang.bind(this, this._containerGetPreferredHeight)); this._container.connect('get-preferred-height', Lang.bind(this, this._containerGetPreferredHeight));
this._container.connect('allocate', Lang.bind(this, this._containerAllocate)); this._container.connect('allocate', Lang.bind(this, this._containerAllocate));
this.actor.add_actor(this._container);
this._hbox = new St.BoxLayout({ style_class: 'panel-status-menu-box' });
this._hbox.add_child(this._container);
this._hbox.add_child(PopupMenu.unicodeArrow(St.Side.BOTTOM));
this.actor.add_child(this._hbox);
this.actor.add_style_class_name('panel-status-button'); this.actor.add_style_class_name('panel-status-button');
// All valid input sources currently in the gsettings // All valid input sources currently in the gsettings
@ -405,6 +398,8 @@ const InputSourceIndicator = new Lang.Class({
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated)); Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
this._sessionUpdated(); this._sessionUpdated();
this.menu.addSettingsAction(_("Region & Language Settings"), 'gnome-region-panel.desktop');
this._sourcesPerWindow = false; this._sourcesPerWindow = false;
this._focusWindowNotifyId = 0; this._focusWindowNotifyId = 0;
this._overviewShowingId = 0; this._overviewShowingId = 0;
@ -542,12 +537,8 @@ const InputSourceIndicator = new Lang.Class({
let engineDesc = this._ibusManager.getEngineDesc(id); let engineDesc = this._ibusManager.getEngineDesc(id);
if (engineDesc) { if (engineDesc) {
let language = IBus.get_language_name(engineDesc.get_language()); let language = IBus.get_language_name(engineDesc.get_language());
let longName = engineDesc.get_longname();
let textdomain = engineDesc.get_textdomain();
if (textdomain != '')
longName = Gettext.dgettext(textdomain, longName);
exists = true; exists = true;
displayName = '%s (%s)'.format(language, longName); displayName = language + ' (' + engineDesc.get_longname() + ')';
shortName = this._makeEngineShortName(engineDesc); shortName = this._makeEngineShortName(engineDesc);
} }
} }
@ -907,7 +898,7 @@ const InputSourceIndicator = new Lang.Class({
for (let i in this._inputSources) { for (let i in this._inputSources) {
let is = this._inputSources[i]; let is = this._inputSources[i];
is.indicatorLabel.allocate_align_fill(box, 0.5, 0.5, false, false, flags); is.indicatorLabel.allocate_align_fill(box, 0.5, 0, false, false, flags);
} }
} }
}); });

Some files were not shown because too many files have changed in this diff Show More