Compare commits
2 Commits
wip/is-swi
...
3.7.3.1-br
Author | SHA1 | Date | |
---|---|---|---|
7d598b7725 | |||
9f890982b2 |
254
NEWS
254
NEWS
@ -1,256 +1,6 @@
|
||||
3.8.0.1
|
||||
3.7.3.1
|
||||
=======
|
||||
* Background bug fixes [Ray; #696712]
|
||||
|
||||
3.8.0
|
||||
=====
|
||||
* Remove blur and desaturation from lock screen [Jasper; #696322]
|
||||
* Remove scroll view fade near edges [Adel; #696404]
|
||||
* dateMenu: Open calendar component when using Evolution [Florian; #696432]
|
||||
* Fix unlocking on fast user switch [Cosimo; #696287]
|
||||
* Tweak screen shield animation [Rui; #696380]
|
||||
* Fix major memory leak when changing backgrounds [Ray; #696157]
|
||||
* Miscellaneous bug fixes [Jasper, Adel, Florian; #696199, #696212, #696422,
|
||||
#696447, #696235]
|
||||
|
||||
Contributors:
|
||||
Giovanni Campagna, Cosimo Cecchi, Adel Gadllah, Rui Matos, Florian Müllner,
|
||||
Jasper St. Pierre, Ray Strode
|
||||
|
||||
Translations:
|
||||
Alexandre Franke [fr], Victor Ibragimov [tg], Arash Mousavi [fa],
|
||||
Gabor Kelemen [hu], Sandeep Sheshrao Shedmake [mr], ManojKumar Giri [or],
|
||||
Shantha kumar [ta], Rajesh Ranjan [hi], Stas Solovey [ru],
|
||||
Shankar Prasad [kn], Dušan Kazik [sk], Ihar Hrachyshka [be],
|
||||
Wouter Bolsterlee [nl], Kris Thomsen [da], Jiro Matsuzawa [ja],
|
||||
Daniel Korostil [uk], Ani Peter [ml], Krishnababu Krothapalli [te],
|
||||
Mantas Kriaučiūnas [lt], Praveen Illa [te]
|
||||
|
||||
3.7.92
|
||||
======
|
||||
* Drop fallback lock implementation [Florian; #693403]
|
||||
* Don't let the user trigger message-tray when in fullscreen [Jasper; #694997]
|
||||
* Scroll search results when using keynav [Jasper; #689681]
|
||||
* Allow raising the shield by starting to type the password [Jasper; #686740]
|
||||
* Improve tracking of fullscreen windows [Owen; #649748]
|
||||
* Improve animation of new windows in overview [Giovanni; #695582]
|
||||
* workspace switcher: Animate new workspaces created by DND [Giovanni; #685285]
|
||||
* Give user time to read messages on login screen [Ray; #694688]
|
||||
* Misc bug fixes and cleanups [Jasper, Ray, Florian, Cosimo, Giovanni, Adel,
|
||||
Stef, Takao, Rui, Neil; #695154, #694993, #695272, #691578, #694321, #695338,
|
||||
#695409, #695458, #695526, #695601, #695471, #695324, #695650, #695656,
|
||||
#695659, #695485, #695395, #694951, #695824, #695841, #695801, #694321,
|
||||
#693708, #695800, #695607, #695882, #691578, #685851, #694371, #690857,
|
||||
#694092, #695747, #696007, #693438, #696064, #696102
|
||||
|
||||
Contributors:
|
||||
Giovanni Campagna, Cosimo Cecchi, Allan Day, Takao Fujiwara, Adel Gadllah,
|
||||
Tim Lunn, Rui Matos, Florian Müllner, Neil Roberts, Jasper St. Pierre,
|
||||
Ray Strode, Stef Walter, Colin Walters, Owen W. Taylor
|
||||
|
||||
Translations:
|
||||
Nilamdyuti Goswami [as], Chao-Hsiung Liao [zh_HK, zh_TW],
|
||||
Yuri Myasoedov [ru], Gheyret Kenji [ug], Baurzhan Muftakhidinov [kk],
|
||||
Ville-Pekka Vainio [fi], Matej Urbančič [sl],
|
||||
Мирослав Николић [sr, sr@latin], Rūdolfs Mazurs [lv], Christian Kirbach [de],
|
||||
Andika Triwidada [id], Gil Forcada [ca], Mattias Põldaru [et],
|
||||
Duarte Loreto [pt], Adam Matoušek [cs], Changwoo Ryu [ko],
|
||||
Ihar Hrachyshka [be], Carles Ferrando [ca@valencia], Sweta Kothari [gu]
|
||||
|
||||
3.7.91
|
||||
======
|
||||
* overview: Fade out controls during DND that are not targets [Cosimo; #686984]
|
||||
* overview: Keep open when a Control key is held [Florian; #686984]
|
||||
* Improve login screen => session transition [Ray; #694321]
|
||||
* Center application grid horizontally [Florian; #694261]
|
||||
* Fix hiding panel when fullscreen windows span multiple monitors [Adel; 646861]
|
||||
* Tweak thresholds of pressure barrier [Jasper; #694467]
|
||||
* Tweak window picker layout [Jasper; #694902]
|
||||
* Expose key grab DBus API to gnome-settings-daemon [Florian; #643111]
|
||||
* Don't always show message tray in overview, add message indicator
|
||||
[Cosimo; #687787]
|
||||
* Tweak startup animation [Ray; #694326]
|
||||
* Add OSD popups and expose them to gnome-settings-daemon [Florian; #613543]
|
||||
* Move loupe icon to the start of the search entry [Jasper; #695069]
|
||||
* Don't show the input switcher with less than 2 items [Rui; 695000]
|
||||
* Fix auto-completion of system modals immediately upon display [Stef; #692937]
|
||||
* Ignore workspaces in alt-tab [Florian; #661156]
|
||||
* Disable copying text from password entries [Florian; #695104]
|
||||
* Use standard styling for ibus candidate popups [Allan; #694796]
|
||||
* Fix calendar changing height on month changes [Giovanni; #641383]
|
||||
* Port the hot corner to use pressure barriers [Jasper; #663661]
|
||||
* Misc bug fixes and cleanups: [Hashem, Giovanni, Alban, Jasper, Cosimo,
|
||||
Florian, Adel, Daniel, Matthias, Ray, Rui, Guillaume, Stef; #685849, #690643,
|
||||
#694292, #693814, #694234, #694365, #694287, #694336, #694256, #694261,
|
||||
#663601, #694441, #694284, #694463, #694475, #687248, #694394, #694320,
|
||||
#694701, #694784, #694858, #694906, #694327, #694876, #694905, #694969,
|
||||
#694970, #694988, #695006, #695001, #694998, #695023, #695002, #695073,
|
||||
#695126, #687748, #694837, #693907, #679851, #694988]
|
||||
|
||||
Contributors:
|
||||
Giovanni Campagna, Cosimo Cecchi, Matthias Clasen, Alban Crequy, Allan Day,
|
||||
Guillaume Desmottes, Adel Gadllah, Rui Matos, Daniel Mustieles,
|
||||
Hashem Nasarat, Jasper St. Pierre, Ray Strode, Stef Walter
|
||||
|
||||
Translations:
|
||||
Yuri Myasoedov [ru], Adam Matoušek [cs], Piotr Drąg [pl], Matej Urbančič [sl],
|
||||
Sweta Kothari [gu], Kjartan Maraas [nb], Nguyễn Thái Ngọc Duy [vi],
|
||||
Chao-Hsiung Liao [zh_HK, zh_TW], Dimitris Spingos [el],
|
||||
Inaki Larranaga Murgoitio [eu], Luca Ferretti [it], A S Alam [pa],
|
||||
Gheyret Kenji [ug], Stas Solovey [ru], Enrico Nicoletto [pt_BR],
|
||||
Fran Diéguez [gl], Daniel Mustieles [es], Aurimas Černius [lt]
|
||||
|
||||
3.7.90
|
||||
======
|
||||
* Let GNOME Shell work on EGL and GLES2 [Neil; #693225, #693438, #693339]
|
||||
* Implement middle-click paste [Jasper; #645019]
|
||||
* Fix top bar transition between session modes [Rui; #692966]
|
||||
* Trigger the message tray with downward pressure [Jasper; #677215]
|
||||
* Don't ask for a password on shutdown [Adel; #693385]
|
||||
* Add a context menu to the message tray [Adel; #691035, #693887]
|
||||
* Use unicode formatting in the date menu [Matthias; #689251]
|
||||
* Use proper ellipsis instead of three dots [Jeremy; #689542]
|
||||
* Tweak screen shield animation [Giovanni; #691964]
|
||||
* Always hide the OSK when showing the message tray [Florian; #662687]
|
||||
* Support sound in notifications [Giovanni; #642831]
|
||||
* Place application popup menus above chrome [Jasper; #633620]
|
||||
* Hide overview elements while searching [Cosimo; #682050]
|
||||
* Implement updated IBus candidate popup designs [Rui; #691902]
|
||||
* Add support for enable-animations preference [Cosimo; #655746]
|
||||
* Don't always show the message tray in the overview [Cosimo; #693987]
|
||||
* Improve arrangement of window previews [Adel; #690313]
|
||||
* Remove builtin settings provider [Giovanni; #690824]
|
||||
* Minimize fullscreen windows when they end up in the background [Adel; #693991]
|
||||
* Add context menu to the background actor [Jasper; #681540]
|
||||
* Handle backgrounds in the shell, improve startup animation [Ray; #682429]
|
||||
* Hide universal access menu when not needed [Giovanni; #681528]
|
||||
* Implement updated app picker designs [Florian; #694192]
|
||||
* Improve login manager -> session transition [Ray; #694062]
|
||||
* Don't use a grid layout in window picker [Adel; #694210]
|
||||
* Use scroll wheel for workspace switching rather than zoom [Florian; #686639]
|
||||
* Misc bug fixes and cleanups: [Jasper, Florian, Debarshi, Adel, Matthias,
|
||||
Giovanni, Daiki, Rico, Bastien, Cosimo, Ray, Allan, Antonio; #693284,
|
||||
#692680, #691746, #693303, #693162, #693161, #693522, #693385, #691715,
|
||||
#688915, #689106, #682429, #693570, #693737, #693458, #692845, 693836,
|
||||
#681540, #679925, #688227, #692773, #693909, #683288, #693854, #693746,
|
||||
#693931, #693924, #693940, #693970, #693935, #693937, #693974, #693936,
|
||||
#693975, #693822, #694030, #685849, #694052, #694035, #694038, #694079,
|
||||
#694064, #681735, #694100, #694057, #694070, #693572, #693896, #686984,
|
||||
#694123, #694125, #693756, #693757, #687556, #694215, 694062, #694227,
|
||||
#694240, #694234, #694264, 694276, 694282, #694241, #689394, #694202,
|
||||
#694265, #694289, #691806, #694290, #694296]
|
||||
|
||||
Contributors:
|
||||
Jeremy Bicha, Giovanni Campagna, Cosimo Cecchi, Matthias Clasen, Allan Day,
|
||||
António Fernandes, Adel Gadllah, Rui Matos, Florian Müllner, Bastien Nocera,
|
||||
Debarshi Ray, Neil Roberts, Jasper St. Pierre, Ray Strode, Rico Tzschichholz,
|
||||
Daiki Ueno
|
||||
|
||||
Translations:
|
||||
Yasumichi Akahoshi [ja], Yoji TOYODA [ja], Dušan Kazik [sk],
|
||||
Wouter Bolsterlee [nl], Matej Urbančič [sl], Gheyret Kenji [ug],
|
||||
Ivaylo Valkov [bg], Daniel Korostil [uk], Gheyret Kenji [ug],
|
||||
Daniel Mustieles [es], Anish A [ml], Gil Forcada [ca],
|
||||
Carles Ferrando [ca@valencia], Мирослав Николић [sr, sr@latin],
|
||||
Aurimas Černius [lt], Rafael Ferreira [pt_BR], Fran Diéguez [gl],
|
||||
Piotr Drąg [pl], Luca Ferretti [it], A S Alam [pa]
|
||||
|
||||
3.7.5
|
||||
=====
|
||||
* MessageTray: pass keyboard events to tray icons [Giovanni; #687425]
|
||||
* network: add support for virtual devices (vlan, bond, bridge) [Dan; #677144]
|
||||
* gdm: Allow right-clicking buttons for left-handed users [Jasper; #688748]
|
||||
* Make list search results span all available horizontal space [Tanner; #691967]
|
||||
* Make Show-Applications button depress when held down [Hashem; #692319]
|
||||
* Set a max width on search results [Cosimo; #692453]
|
||||
* Reserve scrollbar allocation for automatic policy [Cosimo; #686881]
|
||||
* Improve scaling algorithm for window thumbnails [Jasper; #686944]
|
||||
* Fix launching settings panels after g-c-c changes [Jasper; #692483]
|
||||
* Stop launching applications from empty searches [Hashem; #692391]
|
||||
* Implement per-source notification filtering [Giovanni; #685926]
|
||||
* ScreenShield: Omit ActiveChanged() signal at end of fade [Giovanni; #691964]
|
||||
* ScreenShield: Lower the shield on idle before locking [Giovanni; #692560]
|
||||
* Make previews of minimized windows translucent in overview [Florian; #692999]
|
||||
* windowManager: Respect icon geometry when minimizing [Florian; #692997]
|
||||
* ScreenShield: Only show lock icon when actually locked [Giovanni; #693007]
|
||||
* general: Use & instead of 'and' for Settings panels [Jeremy; #689590]
|
||||
* network: Add support for new ModemManager1 interface [Aleksander; #687359]
|
||||
* network: Handle LTE-only modems as GSM ones [Aleksander; #688144]
|
||||
* mobile-providers: Port to libnm-gtk [Aleksander; #688943]
|
||||
* general: Consistently use Title Case in top bar [Jeremy; #689589]
|
||||
* panel: Add :overview pseudo class while in overview [Florian; #693218]
|
||||
* sessionMode: Add support for mode-specific styling [Florian; #693219]
|
||||
* loginManager: Make suspend a NOP in the ConsoleKit patch [Florian; #693162]
|
||||
* screenShield: Inhibit suspend until the screen is locked [Florian; #686482]
|
||||
* Misc bug fixes and cleanups [Jasper, Giovanni, Rui, Cosimo, Florian, Stefano,
|
||||
Adel, Yanko; #691745, #691731, #690171, #689091, #691976, #691963, #684279,
|
||||
#692052, #692091, #642831, #692454, #692715, #692678, #692723, #692677,
|
||||
#683986, #692693, #692749, #692948, #692995, #692996, #692994, #677215,
|
||||
#692586, #693067, #693031, #693049, #643111, #693161, #693220]
|
||||
|
||||
Contributors:
|
||||
Jeremy Bicha, Giovanni Campagna, Cosimo Cecchi, Tanner Doshier,
|
||||
Stefano Facchini, Adel Gadllah, Yanko Kaneti, Rui Matos, Aleksander Morgado,
|
||||
Florian Müllner, Hashem Nasarat, Jasper St. Pierre, Dan Winship
|
||||
|
||||
Translations:
|
||||
Duarte Loreto [pt], Daniel Mustieles [es], Kjartan Maraas [nb],
|
||||
Nilamdyuti Goswami [as], Мирослав Николић [sr,sr@latin],
|
||||
Tobias Endrigkeit [de], Fabio Tomat [fur], Matej Urbančič [sl], A S Alam [pa],
|
||||
Inaki Larranaga Murgoitio [eu], Piotr Drąg [pl], Wouter Bolsterlee [nl],
|
||||
Gheyret Kenji [ug], Yaron Shahrabani [he], Chao-Hsiung Liao [zh_HK,zh_TW],
|
||||
Milo Casagrande [it], Benjamin Steinwender [de]
|
||||
|
||||
3.7.4.1
|
||||
=======
|
||||
* userMenu: Use show-full-name-in-top-bar setting [Bastien; #689561]
|
||||
* dateMenu: Add "Open Clocks" entry [Mathieu; #644390]
|
||||
* screenshot: Immediately show the flash spot [Jasper; #691875]
|
||||
* Misc. bug fixes [Rico, Jeremy]
|
||||
|
||||
Contributors:
|
||||
Jeremy Bicha, Mathieu Bridon, Bastien Nocera, Jasper St. Pierre,
|
||||
Rico Tzschichholz
|
||||
|
||||
Translations:
|
||||
Ihar Hrachyshka [be]
|
||||
|
||||
3.7.4
|
||||
=====
|
||||
* Make menu separators crisp [Giovanni, Allan; #641745]
|
||||
* power: Update for new D-Bus name [Bastien; #690506]
|
||||
* Add smooth scrolling support [Jasper; #687573]
|
||||
* Tweak notification layout [Allan; #688506]
|
||||
* Ping the active window when using the app menu [Giovanni; #684340]
|
||||
* Make password entries insensitive after submission [Jasper; #690594, #690895]
|
||||
* Honor lock-delay GSettings key [Giovanni, Matthias; #690766, #691170]
|
||||
* Use text/calendar preferred app as the calendar app [Giovanni; #690767]
|
||||
* lookingGlass: Move to an inspect() function [Jasper; #690726]
|
||||
* Make OSK animation quicker, snappier [Rui; #688642]
|
||||
* Allow to close chat notifications with Escape [Jasper; #690897]
|
||||
* Honor org.gnome.desktop.screensaver.user-switch-enabled [Giovanni; #691042]
|
||||
* Add a SelectArea() DBus method [Cosimo; #687954]
|
||||
* Support non-absolute paths when saving screenshots [Cosimo; #688004]
|
||||
* OSK: Fix extended keys popups [Rui; #674955]
|
||||
* Don't hide or show the keyboard immediately [Rui; #688646]
|
||||
* Improve padding in power menu [Giovanni; #689297]
|
||||
* Add per-window input source switching [Rui; #691414]
|
||||
* Misc bug fixes and cleanups [Rico, Jasper, Giovanni, Rui, Florian, Dan;
|
||||
#690608, #690589, #690539, #687081, #690667, #690665, #690666, #685856,
|
||||
#690858, #690895, #680414, #690965, #691019, #690590, #681376, #690180,
|
||||
#685513, #689263, #691553, #691720, #691743, #691750]
|
||||
|
||||
Contributors:
|
||||
Giovanni Campagna, Cosimo Cecchi, Matthias Clasen, Allan Day, Rui Matos,
|
||||
Florian Müllner, Bastien Nocera, Jasper St. Pierre, Rico Tzschichholz,
|
||||
Dan Winship
|
||||
|
||||
Translations:
|
||||
Matej Urbančič [sl], Kjartan Maraas [nb], Mattias Põldaru [et],
|
||||
Yaron Shahrabani [he], Aurimas Černius [lt], Khaled Hosny [ar],
|
||||
Fran Diéguez [gl], Daniel Mustieles [es], Piotr Drąg [pl], Balázs Úr [hu],
|
||||
Baurzhan Muftakhidinov [kk], Tobias Endrigkeit [de], Dušan Kazik [sk],
|
||||
Aron Xu [zh_CN], Gheyret Kenji [ug]
|
||||
* Revert 490206b to not depend on NMGTK-0.9.7, which hasn't been released yet
|
||||
|
||||
3.7.3
|
||||
=====
|
||||
|
118
configure.ac
118
configure.ac
@ -1,5 +1,5 @@
|
||||
AC_PREREQ(2.63)
|
||||
AC_INIT([gnome-shell],[3.8.0.1],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
|
||||
AC_INIT([gnome-shell],[3.7.3.1],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
|
||||
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
AC_CONFIG_SRCDIR([src/shell-global.c])
|
||||
@ -16,6 +16,8 @@ m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
|
||||
|
||||
# Checks for programs.
|
||||
AC_PROG_CC
|
||||
# Needed for per-target cflags, like in gnomeshell-taskpanel
|
||||
AM_PROG_CC_C_O
|
||||
|
||||
# Initialize libtool
|
||||
LT_PREREQ([2.2.6])
|
||||
@ -53,28 +55,29 @@ if $PKG_CONFIG --exists gstreamer-1.0 '>=' $GSTREAMER_MIN_VERSION ; then
|
||||
AC_MSG_RESULT(yes)
|
||||
build_recorder=true
|
||||
recorder_modules="gstreamer-1.0 gstreamer-base-1.0 x11 gtk+-3.0"
|
||||
PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-1.0 xfixes)
|
||||
PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-1.0 xfixes gl)
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
|
||||
|
||||
CLUTTER_MIN_VERSION=1.13.4
|
||||
CLUTTER_MIN_VERSION=1.11.11
|
||||
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
|
||||
GJS_MIN_VERSION=1.35.4
|
||||
MUTTER_MIN_VERSION=3.8.0
|
||||
GTK_MIN_VERSION=3.7.9
|
||||
GJS_MIN_VERSION=1.33.2
|
||||
MUTTER_MIN_VERSION=3.7.3
|
||||
GTK_MIN_VERSION=3.3.9
|
||||
GIO_MIN_VERSION=2.35.0
|
||||
LIBECAL_MIN_VERSION=3.5.3
|
||||
LIBEDATASERVER_MIN_VERSION=3.5.3
|
||||
LIBEDATASERVERUI_MIN_VERSION=3.5.3
|
||||
TELEPATHY_GLIB_MIN_VERSION=0.17.5
|
||||
TELEPATHY_LOGGER_MIN_VERSION=0.2.4
|
||||
POLKIT_MIN_VERSION=0.100
|
||||
STARTUP_NOTIFICATION_MIN_VERSION=0.11
|
||||
GCR_MIN_VERSION=3.3.90
|
||||
GNOME_DESKTOP_REQUIRED_VERSION=3.7.90
|
||||
GNOME_DESKTOP_REQUIRED_VERSION=3.7.1
|
||||
GNOME_MENUS_REQUIRED_VERSION=3.5.3
|
||||
NETWORKMANAGER_MIN_VERSION=0.9.6
|
||||
PULSE_MIN_VERS=2.0
|
||||
|
||||
# Collect more than 20 libraries for a prize!
|
||||
@ -87,26 +90,42 @@ PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
|
||||
libgnome-menu-3.0 >= $GNOME_MENUS_REQUIRED_VERSION
|
||||
$recorder_modules
|
||||
gdk-x11-3.0 libsoup-2.4
|
||||
gl
|
||||
clutter-x11-1.0 >= $CLUTTER_MIN_VERSION
|
||||
clutter-glx-1.0 >= $CLUTTER_MIN_VERSION
|
||||
libstartup-notification-1.0 >= $STARTUP_NOTIFICATION_MIN_VERSION
|
||||
gobject-introspection-1.0 >= $GOBJECT_INTROSPECTION_MIN_VERSION
|
||||
libcanberra libcanberra-gtk3
|
||||
libcanberra
|
||||
telepathy-glib >= $TELEPATHY_GLIB_MIN_VERSION
|
||||
telepathy-logger-0.2 >= $TELEPATHY_LOGGER_MIN_VERSION
|
||||
polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes
|
||||
libnm-glib libnm-util >= $NETWORKMANAGER_MIN_VERSION
|
||||
libnm-gtk >= $NETWORKMANAGER_MIN_VERSION
|
||||
libsecret-unstable gcr-3 >= $GCR_MIN_VERSION)
|
||||
libnm-glib libnm-util gnome-keyring-1
|
||||
gcr-3 >= $GCR_MIN_VERSION)
|
||||
|
||||
PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0)
|
||||
|
||||
PKG_CHECK_MODULES(SHELL_HOTPLUG_SNIFFER, gio-2.0 gdk-pixbuf-2.0)
|
||||
|
||||
PKG_CHECK_MODULES(BROWSER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION json-glib-1.0 >= 0.13.2)
|
||||
|
||||
GNOME_KEYBINDINGS_KEYSDIR=`$PKG_CONFIG --variable keysdir gnome-keybindings`
|
||||
AC_SUBST([GNOME_KEYBINDINGS_KEYSDIR])
|
||||
|
||||
GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION])
|
||||
|
||||
saved_CFLAGS=$CFLAGS
|
||||
saved_LIBS=$LIBS
|
||||
CFLAGS=$GNOME_SHELL_CFLAGS
|
||||
LIBS=$GNOME_SHELL_LIBS
|
||||
AC_CHECK_FUNCS(JS_NewGlobalObject XFixesCreatePointerBarrier)
|
||||
CFLAGS=$saved_CFLAGS
|
||||
LIBS=$saved_LIBS
|
||||
|
||||
PKG_CHECK_MODULES(GNOME_SHELL_JS, gio-2.0 gjs-internals-1.0 >= $GJS_MIN_VERSION)
|
||||
PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 >= 0.6.8 x11)
|
||||
PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0)
|
||||
PKG_CHECK_MODULES(SHELL_HOTPLUG_SNIFFER, gio-2.0 gdk-pixbuf-2.0)
|
||||
PKG_CHECK_MODULES(BROWSER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION json-glib-1.0 >= 0.13.2)
|
||||
PKG_CHECK_MODULES(TRAY, gtk+-3.0)
|
||||
PKG_CHECK_MODULES(GVC, libpulse >= $PULSE_MIN_VERS libpulse-mainloop-glib gobject-2.0)
|
||||
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.7.4)
|
||||
PKG_CHECK_MODULES(CARIBOU, caribou-1.0 >= 0.4.8)
|
||||
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.7.2.2)
|
||||
|
||||
AC_MSG_CHECKING([for bluetooth support])
|
||||
PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.1.0],
|
||||
@ -126,10 +145,32 @@ PKG_CHECK_MODULES(CALENDAR_SERVER, libecal-1.2 >= $LIBECAL_MIN_VERSION libedatas
|
||||
AC_SUBST(CALENDAR_SERVER_CFLAGS)
|
||||
AC_SUBST(CALENDAR_SERVER_LIBS)
|
||||
|
||||
GNOME_KEYBINDINGS_KEYSDIR=`$PKG_CONFIG --variable keysdir gnome-keybindings`
|
||||
AC_SUBST([GNOME_KEYBINDINGS_KEYSDIR])
|
||||
AC_ARG_WITH(systemd,
|
||||
AS_HELP_STRING([--with-systemd],
|
||||
[Add systemd support]),
|
||||
[with_systemd=$withval], [with_systemd=auto])
|
||||
|
||||
GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION])
|
||||
PKG_CHECK_MODULES(SYSTEMD,
|
||||
[libsystemd-login libsystemd-daemon],
|
||||
[have_systemd=yes], [have_systemd=no])
|
||||
|
||||
if test "x$with_systemd" = "xauto" ; then
|
||||
if test x$have_systemd = xno ; then
|
||||
use_systemd=no
|
||||
else
|
||||
use_systemd=yes
|
||||
fi
|
||||
else
|
||||
use_systemd=$with_systemd
|
||||
fi
|
||||
|
||||
if test "x$use_systemd" = "xyes"; then
|
||||
if test "x$have_systemd" = "xno"; then
|
||||
AC_MSG_ERROR([Systemd support explicitly required, but systemd not found])
|
||||
fi
|
||||
|
||||
AC_DEFINE(WITH_SYSTEMD, 1, [systemd support])
|
||||
fi
|
||||
|
||||
MUTTER_GIR_DIR=`$PKG_CONFIG --variable=girdir libmutter`
|
||||
MUTTER_TYPELIB_DIR=`$PKG_CONFIG --variable=typelibdir libmutter`
|
||||
@ -156,6 +197,16 @@ fi
|
||||
|
||||
# Sets GLIB_GENMARSHAL and GLIB_MKENUMS
|
||||
AM_PATH_GLIB_2_0()
|
||||
G_IR_SCANNER=`$PKG_CONFIG --variable=g_ir_scanner gobject-introspection-1.0`
|
||||
AC_SUBST(G_IR_SCANNER)
|
||||
G_IR_COMPILER=`$PKG_CONFIG --variable=g_ir_compiler gobject-introspection-1.0`
|
||||
AC_SUBST(G_IR_COMPILER)
|
||||
G_IR_GENERATE=`$PKG_CONFIG --variable=g_ir_generate gobject-introspection-1.0`
|
||||
AC_SUBST(G_IR_GENERATE)
|
||||
GIRDIR=`$PKG_CONFIG --variable=girdir gobject-introspection-1.0`
|
||||
AC_SUBST(GIRDIR)
|
||||
TYPELIBDIR="$($PKG_CONFIG --variable=typelibdir gobject-introspection-1.0)"
|
||||
AC_SUBST(TYPELIBDIR)
|
||||
|
||||
GTK_DOC_CHECK([1.15], [--flavour no-tmpl])
|
||||
|
||||
@ -171,7 +222,32 @@ if test "$enable_man" != no; then
|
||||
fi
|
||||
AM_CONDITIONAL(ENABLE_MAN, test "$enable_man" != no)
|
||||
|
||||
GNOME_COMPILE_WARNINGS([error])
|
||||
# Stay command-line compatible with the gnome-common configure option. Here
|
||||
# minimum/yes/maximum are the same, however.
|
||||
AC_ARG_ENABLE(compile_warnings,
|
||||
AS_HELP_STRING([--enable-compile-warnings=@<:@no/minimum/yes/maximum/error@:>@],[Turn on compiler warnings]),,
|
||||
enable_compile_warnings=error)
|
||||
|
||||
changequote(,)dnl
|
||||
if test "$enable_compile_warnings" != no ; then
|
||||
if test "x$GCC" = "xyes"; then
|
||||
case " $CFLAGS " in
|
||||
*[\ \ ]-Wall[\ \ ]*) ;;
|
||||
*) CFLAGS="$CFLAGS -Wall" ;;
|
||||
esac
|
||||
case " $CFLAGS " in
|
||||
*[\ \ ]-Wmissing-prototypes[\ \ ]*) ;;
|
||||
*) CFLAGS="$CFLAGS -Wmissing-prototypes" ;;
|
||||
esac
|
||||
if test "$enable_compile_warnings" = error ; then
|
||||
case " $CFLAGS " in
|
||||
*[\ \ ]-Werror[\ \ ]*) ;;
|
||||
*) CFLAGS="$CFLAGS -Werror -Wno-error=deprecated-declarations" ;;
|
||||
esac
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
changequote([,])dnl
|
||||
|
||||
AC_ARG_ENABLE(jhbuild-wrapper-script,
|
||||
AS_HELP_STRING([--enable-jhbuild-wrapper-script],[Make "gnome-shell" script work for jhbuild]),,enable_jhbuild_wrapper_script=no)
|
||||
|
@ -1,6 +1,3 @@
|
||||
wandadir = $(pkgdatadir)
|
||||
dist_wanda_DATA = wanda.png
|
||||
|
||||
desktopdir=$(datadir)/applications
|
||||
desktop_DATA = gnome-shell.desktop gnome-shell-extension-prefs.desktop
|
||||
|
||||
@ -15,7 +12,6 @@ desktop_DATA = gnome-shell.desktop gnome-shell-extension-prefs.desktop
|
||||
|
||||
introspectiondir = $(datadir)/dbus-1/interfaces
|
||||
introspection_DATA = \
|
||||
org.gnome.Shell.Screenshot.xml \
|
||||
org.gnome.ShellSearchProvider.xml \
|
||||
org.gnome.ShellSearchProvider2.xml
|
||||
|
||||
|
@ -1,128 +0,0 @@
|
||||
<!DOCTYPE node PUBLIC
|
||||
'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
|
||||
'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
|
||||
<node>
|
||||
|
||||
<!--
|
||||
org.gnome.Shell.Screenshot:
|
||||
@short_description: Screenshot interface
|
||||
|
||||
The interface used to capture pictures of the screen contents.
|
||||
-->
|
||||
<interface name="org.gnome.Shell.Screenshot">
|
||||
|
||||
<!--
|
||||
Screenshot:
|
||||
@filename: The filename for the screenshot
|
||||
@include_cursor: Whether to include the cursor image or not
|
||||
@flash: Whether to flash the screen or not
|
||||
@success: whether the screenshot was captured
|
||||
@filename_used: the file where the screenshot was saved
|
||||
|
||||
Takes a screenshot of the whole screen and saves it
|
||||
in @filename as png image, it returns a boolean
|
||||
indicating whether the operation was successful or not.
|
||||
@filename can either be an absolute path or a basename, in
|
||||
which case the screenshot will be saved in the $XDG_PICTURES_DIR
|
||||
or the home directory if it doesn't exist. The filename used
|
||||
to save the screenshot will be returned in @filename_used.
|
||||
-->
|
||||
<method name="Screenshot">
|
||||
<arg type="b" direction="in" name="include_cursor"/>
|
||||
<arg type="b" direction="in" name="flash"/>
|
||||
<arg type="s" direction="in" name="filename"/>
|
||||
<arg type="b" direction="out" name="success"/>
|
||||
<arg type="s" direction="out" name="filename_used"/>
|
||||
</method>
|
||||
|
||||
<!--
|
||||
ScreenshotWindow:
|
||||
@include_frame: Whether to include the frame or not
|
||||
@include_cursor: Whether to include the cursor image or not
|
||||
@flash: Whether to flash the window area or not
|
||||
@filename: The filename for the screenshot
|
||||
@success: whether the screenshot was captured
|
||||
@filename_used: the file where the screenshot was saved
|
||||
|
||||
Takes a screenshot of the focused window (optionally omitting the frame)
|
||||
and saves it in @filename as png image, it returns a boolean
|
||||
indicating whether the operation was successful or not.
|
||||
@filename can either be an absolute path or a basename, in
|
||||
which case the screenshot will be saved in the $XDG_PICTURES_DIR
|
||||
or the home directory if it doesn't exist. The filename used
|
||||
to save the screenshot will be returned in @filename_used.
|
||||
-->
|
||||
<method name="ScreenshotWindow">
|
||||
<arg type="b" direction="in" name="include_frame"/>
|
||||
<arg type="b" direction="in" name="include_cursor"/>
|
||||
<arg type="b" direction="in" name="flash"/>
|
||||
<arg type="s" direction="in" name="filename"/>
|
||||
<arg type="b" direction="out" name="success"/>
|
||||
<arg type="s" direction="out" name="filename_used"/>
|
||||
</method>
|
||||
|
||||
<!--
|
||||
ScreenshotArea:
|
||||
@x: the X coordinate of the area to capture
|
||||
@y: the Y coordinate of the area to capture
|
||||
@width: the width of the area to capture
|
||||
@height: the height of the area to capture
|
||||
@flash: whether to flash the area or not
|
||||
@filename: the filename for the screenshot
|
||||
@success: whether the screenshot was captured
|
||||
@filename_used: the file where the screenshot was saved
|
||||
|
||||
Takes a screenshot of the passed in area and saves it
|
||||
in @filename as png image, it returns a boolean
|
||||
indicating whether the operation was successful or not.
|
||||
@filename can either be an absolute path or a basename, in
|
||||
which case the screenshot will be saved in the $XDG_PICTURES_DIR
|
||||
or the home directory if it doesn't exist. The filename used
|
||||
to save the screenshot will be returned in @filename_used.
|
||||
-->
|
||||
<method name="ScreenshotArea">
|
||||
<arg type="i" direction="in" name="x"/>
|
||||
<arg type="i" direction="in" name="y"/>
|
||||
<arg type="i" direction="in" name="width"/>
|
||||
<arg type="i" direction="in" name="height"/>
|
||||
<arg type="b" direction="in" name="flash"/>
|
||||
<arg type="s" direction="in" name="filename"/>
|
||||
<arg type="b" direction="out" name="success"/>
|
||||
<arg type="s" direction="out" name="filename_used"/>
|
||||
</method>
|
||||
|
||||
<!--
|
||||
FlashArea:
|
||||
@x: the X coordinate of the area to flash
|
||||
@y: the Y coordinate of the area to flash
|
||||
@width: the width of the area to flash
|
||||
@height: the height of the area to flash
|
||||
|
||||
Renders a flash spot effect in the specified rectangle of the screen.
|
||||
-->
|
||||
<method name="FlashArea">
|
||||
<arg type="i" direction="in" name="x"/>
|
||||
<arg type="i" direction="in" name="y"/>
|
||||
<arg type="i" direction="in" name="width"/>
|
||||
<arg type="i" direction="in" name="height"/>
|
||||
</method>
|
||||
|
||||
<!--
|
||||
SelectArea:
|
||||
@x: the X coordinate of the selected area
|
||||
@y: the Y coordinate of the selected area
|
||||
@width: the width of the selected area
|
||||
@height: the height of the selected area
|
||||
|
||||
Interactively allows the user to select a rectangular area of
|
||||
the screen, and returns its coordinates.
|
||||
-->
|
||||
<method name="SelectArea">
|
||||
<arg type="i" direction="out" name="x"/>
|
||||
<arg type="i" direction="out" name="y"/>
|
||||
<arg type="i" direction="out" name="width"/>
|
||||
<arg type="i" direction="out" name="height"/>
|
||||
</method>
|
||||
|
||||
</interface>
|
||||
</node>
|
@ -46,7 +46,7 @@
|
||||
<!--
|
||||
GetResultMetas:
|
||||
@identifiers: An array of result identifiers as returned by GetInitialResultSet() or GetSubsearchResultSet()
|
||||
@metas: A dictionary describing the given search result, containing a human-readable 'name' (string), along with the result identifier this meta is for, 'id' (string). Optionally, either 'gicon' (a serialized GIcon) or 'icon-data' (raw image data as (iiibiiay) - width, height, rowstride, has-alpha, bits per sample, channels, data) can be specified if the result can be better served with a thumbnail of the content (such as with images). A 'description' field (string) may also be specified if more context would help the user find the desired result.
|
||||
@metas: A dictionary describing the given search result, containing 'id' and 'name' (both strings). Optionally, either 'gicon' (a serialized GIcon) or 'icon-data' (raw image data as (iiibiiay) - width, height, rowstride, has-alpha, bits per sample, channels, data) can be specified if the result can be better served with a thumbnail of the content (such as with images).
|
||||
|
||||
Return an array of meta data used to display each given result
|
||||
-->
|
||||
|
@ -46,7 +46,7 @@
|
||||
<!--
|
||||
GetResultMetas:
|
||||
@identifiers: An array of result identifiers as returned by GetInitialResultSet() or GetSubsearchResultSet()
|
||||
@metas: A dictionary describing the given search result, containing a human-readable 'name' (string), along with the result identifier this meta is for, 'id' (string). Optionally, either 'gicon' (a serialized GIcon) or 'icon-data' (raw image data as (iiibiiay) - width, height, rowstride, has-alpha, bits per sample, channels, data) can be specified if the result can be better served with a thumbnail of the content (such as with images). A 'description' field (string) may also be specified if more context would help the user find the desired result.
|
||||
@metas: A dictionary describing the given search result, containing 'id' and 'name' (both strings). Optionally, either 'gicon' (a serialized GIcon) or 'icon-data' (raw image data as (iiibiiay) - width, height, rowstride, has-alpha, bits per sample, channels, data) can be specified if the result can be better served with a thumbnail of the content (such as with images). A 'description' field (string) may also be specified if more context would help the user find the desired result.
|
||||
|
||||
Return an array of meta data used to display each given result
|
||||
-->
|
||||
|
@ -39,14 +39,6 @@
|
||||
will be displayed in the favorites area.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="app-folder-categories" type="as">
|
||||
<default>[ 'Utilities', 'Sundry' ]</default>
|
||||
<_summary>List of categories that should be displayed as folders</_summary>
|
||||
<_description>
|
||||
Each category name in this list will be represented as folder in the
|
||||
application view, rather than being displayed inline in the main view.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="command-history" type="as">
|
||||
<default>[]</default>
|
||||
<_summary>History for command (Alt-F2) dialog</_summary>
|
||||
@ -73,6 +65,11 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
|
||||
menuitem in single-user, single-session situations.
|
||||
</_description>
|
||||
</key>
|
||||
<key name="show-full-name" type="b">
|
||||
<default>true</default>
|
||||
<_summary>Show full name in the user menu</_summary>
|
||||
<_description>Whether the users full name is shown in the user menu or not.</_description>
|
||||
</key>
|
||||
<key name="remember-mount-password" type="b">
|
||||
<default>false</default>
|
||||
<_summary>Whether to remember password for mounting encrypted or remote filesystems</_summary>
|
||||
|
@ -38,6 +38,7 @@ stage {
|
||||
|
||||
/* small */
|
||||
.app-well-menu,
|
||||
.contact-details-status,
|
||||
.run-dialog-error-label {
|
||||
font-size: 9pt;
|
||||
}
|
||||
@ -125,10 +126,9 @@ StScrollBar StButton#vhandle:active {
|
||||
|
||||
/* PopupMenu */
|
||||
|
||||
.popup-menu-boxpointer,
|
||||
.candidate-popup-boxpointer {
|
||||
.popup-menu-boxpointer {
|
||||
-arrow-border-radius: 8px;
|
||||
-arrow-background-color: rgba(0,0,0,0.9);
|
||||
-arrow-background-color: black;
|
||||
-arrow-border-width: 2px;
|
||||
-arrow-border-color: #a5a5a5;
|
||||
-arrow-base: 24px;
|
||||
@ -206,12 +206,11 @@ StScrollBar StButton#vhandle:active {
|
||||
}
|
||||
|
||||
.popup-separator-menu-item {
|
||||
-gradient-height: 1px;
|
||||
-gradient-start: rgba(255,255,255,0.0);
|
||||
-gradient-end: rgba(255,255,255,0.3);
|
||||
-margin-horizontal: 24px;
|
||||
height: 1px;
|
||||
padding: 8px 0px;
|
||||
-gradient-height: 2px;
|
||||
-gradient-start: rgba(8,8,8,0);
|
||||
-gradient-end: #333333;
|
||||
-margin-horizontal: 1.5em;
|
||||
height: 1em;
|
||||
}
|
||||
|
||||
.popup-alternating-menu-item:alternate {
|
||||
@ -252,10 +251,6 @@ StScrollBar StButton#vhandle:active {
|
||||
icon-size: 1.09em;
|
||||
}
|
||||
|
||||
.popup-battery-percentage {
|
||||
padding-left: 24px;
|
||||
}
|
||||
|
||||
/* Switches */
|
||||
.toggle-switch {
|
||||
width: 65px;
|
||||
@ -286,24 +281,20 @@ StScrollBar StButton#vhandle:active {
|
||||
|
||||
/* Buttons */
|
||||
|
||||
.candidate-page-button,
|
||||
.dash-search-button,
|
||||
.notification-button,
|
||||
.notification-icon-button,
|
||||
.hotplug-notification-item,
|
||||
.hotplug-resident-eject-button,
|
||||
.modal-dialog-button,
|
||||
.app-view-control {
|
||||
.modal-dialog-button {
|
||||
font-weight: bold;
|
||||
border: 1px solid #8b8b8b;
|
||||
background-gradient-direction: vertical;
|
||||
background-gradient-start: rgba(255, 255, 255, 0.2);
|
||||
background-gradient-end: rgba(255, 255, 255, 0);
|
||||
}
|
||||
|
||||
.modal-dialog-button {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.candidate-page-button:hover,
|
||||
.dash-search-button:hover,
|
||||
.notification-button:hover,
|
||||
.notification-icon-button:hover,
|
||||
.hotplug-notification-item:hover,
|
||||
@ -313,37 +304,27 @@ StScrollBar StButton#vhandle:active {
|
||||
background-gradient-end: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.dash-search-button:selected,
|
||||
.dash-search-button:focus,
|
||||
.notification-button:focus,
|
||||
.notification-icon-button:focus,
|
||||
.hotplug-notification-item:focus,
|
||||
.modal-dialog-button:focus,
|
||||
.app-view-control:focus {
|
||||
.modal-dialog-button:focus {
|
||||
border-width: 2px;
|
||||
}
|
||||
|
||||
.app-view-control:focus {
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
.app-view-control:first-child:ltr:focus,
|
||||
.app-view-control:last-child:rtl:focus {
|
||||
border-right-width: 1px;
|
||||
}
|
||||
|
||||
.candidate-page-button:active,
|
||||
.candidate-page-button:pressed,
|
||||
.dash-search-button:active,
|
||||
.dash-search-button:pressed,
|
||||
.notification-button:active,
|
||||
.notification-icon-button:active,
|
||||
.hotplug-notification-item:active,
|
||||
.hotplug-resident-eject-button:active,
|
||||
.modal-dialog-button:active,
|
||||
.modal-dialog-button:pressed,
|
||||
.app-view-control:checked {
|
||||
.modal-dialog-button:pressed {
|
||||
background-gradient-start: rgba(255, 255, 255, 0);
|
||||
background-gradient-end: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.candidate-page-button:insensitive,
|
||||
.notification-button:insensitive,
|
||||
.notification-icon-button:insensitive,
|
||||
.modal-dialog-button:insensitive {
|
||||
@ -353,27 +334,6 @@ StScrollBar StButton#vhandle:active {
|
||||
background-color: rgba(102, 102, 102, 0.15);
|
||||
}
|
||||
|
||||
/* Common radii */
|
||||
|
||||
#searchEntry,
|
||||
.modal-dialog-button,
|
||||
.notification-button,
|
||||
.hotplug-notification-item,
|
||||
.app-view-controls {
|
||||
border-radius: 18px;
|
||||
}
|
||||
|
||||
.app-view-control:first-child:ltr,
|
||||
.app-view-control:last-child:rtl {
|
||||
border-radius: 18px 0px 0px 18px;
|
||||
border-right-width: 0px;
|
||||
}
|
||||
|
||||
.app-view-control:last-child:ltr,
|
||||
.app-view-control:first-child:rtl {
|
||||
border-radius: 0px 18px 18px 0px;
|
||||
}
|
||||
|
||||
/* Entries */
|
||||
|
||||
#searchEntry,
|
||||
@ -394,7 +354,7 @@ StScrollBar StButton#vhandle:active {
|
||||
background-gradient-start: rgba(5,5,6,0.1);
|
||||
background-gradient-end: rgba(254,254,254,0.1);
|
||||
background-gradient-direction: vertical;
|
||||
transition-duration: 300ms;
|
||||
transition-duration: 300;
|
||||
box-shadow: inset 0px 2px 4px rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
@ -429,7 +389,7 @@ StScrollBar StButton#vhandle:active {
|
||||
color: rgb(64, 64, 64);
|
||||
caret-color: rgb(64, 64, 64);
|
||||
font-weight: bold;
|
||||
transition-duration: 0ms;
|
||||
transition-duration: 0;
|
||||
}
|
||||
|
||||
.notification StEntry,
|
||||
@ -460,14 +420,17 @@ StScrollBar StButton#vhandle:active {
|
||||
background-color: black;
|
||||
font-weight: bold;
|
||||
height: 1.86em;
|
||||
transition-duration: 250;
|
||||
}
|
||||
|
||||
#panel.lock-screen {
|
||||
height: 2em;
|
||||
background-color: rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
#panel.unlock-screen,
|
||||
#panel.login-screen {
|
||||
height: 2em;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
@ -540,7 +503,7 @@ StScrollBar StButton#vhandle:active {
|
||||
-minimum-hpadding: 6px;
|
||||
font-weight: bold;
|
||||
color: #ccc;
|
||||
transition-duration: 100ms;
|
||||
transition-duration: 100;
|
||||
}
|
||||
|
||||
#panel.unlock-screen .panel-button,
|
||||
@ -588,7 +551,7 @@ StScrollBar StButton#vhandle:active {
|
||||
}
|
||||
|
||||
.panel-menu {
|
||||
-boxpointer-gap: 4px;
|
||||
-boxpointer-gap: 4px
|
||||
}
|
||||
|
||||
.panel-status-button-box {
|
||||
@ -657,8 +620,20 @@ StScrollBar StButton#vhandle:active {
|
||||
spacing: 24px;
|
||||
}
|
||||
|
||||
.overview-controls {
|
||||
padding-bottom: 32px;
|
||||
#overview-group {
|
||||
spacing: 32px;
|
||||
}
|
||||
|
||||
.workspaces-display {
|
||||
spacing: 32px; /* needs to be the same value as #overview-group */
|
||||
}
|
||||
|
||||
.window-caption {
|
||||
spacing: 25px;
|
||||
}
|
||||
|
||||
.workspace-controls {
|
||||
visible-width: 32px; /* Amount visible before hovering */
|
||||
}
|
||||
|
||||
.workspace-thumbnails-background {
|
||||
@ -678,7 +653,6 @@ StScrollBar StButton#vhandle:active {
|
||||
|
||||
.workspace-thumbnails {
|
||||
spacing: 11px;
|
||||
visible-width: 32px; /* Amount visible before hovering */
|
||||
}
|
||||
|
||||
.workspace-thumbnail-indicator {
|
||||
@ -688,7 +662,6 @@ StScrollBar StButton#vhandle:active {
|
||||
}
|
||||
|
||||
.window-caption {
|
||||
spacing: 25px;
|
||||
background: rgba(0,0,0,0.5);
|
||||
border-radius: 8px;
|
||||
padding: 4px 12px;
|
||||
@ -696,10 +669,6 @@ StScrollBar StButton#vhandle:active {
|
||||
border: 2px solid rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
.window-caption:hover {
|
||||
border: 2px solid rgba(255, 255, 255, 0);
|
||||
}
|
||||
|
||||
.window-close, .notification-close {
|
||||
background-image: url("close-window.svg");
|
||||
background-size: 32px;
|
||||
@ -711,6 +680,10 @@ StScrollBar StButton#vhandle:active {
|
||||
-shell-close-overlap: 16px;
|
||||
}
|
||||
|
||||
.window-caption:hover {
|
||||
border: 2px solid rgba(255, 255, 255, 0);
|
||||
}
|
||||
|
||||
.window-clone-border {
|
||||
border: 4px solid rgba(255, 255, 255, 0.5);
|
||||
border-radius: 4px;
|
||||
@ -743,36 +716,6 @@ StScrollBar StButton#vhandle:active {
|
||||
.window-picker {
|
||||
-horizontal-spacing: 32px;
|
||||
-vertical-spacing: 32px;
|
||||
padding-left: 32px;
|
||||
padding-right: 32px;
|
||||
padding-bottom: 48px;
|
||||
}
|
||||
|
||||
.window-picker.external-monitor {
|
||||
padding: 32px;
|
||||
}
|
||||
|
||||
.messages-indicator {
|
||||
color: #999999;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.messages-indicator-contents {
|
||||
spacing: 12px;
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
|
||||
.messages-indicator-contents:hover {
|
||||
color: white;
|
||||
text-shadow: black 0px 2px 2px;
|
||||
}
|
||||
|
||||
.messages-indicator-highlight {
|
||||
background-gradient-start: transparent;
|
||||
background-gradient-end: #999999;
|
||||
background-gradient-direction: vertical;
|
||||
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
/* Dash */
|
||||
@ -799,9 +742,14 @@ StScrollBar StButton#vhandle:active {
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
#viewSelector {
|
||||
spacing: 1em;
|
||||
}
|
||||
|
||||
/* Search Box */
|
||||
|
||||
#searchEntry {
|
||||
border-radius: 17px;
|
||||
width: 320px;
|
||||
}
|
||||
|
||||
@ -818,20 +766,20 @@ StScrollBar StButton#vhandle:active {
|
||||
/* Search Results */
|
||||
|
||||
#searchResults {
|
||||
padding: 20px 10px 0px 10px;
|
||||
padding: 20px 10px 10px 10px;
|
||||
spacing: 18px;
|
||||
}
|
||||
|
||||
#searchResultsBin {
|
||||
max-width: 1000px;
|
||||
}
|
||||
|
||||
#searchResultsContent {
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
spacing: 16px;
|
||||
}
|
||||
|
||||
#searchResultsContent:rtl {
|
||||
padding-right: 0px;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.search-section {
|
||||
/* This should be equal to #searchResultsContent spacing */
|
||||
spacing: 16px;
|
||||
@ -860,6 +808,24 @@ StScrollBar StButton#vhandle:active {
|
||||
spacing: 3px;
|
||||
}
|
||||
|
||||
/* Text labels are an odd number of pixels tall. The uneven top and bottom
|
||||
* padding compensates for this and ensures that the label is vertically
|
||||
* centered */
|
||||
.dash-search-button {
|
||||
border-radius: 16px;
|
||||
padding-top: 4px;
|
||||
padding-bottom: 5px;
|
||||
width: 300px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.dash-search-button:focus,
|
||||
.dash-search-button:selected {
|
||||
padding-top: 3px;
|
||||
padding-bottom: 4px;
|
||||
width: 298px;
|
||||
}
|
||||
|
||||
.dash-label {
|
||||
border-radius: 7px;
|
||||
padding: 4px 12px;
|
||||
@ -880,29 +846,40 @@ StScrollBar StButton#vhandle:active {
|
||||
icon-size: 96px;
|
||||
}
|
||||
|
||||
.app-display {
|
||||
padding: 8px;
|
||||
.all-app {
|
||||
padding: 16px 25px 16px 16px;
|
||||
spacing: 20px;
|
||||
}
|
||||
|
||||
.app-view-controls {
|
||||
width: 250px;
|
||||
padding-bottom: 32px;
|
||||
.all-app:rtl {
|
||||
padding-right: 16px;
|
||||
padding-left: 25px;
|
||||
}
|
||||
|
||||
.app-view-control {
|
||||
padding: 4px 16px;
|
||||
.app-filter {
|
||||
font-weight: bold;
|
||||
height: 2.85em;
|
||||
color: #aaa;
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.search-display > StBoxLayout,
|
||||
.all-apps > StBoxLayout,
|
||||
.frequent-apps > StBoxLayout {
|
||||
/* horizontal padding to make sure scrollbars or dash don't overlap content */
|
||||
padding: 0px 88px;
|
||||
.app-filter:hover {
|
||||
color: #eee;
|
||||
}
|
||||
|
||||
.app-folder-icon {
|
||||
padding: 5px;
|
||||
.app-filter:selected {
|
||||
color: #ffffff;
|
||||
background-image: url("filter-selected-ltr.svg");
|
||||
background-position: 190px 10px;
|
||||
}
|
||||
|
||||
.app-filter:selected:rtl {
|
||||
background-image: url("filter-selected-rtl.svg");
|
||||
background-position: 10px 10px;
|
||||
}
|
||||
|
||||
.app-filter:focus {
|
||||
outline: 1px solid #aaa;
|
||||
}
|
||||
|
||||
.dash-item-container > StButton {
|
||||
@ -921,6 +898,8 @@ StScrollBar StButton#vhandle:active {
|
||||
}
|
||||
|
||||
.list-search-result-description {
|
||||
font-weight: bold;
|
||||
font-size: 12pt;
|
||||
color: #eeeeec;
|
||||
}
|
||||
|
||||
@ -938,31 +917,16 @@ StScrollBar StButton#vhandle:active {
|
||||
border-radius: 4px;
|
||||
padding: 3px;
|
||||
border: 1px rgba(0,0,0,0);
|
||||
transition-duration: 100ms;
|
||||
transition-duration: 100;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.app-folder-popup {
|
||||
-arrow-border-radius: 8px;
|
||||
-arrow-background-color: black;
|
||||
-arrow-base: 24px;
|
||||
-arrow-rise: 11px;
|
||||
}
|
||||
|
||||
.app-folder-popup-bin {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.app-well-app.running > .overview-icon {
|
||||
text-shadow: black 0px 2px 2px;
|
||||
background-image: url("running-indicator.svg");
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.app-well-app.app-folder > .overview-icon {
|
||||
background-color: rgba(0,0,0,0.5);
|
||||
}
|
||||
|
||||
.app-well-app:hover > .overview-icon,
|
||||
.show-apps:hover > .overview-icon,
|
||||
.search-provider-icon:hover,
|
||||
@ -970,18 +934,10 @@ StScrollBar StButton#vhandle:active {
|
||||
.grid-search-result:hover .overview-icon {
|
||||
background-color: rgba(255,255,255,0.1);
|
||||
text-shadow: black 0px 2px 2px;
|
||||
transition-duration: 100ms;
|
||||
transition-duration: 100;
|
||||
color:white;
|
||||
}
|
||||
|
||||
.app-display .app-well-app > .overview-icon {
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.list-search-result:hover .list-search-result-description {
|
||||
text-shadow: rgba(0,0,0,0.8) 0px 1px 2px;
|
||||
}
|
||||
|
||||
.show-apps {
|
||||
padding: 4px 0;
|
||||
}
|
||||
@ -994,22 +950,19 @@ StScrollBar StButton#vhandle:active {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.app-well-app:checked > .overview-icon,
|
||||
.app-well-app:active > .overview-icon,
|
||||
.show-apps:checked > .overview-icon,
|
||||
.show-apps:active > .overview-icon {
|
||||
.show-apps:checked > .overview-icon {
|
||||
background-gradient-start: rgba(255, 255, 255, .05);
|
||||
background-gradient-end: rgba(255, 255, 255, .15);
|
||||
background-gradient-direction: vertical;
|
||||
border-radius: 4px;
|
||||
box-shadow: inset 0px 1px 2px 0px rgba(0, 0, 0, 1);
|
||||
transition-duration: 100ms;
|
||||
transition-duration: 100;
|
||||
}
|
||||
|
||||
.show-apps:checked .show-apps-icon,
|
||||
.show-apps:focus .show-apps-icon {
|
||||
color: white;
|
||||
transition-duration: 100ms;
|
||||
transition-duration: 100;
|
||||
}
|
||||
|
||||
.app-well-app:focus > .overview-icon,
|
||||
@ -1048,7 +1001,7 @@ StScrollBar StButton#vhandle:active {
|
||||
-minimum-hpadding: 6px;
|
||||
font-weight: bold;
|
||||
color: #ccc;
|
||||
transition-duration: 100ms;
|
||||
transition-duration: 100;
|
||||
padding-left: .3em;
|
||||
padding-right: .3em;
|
||||
}
|
||||
@ -1272,15 +1225,15 @@ StScrollBar StButton#vhandle:active {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.calendar-other-month-day {
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.calendar-day-with-events {
|
||||
font-weight: bold;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.calendar-other-month-day {
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.events-header-vbox {
|
||||
spacing: 6pt;
|
||||
padding-right: .5em;
|
||||
@ -1351,7 +1304,17 @@ StScrollBar StButton#vhandle:active {
|
||||
#message-tray {
|
||||
background: #2e3436 url(message-tray-background.png);
|
||||
background-repeat: repeat;
|
||||
height: 72px;
|
||||
transition-duration: 250;
|
||||
}
|
||||
|
||||
#message-tray:keyboard {
|
||||
/* Same as the OSK */
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
#message-tray:overview {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
outline: 1px solid rgba(128, 128, 128, 0.3);
|
||||
}
|
||||
|
||||
.message-tray-summary {
|
||||
@ -1368,7 +1331,7 @@ StScrollBar StButton#vhandle:active {
|
||||
border-radius: 10px 10px 0px 0px;
|
||||
background: rgba(0,0,0,0.9);
|
||||
padding: 8px 8px 4px 8px;
|
||||
spacing-rows: 4px;
|
||||
spacing-rows: 10px;
|
||||
spacing-columns: 10px;
|
||||
}
|
||||
|
||||
@ -1448,11 +1411,11 @@ StScrollBar StButton#vhandle:active {
|
||||
}
|
||||
|
||||
.notification-actions {
|
||||
padding-top: 18px;
|
||||
spacing: 10px;
|
||||
}
|
||||
|
||||
.notification-button {
|
||||
border-radius: 18px;
|
||||
padding: 4px 42px 5px;
|
||||
}
|
||||
|
||||
@ -1485,6 +1448,7 @@ StScrollBar StButton#vhandle:active {
|
||||
|
||||
.hotplug-notification-item {
|
||||
padding: 2px 10px;
|
||||
border-radius: 18px;
|
||||
}
|
||||
|
||||
.hotplug-notification-item:focus {
|
||||
@ -1609,7 +1573,7 @@ StScrollBar StButton#vhandle:active {
|
||||
.summary-source {
|
||||
border-radius: 4px;
|
||||
padding: 0 6px 0 6px;
|
||||
transition-duration: 100ms;
|
||||
transition-duration: 100;
|
||||
}
|
||||
|
||||
.summary-source-counter {
|
||||
@ -1623,26 +1587,12 @@ StScrollBar StButton#vhandle:active {
|
||||
-shell-counter-overlap-y: 13px;
|
||||
}
|
||||
|
||||
/* OSD */
|
||||
.osd-window {
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
spacing: 1em;
|
||||
}
|
||||
|
||||
.osd-window .level {
|
||||
height: 0.6em;
|
||||
border-radius: 0.3em;
|
||||
background-color: rgba(190,190,190,0.2);
|
||||
}
|
||||
|
||||
/* App Switcher */
|
||||
.switcher-popup {
|
||||
padding: 8px;
|
||||
spacing: 16px;
|
||||
}
|
||||
|
||||
.osd-window,
|
||||
.switcher-list {
|
||||
background: rgba(0,0,0,0.8);
|
||||
border: 1px solid rgba(128,128,128,0.40);
|
||||
@ -1794,6 +1744,8 @@ StScrollBar StButton#vhandle:active {
|
||||
}
|
||||
|
||||
.modal-dialog-button {
|
||||
border-radius: 18px;
|
||||
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
padding: 4px 32px 5px;
|
||||
@ -2144,53 +2096,17 @@ StScrollBar StButton#vhandle:active {
|
||||
}
|
||||
|
||||
/* IBus Candidate Popup */
|
||||
|
||||
.candidate-popup-content {
|
||||
padding: 0.5em;
|
||||
spacing: 0.3em;
|
||||
}
|
||||
|
||||
.candidate-index {
|
||||
padding: 0 0.5em 0 0;
|
||||
color: #cccccc;
|
||||
padding: 0.5em 0.5em 0.5em 0.5em;
|
||||
}
|
||||
|
||||
.candidate-box {
|
||||
padding: 0.3em 0.5em 0.3em 0.5em;
|
||||
.candidate-label {
|
||||
padding: 0.5em 0.5em 0.5em 0.5em;
|
||||
}
|
||||
|
||||
.candidate-box:selected {
|
||||
.candidate-label:selected {
|
||||
border-radius: 4px;
|
||||
background-color: rgba(255,255,255,0.2);
|
||||
}
|
||||
|
||||
.candidate-box:hover {
|
||||
border-radius: 4px;
|
||||
background-color: rgba(255,255,255,0.1);
|
||||
}
|
||||
.candidate-page-button-box {
|
||||
height: 2em;
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.vertical .candidate-page-button-box {
|
||||
padding-top: 0.5em;
|
||||
}
|
||||
|
||||
.horizontal .candidate-page-button-box {
|
||||
padding-left: 0.5em;
|
||||
}
|
||||
|
||||
.candidate-page-button-previous {
|
||||
border-radius: 4px 0px 0px 4px;
|
||||
}
|
||||
|
||||
.candidate-page-button-next {
|
||||
border-radius: 0px 4px 4px 0px;
|
||||
}
|
||||
|
||||
.candidate-page-button-icon {
|
||||
icon-size: 1em;
|
||||
background-color: rgba(255,255,255,0.33);
|
||||
}
|
||||
|
||||
/* Login Dialog */
|
||||
@ -2215,9 +2131,6 @@ StScrollBar StButton#vhandle:active {
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
|
||||
padding-bottom: 80px;
|
||||
padding-top: 80px;
|
||||
|
||||
border-radius: 16px;
|
||||
min-height: 150px;
|
||||
max-height: 700px;
|
||||
@ -2429,18 +2342,10 @@ StScrollBar StButton#vhandle:active {
|
||||
color: orange;
|
||||
}
|
||||
|
||||
.user-widget {
|
||||
.unlock-dialog-user-name-container {
|
||||
spacing: .4em;
|
||||
}
|
||||
|
||||
.user-widget-label {
|
||||
font-size: 16pt;
|
||||
font-weight: bold;
|
||||
text-align: left;
|
||||
padding-left: 15px;
|
||||
text-shadow: black 0px 4px 3px 0px;
|
||||
}
|
||||
|
||||
/* Screen shield */
|
||||
|
||||
.screen-shield-background {
|
||||
@ -2497,7 +2402,6 @@ StScrollBar StButton#vhandle:active {
|
||||
|
||||
.screen-shield-notifications-box {
|
||||
spacing: 18px;
|
||||
max-width: 34em;
|
||||
}
|
||||
|
||||
.screen-shield-notification-source {
|
||||
@ -2532,13 +2436,7 @@ StScrollBar StButton#vhandle:active {
|
||||
}
|
||||
|
||||
.input-source-switcher-symbol {
|
||||
font-size: 34pt;
|
||||
font-size: 42pt;
|
||||
width: 96px;
|
||||
height: 96px;
|
||||
}
|
||||
|
||||
/* Background menu */
|
||||
|
||||
.background-menu {
|
||||
-boxpointer-gap: 4px;
|
||||
}
|
||||
|
BIN
data/wanda.png
BIN
data/wanda.png
Binary file not shown.
Before Width: | Height: | Size: 13 KiB |
@ -88,18 +88,11 @@ doc-gen-org.gnome.Shell.SearchProvider2.xml: $(top_srcdir)/data/org.gnome.ShellS
|
||||
--generate-docbook doc-gen \
|
||||
$(top_srcdir)/data/org.gnome.ShellSearchProvider2.xml
|
||||
|
||||
doc-gen-org.gnome.Shell.Screenshot.xml: $(top_srcdir)/data/org.gnome.Shell.Screenshot.xml
|
||||
gdbus-codegen \
|
||||
--interface-prefix org.gnome.Shell.Screenshot. \
|
||||
--generate-docbook doc-gen \
|
||||
$(top_srcdir)/data/org.gnome.Shell.Screenshot.xml
|
||||
|
||||
# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE).
|
||||
# e.g. content_files=running.sgml building.sgml changes-2.0.sgml
|
||||
content_files= \
|
||||
doc-gen-org.gnome.Shell.SearchProvider.xml \
|
||||
doc-gen-org.gnome.Shell.SearchProvider2.xml \
|
||||
doc-gen-org.gnome.Shell.Screenshot.xml
|
||||
doc-gen-org.gnome.Shell.SearchProvider2.xml
|
||||
|
||||
# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded
|
||||
# These files must be listed here *and* in content_files
|
||||
|
@ -50,6 +50,7 @@
|
||||
<xi:include href="xml/shell-xfixes-cursor.xml"/>
|
||||
<xi:include href="xml/shell-util.xml"/>
|
||||
<xi:include href="xml/shell-mount-operation.xml"/>
|
||||
<xi:include href="xml/shell-mobile-providers.xml"/>
|
||||
<xi:include href="xml/shell-network-agent.xml"/>
|
||||
<xi:include href="xml/shell-polkit-authentication-agent.xml"/>
|
||||
<xi:include href="xml/shell-tp-client.xml"/>
|
||||
|
@ -28,7 +28,6 @@ nobase_dist_js_DATA = \
|
||||
misc/extensionUtils.js \
|
||||
misc/fileUtils.js \
|
||||
misc/gnomeSession.js \
|
||||
misc/hash.js \
|
||||
misc/history.js \
|
||||
misc/jsParse.js \
|
||||
misc/loginManager.js \
|
||||
@ -39,8 +38,6 @@ nobase_dist_js_DATA = \
|
||||
ui/altTab.js \
|
||||
ui/appDisplay.js \
|
||||
ui/appFavorites.js \
|
||||
ui/backgroundMenu.js \
|
||||
ui/background.js \
|
||||
ui/boxpointer.js \
|
||||
ui/calendar.js \
|
||||
ui/checkBox.js \
|
||||
@ -52,6 +49,7 @@ nobase_dist_js_DATA = \
|
||||
ui/extensionSystem.js \
|
||||
ui/extensionDownloader.js \
|
||||
ui/environment.js \
|
||||
ui/flashspot.js \
|
||||
ui/ibusCandidatePopup.js\
|
||||
ui/grabHelper.js \
|
||||
ui/iconGrid.js \
|
||||
@ -69,16 +67,13 @@ nobase_dist_js_DATA = \
|
||||
ui/shellEntry.js \
|
||||
ui/shellMountOperation.js \
|
||||
ui/notificationDaemon.js \
|
||||
ui/osdWindow.js \
|
||||
ui/overview.js \
|
||||
ui/overviewControls.js \
|
||||
ui/panel.js \
|
||||
ui/panelMenu.js \
|
||||
ui/pointerWatcher.js \
|
||||
ui/popupMenu.js \
|
||||
ui/remoteSearch.js \
|
||||
ui/runDialog.js \
|
||||
ui/screenshot.js \
|
||||
ui/screenShield.js \
|
||||
ui/scripting.js \
|
||||
ui/search.js \
|
||||
@ -95,7 +90,6 @@ nobase_dist_js_DATA = \
|
||||
ui/tweener.js \
|
||||
ui/unlockDialog.js \
|
||||
ui/userMenu.js \
|
||||
ui/userWidget.js \
|
||||
ui/viewSelector.js \
|
||||
ui/wanda.js \
|
||||
ui/windowAttentionHandler.js \
|
||||
|
@ -45,7 +45,6 @@ const Application = new Lang.Class({
|
||||
this._extensionPrefsModules = {};
|
||||
|
||||
this._extensionIters = {};
|
||||
this._startupUuid = null;
|
||||
},
|
||||
|
||||
_buildModel: function() {
|
||||
@ -204,7 +203,6 @@ const Application = new Lang.Class({
|
||||
_scanExtensions: function() {
|
||||
let finder = new ExtensionUtils.ExtensionFinder();
|
||||
finder.connect('extension-found', Lang.bind(this, this._extensionFound));
|
||||
finder.connect('extensions-loaded', Lang.bind(this, this._extensionsLoaded));
|
||||
finder.scanExtensions();
|
||||
},
|
||||
|
||||
@ -214,11 +212,6 @@ const Application = new Lang.Class({
|
||||
this._extensionIters[extension.uuid] = iter;
|
||||
},
|
||||
|
||||
_extensionsLoaded: function() {
|
||||
if (this._startupUuid && this._extensionAvailable(this._startupUuid))
|
||||
this._selectExtension(this._startupUuid);
|
||||
this._startupUuid = null;
|
||||
},
|
||||
|
||||
_onActivate: function() {
|
||||
this._window.present();
|
||||
@ -239,10 +232,10 @@ const Application = new Lang.Class({
|
||||
// Strip off "extension:///" prefix which fakes a URI, if it exists
|
||||
uuid = stripPrefix(uuid, "extension:///");
|
||||
|
||||
if (this._extensionAvailable(uuid))
|
||||
this._selectExtension(uuid);
|
||||
else
|
||||
this._startupUuid = uuid;
|
||||
if (!this._extensionAvailable(uuid))
|
||||
return 1;
|
||||
|
||||
this._selectExtension(uuid);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -43,9 +43,8 @@ const Panel = imports.ui.panel;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const UserMenu = imports.ui.userMenu;
|
||||
const UserWidget = imports.ui.userWidget;
|
||||
|
||||
const _FADE_ANIMATION_TIME = 0.25;
|
||||
const _RESIZE_ANIMATION_TIME = 0.25;
|
||||
const _SCROLL_ANIMATION_TIME = 0.5;
|
||||
const _TIMED_LOGIN_IDLE_THRESHOLD = 5.0;
|
||||
const _LOGO_ICON_HEIGHT = 16;
|
||||
@ -56,6 +55,39 @@ const WORK_SPINNER_ANIMATION_TIME = 0.3;
|
||||
|
||||
let _loginDialog = null;
|
||||
|
||||
function _smoothlyResizeActor(actor, width, height) {
|
||||
let finalWidth;
|
||||
let finalHeight;
|
||||
|
||||
if (width < 0)
|
||||
finalWidth = actor.width;
|
||||
else
|
||||
finalWidth = width;
|
||||
|
||||
if (height < 0)
|
||||
finalHeight = actor.height;
|
||||
else
|
||||
finalHeight = height;
|
||||
|
||||
actor.set_size(actor.width, actor.height);
|
||||
|
||||
if (actor.width == finalWidth && actor.height == finalHeight)
|
||||
return null;
|
||||
|
||||
let hold = new Batch.Hold();
|
||||
|
||||
Tweener.addTween(actor,
|
||||
{ width: finalWidth,
|
||||
height: finalHeight,
|
||||
time: _RESIZE_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this, function() {
|
||||
hold.release();
|
||||
})
|
||||
});
|
||||
return hold;
|
||||
}
|
||||
|
||||
const LogoMenuButton = new Lang.Class({
|
||||
Name: 'LogoMenuButton',
|
||||
Extends: PanelMenu.Button,
|
||||
@ -96,7 +128,6 @@ const UserListItem = new Lang.Class({
|
||||
|
||||
let layout = new St.BoxLayout({ vertical: false });
|
||||
this.actor = new St.Button({ style_class: 'login-dialog-user-list-item',
|
||||
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
|
||||
can_focus: true,
|
||||
child: layout,
|
||||
reactive: true,
|
||||
@ -214,23 +245,155 @@ const UserList = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
_showItem: function(item) {
|
||||
let tasks = [function() {
|
||||
return GdmUtil.fadeInActor(item.actor);
|
||||
}];
|
||||
|
||||
let batch = new Batch.ConsecutiveBatch(this, tasks);
|
||||
return batch.run();
|
||||
},
|
||||
|
||||
_onItemActivated: function(activatedItem) {
|
||||
this.emit('activate', activatedItem);
|
||||
},
|
||||
|
||||
updateStyle: function(isExpanded) {
|
||||
giveUpWhitespace: function() {
|
||||
let container = this.actor.get_parent();
|
||||
|
||||
container.child_set(this.actor, { expand: false });
|
||||
},
|
||||
|
||||
takeOverWhitespace: function() {
|
||||
let container = this.actor.get_parent();
|
||||
|
||||
container.child_set(this.actor, { expand: true });
|
||||
},
|
||||
|
||||
pinInPlace: function() {
|
||||
this._box.set_size(this._box.width, this._box.height);
|
||||
},
|
||||
|
||||
shrinkToNaturalHeight: function() {
|
||||
let oldWidth = this._box.width;
|
||||
let oldHeight = this._box.height;
|
||||
this._box.set_size(-1, -1);
|
||||
let [minHeight, naturalHeight] = this._box.get_preferred_height(-1);
|
||||
this._box.set_size(oldWidth, oldHeight);
|
||||
|
||||
let batch = new Batch.ConsecutiveBatch(this,
|
||||
[function() {
|
||||
return _smoothlyResizeActor(this._box, -1, naturalHeight);
|
||||
},
|
||||
|
||||
function() {
|
||||
this._box.set_size(-1, -1);
|
||||
}
|
||||
]);
|
||||
|
||||
return batch.run();
|
||||
},
|
||||
|
||||
hideItemsExcept: function(exception) {
|
||||
let tasks = [];
|
||||
|
||||
if (isExpanded)
|
||||
this._box.add_style_pseudo_class('expanded');
|
||||
else
|
||||
this._box.remove_style_pseudo_class('expanded');
|
||||
for (let userName in this._items) {
|
||||
let item = this._items[userName];
|
||||
|
||||
item.actor.set_hover(false);
|
||||
item.actor.reactive = false;
|
||||
item.actor.can_focus = false;
|
||||
item.syncStyleClasses();
|
||||
item._timedLoginIndicator.scale_x = 0.;
|
||||
if (item != exception)
|
||||
tasks.push(function() {
|
||||
return GdmUtil.fadeOutActor(item.actor);
|
||||
});
|
||||
}
|
||||
|
||||
let batch = new Batch.ConsecutiveBatch(this,
|
||||
[function() {
|
||||
return GdmUtil.fadeOutActor(this.actor.vscroll);
|
||||
},
|
||||
|
||||
new Batch.ConcurrentBatch(this, tasks),
|
||||
|
||||
function() {
|
||||
this._box.remove_style_pseudo_class('expanded');
|
||||
}
|
||||
]);
|
||||
|
||||
return batch.run();
|
||||
},
|
||||
|
||||
hideItems: function() {
|
||||
return this.hideItemsExcept(null);
|
||||
},
|
||||
|
||||
_getExpandedHeight: function() {
|
||||
let hiddenActors = [];
|
||||
for (let userName in this._items) {
|
||||
let item = this._items[userName];
|
||||
if (!item.actor.visible) {
|
||||
item.actor.show();
|
||||
hiddenActors.push(item.actor);
|
||||
}
|
||||
}
|
||||
|
||||
if (!this._box.visible) {
|
||||
this._box.show();
|
||||
hiddenActors.push(this._box);
|
||||
}
|
||||
|
||||
this._box.set_size(-1, -1);
|
||||
let [minHeight, naturalHeight] = this._box.get_preferred_height(-1);
|
||||
|
||||
for (let i = 0; i < hiddenActors.length; i++) {
|
||||
let actor = hiddenActors[i];
|
||||
actor.hide();
|
||||
}
|
||||
|
||||
return naturalHeight;
|
||||
},
|
||||
|
||||
showItems: function() {
|
||||
let tasks = [];
|
||||
|
||||
for (let userName in this._items) {
|
||||
let item = this._items[userName];
|
||||
item.actor.sync_hover();
|
||||
item.actor.reactive = true;
|
||||
item.actor.can_focus = true;
|
||||
item.syncStyleClasses();
|
||||
tasks.push(function() {
|
||||
return this._showItem(item);
|
||||
});
|
||||
}
|
||||
|
||||
let batch = new Batch.ConsecutiveBatch(this,
|
||||
[function() {
|
||||
this.takeOverWhitespace();
|
||||
},
|
||||
|
||||
function() {
|
||||
let fullHeight = this._getExpandedHeight();
|
||||
return _smoothlyResizeActor(this._box, -1, fullHeight);
|
||||
},
|
||||
|
||||
function() {
|
||||
this._box.add_style_pseudo_class('expanded');
|
||||
},
|
||||
|
||||
new Batch.ConcurrentBatch(this, tasks),
|
||||
|
||||
function() {
|
||||
this.actor.set_size(-1, -1);
|
||||
},
|
||||
|
||||
function() {
|
||||
return GdmUtil.fadeInActor(this.actor.vscroll);
|
||||
}]);
|
||||
return batch.run();
|
||||
},
|
||||
|
||||
scrollToItem: function(item) {
|
||||
@ -329,7 +492,6 @@ const SessionListItem = new Lang.Class({
|
||||
this.id = id;
|
||||
|
||||
this.actor = new St.Button({ style_class: 'login-dialog-session-list-item',
|
||||
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
|
||||
can_focus: true,
|
||||
reactive: true,
|
||||
x_fill: true,
|
||||
@ -370,7 +532,6 @@ const SessionListItem = new Lang.Class({
|
||||
color.alpha / 255);
|
||||
cr.arc(width / 2, height / 2, width / 3, 0, 2 * Math.PI);
|
||||
cr.fill();
|
||||
cr.$dispose();
|
||||
},
|
||||
|
||||
_onClicked: function() {
|
||||
@ -390,7 +551,6 @@ const SessionList = new Lang.Class({
|
||||
this.actor.child = this._box;
|
||||
|
||||
this._button = new St.Button({ style_class: 'login-dialog-session-list-button',
|
||||
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
|
||||
can_focus: true,
|
||||
x_fill: true,
|
||||
y_fill: true });
|
||||
@ -402,7 +562,7 @@ const SessionList = new Lang.Class({
|
||||
box.add_actor(this._triangle);
|
||||
|
||||
let label = new St.Label({ style_class: 'login-dialog-session-list-label',
|
||||
text: _("Session…") });
|
||||
text: _("Session...") });
|
||||
box.add_actor(label);
|
||||
|
||||
this._button.connect('clicked',
|
||||
@ -513,7 +673,7 @@ const LoginDialog = new Lang.Class({
|
||||
this.parent({ shellReactive: true,
|
||||
styleClass: 'login-dialog',
|
||||
parentActor: parentActor,
|
||||
keybindingMode: Shell.KeyBindingMode.LOGIN_SCREEN,
|
||||
keybindingMode: Main.KeybindingMode.LOGIN_SCREEN,
|
||||
shouldFadeIn: false });
|
||||
this.connect('destroy',
|
||||
Lang.bind(this, this._onDestroy));
|
||||
@ -553,20 +713,24 @@ const LoginDialog = new Lang.Class({
|
||||
this._settings.connect('changed::' + GdmUtil.DISABLE_USER_LIST_KEY,
|
||||
Lang.bind(this, this._updateDisableUserList));
|
||||
|
||||
this._userSelectionBox = new St.BoxLayout({ style_class: 'login-dialog-user-selection-box',
|
||||
vertical: true });
|
||||
this.contentLayout.add(this._userSelectionBox);
|
||||
|
||||
this._bannerLabel = new St.Label({ style_class: 'login-dialog-banner',
|
||||
text: '' });
|
||||
this._userSelectionBox.add(this._bannerLabel);
|
||||
this.contentLayout.add(this._bannerLabel);
|
||||
this._updateBanner();
|
||||
|
||||
this._titleLabel = new St.Label({ style_class: 'login-dialog-title',
|
||||
text: C_("title", "Sign In"),
|
||||
visible: false });
|
||||
|
||||
this.contentLayout.add(this._titleLabel,
|
||||
{ y_fill: false,
|
||||
y_align: St.Align.START });
|
||||
|
||||
this._userList = new UserList();
|
||||
this._userSelectionBox.add(this._userList.actor,
|
||||
{ expand: true,
|
||||
x_fill: true,
|
||||
y_fill: true });
|
||||
this.contentLayout.add(this._userList.actor,
|
||||
{ expand: true,
|
||||
x_fill: true,
|
||||
y_fill: true });
|
||||
|
||||
this.setInitialKeyFocus(this._userList.actor);
|
||||
|
||||
@ -577,13 +741,6 @@ const LoginDialog = new Lang.Class({
|
||||
x_fill: true,
|
||||
y_fill: true,
|
||||
x_align: St.Align.START });
|
||||
this._promptUser = new St.Bin({ x_fill: true,
|
||||
x_align: St.Align.START });
|
||||
this._promptBox.add(this._promptUser,
|
||||
{ x_align: St.Align.START,
|
||||
x_fill: true,
|
||||
y_fill: true,
|
||||
expand: true });
|
||||
this._promptLabel = new St.Label({ style_class: 'login-dialog-prompt-label' });
|
||||
|
||||
this._promptBox.add(this._promptLabel,
|
||||
@ -594,7 +751,6 @@ const LoginDialog = new Lang.Class({
|
||||
this._promptEntry = new St.Entry({ style_class: 'login-dialog-prompt-entry',
|
||||
can_focus: true });
|
||||
this._promptEntryTextChangedId = 0;
|
||||
this._promptEntryActivateId = 0;
|
||||
this._promptBox.add(this._promptEntry,
|
||||
{ expand: true,
|
||||
x_fill: true,
|
||||
@ -630,7 +786,6 @@ const LoginDialog = new Lang.Class({
|
||||
let notListedLabel = new St.Label({ text: _("Not listed?"),
|
||||
style_class: 'login-dialog-not-listed-label' });
|
||||
this._notListedButton = new St.Button({ style_class: 'login-dialog-not-listed-button',
|
||||
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
|
||||
can_focus: true,
|
||||
child: notListedLabel,
|
||||
reactive: true,
|
||||
@ -639,10 +794,10 @@ const LoginDialog = new Lang.Class({
|
||||
|
||||
this._notListedButton.connect('clicked', Lang.bind(this, this._hideUserListAndLogIn));
|
||||
|
||||
this._userSelectionBox.add(this._notListedButton,
|
||||
{ expand: false,
|
||||
x_align: St.Align.START,
|
||||
x_fill: true });
|
||||
this.contentLayout.add(this._notListedButton,
|
||||
{ expand: false,
|
||||
x_align: St.Align.START,
|
||||
x_fill: true });
|
||||
|
||||
if (!this._userManager.is_loaded)
|
||||
this._userManagerLoadedId = this._userManager.connect('notify::is-loaded',
|
||||
@ -684,15 +839,13 @@ const LoginDialog = new Lang.Class({
|
||||
|
||||
if (enabled && text) {
|
||||
this._bannerLabel.set_text(text);
|
||||
this._bannerLabel.show();
|
||||
this._fadeInBanner();
|
||||
} else {
|
||||
this._bannerLabel.hide();
|
||||
this._fadeOutBanner();
|
||||
}
|
||||
},
|
||||
|
||||
_reset: function() {
|
||||
this._userVerifier.clear();
|
||||
|
||||
this._updateSensitivity(true);
|
||||
this._promptMessage.hide();
|
||||
this._user = null;
|
||||
@ -719,50 +872,73 @@ const LoginDialog = new Lang.Class({
|
||||
if (message) {
|
||||
this._promptMessage.text = message;
|
||||
this._promptMessage.styleClass = styleClass;
|
||||
this._promptMessage.show();
|
||||
GdmUtil.fadeInActor(this._promptMessage);
|
||||
} else {
|
||||
this._promptMessage.hide();
|
||||
GdmUtil.fadeOutActor(this._promptMessage);
|
||||
}
|
||||
},
|
||||
|
||||
_showLoginHint: function(verifier, message) {
|
||||
this._promptLoginHint.set_text(message)
|
||||
this._promptLoginHint.show();
|
||||
this._promptLoginHint.opacity = 255;
|
||||
GdmUtil.fadeInActor(this._promptLoginHint);
|
||||
},
|
||||
|
||||
_hideLoginHint: function() {
|
||||
this._promptLoginHint.hide();
|
||||
GdmUtil.fadeOutActor(this._promptLoginHint);
|
||||
this._promptLoginHint.set_text('');
|
||||
},
|
||||
|
||||
cancel: function() {
|
||||
if (this._verifyingUser)
|
||||
this._userVerifier.cancel();
|
||||
else
|
||||
this._reset();
|
||||
this._userVerifier.cancel();
|
||||
},
|
||||
|
||||
_fadeInPrompt: function() {
|
||||
let tasks = [function() {
|
||||
return GdmUtil.fadeInActor(this._promptLabel);
|
||||
},
|
||||
|
||||
function() {
|
||||
return GdmUtil.fadeInActor(this._promptEntry);
|
||||
},
|
||||
|
||||
function() {
|
||||
// Show it with 0 opacity so we preallocate space for it
|
||||
// in the event we need to fade in the message
|
||||
this._promptLoginHint.opacity = 0;
|
||||
this._promptLoginHint.show();
|
||||
},
|
||||
|
||||
function() {
|
||||
return GdmUtil.fadeInActor(this._promptBox);
|
||||
},
|
||||
|
||||
function() {
|
||||
if (this._user && this._user.is_logged_in())
|
||||
return null;
|
||||
|
||||
if (!this._verifyingUser)
|
||||
return null;
|
||||
|
||||
return GdmUtil.fadeInActor(this._sessionList.actor);
|
||||
},
|
||||
|
||||
function() {
|
||||
this._promptEntry.grab_key_focus();
|
||||
}];
|
||||
|
||||
this._sessionList.actor.hide();
|
||||
let batch = new Batch.ConcurrentBatch(this, tasks);
|
||||
return batch.run();
|
||||
},
|
||||
|
||||
_showPrompt: function(forSecret) {
|
||||
this._sessionList.actor.hide();
|
||||
this._promptLabel.show();
|
||||
this._promptEntry.show();
|
||||
this._promptLoginHint.opacity = 0;
|
||||
this._promptLoginHint.show();
|
||||
this._promptBox.opacity = 0;
|
||||
this._promptBox.show();
|
||||
Tweener.addTween(this._promptBox,
|
||||
{ opacity: 255,
|
||||
time: _FADE_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad' });
|
||||
|
||||
if ((this._user && !this._user.is_logged_in()) || this._verifyingUser)
|
||||
this._sessionList.actor.show();
|
||||
|
||||
this._promptEntry.grab_key_focus();
|
||||
|
||||
let hold = new Batch.Hold();
|
||||
|
||||
let tasks = [function() {
|
||||
return this._fadeInPrompt();
|
||||
},
|
||||
|
||||
function() {
|
||||
this._prepareDialog(forSecret, hold);
|
||||
},
|
||||
|
||||
@ -814,11 +990,6 @@ const LoginDialog = new Lang.Class({
|
||||
Lang.bind(this, function() {
|
||||
this._updateSignInButtonSensitivity(this._promptEntry.text.length > 0);
|
||||
}));
|
||||
|
||||
this._promptEntryActivateId =
|
||||
this._promptEntry.clutter_text.connect('activate', function() {
|
||||
hold.release();
|
||||
});
|
||||
},
|
||||
|
||||
_updateSensitivity: function(sensitive) {
|
||||
@ -843,26 +1014,26 @@ const LoginDialog = new Lang.Class({
|
||||
this._promptEntryTextChangedId = 0;
|
||||
}
|
||||
|
||||
if (this._promptEntryActivateId > 0) {
|
||||
this._promptEntry.clutter_text.disconnect(this._promptEntryActivateId);
|
||||
this._promptEntryActivateId = 0;
|
||||
}
|
||||
let tasks = [function() {
|
||||
this._setWorking(false);
|
||||
|
||||
this._setWorking(false);
|
||||
this._promptBox.hide();
|
||||
this._promptLoginHint.hide();
|
||||
return GdmUtil.fadeOutActor(this._promptBox);
|
||||
},
|
||||
|
||||
this._promptUser.set_child(null);
|
||||
function() {
|
||||
this._promptLoginHint.hide();
|
||||
|
||||
this._updateSensitivity(true);
|
||||
this._promptEntry.set_text('');
|
||||
this._updateSensitivity(true);
|
||||
this._promptEntry.set_text('');
|
||||
|
||||
this._sessionList.close();
|
||||
this._promptLoginHint.hide();
|
||||
this.clearButtons();
|
||||
this._workSpinner = null;
|
||||
this._signInButton = null;
|
||||
}];
|
||||
|
||||
this.clearButtons();
|
||||
this._workSpinner = null;
|
||||
this._signInButton = null;
|
||||
let batch = new Batch.ConsecutiveBatch(this, tasks);
|
||||
|
||||
return batch.run();
|
||||
},
|
||||
|
||||
_setWorking: function(working) {
|
||||
@ -884,8 +1055,7 @@ const LoginDialog = new Lang.Class({
|
||||
transition: 'linear',
|
||||
onCompleteScope: this,
|
||||
onComplete: function() {
|
||||
if (this._workSpinner)
|
||||
this._workSpinner.stop();
|
||||
this._workSpinner.stop();
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -894,7 +1064,6 @@ const LoginDialog = new Lang.Class({
|
||||
_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);
|
||||
|
||||
@ -930,39 +1099,8 @@ const LoginDialog = new Lang.Class({
|
||||
return batch.run();
|
||||
},
|
||||
|
||||
_startSession: function(serviceName) {
|
||||
Tweener.addTween(this.dialogLayout,
|
||||
{ opacity: 0,
|
||||
time: _FADE_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onUpdate: function() {
|
||||
let children = Main.layoutManager.uiGroup.get_children();
|
||||
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
if (children[i] != Main.layoutManager.screenShieldGroup)
|
||||
children[i].opacity = this.dialogLayout.opacity;
|
||||
}
|
||||
},
|
||||
onUpdateScope: this,
|
||||
onComplete: function() {
|
||||
Mainloop.idle_add(Lang.bind(this, function() {
|
||||
this._greeter.call_start_session_when_ready_sync(serviceName, true, null);
|
||||
return false;
|
||||
}));
|
||||
},
|
||||
onCompleteScope: this });
|
||||
},
|
||||
|
||||
_onSessionOpened: function(client, serviceName) {
|
||||
if (!this._userVerifier.hasPendingMessages) {
|
||||
this._startSession(serviceName);
|
||||
} else {
|
||||
let signalId = this._userVerifier.connect('no-more-messages',
|
||||
Lang.bind(this, function() {
|
||||
this._userVerifier.disconnect(signalId);
|
||||
this._startSession(serviceName);
|
||||
}));
|
||||
}
|
||||
this._greeter.call_start_session_when_ready_sync(serviceName, true, null);
|
||||
},
|
||||
|
||||
_waitForItemForUser: function(userName) {
|
||||
@ -1028,7 +1166,7 @@ const LoginDialog = new Lang.Class({
|
||||
function() {
|
||||
// If we're just starting out, start on the right
|
||||
// item.
|
||||
if (!this._userManager.is_loaded) {
|
||||
if (!this.is_loaded) {
|
||||
this._userList.jumpToItem(this._timedLoginItem);
|
||||
}
|
||||
},
|
||||
@ -1089,21 +1227,75 @@ const LoginDialog = new Lang.Class({
|
||||
}));
|
||||
},
|
||||
|
||||
_setUserListExpanded: function(expanded) {
|
||||
this._userList.updateStyle(expanded);
|
||||
this._userSelectionBox.visible = expanded;
|
||||
},
|
||||
|
||||
_hideUserListAndLogIn: function() {
|
||||
this._setUserListExpanded(false);
|
||||
GdmUtil.cloneAndFadeOutActor(this._userSelectionBox);
|
||||
this._askForUsernameAndLogIn();
|
||||
let tasks = [function() {
|
||||
return this._userList.hideItems();
|
||||
},
|
||||
|
||||
function() {
|
||||
return this._userList.giveUpWhitespace();
|
||||
},
|
||||
|
||||
function() {
|
||||
this._userList.actor.hide();
|
||||
},
|
||||
|
||||
new Batch.ConcurrentBatch(this, [this._fadeOutTitleLabel,
|
||||
this._fadeOutNotListedButton]),
|
||||
|
||||
function() {
|
||||
return this._askForUsernameAndLogIn();
|
||||
}];
|
||||
|
||||
let batch = new Batch.ConsecutiveBatch(this, tasks);
|
||||
batch.run();
|
||||
},
|
||||
|
||||
_showUserList: function() {
|
||||
this._hidePrompt();
|
||||
this._setUserListExpanded(true);
|
||||
this._userList.actor.grab_key_focus();
|
||||
let tasks = [this._hidePrompt,
|
||||
|
||||
new Batch.ConcurrentBatch(this, [this._fadeInTitleLabel,
|
||||
this._fadeInNotListedButton]),
|
||||
|
||||
function() {
|
||||
this._sessionList.close();
|
||||
this._promptLoginHint.hide();
|
||||
this._userList.actor.show();
|
||||
this._userList.actor.opacity = 255;
|
||||
return this._userList.showItems();
|
||||
},
|
||||
|
||||
function() {
|
||||
this._userList.actor.reactive = true;
|
||||
this._userList.actor.grab_key_focus();
|
||||
}];
|
||||
|
||||
let batch = new Batch.ConsecutiveBatch(this, tasks);
|
||||
batch.run();
|
||||
},
|
||||
|
||||
_fadeInBanner: function() {
|
||||
return GdmUtil.fadeInActor(this._bannerLabel);
|
||||
},
|
||||
|
||||
_fadeOutBanner: function() {
|
||||
return GdmUtil.fadeOutActor(this._bannerLabel);
|
||||
},
|
||||
|
||||
_fadeInTitleLabel: function() {
|
||||
return GdmUtil.fadeInActor(this._titleLabel);
|
||||
},
|
||||
|
||||
_fadeOutTitleLabel: function() {
|
||||
return GdmUtil.fadeOutActor(this._titleLabel);
|
||||
},
|
||||
|
||||
_fadeInNotListedButton: function() {
|
||||
return GdmUtil.fadeInActor(this._notListedButton);
|
||||
},
|
||||
|
||||
_fadeOutNotListedButton: function() {
|
||||
return GdmUtil.fadeOutActor(this._notListedButton);
|
||||
},
|
||||
|
||||
_beginVerificationForUser: function(userName) {
|
||||
@ -1114,30 +1306,38 @@ const LoginDialog = new Lang.Class({
|
||||
return hold;
|
||||
},
|
||||
|
||||
_beginVerificationForItem: function(item) {
|
||||
let userWidget = new UserWidget.UserWidget(item.user);
|
||||
this._promptUser.set_child(userWidget.actor);
|
||||
|
||||
let tasks = [function() {
|
||||
let userName = item.user.get_user_name();
|
||||
return this._beginVerificationForUser(userName);
|
||||
}];
|
||||
let batch = new Batch.ConsecutiveBatch(this, tasks);
|
||||
return batch.run();
|
||||
},
|
||||
|
||||
_onUserListActivated: function(activatedItem) {
|
||||
let userName;
|
||||
|
||||
let tasks = [function() {
|
||||
return GdmUtil.cloneAndFadeOutActor(this._userSelectionBox);
|
||||
this._userList.actor.reactive = false;
|
||||
return this._userList.pinInPlace();
|
||||
},
|
||||
|
||||
function() {
|
||||
this._setUserListExpanded(false);
|
||||
return this._userList.hideItemsExcept(activatedItem);
|
||||
},
|
||||
|
||||
function() {
|
||||
return this._userList.giveUpWhitespace();
|
||||
},
|
||||
|
||||
new Batch.ConcurrentBatch(this, [this._fadeOutTitleLabel,
|
||||
this._fadeOutNotListedButton]),
|
||||
|
||||
function() {
|
||||
return this._userList.shrinkToNaturalHeight();
|
||||
},
|
||||
|
||||
function() {
|
||||
userName = activatedItem.user.get_user_name();
|
||||
|
||||
return this._beginVerificationForUser(userName);
|
||||
}];
|
||||
|
||||
this._user = activatedItem.user;
|
||||
|
||||
let batch = new Batch.ConcurrentBatch(this, [new Batch.ConsecutiveBatch(this, tasks),
|
||||
this._beginVerificationForItem(activatedItem)]);
|
||||
let batch = new Batch.ConsecutiveBatch(this, tasks);
|
||||
batch.run();
|
||||
},
|
||||
|
||||
@ -1166,6 +1366,15 @@ const LoginDialog = new Lang.Class({
|
||||
Lang.bind(this, function(userManager, user) {
|
||||
this._userList.removeUser(user);
|
||||
}));
|
||||
|
||||
// emitted in idle so caller doesn't have to explicitly check if
|
||||
// it's loaded immediately after construction
|
||||
// (since there's no way the caller could be listening for
|
||||
// 'loaded' yet)
|
||||
Mainloop.idle_add(Lang.bind(this, function() {
|
||||
this.emit('loaded');
|
||||
this.is_loaded = true;
|
||||
}));
|
||||
},
|
||||
|
||||
_onOpened: function() {
|
||||
@ -1180,9 +1389,5 @@ const LoginDialog = new Lang.Class({
|
||||
this.parent();
|
||||
|
||||
Main.ctrlAltTabManager.removeGroup(this.dialogLayout);
|
||||
},
|
||||
|
||||
addCharacter: function(unichar) {
|
||||
this._promptEntry.clutter_text.insert_unichar(unichar);
|
||||
},
|
||||
}
|
||||
});
|
||||
|
146
js/gdm/util.js
146
js/gdm/util.js
@ -1,8 +1,6 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Signals = imports.signals;
|
||||
@ -17,7 +15,6 @@ const Tweener = imports.ui.tweener;
|
||||
const PASSWORD_SERVICE_NAME = 'gdm-password';
|
||||
const FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint';
|
||||
const FADE_ANIMATION_TIME = 0.16;
|
||||
const CLONE_FADE_ANIMATION_TIME = 0.25;
|
||||
|
||||
const LOGIN_SCREEN_SCHEMA = 'org.gnome.login-screen';
|
||||
const FINGERPRINT_AUTHENTICATION_KEY = 'enable-fingerprint-authentication';
|
||||
@ -28,9 +25,6 @@ const ALLOWED_FAILURES_KEY = 'allowed-failures';
|
||||
const LOGO_KEY = 'logo';
|
||||
const DISABLE_USER_LIST_KEY = 'disable-user-list';
|
||||
|
||||
// Give user 16ms to read each character of a PAM message
|
||||
const USER_READ_TIME = 16
|
||||
|
||||
function fadeInActor(actor) {
|
||||
if (actor.opacity == 255 && actor.visible)
|
||||
return null;
|
||||
@ -77,34 +71,6 @@ function fadeOutActor(actor) {
|
||||
return hold;
|
||||
}
|
||||
|
||||
function cloneAndFadeOutActor(actor) {
|
||||
// Immediately hide actor so its sibling can have its space
|
||||
// and position, but leave a non-reactive clone on-screen,
|
||||
// so from the user's point of view it smoothly fades away
|
||||
// and reveals its sibling.
|
||||
actor.hide();
|
||||
|
||||
let clone = new Clutter.Clone({ source: actor,
|
||||
reactive: false });
|
||||
|
||||
Main.uiGroup.add_child(clone);
|
||||
|
||||
let [x, y] = actor.get_transformed_position();
|
||||
clone.set_position(x, y);
|
||||
|
||||
let hold = new Batch.Hold();
|
||||
Tweener.addTween(clone,
|
||||
{ opacity: 0,
|
||||
time: CLONE_FADE_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: function() {
|
||||
clone.destroy();
|
||||
hold.release();
|
||||
}
|
||||
});
|
||||
return hold;
|
||||
}
|
||||
|
||||
const ShellUserVerifier = new Lang.Class({
|
||||
Name: 'ShellUserVerifier',
|
||||
|
||||
@ -118,9 +84,6 @@ const ShellUserVerifier = new Lang.Class({
|
||||
|
||||
this._fprintManager = new Fprint.FprintManager();
|
||||
this._realmManager = new Realmd.Manager();
|
||||
this._messageQueue = [];
|
||||
this._messageQueueTimeoutId = 0;
|
||||
this.hasPendingMessages = false;
|
||||
|
||||
this._failCounter = 0;
|
||||
},
|
||||
@ -160,73 +123,13 @@ const ShellUserVerifier = new Lang.Class({
|
||||
this._userVerifier.run_dispose();
|
||||
this._userVerifier = null;
|
||||
}
|
||||
|
||||
this._clearMessageQueue();
|
||||
},
|
||||
|
||||
answerQuery: function(serviceName, answer) {
|
||||
if (!this._userVerifier.hasPendingMessages) {
|
||||
this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
|
||||
} else {
|
||||
let signalId = this._userVerifier.connect('no-more-messages',
|
||||
Lang.bind(this, function() {
|
||||
this._userVerifier.disconnect(signalId);
|
||||
this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
|
||||
}));
|
||||
}
|
||||
},
|
||||
|
||||
_getIntervalForMessage: function(message) {
|
||||
// We probably could be smarter here
|
||||
return message.length * USER_READ_TIME;
|
||||
},
|
||||
|
||||
finishMessageQueue: function() {
|
||||
if (!this.hasPendingMessages)
|
||||
return;
|
||||
|
||||
this._messageQueue = [];
|
||||
|
||||
this.hasPendingMessages = false;
|
||||
this.emit('no-more-messages');
|
||||
},
|
||||
|
||||
_queueMessageTimeout: function() {
|
||||
if (this._messageQueue.length == 0) {
|
||||
this.finishMessageQueue();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._messageQueueTimeoutId != 0)
|
||||
return;
|
||||
|
||||
let message = this._messageQueue.shift();
|
||||
this.emit('show-message', message.text, message.iconName);
|
||||
|
||||
this._messageQueueTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
|
||||
message.interval,
|
||||
Lang.bind(this, function() {
|
||||
this._messageQueueTimeoutId = 0;
|
||||
this._queueMessageTimeout();
|
||||
}));
|
||||
},
|
||||
|
||||
_queueMessage: function(message, iconName) {
|
||||
let interval = this._getIntervalForMessage(message);
|
||||
|
||||
this.hasPendingMessages = true;
|
||||
this._messageQueue.push({ text: message, interval: interval, iconName: iconName });
|
||||
this._queueMessageTimeout();
|
||||
},
|
||||
|
||||
_clearMessageQueue: function() {
|
||||
this.finishMessageQueue();
|
||||
|
||||
if (this._messageQueueTimeoutId != 0) {
|
||||
GLib.source_remove(this._messageQueueTimeoutId);
|
||||
this._messageQueueTimeoutId = 0;
|
||||
}
|
||||
// Clear any previous message
|
||||
this.emit('show-message', null, null);
|
||||
|
||||
this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
|
||||
},
|
||||
|
||||
_checkForFingerprintReader: function() {
|
||||
@ -246,7 +149,7 @@ const ShellUserVerifier = new Lang.Class({
|
||||
logError(error, where);
|
||||
this._hold.release();
|
||||
|
||||
this._queueMessage(_("Authentication error"), 'login-dialog-message-warning');
|
||||
this.emit('show-message', _("Authentication error"), 'login-dialog-message-warning');
|
||||
this._verificationFailed(false);
|
||||
},
|
||||
|
||||
@ -365,7 +268,7 @@ const ShellUserVerifier = new Lang.Class({
|
||||
// to indicate the user can swipe their finger instead
|
||||
this.emit('show-login-hint', _("(or swipe finger)"));
|
||||
} else if (serviceName == PASSWORD_SERVICE_NAME) {
|
||||
this._queueMessage(info, 'login-dialog-message-info');
|
||||
this.emit('show-message', info, 'login-dialog-message-info');
|
||||
}
|
||||
},
|
||||
|
||||
@ -374,7 +277,7 @@ const ShellUserVerifier = new Lang.Class({
|
||||
// users who haven't enrolled their fingerprint.
|
||||
if (serviceName != PASSWORD_SERVICE_NAME)
|
||||
return;
|
||||
this._queueMessage(problem, 'login-dialog-message-warning');
|
||||
this.emit('show-message', problem, 'login-dialog-message-warning');
|
||||
},
|
||||
|
||||
_showRealmLoginHint: function() {
|
||||
@ -413,6 +316,8 @@ const ShellUserVerifier = new Lang.Class({
|
||||
},
|
||||
|
||||
_onReset: function() {
|
||||
this.clear();
|
||||
|
||||
// Clear previous attempts to authenticate
|
||||
this._failCounter = 0;
|
||||
|
||||
@ -423,15 +328,6 @@ const ShellUserVerifier = new Lang.Class({
|
||||
this.emit('verification-complete');
|
||||
},
|
||||
|
||||
_cancelAndReset: function() {
|
||||
this.cancel();
|
||||
this._onReset();
|
||||
},
|
||||
|
||||
_retry: function() {
|
||||
this.begin(this._userName, new Batch.Hold());
|
||||
},
|
||||
|
||||
_verificationFailed: function(retry) {
|
||||
// For Not Listed / enterprise logins, immediately reset
|
||||
// the dialog
|
||||
@ -443,25 +339,15 @@ const ShellUserVerifier = new Lang.Class({
|
||||
this._failCounter < this._settings.get_int(ALLOWED_FAILURES_KEY);
|
||||
|
||||
if (canRetry) {
|
||||
if (!this._userVerifier.hasPendingMessages) {
|
||||
this._retry();
|
||||
} else {
|
||||
let signalId = this._userVerifier.connect('no-more-messages',
|
||||
Lang.bind(this, function() {
|
||||
this._userVerifier.disconnect(signalId);
|
||||
this._retry();
|
||||
}));
|
||||
}
|
||||
this.clear();
|
||||
this.begin(this._userName, new Batch.Hold());
|
||||
} else {
|
||||
if (!this._userVerifier.hasPendingMessages) {
|
||||
this._cancelAndReset();
|
||||
} else {
|
||||
let signalId = this._userVerifier.connect('no-more-messages',
|
||||
Lang.bind(this, function() {
|
||||
this._userVerifier.disconnect(signalId);
|
||||
this._cancelAndReset();
|
||||
}));
|
||||
}
|
||||
// Allow some time to see the message, then reset everything
|
||||
Mainloop.timeout_add(3000, Lang.bind(this, function() {
|
||||
this.cancel();
|
||||
|
||||
this._onReset();
|
||||
}));
|
||||
}
|
||||
|
||||
this.emit('verification-failed');
|
||||
|
@ -174,15 +174,10 @@ const ExtensionFinder = new Lang.Class({
|
||||
this.emit('extension-found', extension);
|
||||
},
|
||||
|
||||
_extensionsLoaded: function() {
|
||||
this.emit('extensions-loaded');
|
||||
},
|
||||
|
||||
scanExtensions: function() {
|
||||
let perUserDir = Gio.File.new_for_path(global.userdatadir);
|
||||
FileUtils.collectFromDatadirsAsync('extensions',
|
||||
{ processFile: Lang.bind(this, this._loadExtension),
|
||||
loadedCallback: Lang.bind(this, this._extensionsLoaded),
|
||||
includeUserDir: true,
|
||||
data: perUserDir });
|
||||
}
|
||||
|
@ -58,7 +58,6 @@ const SessionManagerIface = <interface name="org.gnome.SessionManager">
|
||||
<arg type="u" direction="in" />
|
||||
<arg type="b" direction="out" />
|
||||
</method>
|
||||
<property name="SessionIsActive" type="b" access="read"/>
|
||||
<signal name="InhibitorAdded">
|
||||
<arg type="o" direction="out"/>
|
||||
</signal>
|
||||
|
141
js/misc/hash.js
141
js/misc/hash.js
@ -1,141 +0,0 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Lang = imports.lang;
|
||||
const System = imports.system;
|
||||
|
||||
const Params = imports.misc.params;
|
||||
|
||||
// This is an implementation of EcmaScript SameValue algorithm,
|
||||
// which returns true if two values are not observably distinguishable.
|
||||
// It was taken from http://wiki.ecmascript.org/doku.php?id=harmony:egal
|
||||
//
|
||||
// In the future, we may want to use the 'is' operator instead.
|
||||
function _sameValue(x, y) {
|
||||
if (x === y) {
|
||||
// 0 === -0, but they are not identical
|
||||
return x !== 0 || 1 / x === 1 / y;
|
||||
}
|
||||
|
||||
// NaN !== NaN, but they are identical.
|
||||
// NaNs are the only non-reflexive value, i.e., if x !== x,
|
||||
// then x is a NaN.
|
||||
// isNaN is broken: it converts its argument to number, so
|
||||
// isNaN("foo") => true
|
||||
return x !== x && y !== y;
|
||||
}
|
||||
|
||||
const _hashers = {
|
||||
object: function(o) { return o ? System.addressOf(o) : 'null'; },
|
||||
function: function(f) { return System.addressOf(f); },
|
||||
string: function(s) { return s; },
|
||||
number: function(n) { return String(n); },
|
||||
undefined: function() { return 'undefined'; },
|
||||
};
|
||||
|
||||
/* Map is meant to be similar in usage to ES6 Map, which is
|
||||
described at http://wiki.ecmascript.org/doku.php?id=harmony:simple_maps_and_sets,
|
||||
without requiring more than ES5 + Gjs extensions.
|
||||
|
||||
Known differences from other implementations:
|
||||
Polyfills around the web usually implement HashMaps for
|
||||
primitive values and reversed WeakMaps for object keys,
|
||||
but we want real maps with real O(1) semantics in all cases,
|
||||
and the easiest way is to have different hashers for different
|
||||
types.
|
||||
|
||||
Known differences from the ES6 specification:
|
||||
- Map is a Lang.Class, not a ES6 class, so inheritance,
|
||||
prototype, sealing, etc. work differently.
|
||||
- items(), keys() and values() don't return iterators,
|
||||
they return actual arrays, so they incur a full copy everytime
|
||||
they're called, and they don't see changes if you mutate
|
||||
the table while iterating
|
||||
(admittedly, the ES6 spec is a bit unclear on this, and
|
||||
the reference code would just blow up)
|
||||
*/
|
||||
const Map = new Lang.Class({
|
||||
Name: 'Map',
|
||||
|
||||
_init: function(iterable) {
|
||||
this._pool = { };
|
||||
|
||||
if (iterable) {
|
||||
for (let i = 0; i < iterable.length; i++) {
|
||||
let [key, value] = iterable[i];
|
||||
this.set(key, value);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_hashKey: function(key) {
|
||||
let type = typeof(key);
|
||||
return type + ':' + _hashers[type](key);
|
||||
},
|
||||
|
||||
_internalGet: function(key) {
|
||||
let hash = this._hashKey(key);
|
||||
let node = this._pool[hash];
|
||||
|
||||
if (node && _sameValue(node.key, key))
|
||||
return [true, node.value];
|
||||
else
|
||||
return [false, null];
|
||||
},
|
||||
|
||||
get: function(key) {
|
||||
return this._internalGet(key)[1];
|
||||
},
|
||||
|
||||
has: function(key) {
|
||||
return this._internalGet(key)[0];
|
||||
},
|
||||
|
||||
set: function(key, value) {
|
||||
let hash = this._hashKey(key);
|
||||
let node = this._pool[hash];
|
||||
|
||||
if (node) {
|
||||
node.key = key;
|
||||
node.value = value;
|
||||
} else {
|
||||
this._pool[hash] = { key: key, value: value };
|
||||
}
|
||||
},
|
||||
|
||||
delete: function(key) {
|
||||
let hash = this._hashKey(key);
|
||||
let node = this._pool[hash];
|
||||
|
||||
if (node && _sameValue(node.key, key)) {
|
||||
delete this._pool[hash];
|
||||
return [node.key, node.value];
|
||||
} else {
|
||||
return [null, null];
|
||||
}
|
||||
},
|
||||
|
||||
keys: function() {
|
||||
let pool = this._pool;
|
||||
return Object.getOwnPropertyNames(pool).map(function(hash) {
|
||||
return pool[hash].key;
|
||||
});
|
||||
},
|
||||
|
||||
values: function() {
|
||||
let pool = this._pool;
|
||||
return Object.getOwnPropertyNames(pool).map(function(hash) {
|
||||
return pool[hash].value;
|
||||
});
|
||||
},
|
||||
|
||||
items: function() {
|
||||
let pool = this._pool;
|
||||
return Object.getOwnPropertyNames(pool).map(function(hash) {
|
||||
return [pool[hash].key, pool[hash].value];
|
||||
});
|
||||
},
|
||||
|
||||
size: function() {
|
||||
return Object.getOwnPropertyNames(this._pool).length;
|
||||
},
|
||||
});
|
@ -5,7 +5,7 @@ const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const UPowerGlib = imports.gi.UPowerGlib;
|
||||
|
||||
const SystemdLoginManagerIface = <interface name='org.freedesktop.login1.Manager'>
|
||||
<method name='PowerOff'>
|
||||
@ -26,23 +26,6 @@ const SystemdLoginManagerIface = <interface name='org.freedesktop.login1.Manager
|
||||
<method name='CanSuspend'>
|
||||
<arg type='s' direction='out'/>
|
||||
</method>
|
||||
<method name='Inhibit'>
|
||||
<arg type='s' direction='in'/>
|
||||
<arg type='s' direction='in'/>
|
||||
<arg type='s' direction='in'/>
|
||||
<arg type='s' direction='in'/>
|
||||
<arg type='h' direction='out'/>
|
||||
</method>
|
||||
<method name='GetSession'>
|
||||
<arg type='s' direction='in'/>
|
||||
<arg type='o' direction='out'/>
|
||||
</method>
|
||||
<method name='ListSessions'>
|
||||
<arg name='sessions' type='a(susso)' direction='out'/>
|
||||
</method>
|
||||
<signal name='PrepareForSleep'>
|
||||
<arg type='b' direction='out'/>
|
||||
</signal>
|
||||
</interface>;
|
||||
|
||||
const SystemdLoginSessionIface = <interface name='org.freedesktop.login1.Session'>
|
||||
@ -68,6 +51,12 @@ const ConsoleKitManagerIface = <interface name='org.freedesktop.ConsoleKit.Manag
|
||||
</interface>;
|
||||
|
||||
const ConsoleKitSessionIface = <interface name='org.freedesktop.ConsoleKit.Session'>
|
||||
<method name='IsActive'>
|
||||
<arg type='b' direction='out' />
|
||||
</method>
|
||||
<signal name='ActiveChanged'>
|
||||
<arg type='b' direction='out' />
|
||||
</signal>
|
||||
<signal name='Lock' />
|
||||
<signal name='Unlock' />
|
||||
</interface>;
|
||||
@ -76,36 +65,7 @@ const ConsoleKitSession = Gio.DBusProxy.makeProxyWrapper(ConsoleKitSessionIface)
|
||||
const ConsoleKitManager = Gio.DBusProxy.makeProxyWrapper(ConsoleKitManagerIface);
|
||||
|
||||
function haveSystemd() {
|
||||
return GLib.access("/run/systemd/seats", 0) >= 0;
|
||||
}
|
||||
|
||||
function versionCompare(required, reference) {
|
||||
required = required.split('.');
|
||||
reference = reference.split('.');
|
||||
|
||||
for (let i = 0; i < required.length; i++) {
|
||||
if (required[i] != reference[i])
|
||||
return required[i] < reference[i];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function canLock() {
|
||||
try {
|
||||
let params = GLib.Variant.new('(ss)', ['org.gnome.DisplayManager.Manager', 'Version']);
|
||||
let result = Gio.DBus.system.call_sync('org.gnome.DisplayManager',
|
||||
'/org/gnome/DisplayManager/Manager',
|
||||
'org.freedesktop.DBus.Properties',
|
||||
'Get', params, null,
|
||||
Gio.DBusCallFlags.NONE,
|
||||
-1, null);
|
||||
|
||||
let version = result.deep_unpack()[0].deep_unpack();
|
||||
return versionCompare('3.5.91', version);
|
||||
} catch(e) {
|
||||
return false;
|
||||
}
|
||||
return GLib.access("/sys/fs/cgroup/systemd", 0) >= 0;
|
||||
}
|
||||
|
||||
let _loginManager = null;
|
||||
@ -133,30 +93,24 @@ const LoginManagerSystemd = new Lang.Class({
|
||||
this._proxy = new SystemdLoginManager(Gio.DBus.system,
|
||||
'org.freedesktop.login1',
|
||||
'/org/freedesktop/login1');
|
||||
this._proxy.connectSignal('PrepareForSleep',
|
||||
Lang.bind(this, this._prepareForSleep));
|
||||
},
|
||||
|
||||
// Having this function is a bit of a hack since the Systemd and ConsoleKit
|
||||
// session objects have different interfaces - but in both cases there are
|
||||
// Lock/Unlock signals, and that's all we count upon at the moment.
|
||||
getCurrentSessionProxy: function(callback) {
|
||||
if (this._currentSession) {
|
||||
callback (this._currentSession);
|
||||
return;
|
||||
getCurrentSessionProxy: function() {
|
||||
if (!this._currentSession) {
|
||||
this._currentSession = new SystemdLoginSession(Gio.DBus.system,
|
||||
'org.freedesktop.login1',
|
||||
'/org/freedesktop/login1/session/' +
|
||||
GLib.getenv('XDG_SESSION_ID'));
|
||||
}
|
||||
|
||||
this._proxy.GetSessionRemote(GLib.getenv('XDG_SESSION_ID'), Lang.bind(this,
|
||||
function(result, error) {
|
||||
if (error) {
|
||||
logError(error, 'Could not get a proxy for the current session');
|
||||
} else {
|
||||
this._currentSession = new SystemdLoginSession(Gio.DBus.system,
|
||||
'org.freedesktop.login1',
|
||||
result[0]);
|
||||
callback(this._currentSession);
|
||||
}
|
||||
}));
|
||||
return this._currentSession;
|
||||
},
|
||||
|
||||
get sessionActive() {
|
||||
return Shell.session_is_active_for_systemd();
|
||||
},
|
||||
|
||||
canPowerOff: function(asyncCallback) {
|
||||
@ -186,15 +140,6 @@ const LoginManagerSystemd = new Lang.Class({
|
||||
});
|
||||
},
|
||||
|
||||
listSessions: function(asyncCallback) {
|
||||
this._proxy.ListSessionsRemote(function(result, error) {
|
||||
if (error)
|
||||
asyncCallback([]);
|
||||
else
|
||||
asyncCallback(result[0]);
|
||||
});
|
||||
},
|
||||
|
||||
powerOff: function() {
|
||||
this._proxy.PowerOffRemote(true);
|
||||
},
|
||||
@ -205,33 +150,8 @@ const LoginManagerSystemd = new Lang.Class({
|
||||
|
||||
suspend: function() {
|
||||
this._proxy.SuspendRemote(true);
|
||||
},
|
||||
|
||||
inhibit: function(reason, callback) {
|
||||
let inVariant = GLib.Variant.new('(ssss)',
|
||||
['sleep',
|
||||
'GNOME Shell',
|
||||
reason,
|
||||
'delay']);
|
||||
this._proxy.call_with_unix_fd_list('Inhibit', inVariant, 0, -1, null, null,
|
||||
Lang.bind(this, function(proxy, result) {
|
||||
let fd = -1;
|
||||
try {
|
||||
let [outVariant, fdList] = proxy.call_with_unix_fd_list_finish(result);
|
||||
fd = fdList.steal_fds(outVariant.deep_unpack())[0];
|
||||
callback(new Gio.UnixInputStream({ fd: fd }));
|
||||
} catch(e) {
|
||||
logError(e, "Error getting systemd inhibitor");
|
||||
callback(null);
|
||||
}
|
||||
}));
|
||||
},
|
||||
|
||||
_prepareForSleep: function(proxy, sender, [aboutToSuspend]) {
|
||||
this.emit('prepare-for-sleep', aboutToSuspend);
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(LoginManagerSystemd.prototype);
|
||||
|
||||
const LoginManagerConsoleKit = new Lang.Class({
|
||||
Name: 'LoginManagerConsoleKit',
|
||||
@ -240,28 +160,34 @@ const LoginManagerConsoleKit = new Lang.Class({
|
||||
this._proxy = new ConsoleKitManager(Gio.DBus.system,
|
||||
'org.freedesktop.ConsoleKit',
|
||||
'/org/freedesktop/ConsoleKit/Manager');
|
||||
this._upClient = new UPowerGlib.Client();
|
||||
},
|
||||
|
||||
// Having this function is a bit of a hack since the Systemd and ConsoleKit
|
||||
// session objects have different interfaces - but in both cases there are
|
||||
// Lock/Unlock signals, and that's all we count upon at the moment.
|
||||
getCurrentSessionProxy: function(callback) {
|
||||
if (this._currentSession) {
|
||||
callback (this._currentSession);
|
||||
return;
|
||||
getCurrentSessionProxy: function() {
|
||||
if (!this._currentSession) {
|
||||
let [currentSessionId] = this._proxy.GetCurrentSessionSync();
|
||||
this._currentSession = new ConsoleKitSession(Gio.DBus.system,
|
||||
'org.freedesktop.ConsoleKit',
|
||||
currentSessionId);
|
||||
}
|
||||
|
||||
this._proxy.GetCurrentSessionRemote(Lang.bind(this,
|
||||
function(result, error) {
|
||||
if (error) {
|
||||
logError(error, 'Could not get a proxy for the current session');
|
||||
} else {
|
||||
this._currentSession = new ConsoleKitSession(Gio.DBus.system,
|
||||
'org.freedesktop.ConsoleKit',
|
||||
result[0]);
|
||||
callback(this._currentSession);
|
||||
}
|
||||
}));
|
||||
return this._currentSession;
|
||||
},
|
||||
|
||||
get sessionActive() {
|
||||
if (this._sessionActive !== undefined)
|
||||
return this._sessionActive;
|
||||
|
||||
let session = this.getCurrentSessionProxy();
|
||||
session.connectSignal('ActiveChanged', Lang.bind(this, function(object, senderName, [isActive]) {
|
||||
this._sessionActive = isActive;
|
||||
}));
|
||||
[this._sessionActive] = session.IsActiveSync();
|
||||
|
||||
return this._sessionActive;
|
||||
},
|
||||
|
||||
canPowerOff: function(asyncCallback) {
|
||||
@ -283,11 +209,10 @@ const LoginManagerConsoleKit = new Lang.Class({
|
||||
},
|
||||
|
||||
canSuspend: function(asyncCallback) {
|
||||
asyncCallback(false);
|
||||
},
|
||||
|
||||
listSessions: function(asyncCallback) {
|
||||
asyncCallback([]);
|
||||
Mainloop.idle_add(Lang.bind(this, function() {
|
||||
asyncCallback(this._upClient.get_can_suspend());
|
||||
return false;
|
||||
}));
|
||||
},
|
||||
|
||||
powerOff: function() {
|
||||
@ -299,12 +224,6 @@ const LoginManagerConsoleKit = new Lang.Class({
|
||||
},
|
||||
|
||||
suspend: function() {
|
||||
this.emit('prepare-for-sleep', true);
|
||||
this.emit('prepare-for-sleep', false);
|
||||
},
|
||||
|
||||
inhibit: function(reason, callback) {
|
||||
callback(null);
|
||||
this._upClient.suspend_sync(null);
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(LoginManagerConsoleKit.prototype);
|
||||
|
@ -2,93 +2,9 @@
|
||||
|
||||
const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
const NMGtk = imports.gi.NMGtk;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
|
||||
// _getMobileProvidersDatabase:
|
||||
//
|
||||
// Gets the database of mobile providers, with references between MCCMNC/SID and
|
||||
// operator name
|
||||
//
|
||||
let _mpd;
|
||||
function _getMobileProvidersDatabase() {
|
||||
if (_mpd == null) {
|
||||
try {
|
||||
_mpd = new NMGtk.MobileProvidersDatabase();
|
||||
_mpd.init(null);
|
||||
} catch (e) {
|
||||
log(e.message);
|
||||
_mpd = null;
|
||||
}
|
||||
}
|
||||
|
||||
return _mpd;
|
||||
}
|
||||
|
||||
// _findProviderForMccMnc:
|
||||
// @operator_name: operator name
|
||||
// @operator_code: operator code
|
||||
//
|
||||
// Given an operator name string (which may not be a real operator name) and an
|
||||
// operator code string, tries to find a proper operator name to display.
|
||||
//
|
||||
function _findProviderForMccMnc(operator_name, operator_code) {
|
||||
if (operator_name) {
|
||||
if (operator_name.length != 0 &&
|
||||
(operator_name.length > 6 || operator_name.length < 5)) {
|
||||
// this looks like a valid name, i.e. not an MCCMNC (that some
|
||||
// devices return when not yet connected
|
||||
return operator_name;
|
||||
}
|
||||
|
||||
if (isNaN(parseInt(operator_name))) {
|
||||
// name is definitely not a MCCMNC, so it may be a name
|
||||
// after all; return that
|
||||
return operator_name;
|
||||
}
|
||||
}
|
||||
|
||||
let needle;
|
||||
if ((!operator_name || operator_name.length == 0) && operator_code)
|
||||
needle = operator_code;
|
||||
else if (operator_name && (operator_name.length == 6 || operator_name.length == 5))
|
||||
needle = operator_name;
|
||||
else // nothing to search
|
||||
return null;
|
||||
|
||||
let mpd = _getMobileProvidersDatabase();
|
||||
if (mpd) {
|
||||
let provider = mpd.lookup_3gpp_mcc_mnc(needle);
|
||||
if (provider)
|
||||
return provider.get_name();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// _findProviderForSid:
|
||||
// @sid: System Identifier of the serving CDMA network
|
||||
//
|
||||
// Tries to find the operator name corresponding to the given SID
|
||||
//
|
||||
function _findProviderForSid(sid) {
|
||||
if (sid == 0)
|
||||
return null;
|
||||
|
||||
let mpd = _getMobileProvidersDatabase();
|
||||
if (mpd) {
|
||||
let provider = mpd.lookup_cdma_sid(sid);
|
||||
if (provider)
|
||||
return provider.get_name();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Support for the old ModemManager interface (MM < 0.7)
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
// The following are not the complete interfaces, just the methods we need
|
||||
// (or may need in the future)
|
||||
|
||||
@ -126,6 +42,76 @@ const ModemCdmaInterface = <interface name="org.freedesktop.ModemManager.Modem.C
|
||||
|
||||
const ModemCdmaProxy = Gio.DBusProxy.makeProxyWrapper(ModemCdmaInterface);
|
||||
|
||||
let _providersTable;
|
||||
function _getProvidersTable() {
|
||||
if (_providersTable)
|
||||
return _providersTable;
|
||||
return _providersTable = Shell.mobile_providers_parse(null,null);
|
||||
}
|
||||
|
||||
function findProviderForMCCMNC(table, needle) {
|
||||
let needlemcc = needle.substring(0, 3);
|
||||
let needlemnc = needle.substring(3, needle.length);
|
||||
|
||||
let name2, name3;
|
||||
for (let iter in table) {
|
||||
let country = table[iter];
|
||||
let providers = country.get_providers();
|
||||
|
||||
// Search through each country's providers
|
||||
for (let i = 0; i < providers.length; i++) {
|
||||
let provider = providers[i];
|
||||
|
||||
// Search through MCC/MNC list
|
||||
let list = provider.get_gsm_mcc_mnc();
|
||||
for (let j = 0; j < list.length; j++) {
|
||||
let mccmnc = list[j];
|
||||
|
||||
// Match both 2-digit and 3-digit MNC; prefer a
|
||||
// 3-digit match if found, otherwise a 2-digit one.
|
||||
if (mccmnc.mcc != needlemcc)
|
||||
continue; // MCC was wrong
|
||||
|
||||
if (!name3 && needle.length == 6 && needlemnc == mccmnc.mnc)
|
||||
name3 = provider.name;
|
||||
|
||||
if (!name2 && needlemnc.substring(0, 2) == mccmnc.mnc.substring(0, 2))
|
||||
name2 = provider.name;
|
||||
|
||||
if (name2 && name3)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return name3 || name2 || null;
|
||||
}
|
||||
|
||||
function findProviderForSid(table, sid) {
|
||||
if (sid == 0)
|
||||
return null;
|
||||
|
||||
// Search through each country
|
||||
for (let iter in table) {
|
||||
let country = table[iter];
|
||||
let providers = country.get_providers();
|
||||
|
||||
// Search through each country's providers
|
||||
for (let i = 0; i < providers.length; i++) {
|
||||
let provider = providers[i];
|
||||
let cdma_sid = provider.get_cdma_sid();
|
||||
|
||||
// Search through CDMA SID list
|
||||
for (let j = 0; j < cdma_sid.length; j++) {
|
||||
if (cdma_sid[j] == sid)
|
||||
return provider.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
const ModemGsm = new Lang.Class({
|
||||
Name: 'ModemGsm',
|
||||
|
||||
@ -141,7 +127,7 @@ const ModemGsm = new Lang.Class({
|
||||
this.emit('notify::signal-quality');
|
||||
}));
|
||||
this._proxy.connectSignal('RegistrationInfo', Lang.bind(this, function(proxy, sender, [status, code, name]) {
|
||||
this.operator_name = _findProviderForMccMnc(name, code);
|
||||
this.operator_name = this._findOperatorName(name, code);
|
||||
this.emit('notify::operator-name');
|
||||
}));
|
||||
this._proxy.GetRegistrationInfoRemote(Lang.bind(this, function([result], err) {
|
||||
@ -151,7 +137,7 @@ const ModemGsm = new Lang.Class({
|
||||
}
|
||||
|
||||
let [status, code, name] = result;
|
||||
this.operator_name = _findProviderForMccMnc(name, code);
|
||||
this.operator_name = this._findOperatorName(name, code);
|
||||
this.emit('notify::operator-name');
|
||||
}));
|
||||
this._proxy.GetSignalQualityRemote(Lang.bind(this, function(result, err) {
|
||||
@ -164,6 +150,32 @@ const ModemGsm = new Lang.Class({
|
||||
}
|
||||
this.emit('notify::signal-quality');
|
||||
}));
|
||||
},
|
||||
|
||||
_findOperatorName: function(name, opCode) {
|
||||
if (name) {
|
||||
if (name && name.length != 0 && (name.length > 6 || name.length < 5)) {
|
||||
// this looks like a valid name, i.e. not an MCCMNC (that some
|
||||
// devices return when not yet connected
|
||||
return name;
|
||||
}
|
||||
if (isNaN(parseInt(name))) {
|
||||
// name is definitely not a MCCMNC, so it may be a name
|
||||
// after all; return that
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
let needle;
|
||||
if ((name == null || name.length == 0) && opCode)
|
||||
needle = opCode;
|
||||
else if (name.length == 6 || name.length == 5)
|
||||
needle = name;
|
||||
else // nothing to search
|
||||
return null;
|
||||
|
||||
let table = _getProvidersTable();
|
||||
return findProviderForMCCMNC(table, needle);
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(ModemGsm.prototype);
|
||||
@ -203,99 +215,15 @@ const ModemCdma = new Lang.Class({
|
||||
// it will return an error if the device is not connected
|
||||
this.operator_name = null;
|
||||
} else {
|
||||
let [bandClass, band, sid] = result;
|
||||
|
||||
this.operator_name = _findProviderForSid(sid)
|
||||
let [bandClass, band, id] = result;
|
||||
if (name.length > 0) {
|
||||
let table = _getProvidersTable();
|
||||
this.operator_name = findProviderForSid(table, id);
|
||||
} else
|
||||
this.operator_name = null;
|
||||
}
|
||||
this.emit('notify::operator-name');
|
||||
}));
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(ModemCdma.prototype);
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Support for the new ModemManager1 interface (MM >= 0.7)
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const BroadbandModemInterface = <interface name="org.freedesktop.ModemManager1.Modem">
|
||||
<property name="SignalQuality" type="(ub)" access="read" />
|
||||
</interface>;
|
||||
const BroadbandModemProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModemInterface);
|
||||
|
||||
const BroadbandModem3gppInterface = <interface name="org.freedesktop.ModemManager1.Modem.Modem3gpp">
|
||||
<property name="OperatorCode" type="s" access="read" />
|
||||
<property name="OperatorName" type="s" access="read" />
|
||||
</interface>;
|
||||
const BroadbandModem3gppProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModem3gppInterface);
|
||||
|
||||
const BroadbandModemCdmaInterface = <interface name="org.freedesktop.ModemManager1.Modem.ModemCdma">
|
||||
<property name="Sid" type="u" access="read" />
|
||||
</interface>;
|
||||
const BroadbandModemCdmaProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModemCdmaInterface);
|
||||
|
||||
const BroadbandModem = new Lang.Class({
|
||||
Name: 'BroadbandModem',
|
||||
|
||||
_init: function(path, capabilities) {
|
||||
this._proxy = new BroadbandModemProxy(Gio.DBus.system, 'org.freedesktop.ModemManager1', path);
|
||||
this._proxy_3gpp = new BroadbandModem3gppProxy(Gio.DBus.system, 'org.freedesktop.ModemManager1', path);
|
||||
this._proxy_cdma = new BroadbandModemCdmaProxy(Gio.DBus.system, 'org.freedesktop.ModemManager1', path);
|
||||
this._capabilities = capabilities;
|
||||
|
||||
this._proxy.connect('g-properties-changed', Lang.bind(this, function(proxy, properties) {
|
||||
if ('SignalQuality' in properties.deep_unpack())
|
||||
this._reloadSignalQuality();
|
||||
}));
|
||||
this._reloadSignalQuality();
|
||||
|
||||
this._proxy_3gpp.connect('g-properties-changed', Lang.bind(this, function(proxy, properties) {
|
||||
let unpacked = properties.deep_unpack();
|
||||
if ('OperatorName' in unpacked || 'OperatorCode' in unpacked)
|
||||
this._reload3gppOperatorName();
|
||||
}));
|
||||
this._reload3gppOperatorName();
|
||||
|
||||
this._proxy_cdma.connect('g-properties-changed', Lang.bind(this, function(proxy, properties) {
|
||||
let unpacked = properties.deep_unpack();
|
||||
if ('Nid' in unpacked || 'Sid' in unpacked)
|
||||
this._reloadCdmaOperatorName();
|
||||
}));
|
||||
this._reloadCdmaOperatorName();
|
||||
},
|
||||
|
||||
_reloadSignalQuality: function() {
|
||||
let [quality, recent] = this._proxy.SignalQuality;
|
||||
this.signal_quality = quality;
|
||||
this.emit('notify::signal-quality');
|
||||
},
|
||||
|
||||
_reloadOperatorName: function() {
|
||||
let new_name = "";
|
||||
if (this.operator_name_3gpp && this.operator_name_3gpp.length > 0)
|
||||
new_name += this.operator_name_3gpp;
|
||||
|
||||
if (this.operator_name_cdma && this.operator_name_cdma.length > 0) {
|
||||
if (new_name != "")
|
||||
new_name += ", ";
|
||||
new_name += this.operator_name_cdma;
|
||||
}
|
||||
|
||||
this.operator_name = new_name;
|
||||
this.emit('notify::operator-name');
|
||||
},
|
||||
|
||||
_reload3gppOperatorName: function() {
|
||||
let name = this._proxy_3gpp.OperatorName;
|
||||
let code = this._proxy_3gpp.OperatorCode;
|
||||
this.operator_name_3gpp = _findProviderForMccMnc(name, code);
|
||||
this._reloadOperatorName();
|
||||
},
|
||||
|
||||
_reloadCdmaOperatorName: function() {
|
||||
let sid = this._proxy_cdma.Sid;
|
||||
this.operator_name_cdma = _findProviderForSid(sid);
|
||||
this._reloadOperatorName();
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(BroadbandModem.prototype);
|
||||
|
145
js/misc/util.js
145
js/misc/util.js
@ -1,13 +1,8 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const GLib = imports.gi.GLib;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const SCROLL_TIME = 0.1;
|
||||
|
||||
// http://daringfireball.net/2010/07/improved_regex_for_matching_urls
|
||||
const _balancedParens = '\\((?:[^\\s()<>]+|(?:\\(?:[^\\s()<>]+\\)))*\\)';
|
||||
@ -164,6 +159,86 @@ function killall(processName) {
|
||||
}
|
||||
}
|
||||
|
||||
// This was ported from network-manager-applet
|
||||
// Copyright 2007 - 2011 Red Hat, Inc.
|
||||
// Author: Dan Williams <dcbw@redhat.com>
|
||||
|
||||
const _IGNORED_WORDS = [
|
||||
'Semiconductor',
|
||||
'Components',
|
||||
'Corporation',
|
||||
'Communications',
|
||||
'Company',
|
||||
'Corp.',
|
||||
'Corp',
|
||||
'Co.',
|
||||
'Inc.',
|
||||
'Inc',
|
||||
'Incorporated',
|
||||
'Ltd.',
|
||||
'Limited.',
|
||||
'Intel',
|
||||
'chipset',
|
||||
'adapter',
|
||||
'[hex]',
|
||||
'NDIS',
|
||||
'Module'
|
||||
];
|
||||
|
||||
const _IGNORED_PHRASES = [
|
||||
'Multiprotocol MAC/baseband processor',
|
||||
'Wireless LAN Controller',
|
||||
'Wireless LAN Adapter',
|
||||
'Wireless Adapter',
|
||||
'Network Connection',
|
||||
'Wireless Cardbus Adapter',
|
||||
'Wireless CardBus Adapter',
|
||||
'54 Mbps Wireless PC Card',
|
||||
'Wireless PC Card',
|
||||
'Wireless PC',
|
||||
'PC Card with XJACK(r) Antenna',
|
||||
'Wireless cardbus',
|
||||
'Wireless LAN PC Card',
|
||||
'Technology Group Ltd.',
|
||||
'Communication S.p.A.',
|
||||
'Business Mobile Networks BV',
|
||||
'Mobile Broadband Minicard Composite Device',
|
||||
'Mobile Communications AB',
|
||||
'(PC-Suite Mode)'
|
||||
];
|
||||
|
||||
function fixupPCIDescription(desc) {
|
||||
desc = desc.replace(/[_,]/, ' ');
|
||||
|
||||
/* Attempt to shorten ID by ignoring certain phrases */
|
||||
for (let i = 0; i < _IGNORED_PHRASES.length; i++) {
|
||||
let item = _IGNORED_PHRASES[i];
|
||||
let pos = desc.indexOf(item);
|
||||
if (pos != -1) {
|
||||
let before = desc.substring(0, pos);
|
||||
let after = desc.substring(pos + item.length, desc.length);
|
||||
desc = before + after;
|
||||
}
|
||||
}
|
||||
|
||||
/* Attmept to shorten ID by ignoring certain individual words */
|
||||
let words = desc.split(' ');
|
||||
let out = [ ];
|
||||
for (let i = 0; i < words.length; i++) {
|
||||
let item = words[i];
|
||||
|
||||
// skip empty items (that come out from consecutive spaces)
|
||||
if (item.length == 0)
|
||||
continue;
|
||||
|
||||
if (_IGNORED_WORDS.indexOf(item) == -1) {
|
||||
out.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
return out.join(' ');
|
||||
}
|
||||
|
||||
// lowerBound:
|
||||
// @array: an array or array-like object, already sorted
|
||||
// according to @cmp
|
||||
@ -213,63 +288,3 @@ function insertSorted(array, val, cmp) {
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
function makeCloseButton() {
|
||||
let closeButton = new St.Button({ style_class: 'notification-close'});
|
||||
|
||||
// This is a bit tricky. St.Bin has its own x-align/y-align properties
|
||||
// that compete with Clutter's properties. This should be fixed for
|
||||
// Clutter 2.0. Since St.Bin doesn't define its own setters, the
|
||||
// setters are a workaround to get Clutter's version.
|
||||
closeButton.set_x_align(Clutter.ActorAlign.END);
|
||||
closeButton.set_y_align(Clutter.ActorAlign.START);
|
||||
|
||||
// XXX Clutter 2.0 workaround: ClutterBinLayout needs expand
|
||||
// to respect the alignments.
|
||||
closeButton.set_x_expand(true);
|
||||
closeButton.set_y_expand(true);
|
||||
|
||||
closeButton.connect('style-changed', function() {
|
||||
let themeNode = closeButton.get_theme_node();
|
||||
closeButton.translation_x = themeNode.get_length('-shell-close-overlap-x');
|
||||
closeButton.translation_y = themeNode.get_length('-shell-close-overlap-y');
|
||||
});
|
||||
|
||||
return closeButton;
|
||||
}
|
||||
|
||||
function ensureActorVisibleInScrollView(scrollView, actor) {
|
||||
let adjustment = scrollView.vscroll.adjustment;
|
||||
let [value, lower, upper, stepIncrement, pageIncrement, pageSize] = adjustment.get_values();
|
||||
|
||||
let offset = 0;
|
||||
let vfade = scrollView.get_effect("fade");
|
||||
if (vfade)
|
||||
offset = vfade.vfade_offset;
|
||||
|
||||
let box = actor.get_allocation_box();
|
||||
let y1 = box.y1, y2 = box.y2;
|
||||
|
||||
let parent = actor.get_parent();
|
||||
while (parent != scrollView) {
|
||||
if (!parent)
|
||||
throw new Error("actor not in scroll view");
|
||||
|
||||
let box = parent.get_allocation_box();
|
||||
y1 += box.y1;
|
||||
y2 += box.y1;
|
||||
parent = parent.get_parent();
|
||||
}
|
||||
|
||||
if (y1 < value + offset)
|
||||
value = Math.max(0, y1 - offset);
|
||||
else if (y2 > value + pageSize - offset)
|
||||
value = Math.min(upper, y2 + offset - pageSize);
|
||||
else
|
||||
return;
|
||||
|
||||
Tweener.addTween(adjustment,
|
||||
{ value: value,
|
||||
time: SCROLL_TIME,
|
||||
transition: 'easeOutQuad' });
|
||||
}
|
||||
|
108
js/ui/altTab.js
108
js/ui/altTab.js
@ -98,13 +98,43 @@ const AppSwitcherPopup = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
_createSwitcher: function() {
|
||||
let apps = Shell.AppSystem.get_default().get_running ();
|
||||
_getAppLists: function() {
|
||||
let tracker = Shell.WindowTracker.get_default();
|
||||
let appSys = Shell.AppSystem.get_default();
|
||||
let allApps = appSys.get_running ();
|
||||
|
||||
if (apps.length == 0)
|
||||
let screen = global.screen;
|
||||
let display = screen.get_display();
|
||||
let windows = display.get_tab_list(Meta.TabList.NORMAL_ALL, screen,
|
||||
screen.get_active_workspace());
|
||||
|
||||
// windows is only the windows on the current workspace. For
|
||||
// each one, if it corresponds to an app we know, move that
|
||||
// app from allApps to apps.
|
||||
let apps = [];
|
||||
for (let i = 0; i < windows.length && allApps.length != 0; i++) {
|
||||
let app = tracker.get_window_app(windows[i]);
|
||||
let index = allApps.indexOf(app);
|
||||
if (index != -1) {
|
||||
apps.push(app);
|
||||
allApps.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Now @apps is a list of apps on the current workspace, in
|
||||
// standard Alt+Tab order (MRU except for minimized windows),
|
||||
// and allApps is a list of apps that only appear on other
|
||||
// workspaces, sorted by user_time, which is good enough.
|
||||
return [apps, allApps];
|
||||
},
|
||||
|
||||
_createSwitcher: function() {
|
||||
let [localApps, otherApps] = this._getAppLists();
|
||||
|
||||
if (localApps.length == 0 && otherApps.length == 0)
|
||||
return false;
|
||||
|
||||
this._switcherList = new AppSwitcher(apps, this);
|
||||
this._switcherList = new AppSwitcher(localApps, otherApps, this);
|
||||
this._items = this._switcherList.icons;
|
||||
|
||||
return true;
|
||||
@ -232,11 +262,15 @@ const AppSwitcherPopup = new Lang.Class({
|
||||
},
|
||||
|
||||
_finish : function(timestamp) {
|
||||
let appIcon = this._items[this._selectedIndex];
|
||||
let window = this._currentWindow > 0 ? this._currentWindow : 0;
|
||||
appIcon.app.activate_window(appIcon.cachedWindows[window], timestamp);
|
||||
|
||||
this.parent();
|
||||
|
||||
let appIcon = this._items[this._selectedIndex];
|
||||
let window;
|
||||
if (this._currentWindow >= 0)
|
||||
window = appIcon.cachedWindows[this._currentWindow];
|
||||
else
|
||||
window = null;
|
||||
appIcon.app.activate_window(window, timestamp);
|
||||
},
|
||||
|
||||
_onDestroy : function() {
|
||||
@ -395,9 +429,9 @@ const WindowSwitcherPopup = new Lang.Class({
|
||||
},
|
||||
|
||||
_finish: function() {
|
||||
Main.activateWindow(this._items[this._selectedIndex].window);
|
||||
|
||||
this.parent();
|
||||
|
||||
Main.activateWindow(this._items[this._selectedIndex].window);
|
||||
}
|
||||
});
|
||||
|
||||
@ -427,26 +461,34 @@ const AppSwitcher = new Lang.Class({
|
||||
Name: 'AppSwitcher',
|
||||
Extends: SwitcherPopup.SwitcherList,
|
||||
|
||||
_init : function(apps, altTabPopup) {
|
||||
_init : function(localApps, otherApps, altTabPopup) {
|
||||
this.parent(true);
|
||||
|
||||
// Construct the AppIcons, add to the popup
|
||||
let activeWorkspace = global.screen.get_active_workspace();
|
||||
let workspaceIcons = [];
|
||||
let otherIcons = [];
|
||||
for (let i = 0; i < localApps.length; i++) {
|
||||
let appIcon = new AppIcon(localApps[i]);
|
||||
// Cache the window list now; we don't handle dynamic changes here,
|
||||
// and we don't want to be continually retrieving it
|
||||
appIcon.cachedWindows = appIcon.app.get_windows();
|
||||
workspaceIcons.push(appIcon);
|
||||
}
|
||||
for (let i = 0; i < otherApps.length; i++) {
|
||||
let appIcon = new AppIcon(otherApps[i]);
|
||||
appIcon.cachedWindows = appIcon.app.get_windows();
|
||||
otherIcons.push(appIcon);
|
||||
}
|
||||
|
||||
this.icons = [];
|
||||
this._arrows = [];
|
||||
|
||||
let windowTracker = Shell.WindowTracker.get_default();
|
||||
let allWindows = global.display.get_tab_list(Meta.TabList.NORMAL,
|
||||
global.screen, null);
|
||||
|
||||
// Construct the AppIcons, add to the popup
|
||||
for (let i = 0; i < apps.length; i++) {
|
||||
let appIcon = new AppIcon(apps[i]);
|
||||
// Cache the window list now; we don't handle dynamic changes here,
|
||||
// and we don't want to be continually retrieving it
|
||||
appIcon.cachedWindows = allWindows.filter(function(w) {
|
||||
return windowTracker.get_window_app (w) == appIcon.app;
|
||||
});
|
||||
this._addIcon(appIcon);
|
||||
}
|
||||
for (let i = 0; i < workspaceIcons.length; i++)
|
||||
this._addIcon(workspaceIcons[i]);
|
||||
if (workspaceIcons.length > 0 && otherIcons.length > 0)
|
||||
this.addSeparator();
|
||||
for (let i = 0; i < otherIcons.length; i++)
|
||||
this._addIcon(otherIcons[i]);
|
||||
|
||||
this._curApp = -1;
|
||||
this._iconSize = 0;
|
||||
@ -472,6 +514,8 @@ const AppSwitcher = new Lang.Class({
|
||||
let [iconMinHeight, iconNaturalHeight] = this.icons[j].label.get_preferred_height(-1);
|
||||
let iconSpacing = iconNaturalHeight + iconPadding + iconBorder;
|
||||
let totalSpacing = this._list.spacing * (this._items.length - 1);
|
||||
if (this._separator)
|
||||
totalSpacing += this._separator.width + this._list.spacing;
|
||||
|
||||
// We just assume the whole screen here due to weirdness happing with the passed width
|
||||
let primary = Main.layoutManager.primaryMonitor;
|
||||
@ -594,12 +638,24 @@ const ThumbnailList = new Lang.Class({
|
||||
_init : function(windows) {
|
||||
this.parent(false);
|
||||
|
||||
let activeWorkspace = global.screen.get_active_workspace();
|
||||
|
||||
// We fake the value of 'separatorAdded' when the app has no window
|
||||
// on the current workspace, to avoid displaying a useless separator in
|
||||
// that case.
|
||||
let separatorAdded = windows.length == 0 || windows[0].get_workspace() != activeWorkspace;
|
||||
|
||||
this._labels = new Array();
|
||||
this._thumbnailBins = new Array();
|
||||
this._clones = new Array();
|
||||
this._windows = windows;
|
||||
|
||||
for (let i = 0; i < windows.length; i++) {
|
||||
if (!separatorAdded && windows[i].get_workspace() != activeWorkspace) {
|
||||
this.addSeparator();
|
||||
separatorAdded = true;
|
||||
}
|
||||
|
||||
let box = new St.BoxLayout({ style_class: 'thumbnail-box',
|
||||
vertical: true });
|
||||
|
||||
|
@ -3,7 +3,6 @@
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const GObject = imports.gi.GObject;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const GMenu = imports.gi.GMenu;
|
||||
const Shell = imports.gi.Shell;
|
||||
@ -15,12 +14,10 @@ const Mainloop = imports.mainloop;
|
||||
const Atk = imports.gi.Atk;
|
||||
|
||||
const AppFavorites = imports.ui.appFavorites;
|
||||
const BoxPointer = imports.ui.boxpointer;
|
||||
const DND = imports.ui.dnd;
|
||||
const IconGrid = imports.ui.iconGrid;
|
||||
const Main = imports.ui.main;
|
||||
const Overview = imports.ui.overview;
|
||||
const OverviewControls = imports.ui.overviewControls;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const Workspace = imports.ui.workspace;
|
||||
@ -29,426 +26,255 @@ const Util = imports.misc.util;
|
||||
|
||||
const MAX_APPLICATION_WORK_MILLIS = 75;
|
||||
const MENU_POPUP_TIMEOUT = 600;
|
||||
const MAX_COLUMNS = 6;
|
||||
|
||||
const INACTIVE_GRID_OPACITY = 77;
|
||||
const FOLDER_SUBICON_FRACTION = .4;
|
||||
|
||||
|
||||
// Recursively load a GMenuTreeDirectory; we could put this in ShellAppSystem too
|
||||
function _loadCategory(dir, view) {
|
||||
let iter = dir.iter();
|
||||
let appSystem = Shell.AppSystem.get_default();
|
||||
let nextType;
|
||||
while ((nextType = iter.next()) != GMenu.TreeItemType.INVALID) {
|
||||
if (nextType == GMenu.TreeItemType.ENTRY) {
|
||||
let entry = iter.get_entry();
|
||||
let app = appSystem.lookup_app_by_tree_entry(entry);
|
||||
if (!entry.get_app_info().get_nodisplay())
|
||||
view.addApp(app);
|
||||
} else if (nextType == GMenu.TreeItemType.DIRECTORY) {
|
||||
let itemDir = iter.get_directory();
|
||||
if (!itemDir.get_is_nodisplay())
|
||||
_loadCategory(itemDir, view);
|
||||
}
|
||||
}
|
||||
};
|
||||
const SCROLL_TIME = 0.1;
|
||||
|
||||
const AlphabeticalView = new Lang.Class({
|
||||
Name: 'AlphabeticalView',
|
||||
Abstract: true,
|
||||
|
||||
_init: function() {
|
||||
this._grid = new IconGrid.IconGrid({ xAlign: St.Align.MIDDLE,
|
||||
columnLimit: MAX_COLUMNS });
|
||||
this._grid = new IconGrid.IconGrid({ xAlign: St.Align.START });
|
||||
this._appSystem = Shell.AppSystem.get_default();
|
||||
|
||||
// Standard hack for ClutterBinLayout
|
||||
this._grid.actor.x_expand = true;
|
||||
|
||||
this._items = {};
|
||||
this._allItems = [];
|
||||
},
|
||||
|
||||
removeAll: function() {
|
||||
this._grid.removeAll();
|
||||
this._items = {};
|
||||
this._allItems = [];
|
||||
},
|
||||
|
||||
_getItemId: function(item) {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
|
||||
_createItemIcon: function(item) {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
|
||||
_compareItems: function(a, b) {
|
||||
throw new Error('Not implemented');
|
||||
},
|
||||
|
||||
_addItem: function(item) {
|
||||
let id = this._getItemId(item);
|
||||
if (this._items[id] !== undefined)
|
||||
return null;
|
||||
|
||||
let itemIcon = this._createItemIcon(item);
|
||||
this._allItems.push(item);
|
||||
this._items[id] = itemIcon;
|
||||
|
||||
return itemIcon;
|
||||
},
|
||||
|
||||
loadGrid: function() {
|
||||
this._allItems.sort(this._compareItems);
|
||||
|
||||
for (let i = 0; i < this._allItems.length; i++) {
|
||||
let id = this._getItemId(this._allItems[i]);
|
||||
if (!id)
|
||||
continue;
|
||||
this._grid.addItem(this._items[id].actor);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const FolderView = new Lang.Class({
|
||||
Name: 'FolderView',
|
||||
Extends: AlphabeticalView,
|
||||
|
||||
_init: function() {
|
||||
this.parent();
|
||||
this.actor = this._grid.actor;
|
||||
},
|
||||
|
||||
_getItemId: function(item) {
|
||||
return item.get_id();
|
||||
},
|
||||
|
||||
_createItemIcon: function(item) {
|
||||
return new AppIcon(item);
|
||||
},
|
||||
|
||||
_compareItems: function(a, b) {
|
||||
return a.compare_by_name(b);
|
||||
},
|
||||
|
||||
addApp: function(app) {
|
||||
this._addItem(app);
|
||||
},
|
||||
|
||||
createFolderIcon: function(size) {
|
||||
let icon = new St.Widget({ layout_manager: new Clutter.BinLayout(),
|
||||
style_class: 'app-folder-icon',
|
||||
width: size, height: size });
|
||||
let subSize = Math.floor(FOLDER_SUBICON_FRACTION * size);
|
||||
|
||||
let aligns = [ Clutter.ActorAlign.START, Clutter.ActorAlign.END ];
|
||||
for (let i = 0; i < Math.min(this._allItems.length, 4); i++) {
|
||||
let texture = this._allItems[i].create_icon_texture(subSize);
|
||||
let bin = new St.Bin({ child: texture,
|
||||
x_expand: true, y_expand: true });
|
||||
bin.set_x_align(aligns[i % 2]);
|
||||
bin.set_y_align(aligns[Math.floor(i / 2)]);
|
||||
icon.add_actor(bin);
|
||||
}
|
||||
|
||||
return icon;
|
||||
}
|
||||
});
|
||||
|
||||
const AllViewLayout = new Lang.Class({
|
||||
Name: 'AllViewLayout',
|
||||
Extends: Clutter.BinLayout,
|
||||
|
||||
vfunc_get_preferred_height: function(container, forWidth) {
|
||||
let minBottom = 0;
|
||||
let naturalBottom = 0;
|
||||
|
||||
for (let child = container.get_first_child();
|
||||
child;
|
||||
child = child.get_next_sibling()) {
|
||||
let childY = child.y;
|
||||
let [childMin, childNatural] = child.get_preferred_height(forWidth);
|
||||
|
||||
if (childMin + childY > minBottom)
|
||||
minBottom = childMin + childY;
|
||||
|
||||
if (childNatural + childY > naturalBottom)
|
||||
naturalBottom = childNatural + childY;
|
||||
}
|
||||
return [minBottom, naturalBottom];
|
||||
}
|
||||
});
|
||||
|
||||
const AllView = new Lang.Class({
|
||||
Name: 'AllView',
|
||||
Extends: AlphabeticalView,
|
||||
|
||||
_init: function() {
|
||||
this.parent();
|
||||
this._pendingAppLaterId = 0;
|
||||
this._appIcons = {}; // desktop file id
|
||||
this._allApps = [];
|
||||
|
||||
let box = new St.BoxLayout({ vertical: true });
|
||||
this._stack = new St.Widget({ layout_manager: new AllViewLayout() });
|
||||
this._stack.add_actor(this._grid.actor);
|
||||
this._eventBlocker = new St.Widget({ x_expand: true, y_expand: true });
|
||||
this._stack.add_actor(this._eventBlocker);
|
||||
box.add(this._stack, { y_align: St.Align.START, expand: true });
|
||||
box.add(this._grid.actor, { y_align: St.Align.START, expand: true });
|
||||
|
||||
this.actor = new St.ScrollView({ x_fill: true,
|
||||
y_fill: false,
|
||||
y_align: St.Align.START,
|
||||
x_expand: true,
|
||||
y_expand: true,
|
||||
overlay_scrollbars: true,
|
||||
style_class: 'all-apps vfade' });
|
||||
style_class: 'vfade' });
|
||||
this.actor.add_actor(box);
|
||||
this.actor.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
|
||||
let action = new Clutter.PanAction({ interpolate: true });
|
||||
action.connect('pan', Lang.bind(this, this._onPan));
|
||||
this.actor.add_action(action);
|
||||
|
||||
this._clickAction = new Clutter.ClickAction();
|
||||
this._clickAction.connect('clicked', Lang.bind(this, function() {
|
||||
if (!this._currentPopup)
|
||||
return;
|
||||
|
||||
let [x, y] = this._clickAction.get_coords();
|
||||
let actor = global.stage.get_actor_at_pos(Clutter.PickMode.ALL, x, y);
|
||||
if (!this._currentPopup.actor.contains(actor))
|
||||
this._currentPopup.popdown();
|
||||
}));
|
||||
this._eventBlocker.add_action(this._clickAction);
|
||||
},
|
||||
|
||||
_onPan: function(action) {
|
||||
this._clickAction.release();
|
||||
|
||||
let [dist, dx, dy] = action.get_motion_delta(0);
|
||||
let adjustment = this.actor.vscroll.adjustment;
|
||||
adjustment.value -= (dy / this.actor.height) * adjustment.page_size;
|
||||
return false;
|
||||
},
|
||||
|
||||
_getItemId: function(item) {
|
||||
if (item instanceof Shell.App)
|
||||
return item.get_id();
|
||||
else if (item instanceof GMenu.TreeDirectory)
|
||||
return item.get_menu_id();
|
||||
else
|
||||
return null;
|
||||
},
|
||||
|
||||
_createItemIcon: function(item) {
|
||||
if (item instanceof Shell.App)
|
||||
return new AppIcon(item);
|
||||
else if (item instanceof GMenu.TreeDirectory)
|
||||
return new FolderIcon(item, this);
|
||||
else
|
||||
return null;
|
||||
},
|
||||
|
||||
_compareItems: function(itemA, itemB) {
|
||||
// bit of a hack: rely on both ShellApp and GMenuTreeDirectory
|
||||
// having a get_name() method
|
||||
let nameA = GLib.utf8_collate_key(itemA.get_name(), -1);
|
||||
let nameB = GLib.utf8_collate_key(itemB.get_name(), -1);
|
||||
return (nameA > nameB) ? 1 : (nameA < nameB ? -1 : 0);
|
||||
removeAll: function() {
|
||||
this._grid.removeAll();
|
||||
this._appIcons = {};
|
||||
this._allApps = [];
|
||||
},
|
||||
|
||||
addApp: function(app) {
|
||||
let appIcon = this._addItem(app);
|
||||
if (appIcon)
|
||||
appIcon.actor.connect('key-focus-in',
|
||||
Lang.bind(this, this._ensureIconVisible));
|
||||
},
|
||||
var id = app.get_id();
|
||||
if (this._appIcons[id] !== undefined)
|
||||
return;
|
||||
|
||||
addFolder: function(dir) {
|
||||
let folderIcon = this._addItem(dir);
|
||||
if (folderIcon)
|
||||
folderIcon.actor.connect('key-focus-in',
|
||||
Lang.bind(this, this._ensureIconVisible));
|
||||
},
|
||||
let appIcon = new AppWellIcon(app);
|
||||
let pos = Util.insertSorted(this._allApps, app, function(a, b) {
|
||||
return a.compare_by_name(b);
|
||||
});
|
||||
this._grid.addItem(appIcon.actor, pos);
|
||||
appIcon.actor.connect('key-focus-in', Lang.bind(this, this._ensureIconVisible));
|
||||
|
||||
addFolderPopup: function(popup) {
|
||||
this._stack.add_actor(popup.actor);
|
||||
popup.connect('open-state-changed', Lang.bind(this,
|
||||
function(popup, isOpen) {
|
||||
this._eventBlocker.reactive = isOpen;
|
||||
this._currentPopup = isOpen ? popup : null;
|
||||
this._updateIconOpacities(isOpen);
|
||||
if (isOpen)
|
||||
this._ensureIconVisible(popup.actor);
|
||||
}));
|
||||
this._appIcons[id] = appIcon;
|
||||
},
|
||||
|
||||
_ensureIconVisible: function(icon) {
|
||||
Util.ensureActorVisibleInScrollView(this.actor, icon);
|
||||
let adjustment = this.actor.vscroll.adjustment;
|
||||
let [value, lower, upper, stepIncrement, pageIncrement, pageSize] = adjustment.get_values();
|
||||
|
||||
let offset = 0;
|
||||
let vfade = this.actor.get_effect("vfade");
|
||||
if (vfade)
|
||||
offset = vfade.fade_offset;
|
||||
|
||||
// If this gets called as part of a right-click, the actor
|
||||
// will be needs_allocation, and so "icon.y" would return 0
|
||||
let box = icon.get_allocation_box();
|
||||
|
||||
if (box.y1 < value + offset)
|
||||
value = Math.max(0, box.y1 - offset);
|
||||
else if (box.y2 > value + pageSize - offset)
|
||||
value = Math.min(upper, box.y2 + offset - pageSize);
|
||||
else
|
||||
return;
|
||||
|
||||
Tweener.addTween(adjustment,
|
||||
{ value: value,
|
||||
time: SCROLL_TIME,
|
||||
transition: 'easeOutQuad' });
|
||||
},
|
||||
|
||||
_updateIconOpacities: function(folderOpen) {
|
||||
for (let id in this._items) {
|
||||
if (folderOpen && !this._items[id].actor.checked)
|
||||
this._items[id].actor.opacity = INACTIVE_GRID_OPACITY;
|
||||
else
|
||||
this._items[id].actor.opacity = 255;
|
||||
setVisibleApps: function(apps) {
|
||||
if (apps == null) { // null implies "all"
|
||||
for (var id in this._appIcons) {
|
||||
var icon = this._appIcons[id];
|
||||
icon.actor.visible = true;
|
||||
}
|
||||
} else {
|
||||
// Set everything to not-visible, then set to visible what we should see
|
||||
for (var id in this._appIcons) {
|
||||
var icon = this._appIcons[id];
|
||||
icon.actor.visible = false;
|
||||
}
|
||||
for (var i = 0; i < apps.length; i++) {
|
||||
var app = apps[i];
|
||||
var id = app.get_id();
|
||||
var icon = this._appIcons[id];
|
||||
icon.actor.visible = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const FrequentView = new Lang.Class({
|
||||
Name: 'FrequentView',
|
||||
|
||||
_init: function() {
|
||||
this._grid = new IconGrid.IconGrid({ xAlign: St.Align.MIDDLE,
|
||||
fillParent: true,
|
||||
columnLimit: MAX_COLUMNS });
|
||||
this.actor = new St.Widget({ style_class: 'frequent-apps',
|
||||
x_expand: true, y_expand: true });
|
||||
this.actor.add_actor(this._grid.actor);
|
||||
|
||||
this._usage = Shell.AppUsage.get_default();
|
||||
},
|
||||
|
||||
removeAll: function() {
|
||||
this._grid.removeAll();
|
||||
},
|
||||
|
||||
loadApps: function() {
|
||||
let mostUsed = this._usage.get_most_used ("");
|
||||
for (let i = 0; i < mostUsed.length; i++) {
|
||||
let appIcon = new AppIcon(mostUsed[i]);
|
||||
this._grid.addItem(appIcon.actor, -1);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const Views = {
|
||||
FREQUENT: 0,
|
||||
ALL: 1
|
||||
};
|
||||
|
||||
const AppDisplay = new Lang.Class({
|
||||
Name: 'AppDisplay',
|
||||
const ViewByCategories = new Lang.Class({
|
||||
Name: 'ViewByCategories',
|
||||
|
||||
_init: function() {
|
||||
this._appSystem = Shell.AppSystem.get_default();
|
||||
this._appSystem.connect('installed-changed', Lang.bind(this, function() {
|
||||
Main.queueDeferredWork(this._allAppsWorkId);
|
||||
}));
|
||||
Main.overview.connect('showing', Lang.bind(this, function() {
|
||||
Main.queueDeferredWork(this._frequentAppsWorkId);
|
||||
}));
|
||||
global.settings.connect('changed::app-folder-categories', Lang.bind(this, function() {
|
||||
Main.queueDeferredWork(this._allAppsWorkId);
|
||||
}));
|
||||
this.actor = new St.BoxLayout({ style_class: 'all-app' });
|
||||
this.actor._delegate = this;
|
||||
|
||||
this._views = [];
|
||||
this._view = new AlphabeticalView();
|
||||
|
||||
let view, button;
|
||||
view = new FrequentView();
|
||||
button = new St.Button({ label: _("Frequent"),
|
||||
style_class: 'app-view-control',
|
||||
can_focus: true,
|
||||
x_expand: true });
|
||||
this._views[Views.FREQUENT] = { 'view': view, 'control': button };
|
||||
// categories can be -1 (the All view) or 0...n-1, where n
|
||||
// is the number of sections
|
||||
// -2 is a flag to indicate that nothing is selected
|
||||
// (used only before the actor is mapped the first time)
|
||||
this._currentCategory = -2;
|
||||
this._categories = [];
|
||||
|
||||
view = new AllView();
|
||||
button = new St.Button({ label: _("All"),
|
||||
style_class: 'app-view-control',
|
||||
can_focus: true,
|
||||
x_expand: true });
|
||||
this._views[Views.ALL] = { 'view': view, 'control': button };
|
||||
this._categoryBox = new St.BoxLayout({ vertical: true,
|
||||
reactive: true,
|
||||
accessible_role: Atk.Role.LIST });
|
||||
this._categoryScroll = new St.ScrollView({ x_fill: false,
|
||||
y_fill: false,
|
||||
style_class: 'vfade' });
|
||||
this._categoryScroll.add_actor(this._categoryBox);
|
||||
this.actor.add(this._view.actor, { expand: true, x_fill: true, y_fill: true });
|
||||
this.actor.add(this._categoryScroll, { expand: false, y_fill: false, y_align: St.Align.START });
|
||||
|
||||
this.actor = new St.BoxLayout({ style_class: 'app-display',
|
||||
vertical: true,
|
||||
x_expand: true, y_expand: true });
|
||||
|
||||
this._viewStack = new St.Widget({ layout_manager: new Clutter.BinLayout(),
|
||||
x_expand: true, y_expand: true });
|
||||
this.actor.add(this._viewStack, { expand: true });
|
||||
|
||||
let layout = new Clutter.BoxLayout({ homogeneous: true });
|
||||
this._controls = new St.Widget({ style_class: 'app-view-controls',
|
||||
layout_manager: layout });
|
||||
this.actor.add(new St.Bin({ child: this._controls }));
|
||||
|
||||
|
||||
for (let i = 0; i < this._views.length; i++) {
|
||||
this._viewStack.add_actor(this._views[i].view.actor);
|
||||
this._controls.add_actor(this._views[i].control);
|
||||
|
||||
let viewIndex = i;
|
||||
this._views[i].control.connect('clicked', Lang.bind(this,
|
||||
function(actor) {
|
||||
this._showView(viewIndex);
|
||||
}));
|
||||
}
|
||||
this._showView(Views.FREQUENT);
|
||||
// Always select the "All" filter when switching to the app view
|
||||
this.actor.connect('notify::mapped', Lang.bind(this,
|
||||
function() {
|
||||
if (this.actor.mapped && this._allCategoryButton)
|
||||
this._selectCategory(-1);
|
||||
}));
|
||||
|
||||
// We need a dummy actor to catch the keyboard focus if the
|
||||
// user Ctrl-Alt-Tabs here before the deferred work creates
|
||||
// our real contents
|
||||
this._focusDummy = new St.Bin({ can_focus: true });
|
||||
this._viewStack.add_actor(this._focusDummy);
|
||||
|
||||
this._allAppsWorkId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplayAllApps));
|
||||
this._frequentAppsWorkId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplayFrequentApps));
|
||||
this.actor.add(this._focusDummy);
|
||||
},
|
||||
|
||||
_showView: function(activeIndex) {
|
||||
for (let i = 0; i < this._views.length; i++) {
|
||||
let actor = this._views[i].view.actor;
|
||||
let params = { time: OverviewControls.SIDE_CONTROLS_ANIMATION_TIME,
|
||||
opacity: (i == activeIndex) ? 255 : 0 };
|
||||
if (i == activeIndex)
|
||||
actor.visible = true;
|
||||
else
|
||||
params.onComplete = function() { actor.hide(); };
|
||||
Tweener.addTween(actor, params);
|
||||
_selectCategory: function(num) {
|
||||
if (this._currentCategory == num) // nothing to do
|
||||
return;
|
||||
|
||||
if (i == activeIndex)
|
||||
this._views[i].control.add_style_pseudo_class('checked');
|
||||
this._currentCategory = num;
|
||||
|
||||
if (num != -1) {
|
||||
var category = this._categories[num];
|
||||
this._allCategoryButton.remove_style_pseudo_class('selected');
|
||||
this._view.setVisibleApps(category.apps);
|
||||
} else {
|
||||
this._allCategoryButton.add_style_pseudo_class('selected');
|
||||
this._view.setVisibleApps(null);
|
||||
}
|
||||
|
||||
for (var i = 0; i < this._categories.length; i++) {
|
||||
if (i == num)
|
||||
this._categories[i].button.add_style_pseudo_class('selected');
|
||||
else
|
||||
this._views[i].control.remove_style_pseudo_class('checked');
|
||||
this._categories[i].button.remove_style_pseudo_class('selected');
|
||||
}
|
||||
},
|
||||
|
||||
_redisplay: function() {
|
||||
this._redisplayFrequentApps();
|
||||
this._redisplayAllApps();
|
||||
// Recursively load a GMenuTreeDirectory; we could put this in ShellAppSystem too
|
||||
_loadCategory: function(dir, appList) {
|
||||
var iter = dir.iter();
|
||||
var nextType;
|
||||
while ((nextType = iter.next()) != GMenu.TreeItemType.INVALID) {
|
||||
if (nextType == GMenu.TreeItemType.ENTRY) {
|
||||
var entry = iter.get_entry();
|
||||
var app = this._appSystem.lookup_app_by_tree_entry(entry);
|
||||
if (!entry.get_app_info().get_nodisplay()) {
|
||||
this._view.addApp(app);
|
||||
appList.push(app);
|
||||
}
|
||||
} else if (nextType == GMenu.TreeItemType.DIRECTORY) {
|
||||
var itemDir = iter.get_directory();
|
||||
if (!itemDir.get_is_nodisplay())
|
||||
this._loadCategory(itemDir, appList);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_redisplayFrequentApps: function() {
|
||||
let view = this._views[Views.FREQUENT].view;
|
||||
_addCategory: function(name, index, dir) {
|
||||
let apps;
|
||||
|
||||
view.removeAll();
|
||||
view.loadApps();
|
||||
if (dir != null) {
|
||||
apps = [];
|
||||
this._loadCategory(dir, apps);
|
||||
|
||||
if (apps.length == 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
let button = new St.Button({ label: GLib.markup_escape_text (name, -1),
|
||||
style_class: 'app-filter',
|
||||
x_align: St.Align.START,
|
||||
can_focus: true ,
|
||||
accessible_role: Atk.Role.LIST_ITEM });
|
||||
button.connect('clicked', Lang.bind(this, function() {
|
||||
this._selectCategory(index);
|
||||
}));
|
||||
|
||||
if (dir == null) {
|
||||
this._allCategoryButton = button;
|
||||
} else {
|
||||
this._categories.push({ apps: apps,
|
||||
name: name,
|
||||
button: button });
|
||||
}
|
||||
|
||||
this._categoryBox.add(button, { expand: true, x_fill: true, y_fill: false });
|
||||
return true;
|
||||
},
|
||||
|
||||
_redisplayAllApps: function() {
|
||||
let view = this._views[Views.ALL].view;
|
||||
_removeAll: function() {
|
||||
this._view.removeAll();
|
||||
this._categories = [];
|
||||
this._categoryBox.destroy_all_children();
|
||||
},
|
||||
|
||||
view.removeAll();
|
||||
refresh: function() {
|
||||
this._removeAll();
|
||||
|
||||
let tree = this._appSystem.get_tree();
|
||||
let root = tree.get_root_directory();
|
||||
/* Translators: Filter to display all applications */
|
||||
this._addCategory(_("All"), -1, null);
|
||||
|
||||
let iter = root.iter();
|
||||
let nextType;
|
||||
let folderCategories = global.settings.get_strv('app-folder-categories');
|
||||
var tree = this._appSystem.get_tree();
|
||||
var root = tree.get_root_directory();
|
||||
|
||||
var iter = root.iter();
|
||||
var nextType;
|
||||
var i = 0;
|
||||
while ((nextType = iter.next()) != GMenu.TreeItemType.INVALID) {
|
||||
if (nextType == GMenu.TreeItemType.DIRECTORY) {
|
||||
let dir = iter.get_directory();
|
||||
var dir = iter.get_directory();
|
||||
if (dir.get_is_nodisplay())
|
||||
continue;
|
||||
|
||||
if (folderCategories.indexOf(dir.get_menu_id()) != -1)
|
||||
view.addFolder(dir);
|
||||
else
|
||||
_loadCategory(dir, view);
|
||||
if (this._addCategory(dir.get_name(), i, dir))
|
||||
i++;
|
||||
}
|
||||
}
|
||||
view.loadGrid();
|
||||
|
||||
this._selectCategory(-1);
|
||||
|
||||
if (this._focusDummy) {
|
||||
let focused = this._focusDummy.has_key_focus();
|
||||
@ -460,6 +286,29 @@ const AppDisplay = new Lang.Class({
|
||||
}
|
||||
});
|
||||
|
||||
/* This class represents a display containing a collection of application items.
|
||||
* The applications are sorted based on their name.
|
||||
*/
|
||||
const AllAppDisplay = new Lang.Class({
|
||||
Name: 'AllAppDisplay',
|
||||
|
||||
_init: function() {
|
||||
this._appSystem = Shell.AppSystem.get_default();
|
||||
this._appSystem.connect('installed-changed', Lang.bind(this, function() {
|
||||
Main.queueDeferredWork(this._workId);
|
||||
}));
|
||||
|
||||
this._appView = new ViewByCategories();
|
||||
this.actor = new St.Bin({ child: this._appView.actor, x_fill: true, y_fill: true });
|
||||
|
||||
this._workId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplay));
|
||||
},
|
||||
|
||||
_redisplay: function() {
|
||||
this._appView.refresh();
|
||||
}
|
||||
});
|
||||
|
||||
const AppSearchProvider = new Lang.Class({
|
||||
Name: 'AppSearchProvider',
|
||||
|
||||
@ -511,163 +360,70 @@ const AppSearchProvider = new Lang.Class({
|
||||
|
||||
createResultActor: function (resultMeta, terms) {
|
||||
let app = resultMeta['id'];
|
||||
let icon = new AppIcon(app);
|
||||
let icon = new AppWellIcon(app);
|
||||
return icon.actor;
|
||||
}
|
||||
});
|
||||
|
||||
const FolderIcon = new Lang.Class({
|
||||
Name: 'FolderIcon',
|
||||
const SettingsSearchProvider = new Lang.Class({
|
||||
Name: 'SettingsSearchProvider',
|
||||
|
||||
_init: function(dir, parentView) {
|
||||
this._dir = dir;
|
||||
this._parentView = parentView;
|
||||
|
||||
this.actor = new St.Button({ style_class: 'app-well-app app-folder',
|
||||
button_mask: St.ButtonMask.ONE,
|
||||
toggle_mode: true,
|
||||
can_focus: true,
|
||||
x_fill: true,
|
||||
y_fill: true });
|
||||
this.actor._delegate = this;
|
||||
|
||||
let label = this._dir.get_name();
|
||||
this.icon = new IconGrid.BaseIcon(label,
|
||||
{ createIcon: Lang.bind(this, this._createIcon) });
|
||||
this.actor.set_child(this.icon.actor);
|
||||
this.actor.label_actor = this.icon.label;
|
||||
|
||||
this.view = new FolderView();
|
||||
this.view.actor.reactive = false;
|
||||
_loadCategory(dir, this.view);
|
||||
this.view.loadGrid();
|
||||
|
||||
this.actor.connect('clicked', Lang.bind(this,
|
||||
function() {
|
||||
this._ensurePopup();
|
||||
this._popup.toggle();
|
||||
}));
|
||||
this.actor.connect('notify::mapped', Lang.bind(this,
|
||||
function() {
|
||||
if (!this.actor.mapped && this._popup)
|
||||
this._popup.popdown();
|
||||
}));
|
||||
_init: function() {
|
||||
this.appInfo = Gio.DesktopAppInfo.new('gnome-control-center.desktop');
|
||||
this._appSys = Shell.AppSystem.get_default();
|
||||
},
|
||||
|
||||
_createIcon: function(size) {
|
||||
return this.view.createFolderIcon(size);
|
||||
},
|
||||
|
||||
_ensurePopup: function() {
|
||||
if (this._popup)
|
||||
return;
|
||||
|
||||
let spaceTop = this.actor.y;
|
||||
let spaceBottom = this._parentView.actor.height - (this.actor.y + this.actor.height);
|
||||
let side = spaceTop > spaceBottom ? St.Side.BOTTOM : St.Side.TOP;
|
||||
|
||||
this._popup = new AppFolderPopup(this, side);
|
||||
this._parentView.addFolderPopup(this._popup);
|
||||
|
||||
// Position the popup above or below the source icon
|
||||
if (side == St.Side.BOTTOM) {
|
||||
this._popup.actor.show();
|
||||
this._popup.actor.y = this.actor.y - this._popup.actor.height;
|
||||
this._popup.actor.hide();
|
||||
} else {
|
||||
this._popup.actor.y = this.actor.y + this.actor.height;
|
||||
getResultMetas: function(prefs, callback) {
|
||||
let metas = [];
|
||||
for (let i = 0; i < prefs.length; i++) {
|
||||
let pref = prefs[i];
|
||||
metas.push({ 'id': pref,
|
||||
'name': pref.get_name(),
|
||||
'createIcon': function() { return null; }
|
||||
});
|
||||
}
|
||||
|
||||
this._popup.connect('open-state-changed', Lang.bind(this,
|
||||
function(popup, isOpen) {
|
||||
if (!isOpen)
|
||||
this.actor.checked = false;
|
||||
}));
|
||||
},
|
||||
});
|
||||
|
||||
const AppFolderPopup = new Lang.Class({
|
||||
Name: 'AppFolderPopup',
|
||||
|
||||
_init: function(source, side) {
|
||||
this._source = source;
|
||||
this._view = source.view;
|
||||
this._arrowSide = side;
|
||||
|
||||
this._isOpen = false;
|
||||
|
||||
this.actor = new St.Widget({ layout_manager: new Clutter.BinLayout(),
|
||||
visible: false,
|
||||
// We don't want to expand really, but look
|
||||
// at the layout manager of our parent...
|
||||
//
|
||||
// DOUBLE HACK: if you set one, you automatically
|
||||
// get the effect for the other direction too, so
|
||||
// we need to set the y_align
|
||||
x_expand: true,
|
||||
y_expand: true,
|
||||
x_align: Clutter.ActorAlign.CENTER,
|
||||
y_align: Clutter.ActorAlign.START });
|
||||
this._boxPointer = new BoxPointer.BoxPointer(this._arrowSide,
|
||||
{ style_class: 'app-folder-popup-bin',
|
||||
x_fill: true,
|
||||
y_fill: true,
|
||||
x_align: St.Align.START });
|
||||
|
||||
this._boxPointer.actor.style_class = 'app-folder-popup';
|
||||
this.actor.add_actor(this._boxPointer.actor);
|
||||
this._boxPointer.bin.set_child(this._view.actor);
|
||||
|
||||
let closeButton = Util.makeCloseButton();
|
||||
closeButton.connect('clicked', Lang.bind(this, this.popdown));
|
||||
this.actor.add_actor(closeButton);
|
||||
|
||||
this._boxPointer.actor.bind_property('opacity', closeButton, 'opacity',
|
||||
GObject.BindingFlags.SYNC_CREATE);
|
||||
|
||||
source.actor.connect('destroy', Lang.bind(this,
|
||||
function() {
|
||||
this.actor.destroy();
|
||||
}));
|
||||
callback(metas);
|
||||
},
|
||||
|
||||
toggle: function() {
|
||||
if (this._isOpen)
|
||||
this.popdown();
|
||||
else
|
||||
this.popup();
|
||||
getInitialResultSet: function(terms) {
|
||||
this.searchSystem.pushResults(this, this._appSys.search_settings(terms));
|
||||
},
|
||||
|
||||
popup: function() {
|
||||
if (this._isOpen)
|
||||
return;
|
||||
|
||||
this.actor.show();
|
||||
|
||||
this._boxPointer.setArrowActor(this._source.actor);
|
||||
this._boxPointer.show(BoxPointer.PopupAnimation.FADE |
|
||||
BoxPointer.PopupAnimation.SLIDE);
|
||||
|
||||
this._isOpen = true;
|
||||
this.emit('open-state-changed', true);
|
||||
getSubsearchResultSet: function(previousResults, terms) {
|
||||
this.searchSystem.pushResults(this, this._appSys.search_settings(terms));
|
||||
},
|
||||
|
||||
popdown: function() {
|
||||
if (!this._isOpen)
|
||||
return;
|
||||
activateResult: function(pref) {
|
||||
pref.activate();
|
||||
},
|
||||
|
||||
this._boxPointer.hide(BoxPointer.PopupAnimation.FADE |
|
||||
BoxPointer.PopupAnimation.SLIDE);
|
||||
this._isOpen = false;
|
||||
this.emit('open-state-changed', false);
|
||||
launchSearch: function(terms) {
|
||||
// FIXME: this should be a remote search provider
|
||||
this.appInfo.launch([], global.create_app_launch_context());
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(AppFolderPopup.prototype);
|
||||
|
||||
const AppIcon = new Lang.Class({
|
||||
Name: 'AppIcon',
|
||||
Extends: IconGrid.BaseIcon,
|
||||
|
||||
_init : function(app, iconParams) {
|
||||
_init : function(app, params) {
|
||||
this.app = app;
|
||||
|
||||
let label = this.app.get_name();
|
||||
|
||||
this.parent(label, params);
|
||||
},
|
||||
|
||||
createIcon: function(iconSize) {
|
||||
return this.app.create_icon_texture(iconSize);
|
||||
}
|
||||
});
|
||||
|
||||
const AppWellIcon = new Lang.Class({
|
||||
Name: 'AppWellIcon',
|
||||
|
||||
_init : function(app, iconParams, onActivateOverride) {
|
||||
this.app = app;
|
||||
this.actor = new St.Button({ style_class: 'app-well-app',
|
||||
reactive: true,
|
||||
@ -677,15 +433,13 @@ const AppIcon = new Lang.Class({
|
||||
y_fill: true });
|
||||
this.actor._delegate = this;
|
||||
|
||||
if (!iconParams)
|
||||
iconParams = {};
|
||||
|
||||
iconParams['createIcon'] = Lang.bind(this, this._createIcon);
|
||||
this.icon = new IconGrid.BaseIcon(app.get_name(), iconParams);
|
||||
this.icon = new AppIcon(app, iconParams);
|
||||
this.actor.set_child(this.icon.actor);
|
||||
|
||||
this.actor.label_actor = this.icon.label;
|
||||
|
||||
// A function callback to override the default "app.activate()"; used by preferences
|
||||
this._onActivateOverride = onActivateOverride;
|
||||
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
|
||||
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
|
||||
this.actor.connect('popup-menu', Lang.bind(this, this._onKeyboardPopupMenu));
|
||||
@ -724,10 +478,6 @@ const AppIcon = new Lang.Class({
|
||||
this._removeMenuTimeout();
|
||||
},
|
||||
|
||||
_createIcon: function(iconSize) {
|
||||
return this.app.create_icon_texture(iconSize);
|
||||
},
|
||||
|
||||
_removeMenuTimeout: function() {
|
||||
if (this._menuTimeoutId > 0) {
|
||||
Mainloop.source_remove(this._menuTimeoutId);
|
||||
@ -785,7 +535,6 @@ const AppIcon = new Lang.Class({
|
||||
popupMenu: function() {
|
||||
this._removeMenuTimeout();
|
||||
this.actor.fake_release();
|
||||
this._draggable.fakeRelease();
|
||||
|
||||
if (!this._menu) {
|
||||
this._menu = new AppIconMenu(this);
|
||||
@ -827,13 +576,16 @@ const AppIcon = new Lang.Class({
|
||||
this.emit('launching');
|
||||
let modifiers = event.get_state();
|
||||
|
||||
if (modifiers & Clutter.ModifierType.CONTROL_MASK
|
||||
&& this.app.state == Shell.AppState.RUNNING) {
|
||||
this.app.open_new_window(-1);
|
||||
if (this._onActivateOverride) {
|
||||
this._onActivateOverride(event);
|
||||
} else {
|
||||
this.app.activate();
|
||||
if (modifiers & Clutter.ModifierType.CONTROL_MASK
|
||||
&& this.app.state == Shell.AppState.RUNNING) {
|
||||
this.app.open_new_window(-1);
|
||||
} else {
|
||||
this.app.activate();
|
||||
}
|
||||
}
|
||||
|
||||
Main.overview.hide();
|
||||
},
|
||||
|
||||
@ -854,7 +606,7 @@ const AppIcon = new Lang.Class({
|
||||
return this.icon.icon;
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(AppIcon.prototype);
|
||||
Signals.addSignalMethods(AppWellIcon.prototype);
|
||||
|
||||
const AppIconMenu = new Lang.Class({
|
||||
Name: 'AppIconMenu',
|
||||
|
@ -1,789 +0,0 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const GDesktopEnums = imports.gi.GDesktopEnums;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const GnomeDesktop = imports.gi.GnomeDesktop;
|
||||
const Lang = imports.lang;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const Params = imports.misc.params;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const BACKGROUND_SCHEMA = 'org.gnome.desktop.background';
|
||||
const PRIMARY_COLOR_KEY = 'primary-color';
|
||||
const SECONDARY_COLOR_KEY = 'secondary-color';
|
||||
const COLOR_SHADING_TYPE_KEY = 'color-shading-type';
|
||||
const BACKGROUND_STYLE_KEY = 'picture-options';
|
||||
const PICTURE_OPACITY_KEY = 'picture-opacity';
|
||||
const PICTURE_URI_KEY = 'picture-uri';
|
||||
|
||||
const FADE_ANIMATION_TIME = 1.0;
|
||||
|
||||
// These parameters affect how often we redraw.
|
||||
// The first is how different (percent crossfaded) the slide show
|
||||
// has to look before redrawing and the second is the minimum
|
||||
// frequency (in seconds) we're willing to wake up
|
||||
const ANIMATION_OPACITY_STEP_INCREMENT = 4.0;
|
||||
const ANIMATION_MIN_WAKEUP_INTERVAL = 1.0;
|
||||
|
||||
let _backgroundCache = null;
|
||||
|
||||
const BackgroundCache = new Lang.Class({
|
||||
Name: 'BackgroundCache',
|
||||
|
||||
_init: function() {
|
||||
this._patterns = [];
|
||||
this._images = [];
|
||||
this._pendingFileLoads = [];
|
||||
this._fileMonitors = {};
|
||||
},
|
||||
|
||||
getPatternContent: function(params) {
|
||||
params = Params.parse(params, { monitorIndex: 0,
|
||||
color: null,
|
||||
secondColor: null,
|
||||
shadingType: null,
|
||||
effects: Meta.BackgroundEffects.NONE });
|
||||
|
||||
let content = null;
|
||||
let candidateContent = null;
|
||||
for (let i = 0; i < this._patterns.length; i++) {
|
||||
if (!this._patterns[i])
|
||||
continue;
|
||||
|
||||
if (this._patterns[i].get_shading() != params.shadingType)
|
||||
continue;
|
||||
|
||||
if (!params.color.equal(this._patterns[i].get_color()))
|
||||
continue;
|
||||
|
||||
if (params.shadingType != GDesktopEnums.BackgroundShading.SOLID &&
|
||||
!params.secondColor.equal(this._patterns[i].get_second_color()))
|
||||
continue;
|
||||
|
||||
candidateContent = this._patterns[i];
|
||||
|
||||
if (params.effects != this._patterns[i].effects)
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (candidateContent) {
|
||||
content = candidateContent.copy(params.monitorIndex, params.effects);
|
||||
} else {
|
||||
content = new Meta.Background({ meta_screen: global.screen,
|
||||
monitor: params.monitorIndex,
|
||||
effects: params.effects });
|
||||
|
||||
if (params.shadingType == GDesktopEnums.BackgroundShading.SOLID) {
|
||||
content.load_color(params.color);
|
||||
} else {
|
||||
content.load_gradient(params.shadingType, params.color, params.secondColor);
|
||||
}
|
||||
}
|
||||
|
||||
this._patterns.push(content);
|
||||
|
||||
return content;
|
||||
},
|
||||
|
||||
_monitorFile: function(filename) {
|
||||
if (this._fileMonitors[filename])
|
||||
return;
|
||||
|
||||
let file = Gio.File.new_for_path(filename);
|
||||
let monitor = file.monitor(Gio.FileMonitorFlags.NONE, null);
|
||||
|
||||
let signalId = monitor.connect('changed',
|
||||
Lang.bind(this, function() {
|
||||
for (let i = 0; i < this._images.length; i++) {
|
||||
if (this._images[i].get_filename() == filename)
|
||||
this._images.splice(i, 1);
|
||||
}
|
||||
|
||||
monitor.disconnect(signalId);
|
||||
|
||||
this.emit('file-changed', filename);
|
||||
}));
|
||||
|
||||
this._fileMonitors[filename] = monitor;
|
||||
},
|
||||
|
||||
_removeContent: function(contentList, content) {
|
||||
let index = contentList.indexOf(content);
|
||||
|
||||
if (index >= 0)
|
||||
contentList.splice(index, 1);
|
||||
},
|
||||
|
||||
removePatternContent: function(content) {
|
||||
this._removeContent(this._patterns, content);
|
||||
},
|
||||
|
||||
removeImageContent: function(content) {
|
||||
this._removeContent(this._images, content);
|
||||
},
|
||||
|
||||
_loadImageContent: function(params) {
|
||||
params = Params.parse(params, { monitorIndex: 0,
|
||||
style: null,
|
||||
filename: null,
|
||||
effects: Meta.BackgroundEffects.NONE,
|
||||
cancellable: null,
|
||||
onFinished: null });
|
||||
|
||||
for (let i = 0; i < this._pendingFileLoads.length; i++) {
|
||||
if (this._pendingFileLoads[i].filename == params.filename &&
|
||||
this._pendingFileLoads[i].style == params.style) {
|
||||
this._pendingFileLoads[i].callers.push({ shouldCopy: true,
|
||||
monitorIndex: params.monitorIndex,
|
||||
effects: params.effects,
|
||||
onFinished: params.onFinished });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this._pendingFileLoads.push({ filename: params.filename,
|
||||
style: params.style,
|
||||
callers: [{ shouldCopy: false,
|
||||
monitorIndex: params.monitorIndex,
|
||||
effects: params.effects,
|
||||
onFinished: params.onFinished }] });
|
||||
|
||||
let content = new Meta.Background({ meta_screen: global.screen,
|
||||
monitor: params.monitorIndex,
|
||||
effects: params.effects });
|
||||
|
||||
content.load_file_async(params.filename,
|
||||
params.style,
|
||||
params.cancellable,
|
||||
Lang.bind(this,
|
||||
function(object, result) {
|
||||
try {
|
||||
content.load_file_finish(result);
|
||||
|
||||
this._monitorFile(params.filename);
|
||||
this._images.push(content);
|
||||
} catch(e) {
|
||||
content = null;
|
||||
}
|
||||
|
||||
for (let i = 0; i < this._pendingFileLoads.length; i++) {
|
||||
let pendingLoad = this._pendingFileLoads[i];
|
||||
if (pendingLoad.filename != params.filename ||
|
||||
pendingLoad.style != params.style)
|
||||
continue;
|
||||
|
||||
for (let j = 0; j < pendingLoad.callers.length; j++) {
|
||||
if (pendingLoad.callers[j].onFinished) {
|
||||
if (content && pendingLoad.callers[j].shouldCopy) {
|
||||
content = object.copy(pendingLoad.callers[j].monitorIndex,
|
||||
pendingLoad.callers[j].effects);
|
||||
|
||||
}
|
||||
|
||||
pendingLoad.callers[j].onFinished(content);
|
||||
}
|
||||
}
|
||||
|
||||
this._pendingFileLoads.splice(i, 1);
|
||||
}
|
||||
}));
|
||||
},
|
||||
|
||||
getImageContent: function(params) {
|
||||
params = Params.parse(params, { monitorIndex: 0,
|
||||
style: null,
|
||||
filename: null,
|
||||
effects: Meta.BackgroundEffects.NONE,
|
||||
cancellable: null,
|
||||
onFinished: null });
|
||||
|
||||
let content = null;
|
||||
let candidateContent = null;
|
||||
for (let i = 0; i < this._images.length; i++) {
|
||||
if (!this._images[i])
|
||||
continue;
|
||||
|
||||
if (this._images[i].get_style() != params.style)
|
||||
continue;
|
||||
|
||||
if (this._images[i].get_filename() != params.filename)
|
||||
continue;
|
||||
|
||||
if (params.style == GDesktopEnums.BackgroundStyle.SPANNED &&
|
||||
this._images[i].monitor_index != this._monitorIndex)
|
||||
continue;
|
||||
|
||||
candidateContent = this._images[i];
|
||||
|
||||
if (params.effects != this._images[i].effects)
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (candidateContent) {
|
||||
content = candidateContent.copy(params.monitorIndex, params.effects);
|
||||
|
||||
if (params.cancellable && params.cancellable.is_cancelled())
|
||||
content = null;
|
||||
else
|
||||
this._images.push(content);
|
||||
|
||||
if (params.onFinished)
|
||||
params.onFinished(content);
|
||||
} else {
|
||||
this._loadImageContent({ filename: params.filename,
|
||||
style: params.style,
|
||||
effects: params.effects,
|
||||
monitorIndex: params.monitorIndex,
|
||||
cancellable: params.cancellable,
|
||||
onFinished: params.onFinished });
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
getAnimation: function(params) {
|
||||
params = Params.parse(params, { filename: null,
|
||||
onLoaded: null });
|
||||
|
||||
if (this._animationFilename == params.filename) {
|
||||
if (params.onLoaded) {
|
||||
GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
|
||||
params.onLoaded(this._animation);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
let animation = new Animation({ filename: params.filename });
|
||||
|
||||
animation.load(Lang.bind(this, function() {
|
||||
this._monitorFile(params.filename);
|
||||
this._animationFilename = params.filename;
|
||||
this._animation = animation;
|
||||
|
||||
if (params.onLoaded) {
|
||||
GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
|
||||
params.onLoaded(this._animation);
|
||||
}));
|
||||
}
|
||||
}));
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(BackgroundCache.prototype);
|
||||
|
||||
function getBackgroundCache() {
|
||||
if (!_backgroundCache)
|
||||
_backgroundCache = new BackgroundCache();
|
||||
return _backgroundCache;
|
||||
}
|
||||
|
||||
const Background = new Lang.Class({
|
||||
Name: 'Background',
|
||||
|
||||
_init: function(params) {
|
||||
params = Params.parse(params, { monitorIndex: 0,
|
||||
layoutManager: Main.layoutManager,
|
||||
effects: Meta.BackgroundEffects.NONE });
|
||||
this.actor = new Meta.BackgroundGroup();
|
||||
this.actor._delegate = this;
|
||||
|
||||
this._destroySignalId = this.actor.connect('destroy',
|
||||
Lang.bind(this, this._destroy));
|
||||
|
||||
this._settings = new Gio.Settings({ schema: BACKGROUND_SCHEMA });
|
||||
this._monitorIndex = params.monitorIndex;
|
||||
this._layoutManager = params.layoutManager;
|
||||
this._effects = params.effects;
|
||||
this._fileWatches = {};
|
||||
this._pattern = null;
|
||||
// contains a single image for static backgrounds and
|
||||
// two images (from and to) for slide shows
|
||||
this._images = {};
|
||||
|
||||
this._brightness = 1.0;
|
||||
this._vignetteSharpness = 0.2;
|
||||
this._saturation = 1.0;
|
||||
this._cancellable = new Gio.Cancellable();
|
||||
this.isLoaded = false;
|
||||
|
||||
this._settings.connect('changed', Lang.bind(this, function() {
|
||||
this.emit('changed');
|
||||
}));
|
||||
|
||||
this._load();
|
||||
},
|
||||
|
||||
_destroy: function() {
|
||||
this._cancellable.cancel();
|
||||
|
||||
if (this._updateAnimationTimeoutId) {
|
||||
GLib.source_remove (this._updateAnimationTimeoutId);
|
||||
this._updateAnimationTimeoutId = 0;
|
||||
}
|
||||
|
||||
let i;
|
||||
let keys = Object.keys(this._fileWatches);
|
||||
for (i = 0; i < keys.length; i++) {
|
||||
this._cache.disconnect(this._fileWatches[keys[i]]);
|
||||
}
|
||||
this._fileWatches = null;
|
||||
|
||||
if (this._pattern) {
|
||||
if (this._pattern.content)
|
||||
this._cache.removePatternContent(this._pattern.content);
|
||||
|
||||
this._pattern.destroy();
|
||||
this._pattern = null;
|
||||
}
|
||||
|
||||
keys = Object.keys(this._images);
|
||||
for (i = 0; i < keys.length; i++) {
|
||||
let actor = this._images[keys[i]];
|
||||
|
||||
if (actor.content)
|
||||
this._cache.removeImageContent(actor.content);
|
||||
|
||||
actor.destroy();
|
||||
this._images[keys[i]] = null;
|
||||
}
|
||||
|
||||
this.actor.disconnect(this._destroySignalId);
|
||||
this._destroySignalId = 0;
|
||||
},
|
||||
|
||||
_setLoaded: function() {
|
||||
if (this.isLoaded)
|
||||
return;
|
||||
|
||||
this.isLoaded = true;
|
||||
|
||||
GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
|
||||
this.emit('loaded');
|
||||
return false;
|
||||
}));
|
||||
},
|
||||
|
||||
_loadPattern: function() {
|
||||
let colorString, res, color, secondColor;
|
||||
|
||||
colorString = this._settings.get_string(PRIMARY_COLOR_KEY);
|
||||
[res, color] = Clutter.Color.from_string(colorString);
|
||||
colorString = this._settings.get_string(SECONDARY_COLOR_KEY);
|
||||
[res, secondColor] = Clutter.Color.from_string(colorString);
|
||||
|
||||
let shadingType = this._settings.get_enum(COLOR_SHADING_TYPE_KEY);
|
||||
|
||||
let content = this._cache.getPatternContent({ monitorIndex: this._monitorIndex,
|
||||
effects: this._effects,
|
||||
color: color,
|
||||
secondColor: secondColor,
|
||||
shadingType: shadingType });
|
||||
|
||||
this._pattern = new Meta.BackgroundActor();
|
||||
this.actor.add_child(this._pattern);
|
||||
|
||||
this._pattern.content = content;
|
||||
},
|
||||
|
||||
_watchCacheFile: function(filename) {
|
||||
if (this._fileWatches[filename])
|
||||
return;
|
||||
|
||||
let signalId = this._cache.connect('file-changed',
|
||||
Lang.bind(this, function(cache, changedFile) {
|
||||
if (changedFile == filename) {
|
||||
this.emit('changed');
|
||||
}
|
||||
}));
|
||||
this._fileWatches[filename] = signalId;
|
||||
},
|
||||
|
||||
_addImage: function(content, index, filename) {
|
||||
content.saturation = this._saturation;
|
||||
content.brightness = this._brightness;
|
||||
content.vignette_sharpness = this._vignetteSharpness;
|
||||
|
||||
let actor = new Meta.BackgroundActor();
|
||||
actor.content = content;
|
||||
|
||||
// The background pattern is the first actor in
|
||||
// the group, and all images should be above that.
|
||||
this.actor.insert_child_at_index(actor, index + 1);
|
||||
|
||||
this._images[index] = actor;
|
||||
this._watchCacheFile(filename);
|
||||
},
|
||||
|
||||
_updateImage: function(content, index, filename) {
|
||||
content.saturation = this._saturation;
|
||||
content.brightness = this._brightness;
|
||||
content.vignette_sharpness = this._vignetteSharpness;
|
||||
|
||||
this._cache.removeImageContent(content);
|
||||
this._images[index].content = content;
|
||||
this._watchCacheFile(filename);
|
||||
},
|
||||
|
||||
_updateAnimationProgress: function() {
|
||||
if (this._images[1])
|
||||
this._images[1].opacity = this._animation.transitionProgress * 255;
|
||||
|
||||
this._queueUpdateAnimation();
|
||||
},
|
||||
|
||||
_updateAnimation: function() {
|
||||
this._updateAnimationTimeoutId = 0;
|
||||
|
||||
this._animation.update(this._layoutManager.monitors[this._monitorIndex]);
|
||||
let files = this._animation.keyFrameFiles;
|
||||
|
||||
if (files.length == 0) {
|
||||
this._setLoaded();
|
||||
this._queueUpdateAnimation();
|
||||
return;
|
||||
}
|
||||
|
||||
let numPendingImages = files.length;
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
if (this._images[i] && this._images[i].content &&
|
||||
this._images[i].content.get_filename() == files[i]) {
|
||||
|
||||
numPendingImages--;
|
||||
if (numPendingImages == 0)
|
||||
this._updateAnimationProgress();
|
||||
continue;
|
||||
}
|
||||
this._cache.getImageContent({ monitorIndex: this._monitorIndex,
|
||||
effects: this._effects,
|
||||
style: this._style,
|
||||
filename: files[i],
|
||||
cancellable: this._cancellable,
|
||||
onFinished: Lang.bind(this, function(content, i) {
|
||||
numPendingImages--;
|
||||
|
||||
if (!content) {
|
||||
this._setLoaded();
|
||||
if (numPendingImages == 0)
|
||||
this._updateAnimationProgress();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._images[i]) {
|
||||
this._addImage(content, i, files[i]);
|
||||
} else {
|
||||
this._updateImage(content, i, files[i]);
|
||||
}
|
||||
|
||||
if (numPendingImages == 0) {
|
||||
this._setLoaded();
|
||||
this._updateAnimationProgress();
|
||||
}
|
||||
}, i)
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_queueUpdateAnimation: function() {
|
||||
if (this._updateAnimationTimeoutId != 0)
|
||||
return;
|
||||
|
||||
if (!this._cancellable || this._cancellable.is_cancelled())
|
||||
return;
|
||||
|
||||
if (!this._animation.transitionDuration)
|
||||
return;
|
||||
|
||||
let nSteps = 255 / ANIMATION_OPACITY_STEP_INCREMENT;
|
||||
let timePerStep = (this._animation.transitionDuration * 1000) / nSteps;
|
||||
|
||||
let interval = Math.max(ANIMATION_MIN_WAKEUP_INTERVAL * 1000,
|
||||
timePerStep);
|
||||
|
||||
if (interval > GLib.MAXUINT32)
|
||||
return;
|
||||
|
||||
this._updateAnimationTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
|
||||
interval,
|
||||
Lang.bind(this, function() {
|
||||
this._updateAnimationTimeoutId = 0;
|
||||
this._updateAnimation();
|
||||
return false;
|
||||
}));
|
||||
},
|
||||
|
||||
_loadAnimation: function(filename) {
|
||||
this._cache.getAnimation({ filename: filename,
|
||||
onLoaded: Lang.bind(this, function(animation) {
|
||||
this._animation = animation;
|
||||
|
||||
if (!this._animation || this._cancellable.is_cancelled()) {
|
||||
this._setLoaded();
|
||||
return;
|
||||
}
|
||||
|
||||
this._updateAnimation();
|
||||
this._watchCacheFile(filename);
|
||||
})
|
||||
});
|
||||
},
|
||||
|
||||
_loadFile: function(filename) {
|
||||
this._cache.getImageContent({ monitorIndex: this._monitorIndex,
|
||||
effects: this._effects,
|
||||
style: this._style,
|
||||
filename: filename,
|
||||
cancellable: this._cancellable,
|
||||
onFinished: Lang.bind(this, function(content) {
|
||||
if (!content) {
|
||||
if (!this._cancellable.is_cancelled())
|
||||
this._loadAnimation(filename);
|
||||
return;
|
||||
}
|
||||
|
||||
this._addImage(content, 0, filename);
|
||||
this._setLoaded();
|
||||
})
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
_load: function () {
|
||||
this._cache = getBackgroundCache();
|
||||
|
||||
this._loadPattern(this._cache);
|
||||
|
||||
this._style = this._settings.get_enum(BACKGROUND_STYLE_KEY);
|
||||
if (this._style == GDesktopEnums.BackgroundStyle.NONE) {
|
||||
this._setLoaded();
|
||||
return;
|
||||
}
|
||||
|
||||
let uri = this._settings.get_string(PICTURE_URI_KEY);
|
||||
let filename = Gio.File.new_for_uri(uri).get_path();
|
||||
|
||||
this._loadFile(filename);
|
||||
},
|
||||
|
||||
get saturation() {
|
||||
return this._saturation;
|
||||
},
|
||||
|
||||
set saturation(saturation) {
|
||||
this._saturation = saturation;
|
||||
|
||||
if (this._pattern && this._pattern.content)
|
||||
this._pattern.content.saturation = saturation;
|
||||
|
||||
let keys = Object.keys(this._images);
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
let image = this._images[keys[i]];
|
||||
if (image && image.content)
|
||||
image.content.saturation = saturation;
|
||||
}
|
||||
},
|
||||
|
||||
get brightness() {
|
||||
return this._brightness;
|
||||
},
|
||||
|
||||
set brightness(factor) {
|
||||
this._brightness = factor;
|
||||
if (this._pattern && this._pattern.content)
|
||||
this._pattern.content.brightness = factor;
|
||||
|
||||
let keys = Object.keys(this._images);
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
let image = this._images[keys[i]];
|
||||
if (image && image.content)
|
||||
image.content.brightness = factor;
|
||||
}
|
||||
},
|
||||
|
||||
get vignetteSharpness() {
|
||||
return this._vignetteSharpness;
|
||||
},
|
||||
|
||||
set vignetteSharpness(sharpness) {
|
||||
this._vignetteSharpness = sharpness;
|
||||
if (this._pattern && this._pattern.content)
|
||||
this._pattern.content.vignette_sharpness = sharpness;
|
||||
|
||||
let keys = Object.keys(this._images);
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
let image = this._images[keys[i]];
|
||||
if (image && image.content)
|
||||
image.content.vignette_sharpness = sharpness;
|
||||
}
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(Background.prototype);
|
||||
|
||||
const SystemBackground = new Lang.Class({
|
||||
Name: 'SystemBackground',
|
||||
|
||||
_init: function() {
|
||||
this._cache = getBackgroundCache();
|
||||
this.actor = new Meta.BackgroundActor();
|
||||
|
||||
this._cache.getImageContent({ style: GDesktopEnums.BackgroundStyle.WALLPAPER,
|
||||
filename: global.datadir + '/theme/noise-texture.png',
|
||||
effects: Meta.BackgroundEffects.NONE,
|
||||
onFinished: Lang.bind(this, function(content) {
|
||||
this.actor.content = content;
|
||||
this.emit('loaded');
|
||||
})
|
||||
});
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(SystemBackground.prototype);
|
||||
|
||||
const Animation = new Lang.Class({
|
||||
Name: 'Animation',
|
||||
|
||||
_init: function(params) {
|
||||
params = Params.parse(params, { filename: null });
|
||||
|
||||
this.filename = params.filename;
|
||||
this.keyFrameFiles = [];
|
||||
this.transitionProgress = 0.0;
|
||||
this.transitionDuration = 0.0;
|
||||
this.loaded = false;
|
||||
},
|
||||
|
||||
load: function(callback) {
|
||||
let file = Gio.File.new_for_path(this.filename);
|
||||
|
||||
this._show = new GnomeDesktop.BGSlideShow({ filename: this.filename });
|
||||
|
||||
this._show.load_async(null,
|
||||
Lang.bind(this,
|
||||
function(object, result) {
|
||||
this.loaded = true;
|
||||
if (callback)
|
||||
callback();
|
||||
}));
|
||||
},
|
||||
|
||||
update: function(monitor) {
|
||||
this.keyFrameFiles = [];
|
||||
|
||||
if (!this._show)
|
||||
return;
|
||||
|
||||
if (this._show.get_num_slides() < 1)
|
||||
return;
|
||||
|
||||
let [progress, duration, isFixed, file1, file2] = this._show.get_current_slide(monitor.width, monitor.height);
|
||||
|
||||
this.transitionDuration = duration;
|
||||
this.transitionProgress = progress;
|
||||
|
||||
if (file1)
|
||||
this.keyFrameFiles.push(file1);
|
||||
|
||||
if (file2)
|
||||
this.keyFrameFiles.push(file2);
|
||||
},
|
||||
});
|
||||
Signals.addSignalMethods(Animation.prototype);
|
||||
|
||||
const BackgroundManager = new Lang.Class({
|
||||
Name: 'BackgroundManager',
|
||||
|
||||
_init: function(params) {
|
||||
params = Params.parse(params, { container: null,
|
||||
layoutManager: Main.layoutManager,
|
||||
monitorIndex: null,
|
||||
effects: Meta.BackgroundEffects.NONE,
|
||||
controlPosition: true });
|
||||
|
||||
this._container = params.container;
|
||||
this._layoutManager = params.layoutManager;
|
||||
this._effects = params.effects;
|
||||
this._monitorIndex = params.monitorIndex;
|
||||
this._controlPosition = params.controlPosition;
|
||||
|
||||
this.background = this._createBackground();
|
||||
this._newBackground = null;
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
if (this._newBackground) {
|
||||
this._newBackground.actor.destroy();
|
||||
this._newBackground = null;
|
||||
}
|
||||
|
||||
if (this.background) {
|
||||
this.background.actor.destroy();
|
||||
this.background = null;
|
||||
}
|
||||
},
|
||||
|
||||
_updateBackground: function(background, monitorIndex) {
|
||||
let newBackground = this._createBackground(monitorIndex);
|
||||
newBackground.vignetteSharpness = background.vignetteSharpness;
|
||||
newBackground.brightness = background.brightness;
|
||||
newBackground.saturation = background.saturation;
|
||||
newBackground.visible = background.visible;
|
||||
|
||||
newBackground.loadedSignalId = newBackground.connect('loaded',
|
||||
Lang.bind(this, function() {
|
||||
newBackground.disconnect(newBackground.loadedSignalId);
|
||||
newBackground.loadedSignalId = 0;
|
||||
Tweener.addTween(background.actor,
|
||||
{ opacity: 0,
|
||||
time: FADE_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this, function() {
|
||||
if (this.background == background) {
|
||||
this.background = newBackground;
|
||||
this._newBackground = null;
|
||||
background.actor.destroy();
|
||||
}
|
||||
this.emit('changed');
|
||||
})
|
||||
});
|
||||
}));
|
||||
|
||||
this._newBackground = newBackground;
|
||||
},
|
||||
|
||||
_createBackground: function() {
|
||||
let background = new Background({ monitorIndex: this._monitorIndex,
|
||||
layoutManager: this._layoutManager,
|
||||
effects: this._effects });
|
||||
this._container.add_child(background.actor);
|
||||
|
||||
let monitor = this._layoutManager.monitors[this._monitorIndex];
|
||||
|
||||
background.actor.set_size(monitor.width, monitor.height);
|
||||
if (this._controlPosition) {
|
||||
background.actor.set_position(monitor.x, monitor.y);
|
||||
background.actor.lower_bottom();
|
||||
}
|
||||
|
||||
background.changeSignalId = background.connect('changed', Lang.bind(this, function() {
|
||||
background.disconnect(background.changeSignalId);
|
||||
background.changeSignalId = 0;
|
||||
this._updateBackground(background, this._monitorIndex);
|
||||
}));
|
||||
|
||||
background.actor.connect('destroy', Lang.bind(this, function() {
|
||||
if (background.changeSignalId)
|
||||
background.disconnect(background.changeSignalId);
|
||||
|
||||
if (background.loadedSignalId)
|
||||
background.disconnect(background.loadedSignalId);
|
||||
}));
|
||||
|
||||
return background;
|
||||
},
|
||||
});
|
||||
Signals.addSignalMethods(BackgroundManager.prototype);
|
@ -1,64 +0,0 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Lang = imports.lang;
|
||||
const St = imports.gi.St;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
const BoxPointer = imports.ui.boxpointer;
|
||||
const Main = imports.ui.main;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
|
||||
const BackgroundMenu = new Lang.Class({
|
||||
Name: 'BackgroundMenu',
|
||||
Extends: PopupMenu.PopupMenu,
|
||||
|
||||
_init: function(source) {
|
||||
this.parent(source, 0, St.Side.TOP);
|
||||
|
||||
this.addSettingsAction(_("Settings"), 'gnome-control-center.desktop');
|
||||
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
this.addSettingsAction(_("Change Background…"), 'gnome-background-panel.desktop');
|
||||
|
||||
this.actor.add_style_class_name('background-menu');
|
||||
|
||||
Main.uiGroup.add_actor(this.actor);
|
||||
this.actor.hide();
|
||||
}
|
||||
});
|
||||
|
||||
function addBackgroundMenu(actor) {
|
||||
let cursor = new St.Bin({ opacity: 0 });
|
||||
Main.uiGroup.add_actor(cursor);
|
||||
|
||||
actor.reactive = true;
|
||||
actor._backgroundMenu = new BackgroundMenu(cursor);
|
||||
actor._backgroundManager = new PopupMenu.PopupMenuManager({ actor: actor });
|
||||
actor._backgroundManager.addMenu(actor._backgroundMenu);
|
||||
|
||||
function openMenu() {
|
||||
let [x, y] = global.get_pointer();
|
||||
cursor.set_position(x, y);
|
||||
actor._backgroundMenu.open(BoxPointer.PopupAnimation.NONE);
|
||||
}
|
||||
|
||||
let clickAction = new Clutter.ClickAction();
|
||||
clickAction.connect('long-press', function(action, actor, state) {
|
||||
if (state == Clutter.LongPressState.QUERY)
|
||||
return action.get_button() == 1 && !actor._backgroundMenu.isOpen;
|
||||
if (state == Clutter.LongPressState.ACTIVATE)
|
||||
openMenu();
|
||||
return true;
|
||||
});
|
||||
clickAction.connect('clicked', function(action) {
|
||||
if (action.get_button() == 3)
|
||||
openMenu();
|
||||
});
|
||||
actor.add_action(clickAction);
|
||||
|
||||
actor.connect('destroy', function() {
|
||||
actor._backgroundMenu.destroy();
|
||||
actor._backgroundMenu = null;
|
||||
actor._backgroundManager = null;
|
||||
});
|
||||
}
|
@ -38,7 +38,6 @@ const BoxPointer = new Lang.Class({
|
||||
this._arrowSide = arrowSide;
|
||||
this._userArrowSide = arrowSide;
|
||||
this._arrowOrigin = 0;
|
||||
this._arrowActor = null;
|
||||
this.actor = new St.Bin({ x_fill: true,
|
||||
y_fill: true });
|
||||
this._container = new Shell.GenericContainer();
|
||||
@ -221,27 +220,31 @@ const BoxPointer = new Lang.Class({
|
||||
this.bin.allocate(childBox, flags);
|
||||
|
||||
if (this._sourceActor && this._sourceActor.mapped) {
|
||||
this._reposition();
|
||||
this._updateFlip();
|
||||
this._reposition(this._sourceActor, this._arrowAlignment);
|
||||
|
||||
if (this._shouldFlip()) {
|
||||
switch (this._arrowSide) {
|
||||
case St.Side.TOP:
|
||||
this._arrowSide = St.Side.BOTTOM;
|
||||
break;
|
||||
case St.Side.BOTTOM:
|
||||
this._arrowSide = St.Side.TOP;
|
||||
break;
|
||||
case St.Side.LEFT:
|
||||
this._arrowSide = St.Side.RIGHT;
|
||||
break;
|
||||
case St.Side.RIGHT:
|
||||
this._arrowSide = St.Side.LEFT;
|
||||
break;
|
||||
}
|
||||
this._reposition(this._sourceActor, this._arrowAlignment);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_drawBorder: function(area) {
|
||||
let themeNode = this.actor.get_theme_node();
|
||||
|
||||
if (this._arrowActor) {
|
||||
let [sourceX, sourceY] = this._arrowActor.get_transformed_position();
|
||||
let [sourceWidth, sourceHeight] = this._arrowActor.get_transformed_size();
|
||||
let [absX, absY] = this.actor.get_transformed_position();
|
||||
|
||||
if (this._arrowSide == St.Side.TOP ||
|
||||
this._arrowSide == St.Side.BOTTOM) {
|
||||
this._arrowOrigin = sourceX - absX + sourceWidth / 2;
|
||||
} else {
|
||||
this._arrowOrigin = sourceY - absY + sourceHeight / 2;
|
||||
}
|
||||
}
|
||||
|
||||
let borderWidth = themeNode.get_length('-arrow-border-width');
|
||||
let base = themeNode.get_length('-arrow-base');
|
||||
let rise = themeNode.get_length('-arrow-rise');
|
||||
@ -402,11 +405,11 @@ const BoxPointer = new Lang.Class({
|
||||
cr.setLineWidth(borderWidth);
|
||||
cr.stroke();
|
||||
}
|
||||
|
||||
cr.$dispose();
|
||||
},
|
||||
|
||||
setPosition: function(sourceActor, alignment) {
|
||||
this._arrowSide = this._userArrowSide;
|
||||
|
||||
// We need to show it now to force an allocation,
|
||||
// so that we can query the correct size.
|
||||
this.actor.show();
|
||||
@ -414,8 +417,7 @@ const BoxPointer = new Lang.Class({
|
||||
this._sourceActor = sourceActor;
|
||||
this._arrowAlignment = alignment;
|
||||
|
||||
this._reposition();
|
||||
this._updateFlip();
|
||||
this._reposition(sourceActor, alignment);
|
||||
},
|
||||
|
||||
setSourceAlignment: function(alignment) {
|
||||
@ -427,10 +429,7 @@ const BoxPointer = new Lang.Class({
|
||||
this.setPosition(this._sourceActor, this._arrowAlignment);
|
||||
},
|
||||
|
||||
_reposition: function() {
|
||||
let sourceActor = this._sourceActor;
|
||||
let alignment = this._arrowAlignment;
|
||||
|
||||
_reposition: function(sourceActor, alignment) {
|
||||
// Position correctly relative to the sourceActor
|
||||
let sourceNode = sourceActor.get_theme_node();
|
||||
let sourceContentBox = sourceNode.get_content_box(sourceActor.get_allocation_box());
|
||||
@ -551,20 +550,10 @@ const BoxPointer = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
// @actor: an actor relative to which the arrow is positioned.
|
||||
// Differently from setPosition, this will not move the boxpointer itself,
|
||||
// on the arrow
|
||||
setArrowActor: function(actor) {
|
||||
if (this._arrowActor != actor) {
|
||||
this._arrowActor = actor;
|
||||
this._border.queue_repaint();
|
||||
}
|
||||
},
|
||||
|
||||
_shiftActor : function() {
|
||||
// Since the position of the BoxPointer depends on the allocated size
|
||||
// of the BoxPointer and the position of the source actor, trying
|
||||
// to position the BoxPointer via the x/y properties will result in
|
||||
// to position the BoxPoiner via the x/y properties will result in
|
||||
// allocation loops and warnings. Instead we do the positioning via
|
||||
// the anchor point, which is independent of allocation, and leave
|
||||
// x == y == 0.
|
||||
@ -572,47 +561,37 @@ const BoxPointer = new Lang.Class({
|
||||
-(this._yPosition + this._yOffset));
|
||||
},
|
||||
|
||||
_calculateArrowSide: function(arrowSide) {
|
||||
_shouldFlip: function() {
|
||||
let sourceAllocation = Shell.util_get_transformed_allocation(this._sourceActor);
|
||||
let [minWidth, minHeight, boxWidth, boxHeight] = this._container.get_preferred_size();
|
||||
let boxAllocation = Shell.util_get_transformed_allocation(this.actor);
|
||||
let boxWidth = boxAllocation.x2 - boxAllocation.x1;
|
||||
let boxHeight = boxAllocation.y2 - boxAllocation.y1;
|
||||
let monitor = Main.layoutManager.findMonitorForActor(this.actor);
|
||||
|
||||
switch (arrowSide) {
|
||||
switch (this._arrowSide) {
|
||||
case St.Side.TOP:
|
||||
if (sourceAllocation.y2 + boxHeight > monitor.y + monitor.height &&
|
||||
if (boxAllocation.y2 > monitor.y + monitor.height &&
|
||||
boxHeight < sourceAllocation.y1 - monitor.y)
|
||||
return St.Side.BOTTOM;
|
||||
return true;
|
||||
break;
|
||||
case St.Side.BOTTOM:
|
||||
if (sourceAllocation.y1 - boxHeight < monitor.y &&
|
||||
if (boxAllocation.y1 < monitor.y &&
|
||||
boxHeight < monitor.y + monitor.height - sourceAllocation.y2)
|
||||
return St.Side.TOP;
|
||||
return true;
|
||||
break;
|
||||
case St.Side.LEFT:
|
||||
if (sourceAllocation.y2 + boxWidth > monitor.x + monitor.width &&
|
||||
if (boxAllocation.x2 > monitor.x + monitor.width &&
|
||||
boxWidth < sourceAllocation.x1 - monitor.x)
|
||||
return St.Side.RIGHT;
|
||||
return true;
|
||||
break;
|
||||
case St.Side.RIGHT:
|
||||
if (sourceAllocation.y1 - boxWidth < monitor.x &&
|
||||
if (boxAllocation.x1 < monitor.x &&
|
||||
boxWidth < monitor.x + monitor.width - sourceAllocation.x2)
|
||||
return St.Side.LEFT;
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
|
||||
return arrowSide;
|
||||
},
|
||||
|
||||
_updateFlip: function() {
|
||||
let arrowSide = this._calculateArrowSide(this._userArrowSide);
|
||||
if (this._arrowSide != arrowSide) {
|
||||
this._arrowSide = arrowSide;
|
||||
this._reposition();
|
||||
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
|
||||
this._container.queue_relayout();
|
||||
return false;
|
||||
}));
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
set xOffset(offset) {
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const St = imports.gi.St;
|
||||
const Signals = imports.signals;
|
||||
@ -63,18 +62,15 @@ function _formatEventTime(event, clockFormat) {
|
||||
} else {
|
||||
switch (clockFormat) {
|
||||
case '24h':
|
||||
/* Translators: Shown in calendar event list, if 24h format,
|
||||
\u2236 is a ratio character, similar to : */
|
||||
ret = event.date.toLocaleFormat(C_("event list time", "%H\u2236%M"));
|
||||
/* Translators: Shown in calendar event list, if 24h format */
|
||||
ret = event.date.toLocaleFormat(C_("event list time", "%H:%M"));
|
||||
break;
|
||||
|
||||
default:
|
||||
/* explicit fall-through */
|
||||
case '12h':
|
||||
/* Translators: Shown in calendar event list, if 12h format,
|
||||
\u2236 is a ratio character, similar to : and \u2009 is
|
||||
a thin space */
|
||||
ret = event.date.toLocaleFormat(C_("event list time", "%l\u2236%M\u2009%p"));
|
||||
/* Transators: Shown in calendar event list, if 12h format */
|
||||
ret = event.date.toLocaleFormat(C_("event list time", "%l:%M %p"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -168,12 +164,6 @@ const EmptyEventSource = new Lang.Class({
|
||||
Name: 'EmptyEventSource',
|
||||
|
||||
_init: function() {
|
||||
this.isLoading = false;
|
||||
this.isDummy = true;
|
||||
this.hasCalendars = false;
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
},
|
||||
|
||||
requestRange: function(begin, end) {
|
||||
@ -197,18 +187,21 @@ const CalendarServerIface = <interface name="org.gnome.Shell.CalendarServer">
|
||||
<arg type="b" direction="in" />
|
||||
<arg type="a(sssbxxa{sv})" direction="out" />
|
||||
</method>
|
||||
<property name="HasCalendars" type="b" access="read" />
|
||||
<signal name="Changed" />
|
||||
</interface>;
|
||||
|
||||
const CalendarServerInfo = Gio.DBusInterfaceInfo.new_for_xml(CalendarServerIface);
|
||||
|
||||
function CalendarServer() {
|
||||
return new Gio.DBusProxy({ g_connection: Gio.DBus.session,
|
||||
g_interface_name: CalendarServerInfo.name,
|
||||
g_interface_info: CalendarServerInfo,
|
||||
g_name: 'org.gnome.Shell.CalendarServer',
|
||||
g_object_path: '/org/gnome/Shell/CalendarServer' });
|
||||
var self = new Gio.DBusProxy({ g_connection: Gio.DBus.session,
|
||||
g_interface_name: CalendarServerInfo.name,
|
||||
g_interface_info: CalendarServerInfo,
|
||||
g_name: 'org.gnome.Shell.CalendarServer',
|
||||
g_object_path: '/org/gnome/Shell/CalendarServer',
|
||||
g_flags: Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES });
|
||||
|
||||
self.init(null);
|
||||
return self;
|
||||
}
|
||||
|
||||
function _datesEqual(a, b) {
|
||||
@ -235,49 +228,18 @@ const DBusEventSource = new Lang.Class({
|
||||
|
||||
_init: function() {
|
||||
this._resetCache();
|
||||
this.isLoading = false;
|
||||
this.isDummy = false;
|
||||
|
||||
this._initialized = false;
|
||||
this._dbusProxy = new CalendarServer();
|
||||
this._dbusProxy.init_async(GLib.PRIORITY_DEFAULT, null, Lang.bind(this, function(object, result) {
|
||||
try {
|
||||
this._dbusProxy.init_finish(result);
|
||||
} catch(e) {
|
||||
log('Error loading calendars: ' + e.message);
|
||||
return;
|
||||
}
|
||||
this._dbusProxy.connectSignal('Changed', Lang.bind(this, this._onChanged));
|
||||
|
||||
this._dbusProxy.connectSignal('Changed', Lang.bind(this, this._onChanged));
|
||||
|
||||
this._dbusProxy.connect('notify::g-name-owner', Lang.bind(this, function() {
|
||||
if (this._dbusProxy.g_name_owner)
|
||||
this._onNameAppeared();
|
||||
else
|
||||
this._onNameVanished();
|
||||
}));
|
||||
|
||||
this._dbusProxy.connect('g-properties-changed', Lang.bind(this, function() {
|
||||
this.emit('notify::has-calendars');
|
||||
}));
|
||||
|
||||
this._initialized = true;
|
||||
this.emit('notify::has-calendars');
|
||||
this._onNameAppeared();
|
||||
this._dbusProxy.connect('notify::g-name-owner', Lang.bind(this, function() {
|
||||
if (this._dbusProxy.g_name_owner)
|
||||
this._onNameAppeared();
|
||||
else
|
||||
this._onNameVanished();
|
||||
}));
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this._dbusProxy.run_dispose();
|
||||
},
|
||||
|
||||
get hasCalendars() {
|
||||
if (this._initialized)
|
||||
return this._dbusProxy.HasCalendars;
|
||||
else
|
||||
return false;
|
||||
},
|
||||
|
||||
_resetCache: function() {
|
||||
this._events = [];
|
||||
this._lastRequestBegin = null;
|
||||
@ -317,15 +279,10 @@ const DBusEventSource = new Lang.Class({
|
||||
}
|
||||
|
||||
this._events = newEvents;
|
||||
this.isLoading = false;
|
||||
this.emit('changed');
|
||||
},
|
||||
|
||||
_loadEvents: function(forceReload) {
|
||||
// Ignore while loading
|
||||
if (!this._initialized)
|
||||
return;
|
||||
|
||||
if (this._curRequestBegin && this._curRequestEnd){
|
||||
let callFlags = Gio.DBusCallFlags.NO_AUTO_START;
|
||||
if (forceReload)
|
||||
@ -340,7 +297,6 @@ const DBusEventSource = new Lang.Class({
|
||||
|
||||
requestRange: function(begin, end, forceReload) {
|
||||
if (forceReload || !(_datesEqual(begin, this._lastRequestBegin) && _datesEqual(end, this._lastRequestEnd))) {
|
||||
this.isLoading = true;
|
||||
this._lastRequestBegin = begin;
|
||||
this._lastRequestEnd = end;
|
||||
this._curRequestBegin = begin;
|
||||
@ -415,11 +371,19 @@ const Calendar = new Lang.Class({
|
||||
// @eventSource: is an object implementing the EventSource API, e.g. the
|
||||
// requestRange(), getEvents(), hasEvents() methods and the ::changed signal.
|
||||
setEventSource: function(eventSource) {
|
||||
if (this._eventSource) {
|
||||
this._eventSource.disconnect(this._eventSourceChangedId);
|
||||
this._eventSource = null;
|
||||
}
|
||||
|
||||
this._eventSource = eventSource;
|
||||
this._eventSource.connect('changed', Lang.bind(this, function() {
|
||||
this._update(false);
|
||||
}));
|
||||
this._update(true);
|
||||
|
||||
if (this._eventSource) {
|
||||
this._eventSourceChangedId = this._eventSource.connect('changed', Lang.bind(this, function() {
|
||||
this._update(false);
|
||||
}));
|
||||
this._update(true);
|
||||
}
|
||||
},
|
||||
|
||||
// Sets the calendar to show a specific date
|
||||
@ -556,44 +520,20 @@ const Calendar = new Lang.Class({
|
||||
children[i].destroy();
|
||||
|
||||
// Start at the beginning of the week before the start of the month
|
||||
//
|
||||
// We want to show always 6 weeks (to keep the calendar menu at the same
|
||||
// height if there are no events), so we pad it according to the following
|
||||
// policy:
|
||||
//
|
||||
// 1 - If a month has 6 weeks, we place no padding (example: Dec 2012)
|
||||
// 2 - If a month has 5 weeks and it starts on week start, we pad one week
|
||||
// before it (example: Apr 2012)
|
||||
// 3 - If a month has 5 weeks and it starts on any other day, we pad one week
|
||||
// after it (example: Nov 2012)
|
||||
// 4 - If a month has 4 weeks, we pad one week before and one after it
|
||||
// (example: Feb 2010)
|
||||
//
|
||||
// Actually computing the number of weeks is complex, but we know that the
|
||||
// problematic categories (2 and 4) always start on week start, and that
|
||||
// all months at the end have 6 weeks.
|
||||
|
||||
let beginDate = new Date(this._selectedDate);
|
||||
beginDate.setDate(1);
|
||||
beginDate.setSeconds(0);
|
||||
beginDate.setHours(12);
|
||||
let year = beginDate.getYear();
|
||||
|
||||
let daysToWeekStart = (7 + beginDate.getDay() - this._weekStart) % 7;
|
||||
let startsOnWeekStart = daysToWeekStart == 0;
|
||||
let weekPadding = startsOnWeekStart ? 7 : 0;
|
||||
|
||||
beginDate.setTime(beginDate.getTime() - (weekPadding + daysToWeekStart) * MSECS_IN_DAY);
|
||||
beginDate.setTime(beginDate.getTime() - daysToWeekStart * MSECS_IN_DAY);
|
||||
|
||||
let iter = new Date(beginDate);
|
||||
let row = 2;
|
||||
// nRows here means 6 weeks + one header + one navbar
|
||||
let nRows = 8;
|
||||
while (row < 8) {
|
||||
while (true) {
|
||||
let button = new St.Button({ label: iter.getDate().toString() });
|
||||
let rtl = button.get_text_direction() == Clutter.TextDirection.RTL;
|
||||
|
||||
if (this._eventSource.isDummy)
|
||||
if (!this._eventSource)
|
||||
button.reactive = false;
|
||||
|
||||
let iterStr = iter.toUTCString();
|
||||
@ -602,9 +542,8 @@ const Calendar = new Lang.Class({
|
||||
this.setDate(newlySelectedDate, false);
|
||||
}));
|
||||
|
||||
let hasEvents = this._eventSource.hasEvents(iter);
|
||||
let hasEvents = this._eventSource && this._eventSource.hasEvents(iter);
|
||||
let styleClass = 'calendar-day-base calendar-day';
|
||||
|
||||
if (_isWorkDay(iter))
|
||||
styleClass += ' calendar-work-day'
|
||||
else
|
||||
@ -644,13 +583,17 @@ const Calendar = new Lang.Class({
|
||||
}
|
||||
|
||||
iter.setTime(iter.getTime() + MSECS_IN_DAY);
|
||||
|
||||
if (iter.getDay() == this._weekStart)
|
||||
if (iter.getDay() == this._weekStart) {
|
||||
// We stop on the first "first day of the week" after the month we are displaying
|
||||
if (iter.getMonth() > this._selectedDate.getMonth() || iter.getYear() > this._selectedDate.getYear())
|
||||
break;
|
||||
row++;
|
||||
}
|
||||
}
|
||||
// Signal to the event source that we are interested in events
|
||||
// only from this date range
|
||||
this._eventSource.requestRange(beginDate, iter, forceReload);
|
||||
if (this._eventSource)
|
||||
this._eventSource.requestRange(beginDate, iter, forceReload);
|
||||
}
|
||||
});
|
||||
|
||||
@ -668,8 +611,16 @@ const EventsList = new Lang.Class({
|
||||
},
|
||||
|
||||
setEventSource: function(eventSource) {
|
||||
if (this._eventSource) {
|
||||
this._eventSource.disconnect(this._eventSourceChangedId);
|
||||
this._eventSource = null;
|
||||
}
|
||||
|
||||
this._eventSource = eventSource;
|
||||
this._eventSource.connect('changed', Lang.bind(this, this._update));
|
||||
|
||||
if (this._eventSource) {
|
||||
this._eventSourceChangedId = this._eventSource.connect('changed', Lang.bind(this, this._update));
|
||||
}
|
||||
},
|
||||
|
||||
_addEvent: function(dayNameBox, timeBox, eventTitleBox, includeDayName, day, time, desc) {
|
||||
@ -686,6 +637,9 @@ const EventsList = new Lang.Class({
|
||||
},
|
||||
|
||||
_addPeriod: function(header, begin, end, includeDayName, showNothingScheduled) {
|
||||
if (!this._eventSource)
|
||||
return;
|
||||
|
||||
let events = this._eventSource.getEvents(begin, end);
|
||||
|
||||
let clockFormat = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);;
|
||||
@ -782,9 +736,6 @@ const EventsList = new Lang.Class({
|
||||
},
|
||||
|
||||
_update: function() {
|
||||
if (this._eventSource.isLoading)
|
||||
return;
|
||||
|
||||
let today = new Date();
|
||||
if (_sameDay (this._date, today)) {
|
||||
this._showToday();
|
||||
|
@ -8,6 +8,7 @@ const Params = imports.misc.params;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
const GnomeSession = imports.misc.gnomeSession;
|
||||
const LoginManager = imports.misc.loginManager;
|
||||
const Main = imports.ui.main;
|
||||
const ShellMountOperation = imports.ui.shellMountOperation;
|
||||
|
||||
@ -32,6 +33,7 @@ const AutomountManager = new Lang.Class({
|
||||
Lang.bind(this, this._InhibitorsChanged));
|
||||
this._inhibited = false;
|
||||
|
||||
this._loginManager = LoginManager.getLoginManager();
|
||||
this._volumeMonitor = Gio.VolumeMonitor.get();
|
||||
},
|
||||
|
||||
@ -83,29 +85,25 @@ const AutomountManager = new Lang.Class({
|
||||
_onDriveConnected: function() {
|
||||
// if we're not in the current ConsoleKit session,
|
||||
// or screensaver is active, don't play sounds
|
||||
if (!this._session.SessionIsActive)
|
||||
if (!this._loginManager.sessionActive)
|
||||
return;
|
||||
|
||||
global.play_theme_sound(0, 'device-added-media',
|
||||
_("External drive connected"),
|
||||
null);
|
||||
global.play_theme_sound(0, 'device-added-media');
|
||||
},
|
||||
|
||||
_onDriveDisconnected: function() {
|
||||
// if we're not in the current ConsoleKit session,
|
||||
// or screensaver is active, don't play sounds
|
||||
if (!this._session.SessionIsActive)
|
||||
if (!this._loginManager.sessionActive)
|
||||
return;
|
||||
|
||||
global.play_theme_sound(0, 'device-removed-media',
|
||||
_("External drive disconnected"),
|
||||
null);
|
||||
global.play_theme_sound(0, 'device-removed-media');
|
||||
},
|
||||
|
||||
_onDriveEjectButton: function(monitor, drive) {
|
||||
// TODO: this code path is not tested, as the GVfs volume monitor
|
||||
// doesn't emit this signal just yet.
|
||||
if (!this._session.SessionIsActive)
|
||||
if (!this._loginManager.sessionActive)
|
||||
return;
|
||||
|
||||
// we force stop/eject in this case, so we don't have to pass a
|
||||
@ -145,7 +143,7 @@ const AutomountManager = new Lang.Class({
|
||||
if (params.checkSession) {
|
||||
// if we're not in the current ConsoleKit session,
|
||||
// don't attempt automount
|
||||
if (!this._session.SessionIsActive)
|
||||
if (!this._loginManager.sessionActive)
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@ const Lang = imports.lang;
|
||||
const Gio = imports.gi.Gio;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const GnomeSession = imports.misc.gnomeSession;
|
||||
const LoginManager = imports.misc.loginManager;
|
||||
const Main = imports.ui.main;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
const ShellMountOperation = imports.ui.shellMountOperation;
|
||||
@ -162,7 +162,8 @@ const AutorunManager = new Lang.Class({
|
||||
Name: 'AutorunManager',
|
||||
|
||||
_init: function() {
|
||||
this._session = new GnomeSession.SessionManager();
|
||||
this._loginManager = LoginManager.getLoginManager();
|
||||
|
||||
this._volumeMonitor = Gio.VolumeMonitor.get();
|
||||
|
||||
this._transDispatcher = new AutorunTransientDispatcher(this);
|
||||
@ -214,7 +215,7 @@ const AutorunManager = new Lang.Class({
|
||||
_onMountAdded: function(monitor, mount) {
|
||||
// don't do anything if our session is not the currently
|
||||
// active one
|
||||
if (!this._session.SessionIsActive)
|
||||
if (!this._loginManager.sessionActive)
|
||||
return;
|
||||
|
||||
this._processMount(mount, true);
|
||||
@ -292,7 +293,7 @@ const AutorunResidentSource = new Lang.Class({
|
||||
|
||||
_init: function(manager) {
|
||||
this.parent(_("Removable Devices"), 'media-removable');
|
||||
this.resident = true;
|
||||
this.showInLockScreen = false;
|
||||
|
||||
this._mounts = [];
|
||||
|
||||
@ -300,10 +301,6 @@ const AutorunResidentSource = new Lang.Class({
|
||||
this._notification = new AutorunResidentNotification(this._manager, this);
|
||||
},
|
||||
|
||||
_createPolicy: function() {
|
||||
return new MessageTray.NotificationPolicy({ showInLockScreen: false });
|
||||
},
|
||||
|
||||
buildRightClickMenu: function() {
|
||||
return null;
|
||||
},
|
||||
|
@ -60,14 +60,18 @@ const KeyringDialog = new Lang.Class({
|
||||
|
||||
this._controlTable = null;
|
||||
|
||||
let buttons = [{ label: '',
|
||||
action: Lang.bind(this, this._onCancelButton),
|
||||
key: Clutter.Escape
|
||||
},
|
||||
{ label: '',
|
||||
action: Lang.bind(this, this._onContinueButton),
|
||||
default: true
|
||||
}]
|
||||
|
||||
this._cancelButton = this.addButton({ label: '',
|
||||
action: Lang.bind(this, this._onCancelButton),
|
||||
key: Clutter.Escape });
|
||||
this._continueButton = this.addButton({ label: '',
|
||||
action: Lang.bind(this, this._onContinueButton),
|
||||
default: true },
|
||||
{ expand: true, x_fill: false, x_align: St.Align.END });
|
||||
this.setButtons(buttons);
|
||||
this._cancelButton = buttons[0].button;
|
||||
this._continueButton = buttons[1].button;
|
||||
|
||||
this.prompt.bind_property('cancel-label', this._cancelButton, 'label', GObject.BindingFlags.SYNC_CREATE);
|
||||
this.prompt.bind_property('continue-label', this._continueButton, 'label', GObject.BindingFlags.SYNC_CREATE);
|
||||
@ -142,14 +146,6 @@ const KeyringDialog = new Lang.Class({
|
||||
this._messageBox.add(table, { x_fill: true, y_fill: true });
|
||||
},
|
||||
|
||||
_updateSensitivity: function(sensitive) {
|
||||
this._passwordEntry.reactive = sensitive;
|
||||
this._passwordEntry.clutter_text.editable = sensitive;
|
||||
|
||||
this._continueButton.can_focus = sensitive;
|
||||
this._continueButton.reactive = sensitive;
|
||||
},
|
||||
|
||||
_ensureOpen: function() {
|
||||
// NOTE: ModalDialog.open() is safe to call if the dialog is
|
||||
// already open - it just returns true without side-effects
|
||||
@ -171,14 +167,12 @@ const KeyringDialog = new Lang.Class({
|
||||
_onShowPassword: function(prompt) {
|
||||
this._buildControlTable();
|
||||
this._ensureOpen();
|
||||
this._updateSensitivity(true);
|
||||
this._passwordEntry.grab_key_focus();
|
||||
},
|
||||
|
||||
_onShowConfirm: function(prompt) {
|
||||
this._buildControlTable();
|
||||
this._ensureOpen();
|
||||
this._updateSensitivity(true);
|
||||
this._continueButton.grab_key_focus();
|
||||
},
|
||||
|
||||
@ -198,7 +192,6 @@ const KeyringDialog = new Lang.Class({
|
||||
},
|
||||
|
||||
_onContinueButton: function() {
|
||||
this._updateSensitivity(false);
|
||||
this.prompt.complete();
|
||||
},
|
||||
|
||||
@ -222,7 +215,7 @@ const KeyringPrompter = new Lang.Class({
|
||||
enable: function() {
|
||||
this._prompter.register(Gio.DBus.session);
|
||||
this._dbusId = Gio.DBus.session.own_name('org.gnome.keyring.SystemPrompter',
|
||||
Gio.BusNameOwnerFlags.ALLOW_REPLACEMENT, null, null);
|
||||
Gio.BusNameOwnerFlags.REPLACE, null, null);
|
||||
},
|
||||
|
||||
disable: function() {
|
||||
|
@ -587,19 +587,18 @@ const NetworkAgent = new Lang.Class({
|
||||
Name: 'NetworkAgent',
|
||||
|
||||
_init: function() {
|
||||
this._native = new Shell.NetworkAgent({ identifier: 'org.gnome.Shell.NetworkAgent' });
|
||||
this._native = new Shell.NetworkAgent({ auto_register: false,
|
||||
identifier: 'org.gnome.Shell.NetworkAgent' });
|
||||
|
||||
this._dialogs = { };
|
||||
this._vpnRequests = { };
|
||||
|
||||
this._native.connect('new-request', Lang.bind(this, this._newRequest));
|
||||
this._native.connect('cancel-request', Lang.bind(this, this._cancelRequest));
|
||||
|
||||
this._enabled = false;
|
||||
},
|
||||
|
||||
enable: function() {
|
||||
this._enabled = true;
|
||||
this._native.auto_register = true;
|
||||
},
|
||||
|
||||
disable: function() {
|
||||
@ -613,15 +612,12 @@ const NetworkAgent = new Lang.Class({
|
||||
this._vpnRequests[requestId].cancel(true);
|
||||
this._vpnRequests = { };
|
||||
|
||||
this._enabled = false;
|
||||
this._native.auto_register = false;
|
||||
if (this._native.registered)
|
||||
this._native.unregister();
|
||||
},
|
||||
|
||||
_newRequest: function(agent, requestId, connection, settingName, hints, flags) {
|
||||
if (!this._enabled) {
|
||||
agent.respond(requestId, Shell.NetworkAgentResponse.USER_CANCELED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (settingName == 'vpn') {
|
||||
this._vpnRequest(requestId, connection, hints, flags);
|
||||
return;
|
||||
|
@ -159,13 +159,14 @@ const AuthenticationDialog = new Lang.Class({
|
||||
messageBox.add(this._nullMessageLabel);
|
||||
this._nullMessageLabel.show();
|
||||
|
||||
this._cancelButton = this.addButton({ label: _("Cancel"),
|
||||
action: Lang.bind(this, this.cancel),
|
||||
key: Clutter.Escape });
|
||||
this._okButton = this.addButton({ label: _("Authenticate"),
|
||||
action: Lang.bind(this, this._onAuthenticateButtonPressed),
|
||||
default: true },
|
||||
{ expand: true, x_fill: false, x_align: St.Align.END });
|
||||
this.setButtons([{ label: _("Cancel"),
|
||||
action: Lang.bind(this, this.cancel),
|
||||
key: Clutter.Escape
|
||||
},
|
||||
{ label: _("Authenticate"),
|
||||
action: Lang.bind(this, this._onAuthenticateButtonPressed),
|
||||
default: true
|
||||
}]);
|
||||
|
||||
this._doneEmitted = false;
|
||||
|
||||
@ -213,17 +214,8 @@ const AuthenticationDialog = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
_updateSensitivity: function(sensitive) {
|
||||
this._passwordEntry.reactive = sensitive;
|
||||
this._passwordEntry.clutter_text.editable = sensitive;
|
||||
|
||||
this._okButton.can_focus = sensitive;
|
||||
this._okButton.reactive = sensitive;
|
||||
},
|
||||
|
||||
_onEntryActivate: function() {
|
||||
let response = this._passwordEntry.get_text();
|
||||
this._updateSensitivity(false);
|
||||
this._session.response(response);
|
||||
// When the user responds, dismiss already shown info and
|
||||
// error texts (if any)
|
||||
@ -277,7 +269,6 @@ const AuthenticationDialog = new Lang.Class({
|
||||
this._passwordBox.show();
|
||||
this._passwordEntry.set_text('');
|
||||
this._passwordEntry.grab_key_focus();
|
||||
this._updateSensitivity(true);
|
||||
this._ensureOpen();
|
||||
},
|
||||
|
||||
@ -335,19 +326,11 @@ const AuthenticationAgent = new Lang.Class({
|
||||
},
|
||||
|
||||
enable: function() {
|
||||
try {
|
||||
this._native.register();
|
||||
} catch(e) {
|
||||
log('Failed to register AuthenticationAgent');
|
||||
}
|
||||
this._native.register();
|
||||
},
|
||||
|
||||
disable: function() {
|
||||
try {
|
||||
this._native.unregister();
|
||||
} catch(e) {
|
||||
log('Failed to unregister AuthenticationAgent');
|
||||
}
|
||||
this._native.unregister();
|
||||
},
|
||||
|
||||
_onInitiate: function(nativeAgent, actionId, message, iconName, cookie, userNames) {
|
||||
|
@ -20,8 +20,8 @@ const Recorder = new Lang.Class({
|
||||
Main.wm.addKeybinding('toggle-recording',
|
||||
this._bindingSettings,
|
||||
Meta.KeyBindingFlags.NONE,
|
||||
Shell.KeyBindingMode.NORMAL |
|
||||
Shell.KeyBindingMode.OVERVIEW,
|
||||
Main.KeybindingMode.NORMAL |
|
||||
Main.KeybindingMode.OVERVIEW,
|
||||
Lang.bind(this, this._toggleRecorder));
|
||||
},
|
||||
|
||||
|
@ -13,7 +13,6 @@ const Tp = imports.gi.TelepathyGLib;
|
||||
const History = imports.misc.history;
|
||||
const Main = imports.ui.main;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
const NotificationDaemon = imports.ui.notificationDaemon;
|
||||
const Params = imports.misc.params;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
|
||||
@ -416,8 +415,6 @@ const TelepathyClient = new Lang.Class({
|
||||
_ensureAppSource: function() {
|
||||
if (this._appSource == null) {
|
||||
this._appSource = new MessageTray.Source(_("Chat"), 'empathy');
|
||||
this._appSource.policy = new NotificationDaemon.NotificationApplicationPolicy('empathy');
|
||||
|
||||
Main.messageTray.add(this._appSource);
|
||||
this._appSource.connect('destroy', Lang.bind(this, function () {
|
||||
this._appSource = null;
|
||||
@ -487,10 +484,6 @@ const ChatSource = new Lang.Class({
|
||||
return rightClickMenu;
|
||||
},
|
||||
|
||||
_createPolicy: function() {
|
||||
return new NotificationDaemon.NotificationApplicationPolicy('empathy');
|
||||
},
|
||||
|
||||
_updateAlias: function() {
|
||||
let oldAlias = this.title;
|
||||
let newAlias = this._contact.get_alias();
|
||||
@ -640,10 +633,6 @@ const ChatSource = new Lang.Class({
|
||||
return this._pendingMessages.length;
|
||||
},
|
||||
|
||||
get indicatorCount() {
|
||||
return this.count;
|
||||
},
|
||||
|
||||
get unseenCount() {
|
||||
return this.count;
|
||||
},
|
||||
@ -1059,10 +1048,6 @@ const ApproverSource = new Lang.Class({
|
||||
}));
|
||||
},
|
||||
|
||||
_createPolicy: function() {
|
||||
return new NotificationDaemon.NotificationApplicationPolicy('empathy');
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
if (this._invalidId != 0) {
|
||||
this._dispatchOp.disconnect(this._invalidId);
|
||||
@ -1361,8 +1346,9 @@ const AccountNotification = new Lang.Class({
|
||||
this.connect('action-invoked', Lang.bind(this, function(self, action) {
|
||||
switch (action) {
|
||||
case 'view':
|
||||
let cmd = 'empathy-accounts --select-account=' +
|
||||
account.get_path_suffix();
|
||||
let cmd = '/usr/bin/empathy-accounts'
|
||||
+ ' --select-account=%s'
|
||||
.format(account.get_path_suffix());
|
||||
let app_info = Gio.app_info_create_from_commandline(cmd, null, 0);
|
||||
app_info.launch([], global.create_app_launch_context());
|
||||
break;
|
||||
|
@ -103,11 +103,6 @@ const CtrlAltTabManager = new Lang.Class({
|
||||
else
|
||||
icon = textureCache.bind_pixbuf_property(windows[i], 'icon');
|
||||
items.push({ name: windows[i].title,
|
||||
proxy: windows[i].get_compositor_private(),
|
||||
focusCallback: Lang.bind(windows[i],
|
||||
function(timestamp) {
|
||||
Main.activateWindow(this, timestamp);
|
||||
}),
|
||||
iconActor: icon,
|
||||
sortGroup: SortGroup.MIDDLE });
|
||||
}
|
||||
|
206
js/ui/dash.js
206
js/ui/dash.js
@ -22,8 +22,11 @@ const DASH_ITEM_LABEL_HIDE_TIME = 0.1;
|
||||
const DASH_ITEM_HOVER_TIMEOUT = 300;
|
||||
|
||||
function getAppFromSource(source) {
|
||||
if (source instanceof AppDisplay.AppIcon) {
|
||||
if (source instanceof AppDisplay.AppWellIcon) {
|
||||
return source.app;
|
||||
} else if (source.metaWindow) {
|
||||
let tracker = Shell.WindowTracker.get_default();
|
||||
return tracker.get_window_app(source.metaWindow);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
@ -33,26 +36,30 @@ function getAppFromSource(source) {
|
||||
// when requesting a size
|
||||
const DashItemContainer = new Lang.Class({
|
||||
Name: 'DashItemContainer',
|
||||
Extends: St.Widget,
|
||||
|
||||
_init: function() {
|
||||
this.parent({ style_class: 'dash-item-container' });
|
||||
this.actor = new Shell.GenericContainer({ style_class: 'dash-item-container' });
|
||||
this.actor.connect('get-preferred-width',
|
||||
Lang.bind(this, this._getPreferredWidth));
|
||||
this.actor.connect('get-preferred-height',
|
||||
Lang.bind(this, this._getPreferredHeight));
|
||||
this.actor.connect('allocate',
|
||||
Lang.bind(this, this._allocate));
|
||||
this.actor._delegate = this;
|
||||
|
||||
this._labelText = "";
|
||||
this.label = new St.Label({ style_class: 'dash-label'});
|
||||
this.label.hide();
|
||||
Main.layoutManager.addChrome(this.label);
|
||||
this.label_actor = this.label;
|
||||
this.actor.label_actor = this.label;
|
||||
|
||||
this.child = null;
|
||||
this._childScale = 0;
|
||||
this._childOpacity = 0;
|
||||
this._childScale = 1;
|
||||
this._childOpacity = 255;
|
||||
this.animatingOut = false;
|
||||
},
|
||||
|
||||
vfunc_allocate: function(box, flags) {
|
||||
this.set_allocation(box, flags);
|
||||
|
||||
_allocate: function(actor, box, flags) {
|
||||
if (this.child == null)
|
||||
return;
|
||||
|
||||
@ -74,28 +81,28 @@ const DashItemContainer = new Lang.Class({
|
||||
this.child.allocate(childBox, flags);
|
||||
},
|
||||
|
||||
vfunc_get_preferred_height: function(forWidth) {
|
||||
let themeNode = this.get_theme_node();
|
||||
_getPreferredHeight: function(actor, forWidth, alloc) {
|
||||
alloc.min_size = 0;
|
||||
alloc.natural_size = 0;
|
||||
|
||||
if (this.child == null)
|
||||
return [0, 0];
|
||||
return;
|
||||
|
||||
forWidth = themeNode.adjust_for_width(forWidth);
|
||||
let [minHeight, natHeight] = this.child.get_preferred_height(forWidth);
|
||||
return themeNode.adjust_preferred_height(minHeight * this.child.scale_y,
|
||||
natHeight * this.child.scale_y);
|
||||
alloc.min_size += minHeight * this.child.scale_y;
|
||||
alloc.natural_size += natHeight * this.child.scale_y;
|
||||
},
|
||||
|
||||
vfunc_get_preferred_width: function(forHeight) {
|
||||
let themeNode = this.get_theme_node();
|
||||
_getPreferredWidth: function(actor, forHeight, alloc) {
|
||||
alloc.min_size = 0;
|
||||
alloc.natural_size = 0;
|
||||
|
||||
if (this.child == null)
|
||||
return [0, 0];
|
||||
return;
|
||||
|
||||
forHeight = themeNode.adjust_for_height(forHeight);
|
||||
let [minWidth, natWidth] = this.child.get_preferred_width(forHeight);
|
||||
return themeNode.adjust_preferred_width(minWidth * this.child.scale_y,
|
||||
natWidth * this.child.scale_y);
|
||||
alloc.min_size = minWidth * this.child.scale_y;
|
||||
alloc.natural_size = natWidth * this.child.scale_y;
|
||||
},
|
||||
|
||||
showLabel: function() {
|
||||
@ -106,9 +113,9 @@ const DashItemContainer = new Lang.Class({
|
||||
this.label.opacity = 0;
|
||||
this.label.show();
|
||||
|
||||
let [stageX, stageY] = this.get_transformed_position();
|
||||
let [stageX, stageY] = this.actor.get_transformed_position();
|
||||
|
||||
let itemHeight = this.allocation.y2 - this.allocation.y1;
|
||||
let itemHeight = this.actor.allocation.y2 - this.actor.allocation.y1;
|
||||
|
||||
let labelHeight = this.label.get_height();
|
||||
let yOffset = Math.floor((itemHeight - labelHeight) / 2)
|
||||
@ -122,7 +129,7 @@ const DashItemContainer = new Lang.Class({
|
||||
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
|
||||
x = stageX - this.label.get_width() - xOffset;
|
||||
else
|
||||
x = stageX + this.get_width() + xOffset;
|
||||
x = stageX + this.actor.get_width() + xOffset;
|
||||
|
||||
this.label.set_position(x, y);
|
||||
Tweener.addTween(this.label,
|
||||
@ -152,25 +159,22 @@ const DashItemContainer = new Lang.Class({
|
||||
if (this.child == actor)
|
||||
return;
|
||||
|
||||
this.destroy_all_children();
|
||||
this.actor.destroy_all_children();
|
||||
|
||||
this.child = actor;
|
||||
this.add_actor(this.child);
|
||||
|
||||
this.child.set_scale_with_gravity(this._childScale, this._childScale,
|
||||
Clutter.Gravity.CENTER);
|
||||
this.child.set_opacity(this._childOpacity);
|
||||
this.actor.add_actor(this.child);
|
||||
},
|
||||
|
||||
show: function(animate) {
|
||||
animateIn: function() {
|
||||
if (this.child == null)
|
||||
return;
|
||||
|
||||
let time = animate ? DASH_ANIMATION_TIME : 0;
|
||||
this.childScale = 0;
|
||||
this.childOpacity = 0;
|
||||
Tweener.addTween(this,
|
||||
{ childScale: 1.0,
|
||||
childOpacity: 255,
|
||||
time: time,
|
||||
time: DASH_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
},
|
||||
@ -179,7 +183,7 @@ const DashItemContainer = new Lang.Class({
|
||||
if (this.label)
|
||||
this.label.destroy();
|
||||
|
||||
this.parent();
|
||||
this.actor.destroy();
|
||||
},
|
||||
|
||||
animateOutAndDestroy: function() {
|
||||
@ -187,18 +191,19 @@ const DashItemContainer = new Lang.Class({
|
||||
this.label.destroy();
|
||||
|
||||
if (this.child == null) {
|
||||
this.destroy();
|
||||
this.actor.destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
this.animatingOut = true;
|
||||
this.childScale = 1.0;
|
||||
Tweener.addTween(this,
|
||||
{ childScale: 0.0,
|
||||
childOpacity: 0,
|
||||
time: DASH_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this, function() {
|
||||
this.destroy();
|
||||
this.actor.destroy();
|
||||
})
|
||||
});
|
||||
},
|
||||
@ -211,7 +216,7 @@ const DashItemContainer = new Lang.Class({
|
||||
|
||||
this.child.set_scale_with_gravity(scale, scale,
|
||||
Clutter.Gravity.CENTER);
|
||||
this.queue_relayout();
|
||||
this.actor.queue_relayout();
|
||||
},
|
||||
|
||||
get childScale() {
|
||||
@ -225,7 +230,7 @@ const DashItemContainer = new Lang.Class({
|
||||
return;
|
||||
|
||||
this.child.set_opacity(opacity);
|
||||
this.queue_redraw();
|
||||
this.actor.queue_redraw();
|
||||
},
|
||||
|
||||
get childOpacity() {
|
||||
@ -356,23 +361,6 @@ const DashActor = new Lang.Class({
|
||||
childBox.y1 = contentBox.y2 - showAppsNatHeight;
|
||||
childBox.y2 = contentBox.y2;
|
||||
showAppsButton.allocate(childBox, flags);
|
||||
},
|
||||
|
||||
vfunc_get_preferred_height: function(forWidth) {
|
||||
// We want to request the natural height of all our children
|
||||
// as our natural height, so we chain up to StWidget (which
|
||||
// then calls BoxLayout), but we only request the showApps
|
||||
// button as the minimum size
|
||||
|
||||
let [, natHeight] = this.parent(forWidth);
|
||||
|
||||
let themeNode = this.get_theme_node();
|
||||
let adjustedForWidth = themeNode.adjust_for_width(forWidth);
|
||||
let [, showAppsButton] = this.get_children();
|
||||
let [minHeight, ] = showAppsButton.get_preferred_height(adjustedForWidth);
|
||||
[minHeight, ] = themeNode.adjust_preferred_height(minHeight, natHeight);
|
||||
|
||||
return [minHeight, natHeight];
|
||||
}
|
||||
});
|
||||
|
||||
@ -398,14 +386,12 @@ const Dash = new Lang.Class({
|
||||
this._container.add_actor(this._box);
|
||||
|
||||
this._showAppsIcon = new ShowAppsIcon();
|
||||
this._showAppsIcon.childScale = 1;
|
||||
this._showAppsIcon.childOpacity = 255;
|
||||
this._showAppsIcon.icon.setIconSize(this.iconSize);
|
||||
this._hookUpLabel(this._showAppsIcon);
|
||||
|
||||
this.showAppsButton = this._showAppsIcon.toggleButton;
|
||||
|
||||
this._container.add_actor(this._showAppsIcon);
|
||||
this._container.add_actor(this._showAppsIcon.actor);
|
||||
|
||||
this.actor = new St.Bin({ child: this._container });
|
||||
this.actor.connect('notify::height', Lang.bind(this,
|
||||
@ -429,10 +415,12 @@ const Dash = new Lang.Class({
|
||||
Lang.bind(this, this._onDragEnd));
|
||||
Main.overview.connect('item-drag-cancelled',
|
||||
Lang.bind(this, this._onDragCancelled));
|
||||
|
||||
// Translators: this is the name of the dock/favorites area on
|
||||
// the left of the overview
|
||||
Main.ctrlAltTabManager.addGroup(this.actor, _("Dash"), 'user-bookmarks-symbolic');
|
||||
Main.overview.connect('window-drag-begin',
|
||||
Lang.bind(this, this._onDragBegin));
|
||||
Main.overview.connect('window-drag-cancelled',
|
||||
Lang.bind(this, this._onDragCancelled));
|
||||
Main.overview.connect('window-drag-end',
|
||||
Lang.bind(this, this._onDragEnd));
|
||||
},
|
||||
|
||||
_onDragBegin: function() {
|
||||
@ -467,7 +455,7 @@ const Dash = new Lang.Class({
|
||||
return DND.DragMotionResult.CONTINUE;
|
||||
|
||||
let showAppsHovered =
|
||||
this._showAppsIcon.contains(dragEvent.targetActor);
|
||||
this._showAppsIcon.actor.contains(dragEvent.targetActor);
|
||||
|
||||
if (!this._box.contains(dragEvent.targetActor) || showAppsHovered)
|
||||
this._clearDragPlaceholder();
|
||||
@ -503,9 +491,9 @@ const Dash = new Lang.Class({
|
||||
},
|
||||
|
||||
_createAppItem: function(app) {
|
||||
let appIcon = new AppDisplay.AppIcon(app,
|
||||
{ setSizeManually: true,
|
||||
showLabel: false });
|
||||
let appIcon = new AppDisplay.AppWellIcon(app,
|
||||
{ setSizeManually: true,
|
||||
showLabel: false });
|
||||
appIcon._draggable.connect('drag-begin',
|
||||
Lang.bind(this, function() {
|
||||
appIcon.actor.opacity = 50;
|
||||
@ -522,7 +510,7 @@ const Dash = new Lang.Class({
|
||||
let item = new DashItemContainer();
|
||||
item.setChild(appIcon.actor);
|
||||
|
||||
// Override default AppIcon label_actor, now the
|
||||
// Override default AppWellIcon label_actor, now the
|
||||
// accessible_name is set at DashItemContainer.setLabelText
|
||||
appIcon.actor.label_actor = null;
|
||||
item.setLabelText(app.get_name());
|
||||
@ -582,13 +570,13 @@ const Dash = new Lang.Class({
|
||||
// animating out (which means they will be destroyed at the end of
|
||||
// the animation)
|
||||
let iconChildren = this._box.get_children().filter(function(actor) {
|
||||
return actor.child &&
|
||||
actor.child._delegate &&
|
||||
actor.child._delegate.icon &&
|
||||
!actor.animatingOut;
|
||||
return actor._delegate.child &&
|
||||
actor._delegate.child._delegate &&
|
||||
actor._delegate.child._delegate.icon &&
|
||||
!actor._delegate.animatingOut;
|
||||
});
|
||||
|
||||
iconChildren.push(this._showAppsIcon);
|
||||
iconChildren.push(this._showAppsIcon.actor);
|
||||
|
||||
if (this._maxHeight == -1)
|
||||
return;
|
||||
@ -601,18 +589,23 @@ const Dash = new Lang.Class({
|
||||
let availHeight = maxContent.y2 - maxContent.y1;
|
||||
let spacing = themeNode.get_length('spacing');
|
||||
|
||||
let firstButton = iconChildren[0].child;
|
||||
let firstIcon = firstButton._delegate.icon;
|
||||
|
||||
let firstIcon = iconChildren[0]._delegate.child._delegate.icon;
|
||||
|
||||
let minHeight, natHeight;
|
||||
|
||||
// Enforce the current icon size during the size request
|
||||
let [currentWidth, currentHeight] = firstIcon.icon.get_size();
|
||||
// Enforce the current icon size during the size request if
|
||||
// the icon is animating
|
||||
if (firstIcon._animating) {
|
||||
let [currentWidth, currentHeight] = firstIcon.icon.get_size();
|
||||
|
||||
firstIcon.icon.set_size(this.iconSize, this.iconSize);
|
||||
[minHeight, natHeight] = firstButton.get_preferred_height(-1);
|
||||
firstIcon.icon.set_size(this.iconSize, this.iconSize);
|
||||
[minHeight, natHeight] = iconChildren[0].get_preferred_height(-1);
|
||||
|
||||
firstIcon.icon.set_size(currentWidth, currentHeight);
|
||||
firstIcon.icon.set_size(currentWidth, currentHeight);
|
||||
} else {
|
||||
[minHeight, natHeight] = iconChildren[0].get_preferred_height(-1);
|
||||
}
|
||||
|
||||
// Subtract icon padding and box spacing from the available height
|
||||
availHeight -= iconChildren.length * (natHeight - this.iconSize) +
|
||||
@ -637,7 +630,7 @@ const Dash = new Lang.Class({
|
||||
|
||||
let scale = oldIconSize / newIconSize;
|
||||
for (let i = 0; i < iconChildren.length; i++) {
|
||||
let icon = iconChildren[i].child._delegate.icon;
|
||||
let icon = iconChildren[i]._delegate.child._delegate.icon;
|
||||
|
||||
// Set the new size immediately, to keep the icons' sizes
|
||||
// in sync with this.iconSize
|
||||
@ -657,11 +650,15 @@ const Dash = new Lang.Class({
|
||||
icon.icon.set_size(icon.icon.width * scale,
|
||||
icon.icon.height * scale);
|
||||
|
||||
icon._animating = true;
|
||||
Tweener.addTween(icon.icon,
|
||||
{ width: targetWidth,
|
||||
height: targetHeight,
|
||||
time: DASH_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: function() {
|
||||
icon._animating = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
@ -672,13 +669,13 @@ const Dash = new Lang.Class({
|
||||
let running = this._appSystem.get_running();
|
||||
|
||||
let children = this._box.get_children().filter(function(actor) {
|
||||
return actor.child &&
|
||||
actor.child._delegate &&
|
||||
actor.child._delegate.app;
|
||||
return actor._delegate.child &&
|
||||
actor._delegate.child._delegate &&
|
||||
actor._delegate.child._delegate.app;
|
||||
});
|
||||
// Apps currently in the dash
|
||||
let oldApps = children.map(function(actor) {
|
||||
return actor.child._delegate.app;
|
||||
return actor._delegate.child._delegate.app;
|
||||
});
|
||||
// Apps supposed to be in the dash
|
||||
let newApps = [];
|
||||
@ -744,7 +741,7 @@ const Dash = new Lang.Class({
|
||||
let insertHere = newApps[newIndex + 1] &&
|
||||
newApps[newIndex + 1] == oldApps[oldIndex];
|
||||
let alreadyRemoved = removedActors.reduce(function(result, actor) {
|
||||
let removedApp = actor.child._delegate.app;
|
||||
let removedApp = actor._delegate.child._delegate.app;
|
||||
return result || removedApp == newApps[newIndex];
|
||||
}, false);
|
||||
|
||||
@ -761,11 +758,11 @@ const Dash = new Lang.Class({
|
||||
}
|
||||
|
||||
for (let i = 0; i < addedItems.length; i++)
|
||||
this._box.insert_child_at_index(addedItems[i].item,
|
||||
this._box.insert_child_at_index(addedItems[i].item.actor,
|
||||
addedItems[i].pos);
|
||||
|
||||
for (let i = 0; i < removedActors.length; i++) {
|
||||
let item = removedActors[i];
|
||||
let item = removedActors[i]._delegate;
|
||||
|
||||
// Don't animate item removal when the overview is transitioning
|
||||
// or hidden
|
||||
@ -779,20 +776,18 @@ const Dash = new Lang.Class({
|
||||
|
||||
// Skip animations on first run when adding the initial set
|
||||
// of items, to avoid all items zooming in at once
|
||||
|
||||
let animate = this._shownInitially && Main.overview.visible &&
|
||||
!Main.overview.animationInProgress;
|
||||
|
||||
if (!this._shownInitially)
|
||||
if (!this._shownInitially) {
|
||||
this._shownInitially = true;
|
||||
|
||||
for (let i = 0; i < addedItems.length; i++) {
|
||||
addedItems[i].item.show(animate);
|
||||
return;
|
||||
}
|
||||
|
||||
// Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=692744
|
||||
// Without it, StBoxLayout may use a stale size cache
|
||||
this._box.queue_relayout();
|
||||
// Don't animate item addition when the overview is transitioning
|
||||
// or hidden
|
||||
if (!Main.overview.visible || Main.overview.animationInProgress)
|
||||
return;
|
||||
|
||||
for (let i = 0; i < addedItems.length; i++)
|
||||
addedItems[i].item.animateIn();
|
||||
},
|
||||
|
||||
_clearDragPlaceholder: function() {
|
||||
@ -823,7 +818,7 @@ const Dash = new Lang.Class({
|
||||
// the remove target has the same size as "normal" items, we don't
|
||||
// need to do the same adjustment there.
|
||||
if (this._dragPlaceholder) {
|
||||
boxHeight -= this._dragPlaceholder.height;
|
||||
boxHeight -= this._dragPlaceholder.actor.height;
|
||||
numChildren--;
|
||||
}
|
||||
|
||||
@ -837,7 +832,7 @@ const Dash = new Lang.Class({
|
||||
if (this._dragPlaceholder) {
|
||||
this._dragPlaceholder.animateOutAndDestroy();
|
||||
this._animatingPlaceholdersCount++;
|
||||
this._dragPlaceholder.connect('destroy',
|
||||
this._dragPlaceholder.actor.connect('destroy',
|
||||
Lang.bind(this, function() {
|
||||
this._animatingPlaceholdersCount--;
|
||||
}));
|
||||
@ -852,7 +847,7 @@ const Dash = new Lang.Class({
|
||||
// an animation
|
||||
let fadeIn;
|
||||
if (this._dragPlaceholder) {
|
||||
this._dragPlaceholder.destroy();
|
||||
this._dragPlaceholder.actor.destroy();
|
||||
fadeIn = false;
|
||||
} else {
|
||||
fadeIn = true;
|
||||
@ -861,9 +856,10 @@ const Dash = new Lang.Class({
|
||||
this._dragPlaceholder = new DragPlaceholderItem();
|
||||
this._dragPlaceholder.child.set_width (this.iconSize);
|
||||
this._dragPlaceholder.child.set_height (this.iconSize / 2);
|
||||
this._box.insert_child_at_index(this._dragPlaceholder,
|
||||
this._box.insert_child_at_index(this._dragPlaceholder.actor,
|
||||
this._dragPlaceholderPos);
|
||||
this._dragPlaceholder.show(fadeIn);
|
||||
if (fadeIn)
|
||||
this._dragPlaceholder.animateIn();
|
||||
}
|
||||
|
||||
// Remove the drag placeholder if we are not in the
|
||||
@ -901,10 +897,10 @@ const Dash = new Lang.Class({
|
||||
let children = this._box.get_children();
|
||||
for (let i = 0; i < this._dragPlaceholderPos; i++) {
|
||||
if (this._dragPlaceholder &&
|
||||
children[i] == this._dragPlaceholder)
|
||||
children[i] == this._dragPlaceholder.actor)
|
||||
continue;
|
||||
|
||||
let childId = children[i].child._delegate.app.get_id();
|
||||
let childId = children[i]._delegate.child._delegate.app.get_id();
|
||||
if (childId == id)
|
||||
continue;
|
||||
if (childId in favorites)
|
||||
|
@ -32,7 +32,6 @@ function _onVertSepRepaint (area)
|
||||
cr.setDash([1, 3], 1); // Hard-code for now
|
||||
cr.setLineWidth(stippleWidth);
|
||||
cr.stroke();
|
||||
cr.$dispose();
|
||||
};
|
||||
|
||||
const DateMenuButton = new Lang.Class({
|
||||
@ -84,26 +83,12 @@ const DateMenuButton = new Lang.Class({
|
||||
}));
|
||||
vbox.add(this._calendar.actor);
|
||||
|
||||
let separator = new PopupMenu.PopupSeparatorMenuItem();
|
||||
separator.setColumnWidths(1);
|
||||
vbox.add(separator.actor, {y_align: St.Align.END, expand: true, y_fill: false});
|
||||
|
||||
this._openCalendarItem = new PopupMenu.PopupMenuItem(_("Open Calendar"));
|
||||
this._openCalendarItem.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
|
||||
this._openCalendarItem.actor.can_focus = false;
|
||||
vbox.add(this._openCalendarItem.actor, {y_align: St.Align.END, expand: true, y_fill: false});
|
||||
|
||||
this._openClocksItem = new PopupMenu.PopupMenuItem(_("Open Clocks"));
|
||||
this._openClocksItem.connect('activate', Lang.bind(this, this._onOpenClocksActivate));
|
||||
this._openClocksItem.actor.can_focus = false;
|
||||
vbox.add(this._openClocksItem.actor, {y_align: St.Align.END, expand: true, y_fill: false});
|
||||
|
||||
Shell.AppSystem.get_default().connect('installed-changed',
|
||||
Lang.bind(this, this._appInstalledChanged));
|
||||
this._appInstalledChanged();
|
||||
|
||||
item = this.menu.addSettingsAction(_("Date & Time Settings"), 'gnome-datetime-panel.desktop');
|
||||
item = this.menu.addSettingsAction(_("Date and Time Settings"), 'gnome-datetime-panel.desktop');
|
||||
if (item) {
|
||||
let separator = new PopupMenu.PopupSeparatorMenuItem();
|
||||
separator.setColumnWidths(1);
|
||||
vbox.add(separator.actor, {y_align: St.Align.END, expand: true, y_fill: false});
|
||||
|
||||
item.actor.show_on_set_parent = false;
|
||||
item.actor.can_focus = false;
|
||||
item.actor.reparent(vbox);
|
||||
@ -123,6 +108,16 @@ const DateMenuButton = new Lang.Class({
|
||||
// Event list
|
||||
vbox.add(this._eventList.actor, { expand: true });
|
||||
|
||||
this._openCalendarItem = new PopupMenu.PopupMenuItem(_("Open Calendar"));
|
||||
this._openCalendarItem.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
|
||||
this._openCalendarItem.actor.can_focus = false;
|
||||
vbox.add(this._openCalendarItem.actor, {y_align: St.Align.END, expand: true, y_fill: false});
|
||||
|
||||
this._calendarSettings = new Gio.Settings({ schema: 'org.gnome.desktop.default-applications.office.calendar' });
|
||||
this._calendarSettings.connect('changed::exec',
|
||||
Lang.bind(this, this._calendarSettingsChanged));
|
||||
this._calendarSettingsChanged();
|
||||
|
||||
// Whenever the menu is opened, select today
|
||||
this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) {
|
||||
if (isOpen) {
|
||||
@ -156,15 +151,14 @@ const DateMenuButton = new Lang.Class({
|
||||
this._sessionUpdated();
|
||||
},
|
||||
|
||||
_appInstalledChanged: function() {
|
||||
let app = Shell.AppSystem.get_default().lookup_app('gnome-clocks.desktop');
|
||||
this._openClocksItem.actor.visible = app !== null;
|
||||
_calendarSettingsChanged: function() {
|
||||
let exec = this._calendarSettings.get_string('exec');
|
||||
let fullExec = GLib.find_program_in_path(exec);
|
||||
this._openCalendarItem.actor.visible = fullExec != null;
|
||||
},
|
||||
|
||||
_updateEventsVisibility: function() {
|
||||
let visible = this._eventSource.hasCalendars;
|
||||
_setEventsVisibility: function(visible) {
|
||||
this._openCalendarItem.actor.visible = visible;
|
||||
this._openClocksItem.actor.visible = visible;
|
||||
this._separator.visible = visible;
|
||||
if (visible) {
|
||||
let alignment = 0.25;
|
||||
@ -179,16 +173,8 @@ const DateMenuButton = new Lang.Class({
|
||||
},
|
||||
|
||||
_setEventSource: function(eventSource) {
|
||||
if (this._eventSource)
|
||||
this._eventSource.destroy();
|
||||
|
||||
this._calendar.setEventSource(eventSource);
|
||||
this._eventList.setEventSource(eventSource);
|
||||
|
||||
this._eventSource = eventSource;
|
||||
this._eventSource.connect('notify::has-calendars', Lang.bind(this, function() {
|
||||
this._updateEventsVisibility();
|
||||
}));
|
||||
},
|
||||
|
||||
_sessionUpdated: function() {
|
||||
@ -197,10 +183,10 @@ const DateMenuButton = new Lang.Class({
|
||||
if (showEvents) {
|
||||
eventSource = new Calendar.DBusEventSource();
|
||||
} else {
|
||||
eventSource = new Calendar.EmptyEventSource();
|
||||
eventSource = null;
|
||||
}
|
||||
this._setEventSource(eventSource);
|
||||
this._updateEventsVisibility();
|
||||
this._setEventsVisibility(showEvents);
|
||||
|
||||
// This needs to be handled manually, as the code to
|
||||
// autohide separators doesn't work across the vbox
|
||||
@ -219,16 +205,24 @@ const DateMenuButton = new Lang.Class({
|
||||
|
||||
_onOpenCalendarActivate: function() {
|
||||
this.menu.close();
|
||||
|
||||
let app = Gio.AppInfo.get_default_for_type('text/calendar', false);
|
||||
if (app.get_id() == 'evolution')
|
||||
app = Gio.DesktopAppInfo.new('evolution-calendar');
|
||||
app.launch([], global.create_app_launch_context());
|
||||
},
|
||||
|
||||
_onOpenClocksActivate: function() {
|
||||
this.menu.close();
|
||||
let app = Shell.AppSystem.get_default().lookup_app('gnome-clocks.desktop');
|
||||
app.activate();
|
||||
let tool = this._calendarSettings.get_string('exec');
|
||||
if (tool.length == 0 || tool.substr(0, 9) == 'evolution') {
|
||||
// TODO: pass the selected day
|
||||
let app = Shell.AppSystem.get_default().lookup_app('evolution-calendar.desktop');
|
||||
app.activate();
|
||||
} else {
|
||||
let needTerm = this._calendarSettings.get_boolean('needs-term');
|
||||
if (needTerm) {
|
||||
let terminalSettings = new Gio.Settings({ schema: 'org.gnome.desktop.default-applications.terminal' });
|
||||
let term = terminalSettings.get_string('exec');
|
||||
let arg = terminalSettings.get_string('exec-arg');
|
||||
if (arg != '')
|
||||
Util.spawn([term, arg, tool]);
|
||||
else
|
||||
Util.spawn([term, tool]);
|
||||
} else {
|
||||
Util.spawnCommandLine(tool)
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
28
js/ui/dnd.js
28
js/ui/dnd.js
@ -85,13 +85,11 @@ const _Draggable = new Lang.Class({
|
||||
|
||||
this.actor.connect('destroy', Lang.bind(this, function() {
|
||||
this._actorDestroyed = true;
|
||||
|
||||
// If the drag actor is destroyed and we were going to fix
|
||||
// up its hover state, fix up the parent hover state instead
|
||||
if (this.actor == this._firstLeaveActor)
|
||||
this._firstLeaveActor = this._dragOrigParent;
|
||||
|
||||
if (this._dragInProgress && this._dragCancellable)
|
||||
if (this._dragInProgress)
|
||||
this._cancelDrag(global.get_current_time());
|
||||
this.disconnectAll();
|
||||
}));
|
||||
@ -104,7 +102,6 @@ const _Draggable = new Lang.Class({
|
||||
this._buttonDown = false; // The mouse button has been pressed and has not yet been released.
|
||||
this._dragInProgress = false; // The drag has been started, and has not been dropped or cancelled yet.
|
||||
this._animationInProgress = false; // The drag is over and the item is in the process of animating to its original position (snapping back or reverting).
|
||||
this._dragCancellable = true;
|
||||
|
||||
// During the drag, we eat enter/leave events so that actors don't prelight.
|
||||
// But we remember the actors that we first left/last entered so we can
|
||||
@ -139,10 +136,9 @@ const _Draggable = new Lang.Class({
|
||||
},
|
||||
|
||||
_ungrabActor: function() {
|
||||
Clutter.ungrab_pointer();
|
||||
if (!this._onEventId)
|
||||
return;
|
||||
|
||||
Clutter.ungrab_pointer();
|
||||
this.actor.disconnect(this._onEventId);
|
||||
this._onEventId = null;
|
||||
},
|
||||
@ -207,19 +203,6 @@ const _Draggable = new Lang.Class({
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* fakeRelease:
|
||||
*
|
||||
* Fake a release event.
|
||||
* Must be called if you want to intercept release events on draggable
|
||||
* actors for other purposes (for example if you're using
|
||||
* PopupMenu.ignoreRelease())
|
||||
*/
|
||||
fakeRelease: function() {
|
||||
this._buttonDown = false;
|
||||
this._ungrabActor();
|
||||
},
|
||||
|
||||
/**
|
||||
* startDrag:
|
||||
* @stageX: X coordinate of event
|
||||
@ -442,11 +425,6 @@ const _Draggable = new Lang.Class({
|
||||
}
|
||||
}
|
||||
|
||||
// At this point it is too late to cancel a drag by destroying
|
||||
// the actor, the fate of which is decided by acceptDrop and its
|
||||
// side-effects
|
||||
this._dragCancellable = false;
|
||||
|
||||
while (target) {
|
||||
if (target._delegate && target._delegate.acceptDrop) {
|
||||
let [r, targX, targY] = target.transform_stage_point(dropX, dropY);
|
||||
@ -455,6 +433,8 @@ const _Draggable = new Lang.Class({
|
||||
targX,
|
||||
targY,
|
||||
event.get_time())) {
|
||||
if (this._actorDestroyed)
|
||||
return true;
|
||||
// If it accepted the drop without taking the actor,
|
||||
// handle it ourselves.
|
||||
if (this._dragActor.get_parent() == Main.uiGroup) {
|
||||
|
@ -19,7 +19,6 @@
|
||||
*/
|
||||
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const AccountsService = imports.gi.AccountsService;
|
||||
@ -51,7 +50,6 @@ const EndSessionDialogIface = <interface name="org.gnome.SessionManager.EndSessi
|
||||
<arg type="u" direction="in" />
|
||||
<arg type="ao" direction="in" />
|
||||
</method>
|
||||
<method name="Close" />
|
||||
<signal name="ConfirmedLogout" />
|
||||
<signal name="ConfirmedReboot" />
|
||||
<signal name="ConfirmedShutdown" />
|
||||
@ -225,8 +223,7 @@ const EndSessionDialog = new Lang.Class({
|
||||
Extends: ModalDialog.ModalDialog,
|
||||
|
||||
_init: function() {
|
||||
this.parent({ styleClass: 'end-session-dialog',
|
||||
destroyOnClose: false });
|
||||
this.parent({ styleClass: 'end-session-dialog' });
|
||||
|
||||
this._user = AccountsService.UserManager.get_default().get_user(GLib.get_user_name());
|
||||
|
||||
@ -380,12 +377,7 @@ const EndSessionDialog = new Lang.Class({
|
||||
let signal = dialogContent.confirmButtons[i].signal;
|
||||
let label = dialogContent.confirmButtons[i].label;
|
||||
buttons.push({ action: Lang.bind(this, function() {
|
||||
this.close(true);
|
||||
let signalId = this.connect('closed',
|
||||
Lang.bind(this, function() {
|
||||
this.disconnect(signalId);
|
||||
this._confirm(signal);
|
||||
}));
|
||||
this._confirm(signal);
|
||||
}),
|
||||
label: label });
|
||||
}
|
||||
@ -393,17 +385,15 @@ const EndSessionDialog = new Lang.Class({
|
||||
this.setButtons(buttons);
|
||||
},
|
||||
|
||||
close: function(skipSignal) {
|
||||
close: function() {
|
||||
this.parent();
|
||||
|
||||
if (!skipSignal)
|
||||
this._dbusImpl.emit_signal('Closed', null);
|
||||
this._dbusImpl.emit_signal('Closed', null);
|
||||
},
|
||||
|
||||
cancel: function() {
|
||||
this._stopTimer();
|
||||
this._dbusImpl.emit_signal('Canceled', null);
|
||||
this.close();
|
||||
this.close(global.get_current_time());
|
||||
},
|
||||
|
||||
_confirm: function(signal) {
|
||||
@ -418,34 +408,22 @@ const EndSessionDialog = new Lang.Class({
|
||||
},
|
||||
|
||||
_startTimer: function() {
|
||||
let startTime = GLib.get_monotonic_time();
|
||||
this._secondsLeft = this._totalSecondsToStayOpen;
|
||||
|
||||
this._timerId = Mainloop.timeout_add_seconds(1, Lang.bind(this,
|
||||
function() {
|
||||
let currentTime = GLib.get_monotonic_time();
|
||||
let secondsElapsed = ((currentTime - startTime) / 1000000);
|
||||
|
||||
this._secondsLeft = this._totalSecondsToStayOpen - secondsElapsed;
|
||||
if (this._secondsLeft > 0) {
|
||||
this._updateDescription();
|
||||
return true;
|
||||
}
|
||||
|
||||
let dialogContent = DialogContent[this._type];
|
||||
let button = dialogContent.confirmButtons[dialogContent.confirmButtons.length - 1];
|
||||
this._confirm(button.signal);
|
||||
|
||||
return false;
|
||||
}));
|
||||
Tweener.addTween(this,
|
||||
{ _secondsLeft: 0,
|
||||
time: this._secondsLeft,
|
||||
transition: 'linear',
|
||||
onUpdate: Lang.bind(this, this._updateDescription),
|
||||
onComplete: Lang.bind(this, function() {
|
||||
let dialogContent = DialogContent[this._type];
|
||||
let button = dialogContent.confirmButtons[dialogContent.confirmButtons.length - 1];
|
||||
this._confirm(button.signal);
|
||||
}),
|
||||
});
|
||||
},
|
||||
|
||||
_stopTimer: function() {
|
||||
if (this._timerId != 0) {
|
||||
Mainloop.source_remove(this._timerId);
|
||||
this._timerId = 0;
|
||||
}
|
||||
|
||||
Tweener.removeTweens(this);
|
||||
this._secondsLeft = 0;
|
||||
},
|
||||
|
||||
@ -462,7 +440,7 @@ const EndSessionDialog = new Lang.Class({
|
||||
let item = new ListItem(app, reason);
|
||||
item.connect('activate',
|
||||
Lang.bind(this, function() {
|
||||
this.close();
|
||||
this.close(global.get_current_time());
|
||||
}));
|
||||
this._applicationList.add(item.actor, { x_fill: true });
|
||||
this._stopTimer();
|
||||
@ -510,9 +488,5 @@ const EndSessionDialog = new Lang.Class({
|
||||
invocation.return_value(null);
|
||||
this.disconnect(signalId);
|
||||
}));
|
||||
},
|
||||
|
||||
Close: function(parameters, invocation) {
|
||||
this.close();
|
||||
}
|
||||
});
|
||||
|
@ -215,7 +215,7 @@ const InstallExtensionDialog = new Lang.Class({
|
||||
},
|
||||
|
||||
_onCancelButtonPressed: function(button, event) {
|
||||
this.close();
|
||||
this.close(global.get_current_time());
|
||||
this._invocation.return_value(GLib.Variant.new('(s)', ['cancelled']));
|
||||
},
|
||||
|
||||
@ -257,7 +257,7 @@ const InstallExtensionDialog = new Lang.Class({
|
||||
gotExtensionZipFile(session, message, uuid, dir, callback, errback);
|
||||
}));
|
||||
|
||||
this.close();
|
||||
this.close(global.get_current_time());
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -106,15 +106,11 @@ function enableExtension(uuid) {
|
||||
|
||||
extensionOrder.push(uuid);
|
||||
|
||||
let stylesheetNames = [global.session_mode + '.css', 'stylesheet.css'];
|
||||
for (let i = 0; i < stylesheetNames.length; i++) {
|
||||
let stylesheetFile = extension.dir.get_child(stylesheetNames[i]);
|
||||
if (stylesheetFile.query_exists(null)) {
|
||||
let theme = St.ThemeContext.get_for_stage(global.stage).get_theme();
|
||||
theme.load_stylesheet(stylesheetFile.get_path());
|
||||
extension.stylesheet = stylesheetFile;
|
||||
break;
|
||||
}
|
||||
let stylesheetFile = extension.dir.get_child('stylesheet.css');
|
||||
if (stylesheetFile.query_exists(null)) {
|
||||
let theme = St.ThemeContext.get_for_stage(global.stage).get_theme();
|
||||
theme.load_stylesheet(stylesheetFile.get_path());
|
||||
extension.stylesheet = stylesheetFile;
|
||||
}
|
||||
|
||||
extension.stateObj.enable();
|
||||
|
45
js/ui/flashspot.js
Normal file
45
js/ui/flashspot.js
Normal file
@ -0,0 +1,45 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Lang = imports.lang;
|
||||
|
||||
const Lightbox = imports.ui.lightbox;
|
||||
const Main = imports.ui.main;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const FLASHSPOT_ANIMATION_TIME = 0.25; // seconds
|
||||
|
||||
const Flashspot = new Lang.Class({
|
||||
Name: 'Flashspot',
|
||||
Extends: Lightbox.Lightbox,
|
||||
|
||||
_init: function(area) {
|
||||
this.parent(Main.uiGroup, { inhibitEvents: true,
|
||||
width: area.width,
|
||||
height: area.height });
|
||||
|
||||
this.actor.style_class = 'flashspot';
|
||||
this.actor.set_position(area.x, area.y);
|
||||
},
|
||||
|
||||
fire: function() {
|
||||
this.actor.opacity = 0;
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 255,
|
||||
time: FLASHSPOT_ANIMATION_TIME,
|
||||
transition: 'linear',
|
||||
onComplete: Lang.bind(this, this._onFireShowComplete)
|
||||
});
|
||||
this.actor.show();
|
||||
},
|
||||
|
||||
_onFireShowComplete: function() {
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 0,
|
||||
time: FLASHSPOT_ANIMATION_TIME,
|
||||
transition: 'linear',
|
||||
onComplete: Lang.bind(this, function() {
|
||||
this.destroy();
|
||||
})
|
||||
});
|
||||
}
|
||||
});
|
@ -1,4 +1,4 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
@ -10,6 +10,17 @@ const St = imports.gi.St;
|
||||
const Main = imports.ui.main;
|
||||
const Params = imports.misc.params;
|
||||
|
||||
function _navigateActor(actor) {
|
||||
if (!actor)
|
||||
return;
|
||||
|
||||
let needsGrab = true;
|
||||
if (actor instanceof St.Widget)
|
||||
needsGrab = !actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
|
||||
if (needsGrab)
|
||||
actor.grab_key_focus();
|
||||
}
|
||||
|
||||
// GrabHelper:
|
||||
// @owner: the actor that owns the GrabHelper
|
||||
// @params: optional parameters to pass to Main.pushModal()
|
||||
@ -35,7 +46,6 @@ const GrabHelper = new Lang.Class({
|
||||
this._keyFocusNotifyId = 0;
|
||||
this._focusWindowChangedId = 0;
|
||||
this._ignoreRelease = false;
|
||||
this._isUngrabbingCount = 0;
|
||||
|
||||
this._modalCount = 0;
|
||||
this._grabFocusCount = 0;
|
||||
@ -67,7 +77,7 @@ const GrabHelper = new Lang.Class({
|
||||
},
|
||||
|
||||
_isWithinGrabbedActor: function(actor) {
|
||||
let currentActor = this.currentGrab.actor;
|
||||
let currentActor = this.currentGrab.actor;
|
||||
while (actor) {
|
||||
if (this._actors.indexOf(actor) != -1)
|
||||
return true;
|
||||
@ -168,18 +178,12 @@ const GrabHelper = new Lang.Class({
|
||||
if (params.grabFocus && !this._takeFocusGrab(hadFocus))
|
||||
return false;
|
||||
|
||||
this._grabStack.push(params);
|
||||
|
||||
if (params.focus) {
|
||||
if (params.focus)
|
||||
params.focus.grab_key_focus();
|
||||
} else if (newFocus && (hadFocus || params.grabFocus)) {
|
||||
if (!newFocus.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false))
|
||||
newFocus.grab_key_focus();
|
||||
}
|
||||
|
||||
if ((params.grabFocus || params.modal) && !this._capturedEventId)
|
||||
this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
|
||||
else if (hadFocus || params.grabFocus)
|
||||
_navigateActor(newFocus);
|
||||
|
||||
this._grabStack.push(params);
|
||||
return true;
|
||||
},
|
||||
|
||||
@ -188,6 +192,8 @@ const GrabHelper = new Lang.Class({
|
||||
if (firstGrab) {
|
||||
if (!Main.pushModal(this._owner, this._modalParams))
|
||||
return false;
|
||||
|
||||
this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
|
||||
}
|
||||
|
||||
this._modalCount++;
|
||||
@ -199,6 +205,11 @@ const GrabHelper = new Lang.Class({
|
||||
if (this._modalCount > 0)
|
||||
return;
|
||||
|
||||
if (this._capturedEventId > 0) {
|
||||
global.stage.disconnect(this._capturedEventId);
|
||||
this._capturedEventId = 0;
|
||||
}
|
||||
|
||||
Main.popModal(this._owner);
|
||||
global.sync_pointer();
|
||||
},
|
||||
@ -234,7 +245,7 @@ const GrabHelper = new Lang.Class({
|
||||
this._keyFocusNotifyId = 0;
|
||||
}
|
||||
|
||||
if (this._focusWindowChangedId > 0) {
|
||||
if (!this._focusWindowChanged > 0) {
|
||||
let metaDisplay = global.screen.get_display();
|
||||
metaDisplay.disconnect(this._focusWindowChangedId);
|
||||
this._focusWindowChangedId = 0;
|
||||
@ -248,7 +259,7 @@ const GrabHelper = new Lang.Class({
|
||||
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
|
||||
}
|
||||
|
||||
global.screen.focus_default_window(global.display.get_current_time_roundtrip());
|
||||
global.screen.focus_default_window(global.get_current_time());
|
||||
},
|
||||
|
||||
// ignoreRelease:
|
||||
@ -276,14 +287,6 @@ const GrabHelper = new Lang.Class({
|
||||
if (grabStackIndex < 0)
|
||||
return;
|
||||
|
||||
// We may get key focus changes when calling onUngrab, which
|
||||
// would cause an extra ungrab() on the next actor in the
|
||||
// stack, which is wrong. Ignore key focus changes during the
|
||||
// ungrab, and restore the saved key focus ourselves afterwards.
|
||||
// We use a count as ungrab() may be re-entrant, as onUngrab()
|
||||
// may ungrab additional actors.
|
||||
this._isUngrabbingCount++;
|
||||
|
||||
let focus = global.stage.key_focus;
|
||||
let hadFocus = focus && this._isWithinGrabbedActor(focus);
|
||||
|
||||
@ -305,31 +308,14 @@ const GrabHelper = new Lang.Class({
|
||||
this._releaseFocusGrab();
|
||||
}
|
||||
|
||||
if (!this.grabbed && this._capturedEventId > 0) {
|
||||
global.stage.disconnect(this._capturedEventId);
|
||||
this._capturedEventId = 0;
|
||||
|
||||
this._ignoreRelease = false;
|
||||
}
|
||||
|
||||
if (hadFocus) {
|
||||
let poppedGrab = poppedGrabs[0];
|
||||
if (poppedGrab.savedFocus)
|
||||
poppedGrab.savedFocus.grab_key_focus();
|
||||
_navigateActor(poppedGrab.savedFocus);
|
||||
}
|
||||
|
||||
this._isUngrabbingCount--;
|
||||
},
|
||||
|
||||
_onCapturedEvent: function(actor, event) {
|
||||
let type = event.type();
|
||||
|
||||
if (type == Clutter.EventType.KEY_PRESS &&
|
||||
event.get_key_symbol() == Clutter.KEY_Escape) {
|
||||
this.ungrab({ isUser: true });
|
||||
return true;
|
||||
}
|
||||
|
||||
let press = type == Clutter.EventType.BUTTON_PRESS;
|
||||
let release = type == Clutter.EventType.BUTTON_RELEASE;
|
||||
let button = press || release;
|
||||
@ -342,6 +328,12 @@ const GrabHelper = new Lang.Class({
|
||||
if (!button && this._modalCount == 0)
|
||||
return false;
|
||||
|
||||
if (type == Clutter.EventType.KEY_PRESS &&
|
||||
event.get_key_symbol() == Clutter.KEY_Escape) {
|
||||
this.ungrab({ isUser: true });
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this._isWithinGrabbedActor(event.get_source()))
|
||||
return false;
|
||||
|
||||
@ -355,16 +347,12 @@ const GrabHelper = new Lang.Class({
|
||||
this._ignoreRelease = true;
|
||||
let i = this._actorInGrabStack(event.get_source()) + 1;
|
||||
this.ungrab({ actor: this._grabStack[i].actor, isUser: true });
|
||||
return true;
|
||||
}
|
||||
|
||||
return this._modalCount > 0;
|
||||
},
|
||||
|
||||
_onKeyFocusChanged: function() {
|
||||
if (this._isUngrabbingCount > 0)
|
||||
return;
|
||||
|
||||
let focus = global.stage.key_focus;
|
||||
if (!focus || !this._isWithinGrabbedActor(focus))
|
||||
this.ungrab({ isUser: true });
|
||||
|
@ -3,149 +3,107 @@
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const IBus = imports.gi.IBus;
|
||||
const Lang = imports.lang;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const BoxPointer = imports.ui.boxpointer;
|
||||
const Main = imports.ui.main;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
|
||||
const MAX_CANDIDATES_PER_PAGE = 16;
|
||||
|
||||
const CandidateArea = new Lang.Class({
|
||||
Name: 'CandidateArea',
|
||||
Extends: PopupMenu.PopupBaseMenuItem,
|
||||
|
||||
_init: function() {
|
||||
this.actor = new St.BoxLayout({ vertical: true,
|
||||
visible: false });
|
||||
this._candidateBoxes = [];
|
||||
this.parent({ reactive: false });
|
||||
|
||||
// St.Table exhibits some sizing problems so let's go with a
|
||||
// clutter layout manager for now.
|
||||
this._table = new Clutter.Actor();
|
||||
this.addActor(this._table);
|
||||
|
||||
this._tableLayout = new Clutter.TableLayout();
|
||||
this._table.set_layout_manager(this._tableLayout);
|
||||
|
||||
this._indexLabels = [];
|
||||
this._candidateLabels = [];
|
||||
for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
|
||||
let box = new St.BoxLayout({ style_class: 'candidate-box',
|
||||
reactive: true,
|
||||
track_hover: true });
|
||||
box._indexLabel = new St.Label({ style_class: 'candidate-index' });
|
||||
box._candidateLabel = new St.Label({ style_class: 'candidate-label' });
|
||||
box.add(box._indexLabel, { y_fill: false });
|
||||
box.add(box._candidateLabel, { y_fill: false });
|
||||
this._candidateBoxes.push(box);
|
||||
this.actor.add(box);
|
||||
|
||||
let j = i;
|
||||
box.connect('button-release-event', Lang.bind(this, function(actor, event) {
|
||||
this.emit('candidate-clicked', j, event.get_button(), event.get_state());
|
||||
}));
|
||||
this._indexLabels.push(new St.Label({ style_class: 'candidate-index' }));
|
||||
this._candidateLabels.push(new St.Label({ style_class: 'candidate-label' }));
|
||||
}
|
||||
|
||||
this._buttonBox = new St.BoxLayout({ style_class: 'candidate-page-button-box' });
|
||||
|
||||
this._previousButton = new St.Button({ style_class: 'candidate-page-button candidate-page-button-previous' });
|
||||
this._previousButton.child = new St.Icon({ style_class: 'candidate-page-button-icon' });
|
||||
this._buttonBox.add(this._previousButton, { expand: true });
|
||||
|
||||
this._nextButton = new St.Button({ style_class: 'candidate-page-button candidate-page-button-next' });
|
||||
this._nextButton.child = new St.Icon({ style_class: 'candidate-page-button-icon' });
|
||||
this._buttonBox.add(this._nextButton, { expand: true });
|
||||
|
||||
this.actor.add(this._buttonBox);
|
||||
|
||||
this._previousButton.connect('clicked', Lang.bind(this, function() {
|
||||
this.emit('previous-page');
|
||||
}));
|
||||
this._nextButton.connect('clicked', Lang.bind(this, function() {
|
||||
this.emit('next-page');
|
||||
}));
|
||||
|
||||
this._orientation = -1;
|
||||
this._cursorPosition = 0;
|
||||
},
|
||||
|
||||
setOrientation: function(orientation) {
|
||||
_setOrientation: function(orientation) {
|
||||
if (this._orientation == orientation)
|
||||
return;
|
||||
|
||||
this._orientation = orientation;
|
||||
|
||||
if (this._orientation == IBus.Orientation.HORIZONTAL) {
|
||||
this.actor.vertical = false;
|
||||
this.actor.remove_style_class_name('vertical');
|
||||
this.actor.add_style_class_name('horizontal');
|
||||
this._previousButton.child.icon_name = 'go-previous-symbolic';
|
||||
this._nextButton.child.icon_name = 'go-next-symbolic';
|
||||
} else { // VERTICAL || SYSTEM
|
||||
this.actor.vertical = true;
|
||||
this.actor.add_style_class_name('vertical');
|
||||
this.actor.remove_style_class_name('horizontal');
|
||||
this._previousButton.child.icon_name = 'go-up-symbolic';
|
||||
this._nextButton.child.icon_name = 'go-down-symbolic';
|
||||
}
|
||||
this._table.remove_all_children();
|
||||
|
||||
if (this._orientation == IBus.Orientation.HORIZONTAL)
|
||||
for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
|
||||
this._tableLayout.pack(this._indexLabels[i], i*2, 0);
|
||||
this._tableLayout.pack(this._candidateLabels[i], i*2 + 1, 0);
|
||||
}
|
||||
else // VERTICAL || SYSTEM
|
||||
for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
|
||||
this._tableLayout.pack(this._indexLabels[i], 0, i);
|
||||
this._tableLayout.pack(this._candidateLabels[i], 1, i);
|
||||
}
|
||||
},
|
||||
|
||||
setCandidates: function(indexes, candidates, cursorPosition, cursorVisible) {
|
||||
setCandidates: function(indexes, candidates, orientation, cursorPosition, cursorVisible) {
|
||||
this._setOrientation(orientation);
|
||||
|
||||
for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
|
||||
let visible = i < candidates.length;
|
||||
let box = this._candidateBoxes[i];
|
||||
box.visible = visible;
|
||||
this._indexLabels[i].visible = visible;
|
||||
this._candidateLabels[i].visible = visible;
|
||||
|
||||
if (!visible)
|
||||
continue;
|
||||
|
||||
box._indexLabel.text = ((indexes && indexes[i]) ? indexes[i] : '%x'.format(i + 1));
|
||||
box._candidateLabel.text = candidates[i];
|
||||
this._indexLabels[i].text = ((indexes && indexes[i]) ? indexes[i] : '%x.'.format(i + 1));
|
||||
this._candidateLabels[i].text = candidates[i];
|
||||
}
|
||||
|
||||
this._candidateBoxes[this._cursorPosition].remove_style_pseudo_class('selected');
|
||||
this._candidateLabels[this._cursorPosition].remove_style_pseudo_class('selected');
|
||||
this._cursorPosition = cursorPosition;
|
||||
if (cursorVisible)
|
||||
this._candidateBoxes[cursorPosition].add_style_pseudo_class('selected');
|
||||
},
|
||||
|
||||
updateButtons: function(wrapsAround, page, nPages) {
|
||||
if (nPages < 2) {
|
||||
this._buttonBox.hide();
|
||||
return;
|
||||
}
|
||||
this._buttonBox.show();
|
||||
this._previousButton.reactive = wrapsAround || page > 0;
|
||||
this._nextButton.reactive = wrapsAround || page < nPages - 1;
|
||||
this._candidateLabels[cursorPosition].add_style_pseudo_class('selected');
|
||||
},
|
||||
});
|
||||
Signals.addSignalMethods(CandidateArea.prototype);
|
||||
|
||||
const CandidatePopup = new Lang.Class({
|
||||
Name: 'CandidatePopup',
|
||||
Extends: PopupMenu.PopupMenu,
|
||||
|
||||
_init: function() {
|
||||
this._cursor = new St.Bin({ opacity: 0 });
|
||||
Main.uiGroup.add_actor(this._cursor);
|
||||
|
||||
this._boxPointer = new BoxPointer.BoxPointer(St.Side.TOP);
|
||||
this._boxPointer.actor.visible = false;
|
||||
this._boxPointer.actor.style_class = 'candidate-popup-boxpointer';
|
||||
Main.layoutManager.addChrome(this._boxPointer.actor);
|
||||
this.parent(this._cursor, 0, St.Side.TOP);
|
||||
this.actor.hide();
|
||||
Main.uiGroup.add_actor(this.actor);
|
||||
|
||||
let box = new St.BoxLayout({ style_class: 'candidate-popup-content',
|
||||
vertical: true });
|
||||
this._boxPointer.bin.set_child(box);
|
||||
this._preeditTextItem = new PopupMenu.PopupMenuItem('', { reactive: false });
|
||||
this._preeditTextItem.actor.hide();
|
||||
this.addMenuItem(this._preeditTextItem);
|
||||
|
||||
this._preeditText = new St.Label({ style_class: 'candidate-popup-text',
|
||||
visible: false });
|
||||
box.add(this._preeditText);
|
||||
this._auxTextItem = new PopupMenu.PopupMenuItem('', { reactive: false });
|
||||
this._auxTextItem.actor.hide();
|
||||
this.addMenuItem(this._auxTextItem);
|
||||
|
||||
this._auxText = new St.Label({ style_class: 'candidate-popup-text',
|
||||
visible: false });
|
||||
box.add(this._auxText);
|
||||
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
|
||||
this._candidateArea = new CandidateArea();
|
||||
box.add(this._candidateArea.actor);
|
||||
|
||||
this._candidateArea.connect('previous-page', Lang.bind(this, function() {
|
||||
this._panelService.page_up();
|
||||
}));
|
||||
this._candidateArea.connect('next-page', Lang.bind(this, function() {
|
||||
this._panelService.page_down();
|
||||
}));
|
||||
this._candidateArea.connect('candidate-clicked', Lang.bind(this, function(ca, index, button, state) {
|
||||
this._panelService.candidate_clicked(index, button, state);
|
||||
}));
|
||||
this._lookupTableItem = new CandidateArea();
|
||||
this._lookupTableItem.actor.hide();
|
||||
this.addMenuItem(this._lookupTableItem);
|
||||
|
||||
this._panelService = null;
|
||||
},
|
||||
@ -159,61 +117,66 @@ const CandidatePopup = new Lang.Class({
|
||||
Lang.bind(this, function(ps, x, y, w, h) {
|
||||
this._cursor.set_position(x, y);
|
||||
this._cursor.set_size(w, h);
|
||||
if (this._boxPointer.actor.visible)
|
||||
this._boxPointer.setPosition(this._cursor, 0);
|
||||
}));
|
||||
panelService.connect('update-preedit-text',
|
||||
Lang.bind(this, function(ps, text, cursorPosition, visible) {
|
||||
this._preeditText.visible = visible;
|
||||
if (visible)
|
||||
this._preeditTextItem.actor.show();
|
||||
else
|
||||
this._preeditTextItem.actor.hide();
|
||||
this._updateVisibility();
|
||||
|
||||
this._preeditText.text = text.get_text();
|
||||
this._preeditTextItem.actor.label_actor.text = text.get_text();
|
||||
|
||||
let attrs = text.get_attributes();
|
||||
if (attrs)
|
||||
this._setTextAttributes(this._preeditText.clutter_text,
|
||||
this._setTextAttributes(this._preeditTextItem.actor.label_actor.clutter_text,
|
||||
attrs);
|
||||
}));
|
||||
panelService.connect('show-preedit-text',
|
||||
Lang.bind(this, function(ps) {
|
||||
this._preeditText.show();
|
||||
this._preeditTextItem.actor.show();
|
||||
this._updateVisibility();
|
||||
}));
|
||||
panelService.connect('hide-preedit-text',
|
||||
Lang.bind(this, function(ps) {
|
||||
this._preeditText.hide();
|
||||
this._preeditTextItem.actor.hide();
|
||||
this._updateVisibility();
|
||||
}));
|
||||
panelService.connect('update-auxiliary-text',
|
||||
Lang.bind(this, function(ps, text, visible) {
|
||||
this._auxText.visible = visible;
|
||||
if (visible)
|
||||
this._auxTextItem.actor.show();
|
||||
else
|
||||
this._auxTextItem.actor.hide();
|
||||
this._updateVisibility();
|
||||
|
||||
this._auxText.text = text.get_text();
|
||||
this._auxTextItem.actor.label_actor.text = text.get_text();
|
||||
}));
|
||||
panelService.connect('show-auxiliary-text',
|
||||
Lang.bind(this, function(ps) {
|
||||
this._auxText.show();
|
||||
this._auxTextItem.actor.show();
|
||||
this._updateVisibility();
|
||||
}));
|
||||
panelService.connect('hide-auxiliary-text',
|
||||
Lang.bind(this, function(ps) {
|
||||
this._auxText.hide();
|
||||
this._auxTextItem.actor.hide();
|
||||
this._updateVisibility();
|
||||
}));
|
||||
panelService.connect('update-lookup-table',
|
||||
Lang.bind(this, function(ps, lookupTable, visible) {
|
||||
this._candidateArea.actor.visible = visible;
|
||||
if (visible)
|
||||
this._lookupTableItem.actor.show();
|
||||
else
|
||||
this._lookupTableItem.actor.hide();
|
||||
this._updateVisibility();
|
||||
|
||||
let nCandidates = lookupTable.get_number_of_candidates();
|
||||
let cursorPos = lookupTable.get_cursor_pos();
|
||||
let pageSize = lookupTable.get_page_size();
|
||||
let nPages = Math.ceil(nCandidates / pageSize);
|
||||
let page = ((cursorPos == 0) ? 0 : Math.floor(cursorPos / pageSize));
|
||||
let startIndex = page * pageSize;
|
||||
let endIndex = Math.min((page + 1) * pageSize, nCandidates);
|
||||
|
||||
let endIndex = Math.min((page + 1) * pageSize,
|
||||
lookupTable.get_number_of_candidates());
|
||||
let indexes = [];
|
||||
let indexLabel;
|
||||
for (let i = 0; indexLabel = lookupTable.get_label(i); ++i)
|
||||
@ -223,41 +186,37 @@ const CandidatePopup = new Lang.Class({
|
||||
for (let i = startIndex; i < endIndex; ++i)
|
||||
candidates.push(lookupTable.get_candidate(i).get_text());
|
||||
|
||||
this._candidateArea.setCandidates(indexes,
|
||||
candidates,
|
||||
cursorPos % pageSize,
|
||||
lookupTable.is_cursor_visible());
|
||||
this._candidateArea.setOrientation(lookupTable.get_orientation());
|
||||
this._candidateArea.updateButtons(lookupTable.is_round(), page, nPages);
|
||||
this._lookupTableItem.setCandidates(indexes,
|
||||
candidates,
|
||||
lookupTable.get_orientation(),
|
||||
cursorPos % pageSize,
|
||||
lookupTable.is_cursor_visible());
|
||||
}));
|
||||
panelService.connect('show-lookup-table',
|
||||
Lang.bind(this, function(ps) {
|
||||
this._candidateArea.actor.show();
|
||||
this._lookupTableItem.actor.show();
|
||||
this._updateVisibility();
|
||||
}));
|
||||
panelService.connect('hide-lookup-table',
|
||||
Lang.bind(this, function(ps) {
|
||||
this._candidateArea.actor.hide();
|
||||
this._lookupTableItem.actor.hide();
|
||||
this._updateVisibility();
|
||||
}));
|
||||
panelService.connect('focus-out',
|
||||
Lang.bind(this, function(ps) {
|
||||
this._boxPointer.hide(BoxPointer.PopupAnimation.NONE);
|
||||
this.close(BoxPointer.PopupAnimation.NONE);
|
||||
}));
|
||||
},
|
||||
|
||||
_updateVisibility: function() {
|
||||
let isVisible = (this._preeditText.visible ||
|
||||
this._auxText.visible ||
|
||||
this._candidateArea.actor.visible);
|
||||
let isVisible = (this._preeditTextItem.actor.visible ||
|
||||
this._auxTextItem.actor.visible ||
|
||||
this._lookupTableItem.actor.visible);
|
||||
|
||||
if (isVisible) {
|
||||
this._boxPointer.setPosition(this._cursor, 0);
|
||||
this._boxPointer.show(BoxPointer.PopupAnimation.NONE);
|
||||
this._boxPointer.actor.raise_top();
|
||||
} else {
|
||||
this._boxPointer.hide(BoxPointer.PopupAnimation.NONE);
|
||||
}
|
||||
if (isVisible)
|
||||
this.open(BoxPointer.PopupAnimation.NONE);
|
||||
else
|
||||
this.close(BoxPointer.PopupAnimation.NONE);
|
||||
},
|
||||
|
||||
_setTextAttributes: function(clutterText, ibusAttrList) {
|
||||
|
@ -176,16 +176,13 @@ const IconGrid = new Lang.Class({
|
||||
_init: function(params) {
|
||||
params = Params.parse(params, { rowLimit: null,
|
||||
columnLimit: null,
|
||||
fillParent: false,
|
||||
xAlign: St.Align.MIDDLE });
|
||||
this._rowLimit = params.rowLimit;
|
||||
this._colLimit = params.columnLimit;
|
||||
this._xAlign = params.xAlign;
|
||||
this._fillParent = params.fillParent;
|
||||
|
||||
this.actor = new St.BoxLayout({ style_class: 'icon-grid',
|
||||
vertical: true });
|
||||
|
||||
// Pulled from CSS, but hardcode some defaults here
|
||||
this._spacing = 0;
|
||||
this._hItemSize = this._vItemSize = ICON_SIZE;
|
||||
@ -199,11 +196,6 @@ const IconGrid = new Lang.Class({
|
||||
},
|
||||
|
||||
_getPreferredWidth: function (grid, forHeight, alloc) {
|
||||
if (this._fillParent)
|
||||
// Ignore all size requests of children and request a size of 0;
|
||||
// later we'll allocate as many children as fit the parent
|
||||
return;
|
||||
|
||||
let children = this._grid.get_children();
|
||||
let nColumns = this._colLimit ? Math.min(this._colLimit,
|
||||
children.length)
|
||||
@ -225,20 +217,12 @@ const IconGrid = new Lang.Class({
|
||||
},
|
||||
|
||||
_getPreferredHeight: function (grid, forWidth, alloc) {
|
||||
if (this._fillParent)
|
||||
// Ignore all size requests of children and request a size of 0;
|
||||
// later we'll allocate as many children as fit the parent
|
||||
return;
|
||||
|
||||
let children = this._getVisibleChildren();
|
||||
let nColumns, spacing;
|
||||
if (forWidth < 0) {
|
||||
let nColumns;
|
||||
if (forWidth < 0)
|
||||
nColumns = children.length;
|
||||
spacing = this._spacing;
|
||||
} else {
|
||||
[nColumns, , spacing] = this._computeLayout(forWidth);
|
||||
}
|
||||
|
||||
else
|
||||
nColumns = this._computeLayout(forWidth)[0];
|
||||
let nRows;
|
||||
if (nColumns > 0)
|
||||
nRows = Math.ceil(children.length / nColumns);
|
||||
@ -246,25 +230,18 @@ const IconGrid = new Lang.Class({
|
||||
nRows = 0;
|
||||
if (this._rowLimit)
|
||||
nRows = Math.min(nRows, this._rowLimit);
|
||||
let totalSpacing = Math.max(0, nRows - 1) * spacing;
|
||||
let totalSpacing = Math.max(0, nRows - 1) * this._spacing;
|
||||
let height = nRows * this._vItemSize + totalSpacing;
|
||||
alloc.min_size = height;
|
||||
alloc.natural_size = height;
|
||||
},
|
||||
|
||||
_allocate: function (grid, box, flags) {
|
||||
if (this._fillParent) {
|
||||
// Reset the passed in box to fill the parent
|
||||
let parentBox = this.actor.get_parent().allocation;
|
||||
let gridBox = this.actor.get_theme_node().get_content_box(parentBox);
|
||||
box = this._grid.get_theme_node().get_content_box(gridBox);
|
||||
}
|
||||
|
||||
let children = this._getVisibleChildren();
|
||||
let availWidth = box.x2 - box.x1;
|
||||
let availHeight = box.y2 - box.y1;
|
||||
|
||||
let [nColumns, usedWidth, spacing] = this._computeLayout(availWidth);
|
||||
let [nColumns, usedWidth] = this._computeLayout(availWidth);
|
||||
|
||||
let leftPadding;
|
||||
switch(this._xAlign) {
|
||||
@ -303,8 +280,7 @@ const IconGrid = new Lang.Class({
|
||||
childBox.x2 = childBox.x1 + width;
|
||||
childBox.y2 = childBox.y1 + height;
|
||||
|
||||
if (this._rowLimit && rowIndex >= this._rowLimit ||
|
||||
this._fillParent && childBox.y2 > availHeight) {
|
||||
if (this._rowLimit && rowIndex >= this._rowLimit) {
|
||||
this._grid.set_skip_paint(children[i], true);
|
||||
} else {
|
||||
children[i].allocate(childBox, flags);
|
||||
@ -318,10 +294,10 @@ const IconGrid = new Lang.Class({
|
||||
}
|
||||
|
||||
if (columnIndex == 0) {
|
||||
y += this._vItemSize + spacing;
|
||||
y += this._vItemSize + this._spacing;
|
||||
x = box.x1 + leftPadding;
|
||||
} else {
|
||||
x += this._hItemSize + spacing;
|
||||
x += this._hItemSize + this._spacing;
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -337,25 +313,16 @@ const IconGrid = new Lang.Class({
|
||||
_computeLayout: function (forWidth) {
|
||||
let nColumns = 0;
|
||||
let usedWidth = 0;
|
||||
let spacing = this._spacing;
|
||||
|
||||
if (this._colLimit) {
|
||||
let itemWidth = this._hItemSize * this._colLimit;
|
||||
let emptyArea = forWidth - itemWidth;
|
||||
spacing = Math.max(this._spacing, emptyArea / (2 * this._colLimit));
|
||||
spacing = Math.round(spacing);
|
||||
}
|
||||
|
||||
while ((this._colLimit == null || nColumns < this._colLimit) &&
|
||||
(usedWidth + this._hItemSize <= forWidth)) {
|
||||
usedWidth += this._hItemSize + spacing;
|
||||
usedWidth += this._hItemSize + this._spacing;
|
||||
nColumns += 1;
|
||||
}
|
||||
|
||||
if (nColumns > 0)
|
||||
usedWidth -= spacing;
|
||||
usedWidth -= this._spacing;
|
||||
|
||||
return [nColumns, usedWidth, spacing];
|
||||
return [nColumns, usedWidth];
|
||||
},
|
||||
|
||||
_onStyleChanged: function() {
|
||||
|
@ -2,21 +2,18 @@
|
||||
|
||||
const Caribou = imports.gi.Caribou;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const DBus = imports.dbus;
|
||||
const Gdk = imports.gi.Gdk;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const BoxPointer = imports.ui.boxpointer;
|
||||
const Layout = imports.ui.layout;
|
||||
const Main = imports.ui.main;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
|
||||
const KEYBOARD_REST_TIME = Layout.KEYBOARD_ANIMATION_TIME * 2 * 1000;
|
||||
|
||||
const KEYBOARD_SCHEMA = 'org.gnome.shell.keyboard';
|
||||
const KEYBOARD_TYPE = 'keyboard-type';
|
||||
|
||||
@ -59,7 +56,14 @@ const Key = new Lang.Class({
|
||||
if (this._key.name == 'Control_L' || this._key.name == 'Alt_L')
|
||||
this._key.latch = true;
|
||||
|
||||
this._key.connect('key-pressed', Lang.bind(this, function ()
|
||||
{ this.actor.checked = true }));
|
||||
this._key.connect('key-released', Lang.bind(this, function ()
|
||||
{ this.actor.checked = false; }));
|
||||
|
||||
if (this._extended_keys.length > 0) {
|
||||
this._grabbed = false;
|
||||
this._eventCaptureId = 0;
|
||||
this._key.connect('notify::show-subkeys', Lang.bind(this, this._onShowSubkeysChanged));
|
||||
this._boxPointer = new BoxPointer.BoxPointer(St.Side.BOTTOM,
|
||||
{ x_fill: true,
|
||||
@ -111,23 +115,52 @@ const Key = new Lang.Class({
|
||||
this._boxPointer.bin.add_actor(this._extended_keyboard);
|
||||
},
|
||||
|
||||
get subkeys() {
|
||||
return this._boxPointer;
|
||||
_onEventCapture: function (actor, event) {
|
||||
let source = event.get_source();
|
||||
let type = event.type();
|
||||
|
||||
if ((type == Clutter.EventType.BUTTON_PRESS ||
|
||||
type == Clutter.EventType.BUTTON_RELEASE) &&
|
||||
this._extended_keyboard.contains(source)) {
|
||||
source.extended_key.press();
|
||||
source.extended_key.release();
|
||||
return false;
|
||||
}
|
||||
if (type == Clutter.EventType.BUTTON_PRESS) {
|
||||
this._boxPointer.actor.hide();
|
||||
this._ungrab();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
_ungrab: function () {
|
||||
global.stage.disconnect(this._eventCaptureId);
|
||||
this._eventCaptureId = 0;
|
||||
this._grabbed = false;
|
||||
Main.popModal(this.actor);
|
||||
},
|
||||
|
||||
_onShowSubkeysChanged: function () {
|
||||
if (this._key.show_subkeys) {
|
||||
this.actor.fake_release();
|
||||
this._boxPointer.actor.raise_top();
|
||||
this._boxPointer.setPosition(this.actor, 0.5);
|
||||
this.emit('show-subkeys');
|
||||
this.actor.fake_release();
|
||||
this._boxPointer.show(BoxPointer.PopupAnimation.FULL);
|
||||
this.actor.set_hover(false);
|
||||
if (!this._grabbed) {
|
||||
Main.pushModal(this.actor);
|
||||
this._eventCaptureId = global.stage.connect('captured-event', Lang.bind(this, this._onEventCapture));
|
||||
this._grabbed = true;
|
||||
}
|
||||
this._key.release();
|
||||
} else {
|
||||
this.emit('hide-subkeys');
|
||||
if (this._grabbed)
|
||||
this._ungrab();
|
||||
this._boxPointer.hide(BoxPointer.PopupAnimation.FULL);
|
||||
}
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(Key.prototype);
|
||||
|
||||
const Keyboard = new Lang.Class({
|
||||
// HACK: we can't set Name, because it collides with Name dbus property
|
||||
@ -142,26 +175,16 @@ const Keyboard = new Lang.Class({
|
||||
this._focusInExtendedKeys = false;
|
||||
|
||||
this._timestamp = global.display.get_current_time_roundtrip();
|
||||
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._redraw));
|
||||
|
||||
this._keyboardSettings = new Gio.Settings({ schema: KEYBOARD_SCHEMA });
|
||||
this._keyboardSettings.connect('changed', Lang.bind(this, this._settingsChanged));
|
||||
this._a11yApplicationsSettings = new Gio.Settings({ schema: A11Y_APPLICATIONS_SCHEMA });
|
||||
this._a11yApplicationsSettings.connect('changed', Lang.bind(this, this._settingsChanged));
|
||||
this._settingsChanged();
|
||||
},
|
||||
|
||||
this._showIdleId = 0;
|
||||
this._subkeysBoxPointer = null;
|
||||
this._capturedEventId = 0;
|
||||
this._capturedPress = false;
|
||||
|
||||
this._keyboardVisible = false;
|
||||
Main.layoutManager.connect('keyboard-visible-changed', Lang.bind(this, function(o, visible) {
|
||||
this._keyboardVisible = visible;
|
||||
}));
|
||||
this._keyboardRequested = false;
|
||||
this._keyboardRestingId = 0;
|
||||
|
||||
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._redraw));
|
||||
init: function () {
|
||||
this._redraw();
|
||||
},
|
||||
|
||||
@ -176,19 +199,25 @@ const Keyboard = new Lang.Class({
|
||||
if (this._keyboard)
|
||||
this._destroyKeyboard();
|
||||
|
||||
if (this._enableKeyboard)
|
||||
this._setupKeyboard();
|
||||
else
|
||||
if (this._enableKeyboard) {
|
||||
// If we've been called because the setting actually just
|
||||
// changed to true (as opposed to being called from
|
||||
// this._init()), then we want to pop up the keyboard.
|
||||
let showKeyboard = (settings != null);
|
||||
|
||||
// However, caribou-gtk-module or this._onKeyFocusChanged
|
||||
// will probably immediately tell us to hide it, so we
|
||||
// have to fake things out so we'll ignore that request.
|
||||
if (showKeyboard)
|
||||
this._timestamp = global.display.get_current_time_roundtrip() + 1;
|
||||
this._setupKeyboard(showKeyboard);
|
||||
} else
|
||||
Main.layoutManager.hideKeyboard(true);
|
||||
},
|
||||
|
||||
_destroyKeyboard: function() {
|
||||
if (this._keyboardNotifyId)
|
||||
this._keyboard.disconnect(this._keyboardNotifyId);
|
||||
if (this._keyboardGroupAddedId)
|
||||
this._keyboard.disconnect(this._keyboardGroupAddedId);
|
||||
if (this._keyboardGroupRemovedId)
|
||||
this._keyboard.disconnect(this._keyboardGroupRemovedId);
|
||||
if (this._focusNotifyId)
|
||||
global.stage.disconnect(this._focusNotifyId);
|
||||
this._keyboard = null;
|
||||
@ -198,7 +227,7 @@ const Keyboard = new Lang.Class({
|
||||
this._destroySource();
|
||||
},
|
||||
|
||||
_setupKeyboard: function() {
|
||||
_setupKeyboard: function(show) {
|
||||
this.actor = new St.BoxLayout({ name: 'keyboard', vertical: true, reactive: true });
|
||||
Main.layoutManager.keyboardBox.add_actor(this.actor);
|
||||
Main.layoutManager.trackChrome(this.actor);
|
||||
@ -219,11 +248,12 @@ const Keyboard = new Lang.Class({
|
||||
this.actor.text_direction = Clutter.TextDirection.LTR;
|
||||
|
||||
this._keyboardNotifyId = this._keyboard.connect('notify::active-group', Lang.bind(this, this._onGroupChanged));
|
||||
this._keyboardGroupAddedId = this._keyboard.connect('group-added', Lang.bind(this, this._onGroupAdded));
|
||||
this._keyboardGroupRemovedId = this._keyboard.connect('group-removed', Lang.bind(this, this._onGroupRemoved));
|
||||
this._focusNotifyId = global.stage.connect('notify::key-focus', Lang.bind(this, this._onKeyFocusChanged));
|
||||
|
||||
this._createSource();
|
||||
if (show)
|
||||
this.show(Main.layoutManager.focusIndex);
|
||||
else
|
||||
this._createSource();
|
||||
},
|
||||
|
||||
_onKeyFocusChanged: function () {
|
||||
@ -243,102 +273,88 @@ const Keyboard = new Lang.Class({
|
||||
return;
|
||||
|
||||
let time = global.get_current_time();
|
||||
if (!(focus instanceof Clutter.Text)) {
|
||||
if (focus instanceof Clutter.Text)
|
||||
this.Show(time);
|
||||
else
|
||||
this.Hide(time);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._showIdleId)
|
||||
this._showIdleId = GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE,
|
||||
Lang.bind(this, function() { this.Show(time); }));
|
||||
},
|
||||
|
||||
_createLayersForGroup: function (gname) {
|
||||
let group = this._keyboard.get_group(gname);
|
||||
group.connect('notify::active-level', Lang.bind(this, this._onLevelChanged));
|
||||
let layers = {};
|
||||
let levels = group.get_levels();
|
||||
for (let j = 0; j < levels.length; ++j) {
|
||||
let lname = levels[j];
|
||||
let level = group.get_level(lname);
|
||||
let layout = new St.BoxLayout({ style_class: 'keyboard-layout',
|
||||
vertical: true });
|
||||
this._loadRows(level, layout);
|
||||
layers[lname] = layout;
|
||||
this.actor.add(layout, { x_fill: false });
|
||||
|
||||
layout.hide();
|
||||
}
|
||||
return layers;
|
||||
},
|
||||
|
||||
_addKeys: function () {
|
||||
let groups = this._keyboard.get_groups();
|
||||
for (let i = 0; i < groups.length; ++i) {
|
||||
let gname = groups[i];
|
||||
this._groups[gname] = this._createLayersForGroup(gname);
|
||||
let group = this._keyboard.get_group(gname);
|
||||
group.connect('notify::active-level', Lang.bind(this, this._onLevelChanged));
|
||||
let layers = {};
|
||||
let levels = group.get_levels();
|
||||
for (let j = 0; j < levels.length; ++j) {
|
||||
let lname = levels[j];
|
||||
let level = group.get_level(lname);
|
||||
let layout = new St.BoxLayout({ style_class: 'keyboard-layout',
|
||||
vertical: true });
|
||||
this._loadRows(level, layout);
|
||||
layers[lname] = layout;
|
||||
this.actor.add(layout, { x_fill: false });
|
||||
|
||||
layout.hide();
|
||||
}
|
||||
this._groups[gname] = layers;
|
||||
}
|
||||
|
||||
this._setActiveLayer();
|
||||
},
|
||||
|
||||
_onCapturedEvent: function(actor, event) {
|
||||
let type = event.type();
|
||||
let press = type == Clutter.EventType.BUTTON_PRESS;
|
||||
let release = type == Clutter.EventType.BUTTON_RELEASE;
|
||||
_getTrayIcon: function () {
|
||||
let trayButton = new St.Button ({ label: _("tray"),
|
||||
style_class: 'keyboard-key' });
|
||||
trayButton.key_width = 1;
|
||||
trayButton.connect('button-press-event', Lang.bind(this, function () {
|
||||
Main.messageTray.toggle();
|
||||
}));
|
||||
|
||||
if (press)
|
||||
this._capturedPress = true;
|
||||
else if (release && this._capturedPress)
|
||||
this._hideSubkeys();
|
||||
Main.overview.connect('showing', Lang.bind(this, function () {
|
||||
trayButton.reactive = false;
|
||||
trayButton.add_style_pseudo_class('grayed');
|
||||
}));
|
||||
Main.overview.connect('hiding', Lang.bind(this, function () {
|
||||
trayButton.reactive = true;
|
||||
trayButton.remove_style_pseudo_class('grayed');
|
||||
}));
|
||||
Main.sessionMode.connect('updated', Lang.bind(this, function() {
|
||||
trayButton.reactive = !Main.sessionMode.isLocked;
|
||||
if (Main.sessionMode.isLocked)
|
||||
trayButton.add_style_pseudo_class('grayed');
|
||||
else
|
||||
trayButton.remove_style_pseudo_class('grayed');
|
||||
}));
|
||||
|
||||
return true;
|
||||
return trayButton;
|
||||
},
|
||||
|
||||
_addRows : function (keys, layout) {
|
||||
let keyboard_row = new St.BoxLayout();
|
||||
for (let i = 0; i < keys.length; ++i) {
|
||||
let children = keys[i].get_children();
|
||||
let left_box = new St.BoxLayout({ style_class: 'keyboard-row' });
|
||||
let center_box = new St.BoxLayout({ style_class: 'keyboard-row' });
|
||||
let right_box = new St.BoxLayout({ style_class: 'keyboard-row' });
|
||||
let left_box = new St.BoxLayout({ style_class: 'keyboard-row' });
|
||||
for (let j = 0; j < children.length; ++j) {
|
||||
if (this._numOfHorizKeys == 0)
|
||||
this._numOfHorizKeys = children.length;
|
||||
let key = children[j];
|
||||
let button = new Key(key);
|
||||
|
||||
switch (key.align) {
|
||||
case 'right':
|
||||
if (key.align == 'right')
|
||||
right_box.add(button.actor);
|
||||
break;
|
||||
case 'center':
|
||||
center_box.add(button.actor);
|
||||
break;
|
||||
case 'left':
|
||||
default:
|
||||
else
|
||||
left_box.add(button.actor);
|
||||
break;
|
||||
}
|
||||
if (key.name == 'Caribou_Prefs') {
|
||||
key.connect('key-released', Lang.bind(this, this.hide));
|
||||
}
|
||||
|
||||
button.connect('show-subkeys', Lang.bind(this, function() {
|
||||
if (this._subkeysBoxPointer)
|
||||
this._subkeysBoxPointer.hide(BoxPointer.PopupAnimation.FULL);
|
||||
this._subkeysBoxPointer = button.subkeys;
|
||||
this._subkeysBoxPointer.show(BoxPointer.PopupAnimation.FULL);
|
||||
if (!this._capturedEventId)
|
||||
this._capturedEventId = this.actor.connect('captured-event',
|
||||
Lang.bind(this, this._onCapturedEvent));
|
||||
}));
|
||||
button.connect('hide-subkeys', Lang.bind(this, function() {
|
||||
this._hideSubkeys();
|
||||
}));
|
||||
// Add new key for hiding message tray
|
||||
right_box.add(this._getTrayIcon());
|
||||
}
|
||||
}
|
||||
keyboard_row.add(left_box, { expand: true, x_fill: false, x_align: St.Align.START });
|
||||
keyboard_row.add(center_box, { expand: true, x_fill: false, x_align: St.Align.MIDDLE });
|
||||
keyboard_row.add(right_box, { expand: true, x_fill: false, x_align: St.Align.END });
|
||||
}
|
||||
layout.add(keyboard_row);
|
||||
@ -411,14 +427,6 @@ const Keyboard = new Lang.Class({
|
||||
this._redraw();
|
||||
},
|
||||
|
||||
_onGroupAdded: function (keyboard, gname) {
|
||||
this._groups[gname] = this._createLayersForGroup(gname);
|
||||
},
|
||||
|
||||
_onGroupRemoved: function (keyboard, gname) {
|
||||
delete this._groups[gname];
|
||||
},
|
||||
|
||||
_setActiveLayer: function () {
|
||||
let active_group_name = this._keyboard.active_group;
|
||||
let active_group = this._keyboard.get_group(active_group_name);
|
||||
@ -454,37 +462,7 @@ const Keyboard = new Lang.Class({
|
||||
actor._extended_keys || actor.extended_key;
|
||||
},
|
||||
|
||||
_clearKeyboardRestTimer: function() {
|
||||
if (!this._keyboardRestingId)
|
||||
return;
|
||||
GLib.source_remove(this._keyboardRestingId);
|
||||
this._keyboardRestingId = 0;
|
||||
},
|
||||
|
||||
show: function (monitor) {
|
||||
this._keyboardRequested = true;
|
||||
|
||||
if (this._keyboardVisible) {
|
||||
if (monitor != Main.layoutManager.keyboardIndex) {
|
||||
Main.layoutManager.keyboardIndex = monitor;
|
||||
this._redraw();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this._clearKeyboardRestTimer();
|
||||
this._keyboardRestingId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
|
||||
KEYBOARD_REST_TIME,
|
||||
Lang.bind(this, function() {
|
||||
this._clearKeyboardRestTimer();
|
||||
this._show(monitor);
|
||||
}));
|
||||
},
|
||||
|
||||
_show: function(monitor) {
|
||||
if (!this._keyboardRequested)
|
||||
return;
|
||||
|
||||
Main.layoutManager.keyboardIndex = monitor;
|
||||
this._redraw();
|
||||
Main.layoutManager.showKeyboard();
|
||||
@ -492,41 +470,10 @@ const Keyboard = new Lang.Class({
|
||||
},
|
||||
|
||||
hide: function () {
|
||||
this._keyboardRequested = false;
|
||||
|
||||
if (!this._keyboardVisible)
|
||||
return;
|
||||
|
||||
this._clearKeyboardRestTimer();
|
||||
this._keyboardRestingId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
|
||||
KEYBOARD_REST_TIME,
|
||||
Lang.bind(this, function() {
|
||||
this._clearKeyboardRestTimer();
|
||||
this._hide();
|
||||
}));
|
||||
},
|
||||
|
||||
_hide: function() {
|
||||
if (this._keyboardRequested)
|
||||
return;
|
||||
|
||||
this._hideSubkeys();
|
||||
Main.layoutManager.hideKeyboard();
|
||||
this._createSource();
|
||||
},
|
||||
|
||||
_hideSubkeys: function() {
|
||||
if (this._subkeysBoxPointer) {
|
||||
this._subkeysBoxPointer.hide(BoxPointer.PopupAnimation.FULL);
|
||||
this._subkeysBoxPointer = null;
|
||||
}
|
||||
if (this._capturedEventId) {
|
||||
this.actor.disconnect(this._capturedEventId);
|
||||
this._capturedEventId = 0;
|
||||
}
|
||||
this._capturedPress = false;
|
||||
},
|
||||
|
||||
_moveTemporarily: function () {
|
||||
let currentWindow = global.screen.get_display().focus_window;
|
||||
let rect = currentWindow.get_outer_rect();
|
||||
@ -555,13 +502,6 @@ const Keyboard = new Lang.Class({
|
||||
return one - two;
|
||||
},
|
||||
|
||||
_clearShowIdle: function() {
|
||||
if (!this._showIdleId)
|
||||
return;
|
||||
GLib.source_remove(this._showIdleId);
|
||||
this._showIdleId = 0;
|
||||
},
|
||||
|
||||
// D-Bus methods
|
||||
Show: function(timestamp) {
|
||||
if (!this._enableKeyboard)
|
||||
@ -570,8 +510,6 @@ const Keyboard = new Lang.Class({
|
||||
if (this._compareTimestamp(timestamp, this._timestamp) < 0)
|
||||
return;
|
||||
|
||||
this._clearShowIdle();
|
||||
|
||||
if (timestamp != Clutter.CURRENT_TIME)
|
||||
this._timestamp = timestamp;
|
||||
this.show(Main.layoutManager.focusIndex);
|
||||
@ -584,8 +522,6 @@ const Keyboard = new Lang.Class({
|
||||
if (this._compareTimestamp(timestamp, this._timestamp) < 0)
|
||||
return;
|
||||
|
||||
this._clearShowIdle();
|
||||
|
||||
if (timestamp != Clutter.CURRENT_TIME)
|
||||
this._timestamp = timestamp;
|
||||
this.hide();
|
||||
@ -620,7 +556,11 @@ const KeyboardSource = new Lang.Class({
|
||||
this.keepTrayOnSummaryClick = true;
|
||||
},
|
||||
|
||||
handleSummaryClick: function(button) {
|
||||
handleSummaryClick: function() {
|
||||
let event = Clutter.get_current_event();
|
||||
if (event.type() != Clutter.EventType.BUTTON_RELEASE)
|
||||
return false;
|
||||
|
||||
this.open();
|
||||
return true;
|
||||
},
|
||||
|
1455
js/ui/layout.js
1455
js/ui/layout.js
File diff suppressed because it is too large
Load Diff
@ -3,7 +3,6 @@
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Lang = imports.lang;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Params = imports.misc.params;
|
||||
@ -102,7 +101,6 @@ const Lightbox = new Lang.Class({
|
||||
},
|
||||
|
||||
show: function() {
|
||||
Tweener.removeTweens(this.actor);
|
||||
if (this._fadeInTime) {
|
||||
this.shown = false;
|
||||
this.actor.opacity = 0;
|
||||
@ -112,20 +110,17 @@ const Lightbox = new Lang.Class({
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this, function() {
|
||||
this.shown = true;
|
||||
this.emit('shown');
|
||||
})
|
||||
});
|
||||
} else {
|
||||
this.actor.opacity = 255 * this._fadeFactor;
|
||||
this.shown = true;
|
||||
this.emit('shown');
|
||||
}
|
||||
this.actor.show();
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
this.shown = false;
|
||||
Tweener.removeTweens(this.actor);
|
||||
if (this._fadeOutTime) {
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 0,
|
||||
@ -202,4 +197,3 @@ const Lightbox = new Lang.Class({
|
||||
this.highlight(null);
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(Lightbox.prototype);
|
||||
|
@ -39,7 +39,6 @@ var commandHeader = 'const Clutter = imports.gi.Clutter; ' +
|
||||
* in the shell core code too. */
|
||||
'const stage = global.stage; ' +
|
||||
/* Special lookingGlass functions */
|
||||
'const inspect = Lang.bind(Main.lookingGlass, Main.lookingGlass.inspect); ' +
|
||||
'const it = Main.lookingGlass.getIt(); ' +
|
||||
'const r = Lang.bind(Main.lookingGlass, Main.lookingGlass.getResult); ';
|
||||
|
||||
@ -685,8 +684,7 @@ const Memory = new Lang.Class({
|
||||
const Extensions = new Lang.Class({
|
||||
Name: 'Extensions',
|
||||
|
||||
_init: function(lookingGlass) {
|
||||
this._lookingGlass = lookingGlass;
|
||||
_init: function() {
|
||||
this.actor = new St.BoxLayout({ vertical: true,
|
||||
name: 'lookingGlassExtensions' });
|
||||
this._noExtensions = new St.Label({ style_class: 'lg-extensions-none',
|
||||
@ -873,7 +871,8 @@ const LookingGlass = new Lang.Class({
|
||||
inspectIcon.connect('button-press-event', Lang.bind(this, function () {
|
||||
let inspector = new Inspector(this);
|
||||
inspector.connect('target', Lang.bind(this, function(i, target, stageX, stageY) {
|
||||
this._pushResult('inspect(' + Math.round(stageX) + ', ' + Math.round(stageY) + ')', target);
|
||||
this._pushResult('<inspect x:' + stageX + ' y:' + stageY + '>',
|
||||
target);
|
||||
}));
|
||||
inspector.connect('closed', Lang.bind(this, function() {
|
||||
this.actor.show();
|
||||
@ -913,7 +912,7 @@ const LookingGlass = new Lang.Class({
|
||||
this._memory = new Memory();
|
||||
notebook.appendPage('Memory', this._memory.actor);
|
||||
|
||||
this._extensions = new Extensions(this);
|
||||
this._extensions = new Extensions();
|
||||
notebook.appendPage('Extensions', this._extensions.actor);
|
||||
|
||||
this._entry.clutter_text.connect('activate', Lang.bind(this, function (o, e) {
|
||||
@ -951,7 +950,9 @@ const LookingGlass = new Lang.Class({
|
||||
|
||||
_updateFont: function() {
|
||||
let fontName = this._interfaceSettings.get_string('monospace-font-name');
|
||||
let fontDesc = Pango.FontDescription.from_string(fontName);
|
||||
// This is mishandled by the scanner - should by Pango.FontDescription_from_string(fontName);
|
||||
// https://bugzilla.gnome.org/show_bug.cgi?id=595889
|
||||
let fontDesc = Pango.font_description_from_string(fontName);
|
||||
// We ignore everything but size and style; you'd be crazy to set your system-wide
|
||||
// monospace font to be bold/oblique/etc. Could easily be added here.
|
||||
this.actor.style =
|
||||
@ -1056,10 +1057,6 @@ const LookingGlass = new Lang.Class({
|
||||
this._entry.text = '';
|
||||
},
|
||||
|
||||
inspect: function(x, y) {
|
||||
return global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y);
|
||||
},
|
||||
|
||||
getIt: function () {
|
||||
return this._it;
|
||||
},
|
||||
@ -1132,7 +1129,7 @@ const LookingGlass = new Lang.Class({
|
||||
if (this._open)
|
||||
return;
|
||||
|
||||
if (!Main.pushModal(this._entry, { keybindingMode: Shell.KeyBindingMode.LOOKING_GLASS }))
|
||||
if (!Main.pushModal(this._entry, { keybindingMode: Main.KeybindingMode.LOOKING_GLASS }))
|
||||
return;
|
||||
|
||||
this._notebook.selectIndex(0);
|
||||
|
193
js/ui/main.js
193
js/ui/main.js
@ -18,13 +18,11 @@ const ExtensionSystem = imports.ui.extensionSystem;
|
||||
const ExtensionDownloader = imports.ui.extensionDownloader;
|
||||
const Keyboard = imports.ui.keyboard;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
const OsdWindow = imports.ui.osdWindow;
|
||||
const Overview = imports.ui.overview;
|
||||
const Panel = imports.ui.panel;
|
||||
const Params = imports.misc.params;
|
||||
const RunDialog = imports.ui.runDialog;
|
||||
const Layout = imports.ui.layout;
|
||||
const LoginManager = imports.misc.loginManager;
|
||||
const LookingGlass = imports.ui.lookingGlass;
|
||||
const NotificationDaemon = imports.ui.notificationDaemon;
|
||||
const WindowAttentionHandler = imports.ui.windowAttentionHandler;
|
||||
@ -33,6 +31,7 @@ const Scripting = imports.ui.scripting;
|
||||
const SessionMode = imports.ui.sessionMode;
|
||||
const ShellDBus = imports.ui.shellDBus;
|
||||
const ShellMountOperation = imports.ui.shellMountOperation;
|
||||
const UnlockDialog = imports.ui.unlockDialog;
|
||||
const WindowManager = imports.ui.windowManager;
|
||||
const Magnifier = imports.ui.magnifier;
|
||||
const XdndHandler = imports.ui.xdndHandler;
|
||||
@ -41,6 +40,19 @@ const Util = imports.misc.util;
|
||||
const OVERRIDES_SCHEMA = 'org.gnome.shell.overrides';
|
||||
const DEFAULT_BACKGROUND_COLOR = Clutter.Color.from_pixel(0x2e3436ff);
|
||||
|
||||
const KeybindingMode = {
|
||||
NONE: 0, // block all keybindings
|
||||
NORMAL: 1 << 0, // window mode
|
||||
OVERVIEW: 1 << 1,
|
||||
LOCK_SCREEN: 1 << 2,
|
||||
UNLOCK_SCREEN: 1 << 3,
|
||||
LOGIN_SCREEN: 1 << 4,
|
||||
MESSAGE_TRAY: 1 << 5,
|
||||
SYSTEM_MODAL: 1 << 6,
|
||||
LOOKING_GLASS: 1 << 7,
|
||||
ALL: ~0,
|
||||
};
|
||||
|
||||
let componentManager = null;
|
||||
let panel = null;
|
||||
let overview = null;
|
||||
@ -52,13 +64,12 @@ let screenShield = null;
|
||||
let notificationDaemon = null;
|
||||
let windowAttentionHandler = null;
|
||||
let ctrlAltTabManager = null;
|
||||
let osdWindow = null;
|
||||
let sessionMode = null;
|
||||
let shellDBusService = null;
|
||||
let shellMountOpDBusService = null;
|
||||
let screenSaverDBus = null;
|
||||
let modalCount = 0;
|
||||
let keybindingMode = Shell.KeyBindingMode.NONE;
|
||||
let keybindingMode = KeybindingMode.NORMAL;
|
||||
let modalActorFocusStack = [];
|
||||
let uiGroup = null;
|
||||
let magnifier = null;
|
||||
@ -70,23 +81,22 @@ let _defaultCssStylesheet = null;
|
||||
let _cssStylesheet = null;
|
||||
let _overridesSettings = null;
|
||||
|
||||
function _sessionUpdated() {
|
||||
_loadDefaultStylesheet();
|
||||
let background = null;
|
||||
|
||||
function _sessionUpdated() {
|
||||
wm.setCustomKeybindingHandler('panel-main-menu',
|
||||
Shell.KeyBindingMode.NORMAL |
|
||||
Shell.KeyBindingMode.OVERVIEW,
|
||||
KeybindingMode.NORMAL |
|
||||
KeybindingMode.OVERVIEW,
|
||||
sessionMode.hasOverview ? Lang.bind(overview, overview.toggle) : null);
|
||||
wm.allowKeybinding('overlay-key', Shell.KeyBindingMode.NORMAL |
|
||||
Shell.KeyBindingMode.OVERVIEW);
|
||||
wm.allowKeybinding('overlay-key', KeybindingMode.NORMAL |
|
||||
KeybindingMode.OVERVIEW);
|
||||
|
||||
wm.setCustomKeybindingHandler('panel-run-dialog',
|
||||
Shell.KeyBindingMode.NORMAL |
|
||||
Shell.KeyBindingMode.OVERVIEW,
|
||||
KeybindingMode.NORMAL |
|
||||
KeybindingMode.OVERVIEW,
|
||||
sessionMode.hasRunDialog ? openRunDialog : null);
|
||||
|
||||
if (!sessionMode.hasRunDialog && lookingGlass)
|
||||
lookingGlass.close();
|
||||
if (sessionMode.isGreeter)
|
||||
screenShield.showDialog();
|
||||
}
|
||||
|
||||
function start() {
|
||||
@ -100,21 +110,9 @@ function start() {
|
||||
Gio.DesktopAppInfo.set_desktop_env('GNOME');
|
||||
|
||||
sessionMode = new SessionMode.SessionMode();
|
||||
sessionMode.connect('sessions-loaded', _sessionsLoaded);
|
||||
sessionMode.init();
|
||||
}
|
||||
|
||||
function _sessionsLoaded() {
|
||||
sessionMode.connect('updated', _sessionUpdated);
|
||||
_initializeUI();
|
||||
|
||||
shellDBusService = new ShellDBus.GnomeShell();
|
||||
shellMountOpDBusService = new ShellMountOperation.GnomeShellMountOpHandler();
|
||||
|
||||
_sessionUpdated();
|
||||
}
|
||||
|
||||
function _initializeUI() {
|
||||
// Ensure ShellWindowTracker and ShellAppUsage are initialized; this will
|
||||
// also initialize ShellAppSystem first. ShellAppSystem
|
||||
// needs to load all the .desktop files, and ShellWindowTracker
|
||||
@ -128,25 +126,47 @@ function _initializeUI() {
|
||||
|
||||
tracker.connect('startup-sequence-changed', _queueCheckWorkspaces);
|
||||
|
||||
_loadDefaultStylesheet();
|
||||
// The stage is always covered so Clutter doesn't need to clear it; however
|
||||
// the color is used as the default contents for the Mutter root background
|
||||
// actor so set it anyways.
|
||||
global.stage.color = DEFAULT_BACKGROUND_COLOR;
|
||||
global.stage.no_clear_hint = true;
|
||||
|
||||
_defaultCssStylesheet = global.datadir + '/theme/gnome-shell.css';
|
||||
loadTheme();
|
||||
|
||||
// Set up stage hierarchy to group all UI actors under one container.
|
||||
uiGroup = new Shell.GenericContainer({ name: 'uiGroup' });
|
||||
uiGroup.connect('allocate',
|
||||
function (actor, box, flags) {
|
||||
let children = uiGroup.get_children();
|
||||
for (let i = 0; i < children.length; i++)
|
||||
children[i].allocate_preferred_size(flags);
|
||||
});
|
||||
uiGroup.connect('get-preferred-width',
|
||||
function(actor, forHeight, alloc) {
|
||||
let width = global.stage.width;
|
||||
[alloc.min_size, alloc.natural_size] = [width, width];
|
||||
});
|
||||
uiGroup.connect('get-preferred-height',
|
||||
function(actor, forWidth, alloc) {
|
||||
let height = global.stage.height;
|
||||
[alloc.min_size, alloc.natural_size] = [height, height];
|
||||
});
|
||||
global.window_group.reparent(uiGroup);
|
||||
global.overlay_group.reparent(uiGroup);
|
||||
global.stage.add_actor(uiGroup);
|
||||
|
||||
// Setup the stage hierarchy early
|
||||
layoutManager = new Layout.LayoutManager();
|
||||
|
||||
// Various parts of the codebase still refers to Main.uiGroup
|
||||
// instead using the layoutManager. This keeps that code
|
||||
// working until it's updated.
|
||||
uiGroup = layoutManager.uiGroup;
|
||||
|
||||
xdndHandler = new XdndHandler.XdndHandler();
|
||||
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
|
||||
osdWindow = new OsdWindow.OsdWindow();
|
||||
overview = new Overview.Overview();
|
||||
wm = new WindowManager.WindowManager();
|
||||
magnifier = new Magnifier.Magnifier();
|
||||
if (LoginManager.canLock())
|
||||
if (UnlockDialog.isSupported())
|
||||
screenShield = new ScreenShield.ScreenShield();
|
||||
|
||||
else
|
||||
screenShield = new ScreenShield.ScreenShieldFallback();
|
||||
panel = new Panel.Panel();
|
||||
messageTray = new MessageTray.MessageTray();
|
||||
keyboard = new Keyboard.Keyboard();
|
||||
@ -155,19 +175,19 @@ function _initializeUI() {
|
||||
componentManager = new Components.ComponentManager();
|
||||
|
||||
layoutManager.init();
|
||||
keyboard.init();
|
||||
overview.init();
|
||||
|
||||
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT,
|
||||
false, -1, 1);
|
||||
global.display.connect('overlay-key', Lang.bind(overview, overview.toggle));
|
||||
sessionMode.connect('updated', _sessionUpdated);
|
||||
_sessionUpdated();
|
||||
|
||||
// Provide the bus object for gnome-session to
|
||||
// initiate logouts.
|
||||
EndSessionDialog.init();
|
||||
|
||||
// We're ready for the session manager to move to the next phase
|
||||
Meta.register_with_session();
|
||||
|
||||
_startDate = new Date();
|
||||
|
||||
log('GNOME Shell started at ' + _startDate);
|
||||
@ -192,18 +212,6 @@ function _initializeUI() {
|
||||
|
||||
ExtensionDownloader.init();
|
||||
ExtensionSystem.init();
|
||||
|
||||
if (sessionMode.isGreeter && screenShield) {
|
||||
layoutManager.connect('startup-prepared', function() {
|
||||
screenShield.showDialog();
|
||||
});
|
||||
}
|
||||
|
||||
layoutManager.connect('startup-complete', function() {
|
||||
if (keybindingMode == Shell.KeyBindingMode.NONE) {
|
||||
keybindingMode = Shell.KeyBindingMode.NORMAL;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let _workspaces = [];
|
||||
@ -387,18 +395,6 @@ function _nWorkspacesChanged() {
|
||||
return false;
|
||||
}
|
||||
|
||||
function _loadDefaultStylesheet() {
|
||||
if (!sessionMode.isPrimary)
|
||||
return;
|
||||
|
||||
let stylesheet = global.datadir + '/theme/' + sessionMode.stylesheetName;
|
||||
if (_defaultCssStylesheet == stylesheet)
|
||||
return;
|
||||
|
||||
_defaultCssStylesheet = stylesheet;
|
||||
loadTheme();
|
||||
}
|
||||
|
||||
/**
|
||||
* getThemeStylesheet:
|
||||
*
|
||||
@ -479,6 +475,17 @@ function notifyError(msg, details) {
|
||||
notify(msg, details);
|
||||
}
|
||||
|
||||
function isWindowActorDisplayedOnWorkspace(win, workspaceIndex) {
|
||||
return win.get_workspace() == workspaceIndex ||
|
||||
(win.get_meta_window() && win.get_meta_window().is_on_all_workspaces());
|
||||
}
|
||||
|
||||
function getWindowActorsForWorkspace(workspaceIndex) {
|
||||
return global.get_window_actors().filter(function (win) {
|
||||
return isWindowActorDisplayedOnWorkspace(win, workspaceIndex);
|
||||
});
|
||||
}
|
||||
|
||||
function _findModal(actor) {
|
||||
for (let i = 0; i < modalActorFocusStack.length; i++) {
|
||||
if (modalActorFocusStack[i].actor == actor)
|
||||
@ -487,6 +494,10 @@ function _findModal(actor) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
function isInModalStack(actor) {
|
||||
return _findModal(actor) != -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* pushModal:
|
||||
* @actor: #ClutterActor which will be given keyboard focus
|
||||
@ -509,7 +520,7 @@ function _findModal(actor) {
|
||||
* - options: Meta.ModalOptions flags to indicate that the pointer is
|
||||
* already grabbed
|
||||
*
|
||||
* - keybindingMode: used to set the current Shell.KeyBindingMode to filter
|
||||
* - keybindingMode: used to set the current Main.KeybindingMode to filter
|
||||
* global keybindings; the default of NONE will filter
|
||||
* out all keybindings
|
||||
*
|
||||
@ -518,7 +529,7 @@ function _findModal(actor) {
|
||||
function pushModal(actor, params) {
|
||||
params = Params.parse(params, { timestamp: global.get_current_time(),
|
||||
options: 0,
|
||||
keybindingMode: Shell.KeyBindingMode.NONE });
|
||||
keybindingMode: KeybindingMode.NONE });
|
||||
|
||||
if (modalCount == 0) {
|
||||
if (!global.begin_modal(params.timestamp, params.options)) {
|
||||
@ -536,20 +547,19 @@ function pushModal(actor, params) {
|
||||
if (index >= 0)
|
||||
popModal(actor);
|
||||
});
|
||||
|
||||
let prevFocus = global.stage.get_key_focus();
|
||||
let prevFocusDestroyId;
|
||||
if (prevFocus != null) {
|
||||
prevFocusDestroyId = prevFocus.connect('destroy', function() {
|
||||
let curFocus = global.stage.get_key_focus();
|
||||
let curFocusDestroyId;
|
||||
if (curFocus != null) {
|
||||
curFocusDestroyId = curFocus.connect('destroy', function() {
|
||||
let index = _findModal(actor);
|
||||
if (index >= 0)
|
||||
modalActorFocusStack[index].prevFocus = null;
|
||||
modalActorFocusStack[index].actor = null;
|
||||
});
|
||||
}
|
||||
modalActorFocusStack.push({ actor: actor,
|
||||
focus: curFocus,
|
||||
destroyId: actorDestroyId,
|
||||
prevFocus: prevFocus,
|
||||
prevFocusDestroyId: prevFocusDestroyId,
|
||||
focusDestroyId: curFocusDestroyId,
|
||||
keybindingMode: keybindingMode });
|
||||
|
||||
keybindingMode = params.keybindingMode;
|
||||
@ -579,7 +589,7 @@ function popModal(actor, timestamp) {
|
||||
global.stage.set_key_focus(null);
|
||||
global.end_modal(timestamp);
|
||||
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
|
||||
keybindingMode = Shell.KeyBindingMode.NORMAL;
|
||||
keybindingMode = KeybindingMode.NORMAL;
|
||||
|
||||
throw new Error('incorrect pop');
|
||||
}
|
||||
@ -590,33 +600,18 @@ function popModal(actor, timestamp) {
|
||||
record.actor.disconnect(record.destroyId);
|
||||
|
||||
if (focusIndex == modalActorFocusStack.length - 1) {
|
||||
if (record.prevFocus)
|
||||
record.prevFocus.disconnect(record.prevFocusDestroyId);
|
||||
if (record.focus)
|
||||
record.focus.disconnect(record.focusDestroyId);
|
||||
keybindingMode = record.keybindingMode;
|
||||
global.stage.set_key_focus(record.prevFocus);
|
||||
global.stage.set_key_focus(record.focus);
|
||||
} else {
|
||||
// If we have:
|
||||
// global.stage.set_focus(a);
|
||||
// Main.pushModal(b);
|
||||
// Main.pushModal(c);
|
||||
// Main.pushModal(d);
|
||||
//
|
||||
// then we have the stack:
|
||||
// [{ prevFocus: a, actor: b },
|
||||
// { prevFocus: b, actor: c },
|
||||
// { prevFocus: c, actor: d }]
|
||||
//
|
||||
// When actor c is destroyed/popped, if we only simply remove the
|
||||
// record, then the focus stack will be [a, c], rather than the correct
|
||||
// [a, b]. Shift the focus stack up before removing the record to ensure
|
||||
// that we get the correct result.
|
||||
let t = modalActorFocusStack[modalActorFocusStack.length - 1];
|
||||
if (t.prevFocus)
|
||||
t.prevFocus.disconnect(t.prevFocusDestroyId);
|
||||
if (t.focus)
|
||||
t.focus.disconnect(t.focusDestroyId);
|
||||
// Remove from the middle, shift the focus chain up
|
||||
for (let i = modalActorFocusStack.length - 1; i > focusIndex; i--) {
|
||||
modalActorFocusStack[i].prevFocus = modalActorFocusStack[i - 1].prevFocus;
|
||||
modalActorFocusStack[i].prevFocusDestroyId = modalActorFocusStack[i - 1].prevFocusDestroyId;
|
||||
modalActorFocusStack[i].focus = modalActorFocusStack[i - 1].focus;
|
||||
modalActorFocusStack[i].focusDestroyId = modalActorFocusStack[i - 1].focusDestroyId;
|
||||
modalActorFocusStack[i].keybindingMode = modalActorFocusStack[i - 1].keybindingMode;
|
||||
}
|
||||
}
|
||||
@ -628,7 +623,7 @@ function popModal(actor, timestamp) {
|
||||
global.end_modal(timestamp);
|
||||
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
|
||||
Meta.enable_unredirect_for_screen(global.screen);
|
||||
keybindingMode = Shell.KeyBindingMode.NORMAL;
|
||||
keybindingMode = KeybindingMode.NORMAL;
|
||||
}
|
||||
|
||||
function createLookingGlass() {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -37,16 +37,14 @@ const ModalDialog = new Lang.Class({
|
||||
params = Params.parse(params, { shellReactive: false,
|
||||
styleClass: null,
|
||||
parentActor: Main.uiGroup,
|
||||
keybindingMode: Shell.KeyBindingMode.SYSTEM_MODAL,
|
||||
shouldFadeIn: true,
|
||||
destroyOnClose: true });
|
||||
keybindingMode: Main.KeybindingMode.SYSTEM_MODAL,
|
||||
shouldFadeIn: true });
|
||||
|
||||
this.state = State.CLOSED;
|
||||
this._hasModal = false;
|
||||
this._keybindingMode = params.keybindingMode;
|
||||
this._shellReactive = params.shellReactive;
|
||||
this._shouldFadeIn = params.shouldFadeIn;
|
||||
this._destroyOnClose = params.destroyOnClose;
|
||||
|
||||
this._group = new St.Widget({ visible: false,
|
||||
x: 0,
|
||||
@ -60,9 +58,7 @@ const ModalDialog = new Lang.Class({
|
||||
|
||||
this._group.connect('destroy', Lang.bind(this, this._onGroupDestroy));
|
||||
|
||||
this._pressedKey = null;
|
||||
this._buttonKeys = {};
|
||||
this._group.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
|
||||
this._group.connect('key-release-event', Lang.bind(this, this._onKeyReleaseEvent));
|
||||
|
||||
this._backgroundBin = new St.Bin();
|
||||
@ -84,7 +80,7 @@ const ModalDialog = new Lang.Class({
|
||||
let stack = new Shell.Stack();
|
||||
this._backgroundBin.child = stack;
|
||||
|
||||
this._eventBlocker = new Clutter.Actor({ reactive: true });
|
||||
this._eventBlocker = new Clutter.Group({ reactive: true });
|
||||
stack.add_actor(this._eventBlocker);
|
||||
stack.add_actor(this.dialogLayout);
|
||||
} else {
|
||||
@ -100,7 +96,8 @@ const ModalDialog = new Lang.Class({
|
||||
y_align: St.Align.START });
|
||||
|
||||
this.buttonLayout = new St.BoxLayout({ style_class: 'modal-dialog-button-box',
|
||||
vertical: false });
|
||||
visible: false,
|
||||
vertical: false });
|
||||
this.dialogLayout.add(this.buttonLayout,
|
||||
{ expand: true,
|
||||
x_align: St.Align.MIDDLE,
|
||||
@ -123,6 +120,7 @@ const ModalDialog = new Lang.Class({
|
||||
|
||||
setButtons: function(buttons) {
|
||||
this.clearButtons();
|
||||
this.buttonLayout.visible = (buttons.length > 0);
|
||||
|
||||
for (let i = 0; i < buttons.length; i++) {
|
||||
let buttonInfo = buttons[i];
|
||||
@ -161,7 +159,6 @@ const ModalDialog = new Lang.Class({
|
||||
keys = [];
|
||||
|
||||
let button = new St.Button({ style_class: 'modal-dialog-button',
|
||||
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
|
||||
reactive: true,
|
||||
can_focus: true,
|
||||
label: label });
|
||||
@ -183,19 +180,10 @@ const ModalDialog = new Lang.Class({
|
||||
return button;
|
||||
},
|
||||
|
||||
_onKeyPressEvent: function(object, event) {
|
||||
this._pressedKey = event.get_key_symbol();
|
||||
},
|
||||
|
||||
_onKeyReleaseEvent: function(object, event) {
|
||||
let pressedKey = this._pressedKey;
|
||||
this._pressedKey = null;
|
||||
|
||||
let symbol = event.get_key_symbol();
|
||||
if (symbol != pressedKey)
|
||||
return false;
|
||||
|
||||
let buttonInfo = this._buttonKeys[symbol];
|
||||
|
||||
if (!buttonInfo)
|
||||
return false;
|
||||
|
||||
@ -278,10 +266,6 @@ const ModalDialog = new Lang.Class({
|
||||
function() {
|
||||
this.state = State.CLOSED;
|
||||
this._group.hide();
|
||||
this.emit('closed');
|
||||
|
||||
if (this._destroyOnClose)
|
||||
this.destroy();
|
||||
})
|
||||
});
|
||||
},
|
||||
|
@ -103,126 +103,6 @@ const STANDARD_TRAY_ICON_IMPLEMENTATIONS = {
|
||||
'ibus-ui-gtk': 'keyboard'
|
||||
};
|
||||
|
||||
const NotificationGenericPolicy = new Lang.Class({
|
||||
Name: 'NotificationGenericPolicy',
|
||||
Extends: MessageTray.NotificationPolicy,
|
||||
|
||||
_init: function() {
|
||||
// Don't chain to parent, it would try setting
|
||||
// our properties to the defaults
|
||||
|
||||
this.id = 'generic';
|
||||
|
||||
this._masterSettings = new Gio.Settings({ schema: 'org.gnome.desktop.notifications' });
|
||||
this._masterSettings.connect('changed', Lang.bind(this, this._changed));
|
||||
},
|
||||
|
||||
store: function() { },
|
||||
|
||||
destroy: function() {
|
||||
this._masterSettings.run_dispose();
|
||||
},
|
||||
|
||||
_changed: function(settings, key) {
|
||||
this.emit('policy-changed', key);
|
||||
},
|
||||
|
||||
get enable() {
|
||||
return true;
|
||||
},
|
||||
|
||||
get enableSound() {
|
||||
return true;
|
||||
},
|
||||
|
||||
get showBanners() {
|
||||
return this._masterSettings.get_boolean('show-banners');
|
||||
},
|
||||
|
||||
get forceExpanded() {
|
||||
return false;
|
||||
},
|
||||
|
||||
get showInLockScreen() {
|
||||
return this._masterSettings.get_boolean('show-in-lock-screen');
|
||||
},
|
||||
|
||||
get detailsInLockScreen() {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
const NotificationApplicationPolicy = new Lang.Class({
|
||||
Name: 'NotificationApplicationPolicy',
|
||||
Extends: MessageTray.NotificationPolicy,
|
||||
|
||||
_init: function(id) {
|
||||
// Don't chain to parent, it would try setting
|
||||
// our properties to the defaults
|
||||
|
||||
this.id = id;
|
||||
this._canonicalId = this._canonicalizeId(id)
|
||||
|
||||
this._masterSettings = new Gio.Settings({ schema: 'org.gnome.desktop.notifications' });
|
||||
this._settings = new Gio.Settings({ schema: 'org.gnome.desktop.notifications.application',
|
||||
path: '/org/gnome/desktop/notifications/application/' + this._canonicalId + '/' });
|
||||
|
||||
this._masterSettings.connect('changed', Lang.bind(this, this._changed));
|
||||
this._settings.connect('changed', Lang.bind(this, this._changed));
|
||||
},
|
||||
|
||||
store: function() {
|
||||
this._settings.set_string('application-id', this.id + '.desktop');
|
||||
|
||||
let apps = this._masterSettings.get_strv('application-children');
|
||||
if (apps.indexOf(this._canonicalId) < 0) {
|
||||
apps.push(this._canonicalId);
|
||||
this._masterSettings.set_strv('application-children', apps);
|
||||
}
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this._masterSettings.run_dispose();
|
||||
this._settings.run_dispose();
|
||||
},
|
||||
|
||||
_changed: function(settings, key) {
|
||||
this.emit('policy-changed', key);
|
||||
},
|
||||
|
||||
_canonicalizeId: function(id) {
|
||||
// Keys are restricted to lowercase alphanumeric characters and dash,
|
||||
// and two dashes cannot be in succession
|
||||
return id.toLowerCase().replace(/[^a-z0-9\-]/g, '-').replace(/--+/g, '-');
|
||||
},
|
||||
|
||||
get enable() {
|
||||
return this._settings.get_boolean('enable');
|
||||
},
|
||||
|
||||
get enableSound() {
|
||||
return this._settings.get_boolean('enable-sound-alerts');
|
||||
},
|
||||
|
||||
get showBanners() {
|
||||
return this._masterSettings.get_boolean('show-banners') &&
|
||||
this._settings.get_boolean('show-banners');
|
||||
},
|
||||
|
||||
get forceExpanded() {
|
||||
return this._settings.get_boolean('force-expanded');
|
||||
},
|
||||
|
||||
get showInLockScreen() {
|
||||
return this._masterSettings.get_boolean('show-in-lock-screen') &&
|
||||
this._settings.get_boolean('show-in-lock-screen');
|
||||
},
|
||||
|
||||
get detailsInLockScreen() {
|
||||
return this._settings.get_boolean('details-in-lock-screen');
|
||||
}
|
||||
});
|
||||
|
||||
const NotificationDaemon = new Lang.Class({
|
||||
Name: 'NotificationDaemon',
|
||||
|
||||
@ -244,45 +124,45 @@ const NotificationDaemon = new Lang.Class({
|
||||
Main.overview.connect('hidden',
|
||||
Lang.bind(this, this._onFocusAppChanged));
|
||||
|
||||
this._trayManager.manage_screen(global.screen, Main.messageTray.actor);
|
||||
this._trayManager.manage_stage(global.stage, Main.messageTray.actor);
|
||||
},
|
||||
|
||||
_imageForNotificationData: function(hints) {
|
||||
if (hints['image-data']) {
|
||||
_iconForNotificationData: function(icon, hints) {
|
||||
// If an icon is not specified, we use 'image-data' or 'image-path' hint for an icon
|
||||
// and don't show a large image. There are currently many applications that use
|
||||
// notify_notification_set_icon_from_pixbuf() from libnotify, which in turn sets
|
||||
// the 'image-data' hint. These applications don't typically pass in 'app_icon'
|
||||
// argument to Notify() and actually expect the pixbuf to be shown as an icon.
|
||||
// So the logic here does the right thing for this case. If both an icon and either
|
||||
// one of 'image-data' or 'image-path' are specified, we show both an icon and
|
||||
// a large image.
|
||||
if (icon) {
|
||||
if (icon.substr(0, 7) == 'file://')
|
||||
return new Gio.FileIcon({ file: Gio.File.new_for_uri(icon) });
|
||||
else if (icon[0] == '/') {
|
||||
return new Gio.FileIcon({ file: Gio.File.new_for_path(icon) });
|
||||
} else
|
||||
return new Gio.ThemedIcon({ name: icon });
|
||||
} else if (hints['image-data']) {
|
||||
let [width, height, rowStride, hasAlpha,
|
||||
bitsPerSample, nChannels, data] = hints['image-data'];
|
||||
return Shell.util_create_pixbuf_from_data(data, GdkPixbuf.Colorspace.RGB, hasAlpha,
|
||||
bitsPerSample, width, height, rowStride);
|
||||
} else if (hints['image-path']) {
|
||||
return new Gio.FileIcon({ file: Gio.File.new_for_path(hints['image-path']) });
|
||||
} else {
|
||||
let stockIcon;
|
||||
switch (hints.urgency) {
|
||||
case Urgency.LOW:
|
||||
case Urgency.NORMAL:
|
||||
stockIcon = 'gtk-dialog-info';
|
||||
break;
|
||||
case Urgency.CRITICAL:
|
||||
stockIcon = 'gtk-dialog-error';
|
||||
break;
|
||||
}
|
||||
return new Gio.ThemedIcon({ name: stockIcon });
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
_fallbackIconForNotificationData: function(hints) {
|
||||
let stockIcon;
|
||||
switch (hints.urgency) {
|
||||
case Urgency.LOW:
|
||||
case Urgency.NORMAL:
|
||||
stockIcon = 'gtk-dialog-info';
|
||||
break;
|
||||
case Urgency.CRITICAL:
|
||||
stockIcon = 'gtk-dialog-error';
|
||||
break;
|
||||
}
|
||||
return new Gio.ThemedIcon({ name: stockIcon });
|
||||
},
|
||||
|
||||
_iconForNotificationData: function(icon) {
|
||||
if (icon) {
|
||||
if (icon.substr(0, 7) == 'file://')
|
||||
return new Gio.FileIcon({ file: Gio.File.new_for_uri(icon) });
|
||||
else if (icon[0] == '/')
|
||||
return new Gio.FileIcon({ file: Gio.File.new_for_path(icon) });
|
||||
else
|
||||
return new Gio.ThemedIcon({ name: icon });
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
_lookupSource: function(title, pid, trayIcon) {
|
||||
@ -333,7 +213,7 @@ const NotificationDaemon = new Lang.Class({
|
||||
}
|
||||
}
|
||||
|
||||
let source = new Source(title, pid, sender, trayIcon, ndata ? ndata.hints['desktop-entry'] : null);
|
||||
let source = new Source(title, pid, sender, trayIcon);
|
||||
source.setTransient(isForTransientNotification);
|
||||
|
||||
if (!isForTransientNotification) {
|
||||
@ -475,8 +355,12 @@ const NotificationDaemon = new Lang.Class({
|
||||
[ndata.id, ndata.icon, ndata.summary, ndata.body,
|
||||
ndata.actions, ndata.hints, ndata.notification];
|
||||
|
||||
let gicon = this._iconForNotificationData(icon, hints);
|
||||
|
||||
if (notification == null) {
|
||||
notification = new MessageTray.Notification(source);
|
||||
notification = new MessageTray.Notification(source, summary, body,
|
||||
{ gicon: gicon,
|
||||
bannerMarkup: true });
|
||||
ndata.notification = notification;
|
||||
notification.connect('destroy', Lang.bind(this,
|
||||
function(n, reason) {
|
||||
@ -499,38 +383,29 @@ const NotificationDaemon = new Lang.Class({
|
||||
function(n, actionId) {
|
||||
this._emitActionInvoked(ndata.id, actionId);
|
||||
}));
|
||||
} else {
|
||||
notification.update(summary, body, { gicon: gicon,
|
||||
bannerMarkup: true,
|
||||
clear: true });
|
||||
}
|
||||
|
||||
// Mark music notifications so they can be shown in the screen shield
|
||||
notification.isMusic = (ndata.hints['category'] == 'x-gnome.music');
|
||||
|
||||
let gicon = this._iconForNotificationData(icon, hints);
|
||||
let gimage = this._imageForNotificationData(hints);
|
||||
|
||||
let image = null;
|
||||
|
||||
// If an icon is not specified, we use 'image-data' or 'image-path' hint for an icon
|
||||
// and don't show a large image. There are currently many applications that use
|
||||
// notify_notification_set_icon_from_pixbuf() from libnotify, which in turn sets
|
||||
// the 'image-data' hint. These applications don't typically pass in 'app_icon'
|
||||
// argument to Notify() and actually expect the pixbuf to be shown as an icon.
|
||||
// So the logic here does the right thing for this case. If both an icon and either
|
||||
// one of 'image-data' or 'image-path' are specified, we show both an icon and
|
||||
// a large image.
|
||||
if (gicon && gimage)
|
||||
image = new St.Icon({ gicon: gimage,
|
||||
icon_size: notification.IMAGE_SIZE });
|
||||
else if (!gicon && gimage)
|
||||
gicon = gimage;
|
||||
else if (!gicon)
|
||||
gicon = this._fallbackIconForNotificationData(hints);
|
||||
|
||||
notification.update(summary, body, { gicon: gicon,
|
||||
bannerMarkup: true,
|
||||
clear: true,
|
||||
soundFile: hints['sound-file'],
|
||||
soundName: hints['sound-name'] });
|
||||
notification.setImage(image);
|
||||
// We only display a large image if an icon is also specified.
|
||||
if (icon && (hints['image-data'] || hints['image-path'])) {
|
||||
let image = null;
|
||||
if (hints['image-data']) {
|
||||
let [width, height, rowStride, hasAlpha,
|
||||
bitsPerSample, nChannels, data] = hints['image-data'];
|
||||
image = St.TextureCache.get_default().load_from_raw(data, hasAlpha,
|
||||
width, height, rowStride, notification.IMAGE_SIZE);
|
||||
} else if (hints['image-path']) {
|
||||
image = St.TextureCache.get_default().load_uri_async(GLib.filename_to_uri(hints['image-path'], null),
|
||||
notification.IMAGE_SIZE,
|
||||
notification.IMAGE_SIZE);
|
||||
}
|
||||
notification.setImage(image);
|
||||
} else {
|
||||
notification.unsetImage();
|
||||
}
|
||||
|
||||
if (actions.length) {
|
||||
notification.setUseActionIcons(hints['action-icons'] == true);
|
||||
@ -560,7 +435,7 @@ const NotificationDaemon = new Lang.Class({
|
||||
// of the 'transient' hint with hints['transient'] rather than hints.transient
|
||||
notification.setTransient(hints['transient'] == true);
|
||||
|
||||
let sourceGIcon = source.useNotificationIcon ? gicon : null;
|
||||
let sourceGIcon = source.useNotificationIcon ? this._iconForNotificationData(icon, hints) : null;
|
||||
source.processNotification(notification, sourceGIcon);
|
||||
},
|
||||
|
||||
@ -584,7 +459,7 @@ const NotificationDaemon = new Lang.Class({
|
||||
// 'icon-multi',
|
||||
'icon-static',
|
||||
'persistence',
|
||||
'sound',
|
||||
// 'sound',
|
||||
];
|
||||
},
|
||||
|
||||
@ -640,22 +515,12 @@ const Source = new Lang.Class({
|
||||
Name: 'NotificationDaemonSource',
|
||||
Extends: MessageTray.Source,
|
||||
|
||||
_init: function(title, pid, sender, trayIcon, appId) {
|
||||
// Need to set the app before chaining up, so
|
||||
// methods called from the parent constructor can find it
|
||||
this.trayIcon = trayIcon;
|
||||
this.pid = pid;
|
||||
this.app = this._getApp(appId);
|
||||
|
||||
_init: function(title, pid, sender, trayIcon) {
|
||||
this.parent(title);
|
||||
|
||||
this.initialTitle = title;
|
||||
|
||||
if (this.app)
|
||||
this.title = this.app.get_name();
|
||||
else
|
||||
this.useNotificationIcon = true;
|
||||
|
||||
this.pid = pid;
|
||||
if (sender)
|
||||
this._nameWatcherId = Gio.DBus.session.watch_name(sender,
|
||||
Gio.BusNameWatcherFlags.NONE,
|
||||
@ -664,19 +529,16 @@ const Source = new Lang.Class({
|
||||
else
|
||||
this._nameWatcherId = 0;
|
||||
|
||||
if (this.trayIcon) {
|
||||
// Try again finding the app, using the WM_CLASS from the tray icon
|
||||
this._setSummaryIcon(this.trayIcon);
|
||||
this.useNotificationIcon = false;
|
||||
}
|
||||
},
|
||||
this._setApp();
|
||||
if (this.app)
|
||||
this.title = this.app.get_name();
|
||||
else
|
||||
this.useNotificationIcon = true;
|
||||
|
||||
_createPolicy: function() {
|
||||
if (this.app) {
|
||||
let id = this.app.get_id().replace(/\.desktop$/,'');
|
||||
return new NotificationApplicationPolicy(id);
|
||||
} else {
|
||||
return new NotificationGenericPolicy();
|
||||
this.trayIcon = trayIcon;
|
||||
if (this.trayIcon) {
|
||||
this._setSummaryIcon(this.trayIcon);
|
||||
this.useNotificationIcon = false;
|
||||
}
|
||||
},
|
||||
|
||||
@ -703,17 +565,19 @@ const Source = new Lang.Class({
|
||||
this.notify(notification);
|
||||
},
|
||||
|
||||
handleSummaryClick: function(button) {
|
||||
handleSummaryClick: function() {
|
||||
if (!this.trayIcon)
|
||||
return false;
|
||||
|
||||
let event = Clutter.get_current_event();
|
||||
if (event.type() != Clutter.EventType.BUTTON_RELEASE)
|
||||
return false;
|
||||
|
||||
// Left clicks are passed through only where there aren't unacknowledged
|
||||
// notifications, so it possible to open them in summary mode; right
|
||||
// clicks are always forwarded, as the right click menu is not useful for
|
||||
// tray icons
|
||||
if (button == 1 &&
|
||||
if (event.get_button() == 1 &&
|
||||
this.notifications.length > 0)
|
||||
return false;
|
||||
|
||||
@ -726,7 +590,7 @@ const Source = new Lang.Class({
|
||||
return true;
|
||||
},
|
||||
|
||||
_getApp: function(appId) {
|
||||
_getApp: function() {
|
||||
let app;
|
||||
|
||||
app = Shell.WindowTracker.get_default().get_app_from_pid(this.pid);
|
||||
@ -734,13 +598,7 @@ const Source = new Lang.Class({
|
||||
return app;
|
||||
|
||||
if (this.trayIcon) {
|
||||
app = Shell.AppSystem.get_default().lookup_wmclass(this.trayIcon.wm_class);
|
||||
if (app != null)
|
||||
return app;
|
||||
}
|
||||
|
||||
if (appId) {
|
||||
app = Shell.AppSystem.get_default().lookup_app(appId + '.desktop');
|
||||
app = Shell.AppSystem.get_default().lookup_wmclass(this.trayIcon.wmclass);
|
||||
if (app != null)
|
||||
return app;
|
||||
}
|
||||
@ -748,11 +606,11 @@ const Source = new Lang.Class({
|
||||
return null;
|
||||
},
|
||||
|
||||
_setApp: function(appId) {
|
||||
_setApp: function() {
|
||||
if (this.app)
|
||||
return;
|
||||
|
||||
this.app = this._getApp(appId);
|
||||
this.app = this._getApp();
|
||||
if (!this.app)
|
||||
return;
|
||||
|
||||
|
@ -1,179 +0,0 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Lang = imports.lang;
|
||||
const Layout = imports.ui.layout;
|
||||
const Main = imports.ui.main;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const HIDE_TIMEOUT = 1500;
|
||||
const FADE_TIME = 0.1;
|
||||
const LEVEL_ANIMATION_TIME = 0.1;
|
||||
|
||||
const LevelBar = new Lang.Class({
|
||||
Name: 'LevelBar',
|
||||
|
||||
_init: function() {
|
||||
this._level = 0;
|
||||
|
||||
this.actor = new St.Bin({ style_class: 'level',
|
||||
x_fill: true, y_fill: true });
|
||||
this._bar = new St.DrawingArea();
|
||||
this._bar.connect('repaint', Lang.bind(this, this._repaint));
|
||||
|
||||
this.actor.set_child(this._bar);
|
||||
},
|
||||
|
||||
get level() {
|
||||
return this._level;
|
||||
},
|
||||
|
||||
set level(value) {
|
||||
let newValue = Math.max(0, Math.min(value, 100));
|
||||
if (newValue == this._level)
|
||||
return;
|
||||
this._level = newValue;
|
||||
this._bar.queue_repaint();
|
||||
},
|
||||
|
||||
_repaint: function() {
|
||||
let cr = this._bar.get_context();
|
||||
|
||||
let node = this.actor.get_theme_node();
|
||||
let radius = node.get_border_radius(0); // assume same radius for all corners
|
||||
Clutter.cairo_set_source_color(cr, node.get_foreground_color());
|
||||
|
||||
let [w, h] = this._bar.get_surface_size();
|
||||
w *= (this._level / 100.);
|
||||
|
||||
if (w == 0)
|
||||
return;
|
||||
|
||||
cr.moveTo(radius, 0);
|
||||
if (w >= radius)
|
||||
cr.arc(w - radius, radius, radius, 1.5 * Math.PI, 2. * Math.PI);
|
||||
else
|
||||
cr.lineTo(w, 0);
|
||||
if (w >= radius)
|
||||
cr.arc(w - radius, h - radius, radius, 0, 0.5 * Math.PI);
|
||||
else
|
||||
cr.lineTo(w, h);
|
||||
cr.arc(radius, h - radius, radius, 0.5 * Math.PI, Math.PI);
|
||||
cr.arc(radius, radius, radius, Math.PI, 1.5 * Math.PI);
|
||||
cr.fill();
|
||||
}
|
||||
});
|
||||
|
||||
const OsdWindow = new Lang.Class({
|
||||
Name: 'OsdWindow',
|
||||
|
||||
_init: function() {
|
||||
this.actor = new St.Widget({ x_expand: true,
|
||||
y_expand: true,
|
||||
x_align: Clutter.ActorAlign.CENTER,
|
||||
y_align: Clutter.ActorAlign.CENTER });
|
||||
this.actor.add_constraint(new Layout.MonitorConstraint({ primary: true }));
|
||||
this._box = new St.BoxLayout({ style_class: 'osd-window',
|
||||
vertical: true });
|
||||
this.actor.add_actor(this._box);
|
||||
|
||||
this._icon = new St.Icon();
|
||||
this._box.add(this._icon, { expand: true });
|
||||
|
||||
this._label = new St.Label();
|
||||
this._box.add(this._label);
|
||||
|
||||
this._level = new LevelBar();
|
||||
this._box.add(this._level.actor);
|
||||
|
||||
this._hideTimeoutId = 0;
|
||||
this._reset();
|
||||
|
||||
Main.layoutManager.connect('monitors-changed',
|
||||
Lang.bind(this, this._monitorsChanged));
|
||||
this._monitorsChanged();
|
||||
|
||||
Main.layoutManager.addChrome(this.actor, { affectsInputRegion: false });
|
||||
},
|
||||
|
||||
setIcon: function(icon) {
|
||||
this._icon.gicon = icon;
|
||||
},
|
||||
|
||||
setLabel: function(label) {
|
||||
this._label.visible = (label != undefined);
|
||||
if (label)
|
||||
this._label.text = label;
|
||||
},
|
||||
|
||||
setLevel: function(level) {
|
||||
this._level.actor.visible = (level != undefined);
|
||||
if (this.actor.visible)
|
||||
Tweener.addTween(this._level,
|
||||
{ level: level,
|
||||
time: LEVEL_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad' });
|
||||
else
|
||||
this._level.level = level;
|
||||
},
|
||||
|
||||
show: function() {
|
||||
if (!this._icon.gicon)
|
||||
return;
|
||||
|
||||
if (!this.actor.visible) {
|
||||
this.actor.show();
|
||||
this.actor.opacity = 0;
|
||||
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 255,
|
||||
time: FADE_TIME,
|
||||
transition: 'easeOutQuad' });
|
||||
}
|
||||
|
||||
if (this._hideTimeoutId)
|
||||
Mainloop.source_remove(this._hideTimeoutId);
|
||||
this._hideTimeoutId = Mainloop.timeout_add(HIDE_TIMEOUT,
|
||||
Lang.bind(this, this._hide));
|
||||
},
|
||||
|
||||
cancel: function() {
|
||||
if (!this._hideTimeoutId)
|
||||
return;
|
||||
|
||||
Mainloop.source_remove(this._hideTimeoutId);
|
||||
this._hideTimeoutId = 0;
|
||||
this._hide();
|
||||
},
|
||||
|
||||
_hide: function() {
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 0,
|
||||
time: FADE_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this, this._reset) });
|
||||
},
|
||||
|
||||
_reset: function() {
|
||||
this.actor.hide();
|
||||
this.setLabel(null);
|
||||
this.setLevel(null);
|
||||
},
|
||||
|
||||
_monitorsChanged: function() {
|
||||
/* assume 110x110 on a 640x480 display and scale from there */
|
||||
let monitor = Main.layoutManager.primaryMonitor;
|
||||
let scalew = monitor.width / 640.0;
|
||||
let scaleh = monitor.height / 480.0;
|
||||
let scale = Math.min(scalew, scaleh);
|
||||
let size = 110 * Math.max(1, scale);
|
||||
|
||||
this._box.set_size(size, size);
|
||||
this._box.translation_y = monitor.height / 4;
|
||||
|
||||
this._icon.icon_size = size / 2;
|
||||
}
|
||||
});
|
@ -10,13 +10,10 @@ const St = imports.gi.St;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Gdk = imports.gi.Gdk;
|
||||
|
||||
const Background = imports.ui.background;
|
||||
const Dash = imports.ui.dash;
|
||||
const DND = imports.ui.dnd;
|
||||
const LayoutManager = imports.ui.layout;
|
||||
const Main = imports.ui.main;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
const OverviewControls = imports.ui.overviewControls;
|
||||
const Panel = imports.ui.panel;
|
||||
const Params = imports.misc.params;
|
||||
const Tweener = imports.ui.tweener;
|
||||
@ -26,14 +23,25 @@ const WorkspaceThumbnail = imports.ui.workspaceThumbnail;
|
||||
// Time for initial animation going into Overview mode
|
||||
const ANIMATION_TIME = 0.25;
|
||||
|
||||
// Must be less than ANIMATION_TIME, since we switch to
|
||||
// or from the overview completely after ANIMATION_TIME,
|
||||
// and don't want the shading animation to get cut off
|
||||
const SHADE_ANIMATION_TIME = .20;
|
||||
|
||||
const DND_WINDOW_SWITCH_TIMEOUT = 1250;
|
||||
|
||||
const OVERVIEW_ACTIVATION_TIMEOUT = 0.5;
|
||||
const GLSL_DIM_EFFECT_DECLARATIONS = '';
|
||||
const GLSL_DIM_EFFECT_CODE = '\
|
||||
vec2 dist = cogl_tex_coord_in[0].xy - vec2(0.5, 0.5); \
|
||||
float elipse_radius = 0.5; \
|
||||
/* from https://bugzilla.gnome.org/show_bug.cgi?id=669798: \
|
||||
the alpha on the gradient goes from 250 at its darkest to 180 at its most transparent. */ \
|
||||
float y = 250.0 / 255.0; \
|
||||
float x = 180.0 / 255.0; \
|
||||
/* interpolate darkening value, based on distance from screen center */ \
|
||||
float val = min(length(dist), elipse_radius); \
|
||||
float a = mix(x, y, val / elipse_radius); \
|
||||
/* dim_factor varies from [1.0 -> 0.5] when overview is showing \
|
||||
We use it to smooth value, then we clamp it to valid color interval */ \
|
||||
a = clamp(a - cogl_color_in.r + 0.5, 0.0, 1.0); \
|
||||
/* We\'re blending between: color and black color (obviously omitted in the equation) */ \
|
||||
cogl_color_out.xyz = cogl_color_out.xyz * (1.0 - a); \
|
||||
cogl_color_out.a = 1.0;';
|
||||
|
||||
const ShellInfo = new Lang.Class({
|
||||
Name: 'ShellInfo',
|
||||
@ -109,50 +117,41 @@ const Overview = new Lang.Class({
|
||||
|
||||
this._overviewCreated = true;
|
||||
|
||||
// The main Background actors are inside global.window_group which are
|
||||
// The main BackgroundActor is inside global.window_group which is
|
||||
// hidden when displaying the overview, so we create a new
|
||||
// one. Instances of this class share a single CoglTexture behind the
|
||||
// scenes which allows us to show the background with different
|
||||
// rendering options without duplicating the texture data.
|
||||
let monitor = Main.layoutManager.primaryMonitor;
|
||||
this._background = Meta.BackgroundActor.new_for_screen(global.screen);
|
||||
this._background.add_glsl_snippet(Meta.SnippetHook.FRAGMENT,
|
||||
GLSL_DIM_EFFECT_DECLARATIONS,
|
||||
GLSL_DIM_EFFECT_CODE,
|
||||
false);
|
||||
this._background.hide();
|
||||
global.overlay_group.add_actor(this._background);
|
||||
|
||||
this._desktopFade = new St.Bin();
|
||||
global.overlay_group.add_actor(this._desktopFade);
|
||||
|
||||
let layout = new Clutter.BinLayout();
|
||||
this._stack = new Clutter.Actor({ layout_manager: layout });
|
||||
this._stack.add_constraint(new LayoutManager.MonitorConstraint({ primary: true }));
|
||||
|
||||
/* Translators: This is the main view to select
|
||||
activities. See also note for "Activities" string. */
|
||||
this._overview = new St.BoxLayout({ name: 'overview',
|
||||
accessible_name: _("Overview"),
|
||||
reactive: true,
|
||||
vertical: true,
|
||||
x_expand: true,
|
||||
y_expand: true });
|
||||
vertical: true });
|
||||
this._overview._delegate = this;
|
||||
|
||||
this._groupStack = new St.Widget({ layout_manager: new Clutter.BinLayout(),
|
||||
x_expand: true, y_expand: true,
|
||||
clip_to_allocation: true });
|
||||
this._group = new St.BoxLayout({ name: 'overview-group',
|
||||
reactive: true,
|
||||
x_expand: true, y_expand: true });
|
||||
this._groupStack.add_actor(this._group);
|
||||
this._group = new St.BoxLayout({ name: 'overview-group' });
|
||||
|
||||
this._backgroundGroup = new Meta.BackgroundGroup();
|
||||
global.overlay_group.add_child(this._backgroundGroup);
|
||||
this._backgroundGroup.hide();
|
||||
this._bgManagers = [];
|
||||
|
||||
this._activationTime = 0;
|
||||
this._capturedEventId = 0;
|
||||
this._buttonPressId = 0;
|
||||
|
||||
this.visible = false; // animating to overview, in overview, animating out
|
||||
this._shown = false; // show() and not hide()
|
||||
this._shownTemporarily = false; // showTemporarily() and not hideTemporarily()
|
||||
this._modal = false; // have a modal grab
|
||||
this.animationInProgress = false;
|
||||
this.visibleTarget = false;
|
||||
this._hideInProgress = false;
|
||||
|
||||
// During transitions, we raise this to the top to avoid having the overview
|
||||
// area be reactive; it causes too many issues such as double clicks on
|
||||
@ -162,9 +161,8 @@ const Overview = new Lang.Class({
|
||||
this._overview.add_actor(this._coverPane);
|
||||
this._coverPane.connect('event', Lang.bind(this, function (actor, event) { return true; }));
|
||||
|
||||
this._stack.hide();
|
||||
this._stack.add_actor(this._overview);
|
||||
global.overlay_group.add_actor(this._stack);
|
||||
this._overview.hide();
|
||||
global.overlay_group.add_actor(this._overview);
|
||||
|
||||
this._coverPane.hide();
|
||||
|
||||
@ -177,7 +175,6 @@ const Overview = new Lang.Class({
|
||||
Main.xdndHandler.connect('drag-end', Lang.bind(this, this._onDragEnd));
|
||||
|
||||
global.screen.connect('restacked', Lang.bind(this, this._onRestacked));
|
||||
this._group.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
|
||||
|
||||
this._windowSwitchTimeoutId = 0;
|
||||
this._windowSwitchTimestamp = 0;
|
||||
@ -189,56 +186,6 @@ const Overview = new Lang.Class({
|
||||
this.init();
|
||||
},
|
||||
|
||||
_updateBackgrounds: function() {
|
||||
for (let i = 0; i < this._bgManagers.length; i++)
|
||||
this._bgManagers[i].destroy();
|
||||
|
||||
this._bgManagers = [];
|
||||
|
||||
for (let i = 0; i < Main.layoutManager.monitors.length; i++) {
|
||||
let bgManager = new Background.BackgroundManager({ container: this._backgroundGroup,
|
||||
monitorIndex: i,
|
||||
effects: Meta.BackgroundEffects.VIGNETTE });
|
||||
this._bgManagers.push(bgManager);
|
||||
}
|
||||
},
|
||||
|
||||
_unshadeBackgrounds: function() {
|
||||
let backgrounds = this._backgroundGroup.get_children();
|
||||
for (let i = 0; i < backgrounds.length; i++) {
|
||||
let background = backgrounds[i]._delegate;
|
||||
|
||||
Tweener.addTween(background,
|
||||
{ brightness: 1.0,
|
||||
time: SHADE_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
Tweener.addTween(background,
|
||||
{ vignetteSharpness: 0.0,
|
||||
time: SHADE_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_shadeBackgrounds: function() {
|
||||
let backgrounds = this._backgroundGroup.get_children();
|
||||
for (let i = 0; i < backgrounds.length; i++) {
|
||||
let background = backgrounds[i]._delegate;
|
||||
|
||||
Tweener.addTween(background,
|
||||
{ brightness: 0.8,
|
||||
time: SHADE_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
Tweener.addTween(background,
|
||||
{ vignetteSharpness: 0.7,
|
||||
time: SHADE_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_sessionUpdated: function() {
|
||||
this.isDummy = !Main.sessionMode.hasOverview;
|
||||
this._createOverview();
|
||||
@ -268,46 +215,43 @@ const Overview = new Lang.Class({
|
||||
in the search entry when no search is
|
||||
active; it should not exceed ~30
|
||||
characters. */
|
||||
hint_text: _("Type to search…"),
|
||||
hint_text: _("Type to search..."),
|
||||
track_hover: true,
|
||||
can_focus: true });
|
||||
this._searchEntryBin = new St.Bin({ child: this._searchEntry,
|
||||
x_align: St.Align.MIDDLE });
|
||||
this._overview.add_actor(this._searchEntryBin);
|
||||
|
||||
// Create controls
|
||||
this._dash = new Dash.Dash();
|
||||
this._viewSelector = new ViewSelector.ViewSelector(this._searchEntry,
|
||||
this._dash.showAppsButton);
|
||||
this._thumbnailsBox = new WorkspaceThumbnail.ThumbnailsBox();
|
||||
this._controls = new OverviewControls.ControlsManager(this._dash,
|
||||
this._thumbnailsBox,
|
||||
this._viewSelector);
|
||||
|
||||
this._controls.dashActor.x_align = Clutter.ActorAlign.START;
|
||||
this._controls.dashActor.y_expand = true;
|
||||
|
||||
// Put the dash in a separate layer to allow content to be centered
|
||||
this._groupStack.add_actor(this._controls.dashActor);
|
||||
|
||||
// Pack all the actors into the group
|
||||
this._group.add_actor(this._controls.dashSpacer);
|
||||
this._group.add(this._viewSelector.actor, { x_fill: true,
|
||||
expand: true });
|
||||
this._group.add_actor(this._controls.thumbnailsActor);
|
||||
|
||||
// Add our same-line elements after the search entry
|
||||
this._overview.add(this._groupStack, { y_fill: true, expand: true });
|
||||
|
||||
this._stack.add_actor(this._controls.indicatorActor);
|
||||
|
||||
// TODO - recalculate everything when desktop size changes
|
||||
this._dash = new Dash.Dash();
|
||||
this._group.add_actor(this._dash.actor);
|
||||
this.dashIconSize = this._dash.iconSize;
|
||||
this._dash.connect('icon-size-changed',
|
||||
Lang.bind(this, function() {
|
||||
this.dashIconSize = this._dash.iconSize;
|
||||
}));
|
||||
|
||||
// Translators: this is the name of the dock/favorites area on
|
||||
// the left of the overview
|
||||
Main.ctrlAltTabManager.addGroup(this._dash.actor, _("Dash"), 'user-bookmarks-symbolic');
|
||||
|
||||
this._viewSelector = new ViewSelector.ViewSelector(this._searchEntry,
|
||||
this._dash.showAppsButton);
|
||||
this._group.add(this._viewSelector.actor, { x_fill: true,
|
||||
expand: true });
|
||||
|
||||
// Add our same-line elements after the search entry
|
||||
this._overview.add(this._group, { y_fill: true,
|
||||
expand: true });
|
||||
|
||||
// Then account for message tray
|
||||
this._messageTrayGhost = new St.Bin({ style_class: 'message-tray-summary',
|
||||
reactive: false,
|
||||
opacity: 0,
|
||||
x_fill: true,
|
||||
y_fill: true });
|
||||
this._overview.add_actor(this._messageTrayGhost);
|
||||
|
||||
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._relayout));
|
||||
this._relayout();
|
||||
},
|
||||
@ -333,22 +277,18 @@ const Overview = new Lang.Class({
|
||||
},
|
||||
|
||||
_onDragBegin: function() {
|
||||
this._inXdndDrag = true;
|
||||
|
||||
DND.addDragMonitor(this._dragMonitor);
|
||||
// Remember the workspace we started from
|
||||
this._lastActiveWorkspaceIndex = global.screen.get_active_workspace_index();
|
||||
},
|
||||
|
||||
_onDragEnd: function(time) {
|
||||
this._inXdndDrag = false;
|
||||
|
||||
// In case the drag was canceled while in the overview
|
||||
// we have to go back to where we started and hide
|
||||
// the overview
|
||||
if (this._shown) {
|
||||
if (this._shownTemporarily) {
|
||||
global.screen.get_workspace_by_index(this._lastActiveWorkspaceIndex).activate(time);
|
||||
this.hide();
|
||||
this.hideTemporarily();
|
||||
}
|
||||
this._resetWindowSwitchTimeout();
|
||||
this._lastHoveredWindow = null;
|
||||
@ -396,7 +336,7 @@ const Overview = new Lang.Class({
|
||||
this._needsFakePointerEvent = true;
|
||||
Main.activateWindow(dragEvent.targetActor._delegate.metaWindow,
|
||||
this._windowSwitchTimestamp);
|
||||
this.hide();
|
||||
this.hideTemporarily();
|
||||
this._lastHoveredWindow = null;
|
||||
}));
|
||||
}
|
||||
@ -404,10 +344,6 @@ const Overview = new Lang.Class({
|
||||
return DND.DragMotionResult.CONTINUE;
|
||||
},
|
||||
|
||||
_onScrollEvent: function(actor, event) {
|
||||
this.emit('scroll-event', event);
|
||||
},
|
||||
|
||||
addAction: function(action) {
|
||||
if (this.isDummy)
|
||||
return;
|
||||
@ -437,12 +373,16 @@ const Overview = new Lang.Class({
|
||||
// when it is next shown.
|
||||
this.hide();
|
||||
|
||||
let workArea = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex);
|
||||
let primary = Main.layoutManager.primaryMonitor;
|
||||
|
||||
this._coverPane.set_position(0, workArea.y);
|
||||
this._coverPane.set_size(workArea.width, workArea.height);
|
||||
let contentY = Main.panel.actor.height;
|
||||
let contentHeight = primary.height - contentY - Main.messageTray.actor.height;
|
||||
|
||||
this._updateBackgrounds();
|
||||
this._overview.set_position(primary.x, primary.y);
|
||||
this._overview.set_size(primary.width, primary.height);
|
||||
|
||||
this._coverPane.set_position(0, contentY);
|
||||
this._coverPane.set_size(primary.width, contentHeight);
|
||||
},
|
||||
|
||||
_onRestacked: function() {
|
||||
@ -492,10 +432,9 @@ const Overview = new Lang.Class({
|
||||
if (this._shown)
|
||||
return;
|
||||
this._shown = true;
|
||||
|
||||
if (!this._syncInputMode())
|
||||
this._syncInputMode();
|
||||
if (!this._modal)
|
||||
return;
|
||||
|
||||
this._animateVisible();
|
||||
},
|
||||
|
||||
@ -527,8 +466,6 @@ const Overview = new Lang.Class({
|
||||
|
||||
this.visible = true;
|
||||
this.animationInProgress = true;
|
||||
this.visibleTarget = true;
|
||||
this._activationTime = Date.now() / 1000;
|
||||
|
||||
// All the the actors in the window group are completely obscured,
|
||||
// hiding the group holding them while the Overview is displayed greatly
|
||||
@ -540,25 +477,49 @@ const Overview = new Lang.Class({
|
||||
//
|
||||
// Disable unredirection while in the overview
|
||||
Meta.disable_unredirect_for_screen(global.screen);
|
||||
this._stack.show();
|
||||
this._backgroundGroup.show();
|
||||
global.window_group.hide();
|
||||
this._overview.show();
|
||||
this._background.show();
|
||||
this._viewSelector.show();
|
||||
|
||||
this._stack.opacity = 0;
|
||||
Tweener.addTween(this._stack,
|
||||
this._overview.opacity = 0;
|
||||
Tweener.addTween(this._overview,
|
||||
{ opacity: 255,
|
||||
transition: 'easeOutQuad',
|
||||
time: ANIMATION_TIME,
|
||||
onComplete: this._showDone,
|
||||
onCompleteScope: this
|
||||
});
|
||||
this._shadeBackgrounds();
|
||||
|
||||
Tweener.addTween(this._background,
|
||||
{ dim_factor: 0.8,
|
||||
time: ANIMATION_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
|
||||
this._coverPane.raise_top();
|
||||
this._coverPane.show();
|
||||
this.emit('showing');
|
||||
},
|
||||
|
||||
// showTemporarily:
|
||||
//
|
||||
// Animates the overview visible without grabbing mouse and keyboard input;
|
||||
// if show() has already been called, this has no immediate effect, but
|
||||
// will result in the overview not being hidden until hideTemporarily() is
|
||||
// called.
|
||||
showTemporarily: function() {
|
||||
if (this.isDummy)
|
||||
return;
|
||||
|
||||
if (this._shownTemporarily)
|
||||
return;
|
||||
|
||||
this._syncInputMode();
|
||||
this._animateVisible();
|
||||
this._shownTemporarily = true;
|
||||
},
|
||||
|
||||
// hide:
|
||||
//
|
||||
// Reverses the effect of show()
|
||||
@ -569,22 +530,30 @@ const Overview = new Lang.Class({
|
||||
if (!this._shown)
|
||||
return;
|
||||
|
||||
let event = Clutter.get_current_event();
|
||||
if (event) {
|
||||
let type = event.type();
|
||||
let button = (type == Clutter.EventType.BUTTON_PRESS ||
|
||||
type == Clutter.EventType.BUTTON_RELEASE);
|
||||
let ctrl = (event.get_state() & Clutter.ModifierType.CONTROL_MASK) != 0;
|
||||
if (button && ctrl)
|
||||
return;
|
||||
}
|
||||
|
||||
this._animateNotVisible();
|
||||
if (!this._shownTemporarily)
|
||||
this._animateNotVisible();
|
||||
|
||||
this._shown = false;
|
||||
this._syncInputMode();
|
||||
},
|
||||
|
||||
// hideTemporarily:
|
||||
//
|
||||
// Reverses the effect of showTemporarily()
|
||||
hideTemporarily: function() {
|
||||
if (this.isDummy)
|
||||
return;
|
||||
|
||||
if (!this._shownTemporarily)
|
||||
return;
|
||||
|
||||
if (!this._shown)
|
||||
this._animateNotVisible();
|
||||
|
||||
this._shownTemporarily = false;
|
||||
this._syncInputMode();
|
||||
},
|
||||
|
||||
toggle: function() {
|
||||
if (this.isDummy)
|
||||
return;
|
||||
@ -595,20 +564,6 @@ const Overview = new Lang.Class({
|
||||
this.show();
|
||||
},
|
||||
|
||||
// Checks if the Activities button is currently sensitive to
|
||||
// clicks. The first call to this function within the
|
||||
// OVERVIEW_ACTIVATION_TIMEOUT time of the hot corner being
|
||||
// triggered will return false. This avoids opening and closing
|
||||
// the overview if the user both triggered the hot corner and
|
||||
// clicked the Activities button.
|
||||
shouldToggleByCornerOrButton: function() {
|
||||
if (this.animationInProgress)
|
||||
return false;
|
||||
if (this._activationTime == 0 || Date.now() / 1000 - this._activationTime > OVERVIEW_ACTIVATION_TIMEOUT)
|
||||
return true;
|
||||
return false;
|
||||
},
|
||||
|
||||
//// Private methods ////
|
||||
|
||||
_syncInputMode: function() {
|
||||
@ -616,23 +571,22 @@ const Overview = new Lang.Class({
|
||||
// overview we don't have a problem with the release of a press/release
|
||||
// going to an application.
|
||||
if (this.animationInProgress)
|
||||
return true;
|
||||
return;
|
||||
|
||||
if (this._shown) {
|
||||
let shouldBeModal = !this._inXdndDrag;
|
||||
if (shouldBeModal) {
|
||||
if (!this._modal) {
|
||||
if (Main.pushModal(this._overview,
|
||||
{ keybindingMode: Shell.KeyBindingMode.OVERVIEW })) {
|
||||
this._modal = true;
|
||||
} else {
|
||||
this.hide();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
global.stage_input_mode = Shell.StageInputMode.FULLSCREEN;
|
||||
if (!this._modal) {
|
||||
if (Main.pushModal(this._overview,
|
||||
{ keybindingMode: Main.KeybindingMode.OVERVIEW }))
|
||||
this._modal = true;
|
||||
else
|
||||
this.hide();
|
||||
}
|
||||
} else if (this._shownTemporarily) {
|
||||
if (this._modal) {
|
||||
Main.popModal(this._overview);
|
||||
this._modal = false;
|
||||
}
|
||||
global.stage_input_mode = Shell.StageInputMode.FULLSCREEN;
|
||||
} else {
|
||||
if (this._modal) {
|
||||
Main.popModal(this._overview);
|
||||
@ -641,7 +595,6 @@ const Overview = new Lang.Class({
|
||||
else if (global.stage_input_mode == Shell.StageInputMode.FULLSCREEN)
|
||||
global.stage_input_mode = Shell.StageInputMode.NORMAL;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
_animateNotVisible: function() {
|
||||
@ -649,19 +602,24 @@ const Overview = new Lang.Class({
|
||||
return;
|
||||
|
||||
this.animationInProgress = true;
|
||||
this.visibleTarget = false;
|
||||
this._hideInProgress = true;
|
||||
|
||||
this._viewSelector.zoomFromOverview();
|
||||
|
||||
// Make other elements fade out.
|
||||
Tweener.addTween(this._stack,
|
||||
Tweener.addTween(this._overview,
|
||||
{ opacity: 0,
|
||||
transition: 'easeOutQuad',
|
||||
time: ANIMATION_TIME,
|
||||
onComplete: this._hideDone,
|
||||
onCompleteScope: this
|
||||
});
|
||||
this._unshadeBackgrounds();
|
||||
|
||||
Tweener.addTween(this._background,
|
||||
{ dim_factor: 1.0,
|
||||
time: ANIMATION_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
|
||||
this._coverPane.raise_top();
|
||||
this._coverPane.show();
|
||||
@ -675,7 +633,7 @@ const Overview = new Lang.Class({
|
||||
|
||||
this.emit('shown');
|
||||
// Handle any calls to hide* while we were showing
|
||||
if (!this._shown)
|
||||
if (!this._shown && !this._shownTemporarily)
|
||||
this._animateNotVisible();
|
||||
|
||||
this._syncInputMode();
|
||||
@ -686,19 +644,22 @@ const Overview = new Lang.Class({
|
||||
// Re-enable unredirection
|
||||
Meta.enable_unredirect_for_screen(global.screen);
|
||||
|
||||
global.window_group.show();
|
||||
|
||||
this._viewSelector.hide();
|
||||
this._desktopFade.hide();
|
||||
this._backgroundGroup.hide();
|
||||
this._stack.hide();
|
||||
this._background.hide();
|
||||
this._overview.hide();
|
||||
|
||||
this.visible = false;
|
||||
this.animationInProgress = false;
|
||||
this._hideInProgress = false;
|
||||
|
||||
this._coverPane.hide();
|
||||
|
||||
this.emit('hidden');
|
||||
// Handle any calls to show* while we were hiding
|
||||
if (this._shown)
|
||||
if (this._shown || this._shownTemporarily)
|
||||
this._animateVisible();
|
||||
|
||||
this._syncInputMode();
|
||||
|
@ -1,554 +0,0 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Lang = imports.lang;
|
||||
const Meta = imports.gi.Meta;
|
||||
const St = imports.gi.St;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const Params = imports.misc.params;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const ViewSelector = imports.ui.viewSelector;
|
||||
|
||||
const SIDE_CONTROLS_ANIMATION_TIME = 0.16;
|
||||
|
||||
function getRtlSlideDirection(direction, actor) {
|
||||
let rtl = (actor.text_direction == Clutter.TextDirection.RTL);
|
||||
if (rtl)
|
||||
direction = (direction == SlideDirection.LEFT) ?
|
||||
SlideDirection.RIGHT : SlideDirection.LEFT;
|
||||
|
||||
return direction;
|
||||
};
|
||||
|
||||
const SlideDirection = {
|
||||
LEFT: 0,
|
||||
RIGHT: 1
|
||||
};
|
||||
|
||||
const SlideLayout = new Lang.Class({
|
||||
Name: 'SlideLayout',
|
||||
Extends: Clutter.FixedLayout,
|
||||
|
||||
_init: function(params) {
|
||||
this._slideX = 1;
|
||||
this._direction = SlideDirection.LEFT;
|
||||
|
||||
this.parent(params);
|
||||
},
|
||||
|
||||
vfunc_get_preferred_width: function(container, forHeight) {
|
||||
let child = container.get_first_child();
|
||||
|
||||
let [minWidth, natWidth] = child.get_preferred_width(forHeight);
|
||||
|
||||
minWidth *= this._slideX;
|
||||
natWidth *= this._slideX;
|
||||
|
||||
return [minWidth, natWidth];
|
||||
},
|
||||
|
||||
vfunc_allocate: function(container, box, flags) {
|
||||
let child = container.get_first_child();
|
||||
|
||||
let [, , natWidth, natHeight] = child.get_preferred_size();
|
||||
let availWidth = Math.round(box.x2 - box.x1);
|
||||
let availHeight = Math.round(box.y2 - box.y1);
|
||||
|
||||
let realDirection = getRtlSlideDirection(this._direction, child);
|
||||
let translationX = (realDirection == SlideDirection.LEFT) ?
|
||||
(availWidth - natWidth) : (natWidth - availWidth);
|
||||
|
||||
let actorBox = new Clutter.ActorBox({ x1: translationX,
|
||||
y1: 0,
|
||||
x2: child.x_expand ? availWidth : natWidth,
|
||||
y2: child.y_expand ? availHeight : natHeight });
|
||||
|
||||
child.allocate(actorBox, flags);
|
||||
},
|
||||
|
||||
set slideX(value) {
|
||||
this._slideX = value;
|
||||
this.layout_changed();
|
||||
},
|
||||
|
||||
get slideX() {
|
||||
return this._slideX;
|
||||
},
|
||||
|
||||
set slideDirection(direction) {
|
||||
this._direction = direction;
|
||||
this.layout_changed();
|
||||
},
|
||||
|
||||
get slideDirection() {
|
||||
return this._direction;
|
||||
}
|
||||
});
|
||||
|
||||
const SlidingControl = new Lang.Class({
|
||||
Name: 'SlidingControl',
|
||||
|
||||
_init: function(params) {
|
||||
params = Params.parse(params, { slideDirection: SlideDirection.LEFT });
|
||||
|
||||
this.visible = true;
|
||||
this.inDrag = false;
|
||||
|
||||
this.layout = new SlideLayout();
|
||||
this.layout.slideDirection = params.slideDirection;
|
||||
this.actor = new St.Widget({ layout_manager: this.layout,
|
||||
style_class: 'overview-controls',
|
||||
clip_to_allocation: true });
|
||||
|
||||
Main.overview.connect('showing', Lang.bind(this, this._onOverviewShowing));
|
||||
|
||||
Main.overview.connect('item-drag-begin', Lang.bind(this, this._onDragBegin));
|
||||
Main.overview.connect('item-drag-end', Lang.bind(this, this._onDragEnd));
|
||||
Main.overview.connect('item-drag-cancelled', Lang.bind(this, this._onDragEnd));
|
||||
|
||||
Main.overview.connect('window-drag-begin', Lang.bind(this, this._onWindowDragBegin));
|
||||
Main.overview.connect('window-drag-cancelled', Lang.bind(this, this._onWindowDragEnd));
|
||||
Main.overview.connect('window-drag-end', Lang.bind(this, this._onWindowDragEnd));
|
||||
},
|
||||
|
||||
getSlide: function() {
|
||||
throw new Error('getSlide() must be overridden');
|
||||
},
|
||||
|
||||
updateSlide: function() {
|
||||
Tweener.addTween(this.layout, { slideX: this.getSlide(),
|
||||
time: SIDE_CONTROLS_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad' });
|
||||
},
|
||||
|
||||
getVisibleWidth: function() {
|
||||
let child = this.actor.get_first_child();
|
||||
let [, , natWidth, ] = child.get_preferred_size();
|
||||
return natWidth;
|
||||
},
|
||||
|
||||
_getTranslation: function() {
|
||||
let child = this.actor.get_first_child();
|
||||
let direction = getRtlSlideDirection(this.layout.slideDirection, child);
|
||||
let visibleWidth = this.getVisibleWidth();
|
||||
|
||||
if (direction == SlideDirection.LEFT)
|
||||
return - visibleWidth;
|
||||
else
|
||||
return visibleWidth;
|
||||
},
|
||||
|
||||
_updateTranslation: function() {
|
||||
let translationStart = 0;
|
||||
let translationEnd = 0;
|
||||
let translation = this._getTranslation();
|
||||
|
||||
if (this.visible) {
|
||||
translationStart = translation;
|
||||
} else {
|
||||
translationEnd = translation;
|
||||
}
|
||||
|
||||
if (this.actor.translation_x == translationEnd)
|
||||
return;
|
||||
|
||||
this.actor.translation_x = translationStart;
|
||||
Tweener.addTween(this.actor, { translation_x: translationEnd,
|
||||
time: SIDE_CONTROLS_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
},
|
||||
|
||||
_onOverviewShowing: function() {
|
||||
// reset any translation and make sure the actor is visible when
|
||||
// entering the overview
|
||||
this.visible = true;
|
||||
this.layout.slideX = this.getSlide();
|
||||
this.actor.translation_x = 0;
|
||||
},
|
||||
|
||||
_onWindowDragBegin: function() {
|
||||
this._onDragBegin();
|
||||
},
|
||||
|
||||
_onWindowDragEnd: function() {
|
||||
this._onDragEnd();
|
||||
},
|
||||
|
||||
_onDragBegin: function() {
|
||||
this.inDrag = true;
|
||||
this.actor.translation_x = 0;
|
||||
this.updateSlide();
|
||||
},
|
||||
|
||||
_onDragEnd: function() {
|
||||
this.inDrag = false;
|
||||
this.updateSlide();
|
||||
},
|
||||
|
||||
fadeIn: function() {
|
||||
Tweener.addTween(this.actor, { opacity: 255,
|
||||
time: SIDE_CONTROLS_ANIMATION_TIME / 2,
|
||||
transition: 'easeInQuad'
|
||||
});
|
||||
},
|
||||
|
||||
fadeHalf: function() {
|
||||
Tweener.addTween(this.actor, { opacity: 128,
|
||||
time: SIDE_CONTROLS_ANIMATION_TIME / 2,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
},
|
||||
|
||||
slideIn: function() {
|
||||
this.visible = true;
|
||||
// we will update slideX and the translation from pageEmpty
|
||||
},
|
||||
|
||||
slideOut: function() {
|
||||
this.visible = false;
|
||||
this._updateTranslation();
|
||||
// we will update slideX from pageEmpty
|
||||
},
|
||||
|
||||
pageEmpty: function() {
|
||||
// When pageEmpty is received, there's no visible view in the
|
||||
// selector; this means we can now safely set the full slide for
|
||||
// the next page, since slideIn or slideOut might have been called,
|
||||
// changing the visiblity
|
||||
this.layout.slideX = this.getSlide();
|
||||
this._updateTranslation();
|
||||
}
|
||||
});
|
||||
|
||||
const ThumbnailsSlider = new Lang.Class({
|
||||
Name: 'ThumbnailsSlider',
|
||||
Extends: SlidingControl,
|
||||
|
||||
_init: function(thumbnailsBox) {
|
||||
this.parent({ slideDirection: SlideDirection.RIGHT });
|
||||
|
||||
this._thumbnailsBox = thumbnailsBox;
|
||||
|
||||
// SlideLayout reads the actor's expand flags to decide
|
||||
// whether to allocate the natural size to its child, or the whole
|
||||
// available allocation
|
||||
this._thumbnailsBox.actor.y_expand = true;
|
||||
|
||||
this.actor.request_mode = Clutter.RequestMode.WIDTH_FOR_HEIGHT;
|
||||
this.actor.reactive = true;
|
||||
this.actor.track_hover = true;
|
||||
this.actor.add_actor(this._thumbnailsBox.actor);
|
||||
|
||||
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this.updateSlide));
|
||||
this.actor.connect('notify::hover', Lang.bind(this, this.updateSlide));
|
||||
},
|
||||
|
||||
_getAlwaysZoomOut: function() {
|
||||
// Always show the pager when hover, during a drag, or if workspaces are
|
||||
// actually used, e.g. there are windows on more than one
|
||||
let alwaysZoomOut = this.actor.hover || this.inDrag || !Meta.prefs_get_dynamic_workspaces() || global.screen.n_workspaces > 2;
|
||||
|
||||
if (!alwaysZoomOut) {
|
||||
let monitors = Main.layoutManager.monitors;
|
||||
let primary = Main.layoutManager.primaryMonitor;
|
||||
|
||||
/* Look for any monitor to the right of the primary, if there is
|
||||
* one, we always keep zoom out, otherwise its hard to reach
|
||||
* the thumbnail area without passing into the next monitor. */
|
||||
for (let i = 0; i < monitors.length; i++) {
|
||||
if (monitors[i].x >= primary.x + primary.width) {
|
||||
alwaysZoomOut = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return alwaysZoomOut;
|
||||
},
|
||||
|
||||
getSlide: function() {
|
||||
if (!this.visible)
|
||||
return 0;
|
||||
|
||||
let alwaysZoomOut = this._getAlwaysZoomOut();
|
||||
if (alwaysZoomOut)
|
||||
return 1;
|
||||
|
||||
let child = this.actor.get_first_child();
|
||||
let preferredHeight = child.get_preferred_height(-1)[1];
|
||||
let expandedWidth = child.get_preferred_width(preferredHeight)[1];
|
||||
let visibleWidth = child.get_theme_node().get_length('visible-width');
|
||||
|
||||
return visibleWidth / expandedWidth;
|
||||
},
|
||||
|
||||
getVisibleWidth: function() {
|
||||
let alwaysZoomOut = this._getAlwaysZoomOut();
|
||||
if (alwaysZoomOut)
|
||||
return this.parent();
|
||||
|
||||
let child = this.actor.get_first_child();
|
||||
return child.get_theme_node().get_length('visible-width');
|
||||
}
|
||||
});
|
||||
|
||||
const DashSlider = new Lang.Class({
|
||||
Name: 'DashSlider',
|
||||
Extends: SlidingControl,
|
||||
|
||||
_init: function(dash) {
|
||||
this.parent({ slideDirection: SlideDirection.LEFT });
|
||||
|
||||
this._dash = dash;
|
||||
|
||||
// SlideLayout reads the actor's expand flags to decide
|
||||
// whether to allocate the natural size to its child, or the whole
|
||||
// available allocation
|
||||
this._dash.actor.x_expand = true;
|
||||
this._dash.actor.y_expand = true;
|
||||
this.actor.add_actor(this._dash.actor);
|
||||
|
||||
this._dash.connect('icon-size-changed', Lang.bind(this, this.updateSlide));
|
||||
},
|
||||
|
||||
getSlide: function() {
|
||||
if (this.visible || this.inDrag)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
},
|
||||
|
||||
_onWindowDragBegin: function() {
|
||||
this.fadeHalf();
|
||||
},
|
||||
|
||||
_onWindowDragEnd: function() {
|
||||
this.fadeIn();
|
||||
}
|
||||
});
|
||||
|
||||
const DashSpacer = new Lang.Class({
|
||||
Name: 'DashSpacer',
|
||||
Extends: St.Widget,
|
||||
|
||||
_init: function(params) {
|
||||
this.parent(params);
|
||||
|
||||
this._bindConstraint = null;
|
||||
},
|
||||
|
||||
setDashActor: function(dashActor) {
|
||||
if (this._bindConstraint) {
|
||||
this.remove_constraint(this._bindConstraint);
|
||||
this._bindConstraint = null;
|
||||
}
|
||||
|
||||
if (dashActor) {
|
||||
this._bindConstraint = new Clutter.BindConstraint({ source: dashActor,
|
||||
coordinate: Clutter.BindCoordinate.SIZE });
|
||||
this.add_constraint(this._bindConstraint);
|
||||
}
|
||||
},
|
||||
|
||||
vfunc_get_preferred_width: function(forHeight) {
|
||||
let box = this.get_allocation_box();
|
||||
let minWidth = this.parent(forHeight)[0];
|
||||
let natWidth = box.x2 - box.x1;
|
||||
return [minWidth, natWidth];
|
||||
},
|
||||
|
||||
vfunc_get_preferred_height: function(forWidth) {
|
||||
let box = this.get_allocation_box();
|
||||
let minHeight = this.parent(forWidth)[0];
|
||||
let natHeight = box.y2 - box.y1;
|
||||
return [minHeight, natHeight];
|
||||
}
|
||||
});
|
||||
|
||||
const MessagesIndicator = new Lang.Class({
|
||||
Name: 'MessagesIndicator',
|
||||
|
||||
_init: function(viewSelector) {
|
||||
this._count = 0;
|
||||
this._sources = [];
|
||||
this._viewSelector = viewSelector;
|
||||
|
||||
this._container = new St.BoxLayout({ style_class: 'messages-indicator-contents',
|
||||
reactive: true,
|
||||
track_hover: true,
|
||||
x_expand: true,
|
||||
y_expand: true,
|
||||
x_align: Clutter.ActorAlign.CENTER });
|
||||
|
||||
this._icon = new St.Icon({ icon_name: 'user-idle-symbolic',
|
||||
icon_size: 16 });
|
||||
this._container.add_actor(this._icon);
|
||||
|
||||
this._label = new St.Label();
|
||||
this._container.add_actor(this._label);
|
||||
|
||||
this._highlight = new St.Widget({ style_class: 'messages-indicator-highlight',
|
||||
x_expand: true,
|
||||
y_expand: true,
|
||||
y_align: Clutter.ActorAlign.END,
|
||||
visible: false });
|
||||
|
||||
this._container.connect('notify::hover', Lang.bind(this,
|
||||
function() {
|
||||
this._highlight.visible = this._container.hover;
|
||||
}));
|
||||
|
||||
let clickAction = new Clutter.ClickAction();
|
||||
this._container.add_action(clickAction);
|
||||
clickAction.connect('clicked', Lang.bind(this,
|
||||
function() {
|
||||
Main.messageTray.openTray();
|
||||
}));
|
||||
|
||||
Main.messageTray.connect('showing', Lang.bind(this,
|
||||
function() {
|
||||
this._highlight.visible = false;
|
||||
this._container.hover = false;
|
||||
}));
|
||||
|
||||
let layout = new Clutter.BinLayout();
|
||||
this.actor = new St.Widget({ layout_manager: layout,
|
||||
style_class: 'messages-indicator',
|
||||
y_expand: true,
|
||||
y_align: Clutter.ActorAlign.END,
|
||||
visible: false });
|
||||
this.actor.add_actor(this._container);
|
||||
this.actor.add_actor(this._highlight);
|
||||
|
||||
Main.messageTray.connect('source-added', Lang.bind(this, this._onSourceAdded));
|
||||
Main.messageTray.connect('source-removed', Lang.bind(this, this._onSourceRemoved));
|
||||
|
||||
let sources = Main.messageTray.getSources();
|
||||
sources.forEach(Lang.bind(this, function(source) { this._onSourceAdded(null, source); }));
|
||||
|
||||
this._viewSelector.connect('page-changed', Lang.bind(this, this._updateVisibility));
|
||||
Main.overview.connect('showing', Lang.bind(this, this._updateVisibility));
|
||||
},
|
||||
|
||||
_onSourceAdded: function(tray, source) {
|
||||
if (source.trayIcon)
|
||||
return;
|
||||
|
||||
if (source.isTransient)
|
||||
return;
|
||||
|
||||
source.connect('count-updated', Lang.bind(this, this._updateCount));
|
||||
this._sources.push(source);
|
||||
this._updateCount();
|
||||
},
|
||||
|
||||
_onSourceRemoved: function(tray, source) {
|
||||
this._sources.splice(this._sources.indexOf(source), 1);
|
||||
this._updateCount();
|
||||
},
|
||||
|
||||
_updateCount: function() {
|
||||
let count = 0;
|
||||
let hasChats = false;
|
||||
this._sources.forEach(Lang.bind(this,
|
||||
function(source) {
|
||||
count += source.indicatorCount;
|
||||
hasChats |= source.isChat;
|
||||
}));
|
||||
|
||||
this._count = count;
|
||||
this._label.text = ngettext("%d new message",
|
||||
"%d new messages",
|
||||
count).format(count);
|
||||
|
||||
this._icon.visible = hasChats;
|
||||
this._updateVisibility();
|
||||
},
|
||||
|
||||
_updateVisibility: function() {
|
||||
let activePage = this._viewSelector.getActivePage();
|
||||
let visible = ((this._count > 0) && (activePage == ViewSelector.ViewPage.WINDOWS));
|
||||
|
||||
this.actor.visible = visible;
|
||||
}
|
||||
});
|
||||
|
||||
const ControlsManager = new Lang.Class({
|
||||
Name: 'ControlsManager',
|
||||
|
||||
_init: function(dash, thumbnails, viewSelector) {
|
||||
this._dashSlider = new DashSlider(dash);
|
||||
this.dashActor = this._dashSlider.actor;
|
||||
this.dashSpacer = new DashSpacer();
|
||||
this.dashSpacer.setDashActor(this.dashActor);
|
||||
|
||||
this._thumbnailsSlider = new ThumbnailsSlider(thumbnails);
|
||||
this.thumbnailsActor = this._thumbnailsSlider.actor;
|
||||
|
||||
this._indicator = new MessagesIndicator(viewSelector);
|
||||
this.indicatorActor = this._indicator.actor;
|
||||
|
||||
this._viewSelector = viewSelector;
|
||||
this._viewSelector.connect('page-changed', Lang.bind(this, this._setVisibility));
|
||||
this._viewSelector.connect('page-empty', Lang.bind(this, this._onPageEmpty));
|
||||
|
||||
Main.overview.connect('showing', Lang.bind(this, this._updateSpacerVisibility));
|
||||
Main.overview.connect('item-drag-begin', Lang.bind(this,
|
||||
function() {
|
||||
let activePage = this._viewSelector.getActivePage();
|
||||
if (activePage != ViewSelector.ViewPage.WINDOWS)
|
||||
this._viewSelector.fadeHalf();
|
||||
}));
|
||||
Main.overview.connect('item-drag-end', Lang.bind(this,
|
||||
function() {
|
||||
this._viewSelector.fadeIn();
|
||||
}));
|
||||
Main.overview.connect('item-drag-cancelled', Lang.bind(this,
|
||||
function() {
|
||||
this._viewSelector.fadeIn();
|
||||
}));
|
||||
},
|
||||
|
||||
_setVisibility: function() {
|
||||
// Ignore the case when we're leaving the overview, since
|
||||
// actors will be made visible again when entering the overview
|
||||
// next time, and animating them while doing so is just
|
||||
// unnecessary noise
|
||||
if (!Main.overview.visible ||
|
||||
(Main.overview.animationInProgress && !Main.overview.visibleTarget))
|
||||
return;
|
||||
|
||||
let activePage = this._viewSelector.getActivePage();
|
||||
let dashVisible = (activePage == ViewSelector.ViewPage.WINDOWS ||
|
||||
activePage == ViewSelector.ViewPage.APPS);
|
||||
let thumbnailsVisible = (activePage == ViewSelector.ViewPage.WINDOWS);
|
||||
|
||||
if (dashVisible)
|
||||
this._dashSlider.slideIn();
|
||||
else
|
||||
this._dashSlider.slideOut();
|
||||
|
||||
if (thumbnailsVisible)
|
||||
this._thumbnailsSlider.slideIn();
|
||||
else
|
||||
this._thumbnailsSlider.slideOut();
|
||||
},
|
||||
|
||||
_updateSpacerVisibility: function() {
|
||||
if (Main.overview.animationInProgress && !Main.overview.visibleTarget)
|
||||
return;
|
||||
|
||||
let activePage = this._viewSelector.getActivePage();
|
||||
this.dashSpacer.visible = (activePage == ViewSelector.ViewPage.WINDOWS);
|
||||
},
|
||||
|
||||
_onPageEmpty: function() {
|
||||
this._dashSlider.pageEmpty();
|
||||
this._thumbnailsSlider.pageEmpty();
|
||||
|
||||
this._updateSpacerVisibility();
|
||||
}
|
||||
});
|
150
js/ui/panel.js
150
js/ui/panel.js
@ -18,6 +18,7 @@ const Atk = imports.gi.Atk;
|
||||
const Config = imports.misc.config;
|
||||
const CtrlAltTab = imports.ui.ctrlAltTab;
|
||||
const DND = imports.ui.dnd;
|
||||
const Layout = imports.ui.layout;
|
||||
const Overview = imports.ui.overview;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
@ -202,7 +203,7 @@ const TextShadower = new Lang.Class({
|
||||
let child = children[i];
|
||||
let childBox = new Clutter.ActorBox();
|
||||
// The order of the labels here is arbitrary, except
|
||||
// we know the "real" label is at the end because Clutter.Actor
|
||||
// we know the "real" label is at the end because Clutter.Group
|
||||
// sorts by Z order
|
||||
switch (i) {
|
||||
case 0: // top
|
||||
@ -314,7 +315,7 @@ const AppMenuButton = new Lang.Class({
|
||||
},
|
||||
|
||||
show: function() {
|
||||
if (this._visible)
|
||||
if (this._visible || Main.screenShield.locked)
|
||||
return;
|
||||
|
||||
this._visible = true;
|
||||
@ -597,11 +598,6 @@ const AppMenuButton = new Lang.Class({
|
||||
return;
|
||||
|
||||
menu = new PopupMenu.RemoteMenu(this.actor, this._targetApp.menu, this._targetApp.action_group);
|
||||
menu.connect('activate', Lang.bind(this, function() {
|
||||
let win = this._targetApp.get_windows()[0];
|
||||
win.check_alive(global.get_current_time());
|
||||
}));
|
||||
|
||||
} else {
|
||||
if (this.menu.isDummyQuitMenu)
|
||||
return;
|
||||
@ -629,15 +625,23 @@ const ActivitiesButton = new Lang.Class({
|
||||
this.parent(0.0, null, true);
|
||||
this.actor.accessible_role = Atk.Role.TOGGLE_BUTTON;
|
||||
|
||||
let container = new Shell.GenericContainer();
|
||||
container.connect('get-preferred-width', Lang.bind(this, this._containerGetPreferredWidth));
|
||||
container.connect('get-preferred-height', Lang.bind(this, this._containerGetPreferredHeight));
|
||||
container.connect('allocate', Lang.bind(this, this._containerAllocate));
|
||||
this.actor.add_actor(container);
|
||||
this.actor.name = 'panelActivities';
|
||||
|
||||
/* Translators: If there is no suitable word for "Activities"
|
||||
in your language, you can use the word for "Overview". */
|
||||
this._label = new St.Label({ text: _("Activities") });
|
||||
this.actor.add_actor(this._label);
|
||||
container.add_actor(this._label);
|
||||
|
||||
this.actor.label_actor = this._label;
|
||||
|
||||
this.hotCorner = new Layout.HotCorner();
|
||||
container.add_actor(this.hotCorner.actor);
|
||||
|
||||
this.actor.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
|
||||
this.actor.connect_after('button-release-event', Lang.bind(this, this._onButtonRelease));
|
||||
this.actor.connect_after('key-release-event', Lang.bind(this, this._onKeyRelease));
|
||||
@ -652,6 +656,44 @@ const ActivitiesButton = new Lang.Class({
|
||||
}));
|
||||
|
||||
this._xdndTimeOut = 0;
|
||||
|
||||
// Since the hot corner uses stage coordinates, Clutter won't
|
||||
// queue relayouts for us when the panel moves. Queue a relayout
|
||||
// when that happens.
|
||||
Main.layoutManager.connect('panel-box-changed', Lang.bind(this, function() {
|
||||
container.queue_relayout();
|
||||
}));
|
||||
},
|
||||
|
||||
_containerGetPreferredWidth: function(actor, forHeight, alloc) {
|
||||
[alloc.min_size, alloc.natural_size] = this._label.get_preferred_width(forHeight);
|
||||
},
|
||||
|
||||
_containerGetPreferredHeight: function(actor, forWidth, alloc) {
|
||||
[alloc.min_size, alloc.natural_size] = this._label.get_preferred_height(forWidth);
|
||||
},
|
||||
|
||||
_containerAllocate: function(actor, box, flags) {
|
||||
this._label.allocate(box, flags);
|
||||
|
||||
// The hot corner needs to be outside any padding/alignment
|
||||
// that has been imposed on us
|
||||
let primary = Main.layoutManager.primaryMonitor;
|
||||
let hotBox = new Clutter.ActorBox();
|
||||
let ok, x, y;
|
||||
if (actor.get_text_direction() == Clutter.TextDirection.LTR) {
|
||||
[ok, x, y] = actor.transform_stage_point(primary.x, primary.y)
|
||||
} else {
|
||||
[ok, x, y] = actor.transform_stage_point(primary.x + primary.width, primary.y);
|
||||
// hotCorner.actor has northeast gravity, so we don't need
|
||||
// to adjust x for its width
|
||||
}
|
||||
|
||||
hotBox.x1 = Math.round(x);
|
||||
hotBox.x2 = hotBox.x1 + this.hotCorner.actor.width;
|
||||
hotBox.y1 = Math.round(y);
|
||||
hotBox.y2 = hotBox.y1 + this.hotCorner.actor.height;
|
||||
this.hotCorner.actor.allocate(hotBox, flags);
|
||||
},
|
||||
|
||||
handleDragOver: function(source, actor, x, y, time) {
|
||||
@ -661,14 +703,14 @@ const ActivitiesButton = new Lang.Class({
|
||||
if (this._xdndTimeOut != 0)
|
||||
Mainloop.source_remove(this._xdndTimeOut);
|
||||
this._xdndTimeOut = Mainloop.timeout_add(BUTTON_DND_ACTIVATION_TIMEOUT,
|
||||
Lang.bind(this, this._xdndToggleOverview, actor));
|
||||
Lang.bind(this, this._xdndShowOverview, actor));
|
||||
|
||||
return DND.DragMotionResult.CONTINUE;
|
||||
},
|
||||
|
||||
_onCapturedEvent: function(actor, event) {
|
||||
if (event.type() == Clutter.EventType.BUTTON_PRESS) {
|
||||
if (!Main.overview.shouldToggleByCornerOrButton())
|
||||
if (!this.hotCorner.shouldToggleOverviewOnClick())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -685,12 +727,16 @@ const ActivitiesButton = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
_xdndToggleOverview: function(actor) {
|
||||
_xdndShowOverview: function(actor) {
|
||||
let [x, y, mask] = global.get_pointer();
|
||||
let pickedActor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y);
|
||||
|
||||
if (pickedActor == this.actor && Main.overview.shouldToggleByCornerOrButton())
|
||||
Main.overview.toggle();
|
||||
if (pickedActor == this.actor) {
|
||||
if (!Main.overview.visible && !Main.overview.animationInProgress) {
|
||||
Main.overview.showTemporarily();
|
||||
Main.overview.beginItemDrag(actor);
|
||||
}
|
||||
}
|
||||
|
||||
Mainloop.source_remove(this._xdndTimeOut);
|
||||
this._xdndTimeOut = 0;
|
||||
@ -700,9 +746,12 @@ const ActivitiesButton = new Lang.Class({
|
||||
const PanelCorner = new Lang.Class({
|
||||
Name: 'PanelCorner',
|
||||
|
||||
_init: function(side) {
|
||||
_init: function(box, side) {
|
||||
this._side = side;
|
||||
|
||||
this._box = box;
|
||||
this._box.connect('style-changed', Lang.bind(this, this._boxStyleChanged));
|
||||
|
||||
this.actor = new St.DrawingArea({ style_class: 'panel-corner' });
|
||||
this.actor.connect('style-changed', Lang.bind(this, this._styleChanged));
|
||||
this.actor.connect('repaint', Lang.bind(this, this._repaint));
|
||||
@ -758,12 +807,12 @@ const PanelCorner = new Lang.Class({
|
||||
return children[index];
|
||||
},
|
||||
|
||||
setStyleParent: function(box) {
|
||||
_boxStyleChanged: function() {
|
||||
let side = this._side;
|
||||
|
||||
let rtlAwareContainer = box instanceof St.BoxLayout;
|
||||
let rtlAwareContainer = this._box instanceof St.BoxLayout;
|
||||
if (rtlAwareContainer &&
|
||||
box.get_text_direction() == Clutter.TextDirection.RTL) {
|
||||
this._box.get_text_direction() == Clutter.TextDirection.RTL) {
|
||||
if (this._side == St.Side.LEFT)
|
||||
side = St.Side.RIGHT;
|
||||
else if (this._side == St.Side.RIGHT)
|
||||
@ -772,9 +821,9 @@ const PanelCorner = new Lang.Class({
|
||||
|
||||
let button;
|
||||
if (side == St.Side.LEFT)
|
||||
button = this._findLeftmostButton(box);
|
||||
button = this._findLeftmostButton(this._box);
|
||||
else if (side == St.Side.RIGHT)
|
||||
button = this._findRightmostButton(box);
|
||||
button = this._findRightmostButton(this._box);
|
||||
|
||||
if (button) {
|
||||
if (this._button && this._buttonStyleChangedSignalId) {
|
||||
@ -801,7 +850,7 @@ const PanelCorner = new Lang.Class({
|
||||
|
||||
// The corner doesn't support theme transitions, so override
|
||||
// the .panel-button default
|
||||
button.style = 'transition-duration: 0ms';
|
||||
button.style = 'transition-duration: 0';
|
||||
}
|
||||
},
|
||||
|
||||
@ -814,8 +863,8 @@ const PanelCorner = new Lang.Class({
|
||||
let backgroundColor = node.get_color('-panel-corner-background-color');
|
||||
let borderColor = node.get_color('-panel-corner-border-color');
|
||||
|
||||
let overlap = borderColor.alpha != 0;
|
||||
let offsetY = overlap ? 0 : borderWidth;
|
||||
let noOverlap = borderColor.alpha == 0;
|
||||
let offsetY = noOverlap ? borderWidth : 0;
|
||||
|
||||
let cr = this.actor.get_context();
|
||||
cr.setOperator(Cairo.Operator.SOURCE);
|
||||
@ -839,18 +888,17 @@ const PanelCorner = new Lang.Class({
|
||||
Clutter.cairo_set_source_color(cr, over);
|
||||
cr.fill();
|
||||
|
||||
if (overlap) {
|
||||
let offset = borderWidth;
|
||||
Clutter.cairo_set_source_color(cr, backgroundColor);
|
||||
if (noOverlap)
|
||||
return;
|
||||
|
||||
cr.save();
|
||||
cr.translate(xOffsetDirection * offset, - offset);
|
||||
cr.appendPath(savedPath);
|
||||
cr.fill();
|
||||
cr.restore();
|
||||
}
|
||||
let offset = borderWidth;
|
||||
Clutter.cairo_set_source_color(cr, backgroundColor);
|
||||
|
||||
cr.$dispose();
|
||||
cr.save();
|
||||
cr.translate(xOffsetDirection * offset, - offset);
|
||||
cr.appendPath(savedPath);
|
||||
cr.fill();
|
||||
cr.restore();
|
||||
},
|
||||
|
||||
_styleChanged: function() {
|
||||
@ -869,7 +917,6 @@ const PANEL_ITEM_IMPLEMENTATIONS = {
|
||||
'appMenu': AppMenuButton,
|
||||
'dateMenu': imports.ui.dateMenu.DateMenuButton,
|
||||
'a11y': imports.ui.status.accessibility.ATIndicator,
|
||||
'a11yGreeter': imports.ui.status.accessibility.ATGreeterIndicator,
|
||||
'volume': imports.ui.status.volume.Indicator,
|
||||
'battery': imports.ui.status.power.Indicator,
|
||||
'lockScreen': imports.ui.status.lockScreenMenu.Indicator,
|
||||
@ -911,10 +958,17 @@ const Panel = new Lang.Class({
|
||||
this._rightBox = new St.BoxLayout({ name: 'panelRight' });
|
||||
this.actor.add_actor(this._rightBox);
|
||||
|
||||
this._leftCorner = new PanelCorner(St.Side.LEFT);
|
||||
if (this.actor.get_text_direction() == Clutter.TextDirection.RTL)
|
||||
this._leftCorner = new PanelCorner(this._rightBox, St.Side.LEFT);
|
||||
else
|
||||
this._leftCorner = new PanelCorner(this._leftBox, St.Side.LEFT);
|
||||
|
||||
this.actor.add_actor(this._leftCorner.actor);
|
||||
|
||||
this._rightCorner = new PanelCorner(St.Side.RIGHT);
|
||||
if (this.actor.get_text_direction() == Clutter.TextDirection.RTL)
|
||||
this._rightCorner = new PanelCorner(this._leftBox, St.Side.RIGHT);
|
||||
else
|
||||
this._rightCorner = new PanelCorner(this._rightBox, St.Side.RIGHT);
|
||||
this.actor.add_actor(this._rightCorner.actor);
|
||||
|
||||
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
||||
@ -922,13 +976,6 @@ const Panel = new Lang.Class({
|
||||
this.actor.connect('allocate', Lang.bind(this, this._allocate));
|
||||
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
|
||||
|
||||
Main.overview.connect('showing', Lang.bind(this, function () {
|
||||
this.actor.add_style_pseudo_class('overview');
|
||||
}));
|
||||
Main.overview.connect('hiding', Lang.bind(this, function () {
|
||||
this.actor.remove_style_pseudo_class('overview');
|
||||
}));
|
||||
|
||||
Main.layoutManager.panelBox.add(this.actor);
|
||||
Main.ctrlAltTabManager.addGroup(this.actor, _("Top Bar"), 'emblem-system-symbolic',
|
||||
{ sortGroup: CtrlAltTab.SortGroup.TOP });
|
||||
@ -1015,9 +1062,6 @@ const Panel = new Lang.Class({
|
||||
},
|
||||
|
||||
_onButtonPress: function(actor, event) {
|
||||
if (Main.modalCount > 0)
|
||||
return false;
|
||||
|
||||
if (event.get_source() != actor)
|
||||
return false;
|
||||
|
||||
@ -1097,13 +1141,17 @@ const Panel = new Lang.Class({
|
||||
this._sessionStyle = Main.sessionMode.panelStyle;
|
||||
if (this._sessionStyle)
|
||||
this._addStyleClassName(this._sessionStyle);
|
||||
},
|
||||
|
||||
if (this.actor.get_text_direction() == Clutter.TextDirection.RTL) {
|
||||
this._leftCorner.setStyleParent(this._rightBox);
|
||||
this._rightCorner.setStyleParent(this._leftBox);
|
||||
} else {
|
||||
this._leftCorner.setStyleParent(this._leftBox);
|
||||
this._rightCorner.setStyleParent(this._rightBox);
|
||||
_initBox: function(elements, box) {
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
let role = elements[i];
|
||||
let constructor = PANEL_ITEM_IMPLEMENTATIONS[role];
|
||||
if (!constructor) {
|
||||
// panel icon is not supported (can happen for
|
||||
// bluetooth or network)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -205,8 +205,10 @@ const Button = new Lang.Class({
|
||||
// Setting the max-height won't do any good if the minimum height of the
|
||||
// menu is higher then the screen; it's useful if part of the menu is
|
||||
// scrollable so the minimum height is smaller than the natural height
|
||||
let workArea = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex);
|
||||
this.menu.actor.style = ('max-height: ' + Math.round(workArea.height) + 'px;');
|
||||
let monitor = Main.layoutManager.primaryMonitor;
|
||||
this.menu.actor.style = ('max-height: ' +
|
||||
Math.round(monitor.height - Main.panel.actor.height) +
|
||||
'px;');
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
@ -242,21 +244,18 @@ const SystemStatusButton = new Lang.Class({
|
||||
this.setIcon(iconName);
|
||||
},
|
||||
|
||||
get icons() {
|
||||
return this._box.get_children();
|
||||
},
|
||||
|
||||
addIcon: function(gicon) {
|
||||
let icon = new St.Icon({ gicon: gicon,
|
||||
style_class: 'system-status-icon' });
|
||||
this._box.add_actor(icon);
|
||||
|
||||
this.emit('icons-changed');
|
||||
|
||||
return icon;
|
||||
},
|
||||
|
||||
setIcon: function(iconName) {
|
||||
// Need to first add a NULL GIcon and then set icon_name, to ensure
|
||||
// compatibility with -symbolic fallbacks
|
||||
|
||||
if (!this.mainIcon)
|
||||
this.mainIcon = this.addIcon(null);
|
||||
this.mainIcon.icon_name = iconName;
|
||||
|
@ -41,9 +41,10 @@ const PointerWatcher = new Lang.Class({
|
||||
Name: 'PointerWatcher',
|
||||
|
||||
_init: function() {
|
||||
this._idleMonitor = new GnomeDesktop.IdleMonitor();
|
||||
this._idleMonitor.add_idle_watch(IDLE_TIME, Lang.bind(this, this._onIdleMonitorBecameIdle));
|
||||
this._idle = this._idleMonitor.get_idletime() > IDLE_TIME;
|
||||
let idleMonitor = new GnomeDesktop.IdleMonitor();
|
||||
idleMonitor.connect('became-active', Lang.bind(this, this._onIdleMonitorBecameActive));
|
||||
idleMonitor.add_watch(IDLE_TIME, Lang.bind(this, this._onIdleMonitorBecameIdle));
|
||||
this._idle = idleMonitor.get_idletime() > IDLE_TIME;
|
||||
this._watches = [];
|
||||
this.pointerX = null;
|
||||
this.pointerY = null;
|
||||
@ -86,7 +87,6 @@ const PointerWatcher = new Lang.Class({
|
||||
|
||||
_onIdleMonitorBecameIdle: function(monitor) {
|
||||
this._idle = true;
|
||||
this._idleMonitor.add_user_active_watch(Lang.bind(this, this._onIdleMonitorBecameActive));
|
||||
this._updateTimeout();
|
||||
},
|
||||
|
||||
|
@ -207,7 +207,6 @@ const PopupBaseMenuItem = new Lang.Class({
|
||||
color.alpha / 255);
|
||||
cr.arc(width / 2, height / 2, width / 3, 0, 2 * Math.PI);
|
||||
cr.fill();
|
||||
cr.$dispose();
|
||||
},
|
||||
|
||||
// This returns column widths in logical order (i.e. from the dot
|
||||
@ -605,7 +604,6 @@ const PopupSliderMenuItem = new Lang.Class({
|
||||
color.alpha / 255);
|
||||
cr.arc(handleX, handleY, handleRadius, 0, 2 * Math.PI);
|
||||
cr.fill();
|
||||
cr.$dispose();
|
||||
},
|
||||
|
||||
_startDragging: function(actor, event) {
|
||||
@ -638,34 +636,20 @@ const PopupSliderMenuItem = new Lang.Class({
|
||||
return true;
|
||||
},
|
||||
|
||||
scroll: function(event) {
|
||||
_onScrollEvent: function (actor, event) {
|
||||
let direction = event.get_scroll_direction();
|
||||
let delta;
|
||||
|
||||
if (event.is_pointer_emulated())
|
||||
return;
|
||||
|
||||
if (direction == Clutter.ScrollDirection.DOWN) {
|
||||
delta = -SLIDER_SCROLL_STEP;
|
||||
} else if (direction == Clutter.ScrollDirection.UP) {
|
||||
delta = +SLIDER_SCROLL_STEP;
|
||||
} else if (direction == Clutter.ScrollDirection.SMOOTH) {
|
||||
let [dx, dy] = event.get_scroll_delta();
|
||||
// Even though the slider is horizontal, use dy to match
|
||||
// the UP/DOWN above.
|
||||
delta = -dy / 10;
|
||||
this._value = Math.max(0, this._value - SLIDER_SCROLL_STEP);
|
||||
}
|
||||
else if (direction == Clutter.ScrollDirection.UP) {
|
||||
this._value = Math.min(1, this._value + SLIDER_SCROLL_STEP);
|
||||
}
|
||||
|
||||
this._value = Math.min(Math.max(0, this._value + delta), 1);
|
||||
|
||||
this._slider.queue_repaint();
|
||||
this.emit('value-changed', this._value);
|
||||
},
|
||||
|
||||
_onScrollEvent: function(actor, event) {
|
||||
this.scroll(event);
|
||||
},
|
||||
|
||||
_motionEvent: function(actor, event) {
|
||||
let absX, absY;
|
||||
[absX, absY] = event.get_coords();
|
||||
@ -867,6 +851,7 @@ const PopupMenuBase = new Lang.Class({
|
||||
this.blockSourceEvents = false;
|
||||
|
||||
this._activeMenuItem = null;
|
||||
this._childMenus = [];
|
||||
this._settingsActions = { };
|
||||
|
||||
this._sessionUpdatedId = Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
|
||||
@ -888,7 +873,7 @@ const PopupMenuBase = new Lang.Class({
|
||||
|
||||
addSettingsAction: function(title, desktopFile) {
|
||||
let menuItem = this.addAction(title, function() {
|
||||
let app = Shell.AppSystem.get_default().lookup_app(desktopFile);
|
||||
let app = Shell.AppSystem.get_default().lookup_setting(desktopFile);
|
||||
|
||||
if (!app) {
|
||||
log('Settings panel for desktop file ' + desktopFile + ' could not be loaded!');
|
||||
@ -913,15 +898,29 @@ const PopupMenuBase = new Lang.Class({
|
||||
},
|
||||
|
||||
isEmpty: function() {
|
||||
let hasVisibleChildren = this.box.get_children().some(function(child) {
|
||||
return child.visible;
|
||||
});
|
||||
|
||||
return !hasVisibleChildren;
|
||||
return this.box.get_n_children() == 0;
|
||||
},
|
||||
|
||||
isChildMenu: function() {
|
||||
return false;
|
||||
isChildMenu: function(menu) {
|
||||
return this._childMenus.indexOf(menu) != -1;
|
||||
},
|
||||
|
||||
addChildMenu: function(menu) {
|
||||
if (this.isChildMenu(menu))
|
||||
return;
|
||||
|
||||
this._childMenus.push(menu);
|
||||
this.emit('child-menu-added', menu);
|
||||
},
|
||||
|
||||
removeChildMenu: function(menu) {
|
||||
let index = this._childMenus.indexOf(menu);
|
||||
|
||||
if (index == -1)
|
||||
return;
|
||||
|
||||
this._childMenus.splice(index, 1);
|
||||
this.emit('child-menu-removed', menu);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1196,8 +1195,6 @@ const PopupMenu = new Lang.Class({
|
||||
|
||||
global.focus_manager.add_group(this.actor);
|
||||
this.actor.reactive = true;
|
||||
|
||||
this._childMenus = [];
|
||||
},
|
||||
|
||||
_boxGetPreferredWidth: function (actor, forHeight, alloc) {
|
||||
@ -1224,28 +1221,6 @@ const PopupMenu = new Lang.Class({
|
||||
this._boxPointer.setSourceAlignment(alignment);
|
||||
},
|
||||
|
||||
isChildMenu: function(menu) {
|
||||
return this._childMenus.indexOf(menu) != -1;
|
||||
},
|
||||
|
||||
addChildMenu: function(menu) {
|
||||
if (this.isChildMenu(menu))
|
||||
return;
|
||||
|
||||
this._childMenus.push(menu);
|
||||
this.emit('child-menu-added', menu);
|
||||
},
|
||||
|
||||
removeChildMenu: function(menu) {
|
||||
let index = this._childMenus.indexOf(menu);
|
||||
|
||||
if (index == -1)
|
||||
return;
|
||||
|
||||
this._childMenus.splice(index, 1);
|
||||
this.emit('child-menu-removed', menu);
|
||||
},
|
||||
|
||||
open: function(animate) {
|
||||
if (this.isOpen)
|
||||
return;
|
||||
@ -1267,10 +1242,6 @@ const PopupMenu = new Lang.Class({
|
||||
if (this._activeMenuItem)
|
||||
this._activeMenuItem.setActive(false);
|
||||
|
||||
this._childMenus.forEach(function(childMenu) {
|
||||
childMenu.close();
|
||||
});
|
||||
|
||||
if (this._boxPointer.actor.visible)
|
||||
this._boxPointer.hide(animate);
|
||||
|
||||
@ -1691,7 +1662,9 @@ const PopupComboBoxMenuItem = new Lang.Class({
|
||||
_getTopMenu: function() {
|
||||
let actor = this.actor.get_parent();
|
||||
while (actor) {
|
||||
if (actor._delegate && actor._delegate instanceof PopupMenu)
|
||||
if (actor._delegate &&
|
||||
(actor._delegate instanceof PopupMenu ||
|
||||
actor._delegate instanceof PopupComboMenu))
|
||||
return actor._delegate;
|
||||
|
||||
actor = actor.get_parent();
|
||||
@ -2086,7 +2059,7 @@ const PopupMenuManager = new Lang.Class({
|
||||
},
|
||||
|
||||
removeMenu: function(menu) {
|
||||
if (menu == this.activeMenu)
|
||||
if (menu == this._activeMenu)
|
||||
this._closeMenu(menu);
|
||||
|
||||
let position = this._findMenu(menu);
|
||||
@ -2110,9 +2083,9 @@ const PopupMenuManager = new Lang.Class({
|
||||
},
|
||||
|
||||
get activeMenu() {
|
||||
let firstGrab = this._grabHelper.grabStack[0];
|
||||
if (firstGrab)
|
||||
return firstGrab.actor._delegate;
|
||||
let actor = this._grabHelper.currentGrab.actor;
|
||||
if (actor)
|
||||
return actor._delegate;
|
||||
else
|
||||
return null;
|
||||
},
|
||||
|
@ -1,11 +1,9 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const GdkPixbuf = imports.gi.GdkPixbuf;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const St = imports.gi.St;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
const FileUtils = imports.misc.fileUtils;
|
||||
const Search = imports.ui.search;
|
||||
@ -123,9 +121,7 @@ function loadRemoteSearchProvider(file, info, data) {
|
||||
function remoteProvidersLoaded(loadState) {
|
||||
let searchSettings = new Gio.Settings({ schema: Search.SEARCH_PROVIDERS_SCHEMA });
|
||||
let sortOrder = searchSettings.get_strv('sort-order');
|
||||
|
||||
// Special case gnome-control-center to be always active and always first
|
||||
sortOrder.unshift('gnome-control-center.desktop');
|
||||
let numSorted = sortOrder.length;
|
||||
|
||||
loadState.loadedProviders.sort(
|
||||
function(providerA, providerB) {
|
||||
@ -139,11 +135,16 @@ function remoteProvidersLoaded(loadState) {
|
||||
idxB = sortOrder.indexOf(appIdB);
|
||||
|
||||
// if no provider is found in the order, use alphabetical order
|
||||
if ((idxA == -1) && (idxB == -1)) {
|
||||
let nameA = providerA.appInfo.get_name();
|
||||
let nameB = providerB.appInfo.get_name();
|
||||
if ((idxA == -1) && (idxB == -1))
|
||||
return GLib.utf8_collate(providerA.title, providerB.title);
|
||||
|
||||
return GLib.utf8_collate(nameA, nameB);
|
||||
if (numSorted > 1) {
|
||||
// if providerA is the last, it goes after everything
|
||||
if ((idxA + 1) == numSorted)
|
||||
return 1;
|
||||
// if providerB is the last, it goes after everything
|
||||
else if ((idxB + 1) == numSorted)
|
||||
return -1;
|
||||
}
|
||||
|
||||
// if providerA isn't found, it's sorted after providerB
|
||||
@ -186,18 +187,18 @@ const RemoteSearchProvider = new Lang.Class({
|
||||
},
|
||||
|
||||
createIcon: function(size, meta) {
|
||||
let gicon;
|
||||
if (meta['gicon']) {
|
||||
gicon = Gio.icon_new_for_string(meta['gicon']);
|
||||
return new St.Icon({ gicon: Gio.icon_new_for_string(meta['gicon']),
|
||||
icon_size: size });
|
||||
} else if (meta['icon-data']) {
|
||||
let [width, height, rowStride, hasAlpha,
|
||||
bitsPerSample, nChannels, data] = meta['icon-data'];
|
||||
gicon = Shell.util_create_pixbuf_from_data(data, GdkPixbuf.Colorspace.RGB, hasAlpha,
|
||||
bitsPerSample, width, height, rowStride);
|
||||
let textureCache = St.TextureCache.get_default();
|
||||
return textureCache.load_from_raw(data, hasAlpha,
|
||||
width, height, rowStride, size);
|
||||
}
|
||||
|
||||
return new St.Icon({ gicon: gicon,
|
||||
icon_size: size });
|
||||
return null;
|
||||
},
|
||||
|
||||
_getResultsFinished: function(results, error) {
|
||||
@ -214,7 +215,7 @@ const RemoteSearchProvider = new Lang.Class({
|
||||
Lang.bind(this, this._getResultsFinished),
|
||||
this._cancellable);
|
||||
} catch(e) {
|
||||
log('Error calling GetInitialResultSet for provider %s: %s'.format(this.id, e.toString()));
|
||||
log('Error calling GetInitialResultSet for provider %s: %s'.format( this.title, e.toString()));
|
||||
this.searchSystem.pushResults(this, []);
|
||||
}
|
||||
},
|
||||
@ -227,7 +228,7 @@ const RemoteSearchProvider = new Lang.Class({
|
||||
Lang.bind(this, this._getResultsFinished),
|
||||
this._cancellable);
|
||||
} catch(e) {
|
||||
log('Error calling GetSubsearchResultSet for provider %s: %s'.format(this.id, e.toString()));
|
||||
log('Error calling GetSubsearchResultSet for provider %s: %s'.format(this.title, e.toString()));
|
||||
this.searchSystem.pushResults(this, []);
|
||||
}
|
||||
},
|
||||
@ -244,7 +245,6 @@ const RemoteSearchProvider = new Lang.Class({
|
||||
metas[i][prop] = metas[i][prop].deep_unpack();
|
||||
resultMetas.push({ id: metas[i]['id'],
|
||||
name: metas[i]['name'],
|
||||
description: metas[i]['description'],
|
||||
createIcon: Lang.bind(this,
|
||||
this.createIcon, metas[i]) });
|
||||
}
|
||||
@ -259,7 +259,7 @@ const RemoteSearchProvider = new Lang.Class({
|
||||
Lang.bind(this, this._getResultMetasFinished, callback),
|
||||
this._cancellable);
|
||||
} catch(e) {
|
||||
log('Error calling GetResultMetas for provider %s: %s'.format(this.id, e.toString()));
|
||||
log('Error calling GetResultMetas for provider %s: %s'.format(this.title, e.toString()));
|
||||
callback([]);
|
||||
}
|
||||
},
|
||||
|
@ -30,6 +30,138 @@ const EXEC_ARG_KEY = 'exec-arg';
|
||||
|
||||
const DIALOG_GROW_TIME = 0.1;
|
||||
|
||||
const CommandCompleter = new Lang.Class({
|
||||
Name: 'CommandCompleter',
|
||||
|
||||
_init : function() {
|
||||
this._changedCount = 0;
|
||||
this._paths = GLib.getenv('PATH').split(':');
|
||||
this._paths.push(GLib.get_home_dir());
|
||||
this._valid = false;
|
||||
this._updateInProgress = false;
|
||||
this._childs = new Array(this._paths.length);
|
||||
this._monitors = new Array(this._paths.length);
|
||||
for (let i = 0; i < this._paths.length; i++) {
|
||||
this._childs[i] = [];
|
||||
let file = Gio.file_new_for_path(this._paths[i]);
|
||||
let info;
|
||||
try {
|
||||
info = file.query_info(Gio.FILE_ATTRIBUTE_STANDARD_TYPE, Gio.FileQueryInfoFlags.NONE, null);
|
||||
} catch (e) {
|
||||
// FIXME catchall
|
||||
this._paths[i] = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (info.get_attribute_uint32(Gio.FILE_ATTRIBUTE_STANDARD_TYPE) != Gio.FileType.DIRECTORY)
|
||||
continue;
|
||||
|
||||
this._paths[i] = file.get_path();
|
||||
this._monitors[i] = file.monitor_directory(Gio.FileMonitorFlags.NONE, null);
|
||||
if (this._monitors[i] != null) {
|
||||
this._monitors[i].connect('changed', Lang.bind(this, this._onChanged));
|
||||
}
|
||||
}
|
||||
this._paths = this._paths.filter(function(a) {
|
||||
return a != null;
|
||||
});
|
||||
this._update(0);
|
||||
},
|
||||
|
||||
update : function() {
|
||||
if (this._valid)
|
||||
return;
|
||||
this._update(0);
|
||||
},
|
||||
|
||||
_update : function(i) {
|
||||
if (i == 0 && this._updateInProgress)
|
||||
return;
|
||||
this._updateInProgress = true;
|
||||
this._changedCount = 0;
|
||||
this._i = i;
|
||||
if (i >= this._paths.length) {
|
||||
this._valid = true;
|
||||
this._updateInProgress = false;
|
||||
return;
|
||||
}
|
||||
let file = Gio.file_new_for_path(this._paths[i]);
|
||||
this._childs[this._i] = [];
|
||||
FileUtils.listDirAsync(file, Lang.bind(this, function (files) {
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
this._childs[this._i].push(files[i].get_name());
|
||||
}
|
||||
this._update(this._i + 1);
|
||||
}));
|
||||
},
|
||||
|
||||
_onChanged : function(m, f, of, type) {
|
||||
if (!this._valid)
|
||||
return;
|
||||
let path = f.get_parent().get_path();
|
||||
let k = undefined;
|
||||
for (let i = 0; i < this._paths.length; i++) {
|
||||
if (this._paths[i] == path)
|
||||
k = i;
|
||||
}
|
||||
if (k === undefined) {
|
||||
return;
|
||||
}
|
||||
if (type == Gio.FileMonitorEvent.CREATED) {
|
||||
this._childs[k].push(f.get_basename());
|
||||
}
|
||||
if (type == Gio.FileMonitorEvent.DELETED) {
|
||||
this._changedCount++;
|
||||
if (this._changedCount > MAX_FILE_DELETED_BEFORE_INVALID) {
|
||||
this._valid = false;
|
||||
}
|
||||
let name = f.get_basename();
|
||||
this._childs[k] = this._childs[k].filter(function(e) {
|
||||
return e != name;
|
||||
});
|
||||
}
|
||||
if (type == Gio.FileMonitorEvent.UNMOUNTED) {
|
||||
this._childs[k] = [];
|
||||
}
|
||||
},
|
||||
|
||||
getCompletion: function(text) {
|
||||
let common = '';
|
||||
let notInit = true;
|
||||
if (!this._valid) {
|
||||
this._update(0);
|
||||
return common;
|
||||
}
|
||||
function _getCommon(s1, s2) {
|
||||
let k = 0;
|
||||
for (; k < s1.length && k < s2.length; k++) {
|
||||
if (s1[k] != s2[k])
|
||||
break;
|
||||
}
|
||||
if (k == 0)
|
||||
return '';
|
||||
return s1.substr(0, k);
|
||||
}
|
||||
function _hasPrefix(s1, prefix) {
|
||||
return s1.indexOf(prefix) == 0;
|
||||
}
|
||||
for (let i = 0; i < this._childs.length; i++) {
|
||||
for (let k = 0; k < this._childs[i].length; k++) {
|
||||
if (!_hasPrefix(this._childs[i][k], text))
|
||||
continue;
|
||||
if (notInit) {
|
||||
common = this._childs[i][k];
|
||||
notInit = false;
|
||||
}
|
||||
common = _getCommon(common, this._childs[i][k]);
|
||||
}
|
||||
}
|
||||
if (common.length)
|
||||
return common.substr(text.length);
|
||||
return common;
|
||||
}
|
||||
});
|
||||
|
||||
const RunDialog = new Lang.Class({
|
||||
Name: 'RunDialog',
|
||||
Extends: ModalDialog.ModalDialog,
|
||||
@ -110,6 +242,8 @@ const RunDialog = new Lang.Class({
|
||||
key: Clutter.Escape }]);
|
||||
|
||||
this._pathCompleter = new Gio.FilenameCompleter();
|
||||
this._commandCompleter = new CommandCompleter();
|
||||
this._group.connect('notify::visible', Lang.bind(this._commandCompleter, this._commandCompleter.update));
|
||||
|
||||
this._history = new History.HistoryManager({ gsettingsKey: HISTORY_KEY,
|
||||
entry: this._entryText });
|
||||
@ -125,6 +259,18 @@ const RunDialog = new Lang.Class({
|
||||
|
||||
return true;
|
||||
}
|
||||
if (symbol == Clutter.slash) {
|
||||
// Need preload data before get completion. GFilenameCompleter load content of parent directory.
|
||||
// Parent directory for /usr/include/ is /usr/. So need to add fake name('a').
|
||||
let text = o.get_text().concat('/a');
|
||||
let prefix;
|
||||
if (text.lastIndexOf(' ') == -1)
|
||||
prefix = text;
|
||||
else
|
||||
prefix = text.substr(text.lastIndexOf(' ') + 1);
|
||||
this._getCompletion(prefix);
|
||||
return false;
|
||||
}
|
||||
if (symbol == Clutter.Tab) {
|
||||
let text = o.get_text();
|
||||
let prefix;
|
||||
@ -136,6 +282,8 @@ const RunDialog = new Lang.Class({
|
||||
if (postfix != null && postfix.length > 0) {
|
||||
o.insert_text(postfix, -1);
|
||||
o.set_cursor_position(text.length + postfix.length);
|
||||
if (postfix[postfix.length - 1] == '/')
|
||||
this._getCompletion(text + postfix + 'a');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -143,53 +291,11 @@ const RunDialog = new Lang.Class({
|
||||
}));
|
||||
},
|
||||
|
||||
_getCommandCompletion: function(text) {
|
||||
function _getCommon(s1, s2) {
|
||||
if (s1 == null)
|
||||
return s2;
|
||||
|
||||
let k = 0;
|
||||
for (; k < s1.length && k < s2.length; k++) {
|
||||
if (s1[k] != s2[k])
|
||||
break;
|
||||
}
|
||||
if (k == 0)
|
||||
return '';
|
||||
return s1.substr(0, k);
|
||||
}
|
||||
|
||||
let paths = GLib.getenv('PATH').split(':');
|
||||
paths.push(GLib.get_home_dir());
|
||||
let someResults = paths.map(function(path) {
|
||||
let results = [];
|
||||
try {
|
||||
let file = Gio.File.new_for_path(path);
|
||||
let fileEnum = file.enumerate_children('standard::name', Gio.FileQueryInfoFlags.NONE, null);
|
||||
let info;
|
||||
while ((info = fileEnum.next_file(null))) {
|
||||
let name = info.get_name();
|
||||
if (name.slice(0, text.length) == text)
|
||||
results.push(name);
|
||||
}
|
||||
} catch (e if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND) &&
|
||||
!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_DIRECTORY))) {
|
||||
log(e);
|
||||
} finally {
|
||||
return results;
|
||||
}
|
||||
});
|
||||
let results = someResults.reduce(function(a, b) {
|
||||
return a.concat(b);
|
||||
}, []);
|
||||
let common = results.reduce(_getCommon, null);
|
||||
return common.substr(text.length);
|
||||
},
|
||||
|
||||
_getCompletion : function(text) {
|
||||
if (text.indexOf('/') != -1) {
|
||||
return this._pathCompleter.get_completion_suffix(text);
|
||||
} else {
|
||||
return this._getCommandCompletion(text);
|
||||
return this._commandCompleter.getCompletion(text);
|
||||
}
|
||||
},
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,277 +0,0 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gdk = imports.gi.Gdk;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Lang = imports.lang;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Lightbox = imports.ui.lightbox;
|
||||
const Main = imports.ui.main;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const ScreenshotIface = <interface name="org.gnome.Shell.Screenshot">
|
||||
<method name="ScreenshotArea">
|
||||
<arg type="i" direction="in" name="x"/>
|
||||
<arg type="i" direction="in" name="y"/>
|
||||
<arg type="i" direction="in" name="width"/>
|
||||
<arg type="i" direction="in" name="height"/>
|
||||
<arg type="b" direction="in" name="flash"/>
|
||||
<arg type="s" direction="in" name="filename"/>
|
||||
<arg type="b" direction="out" name="success"/>
|
||||
<arg type="s" direction="out" name="filename_used"/>
|
||||
</method>
|
||||
<method name="ScreenshotWindow">
|
||||
<arg type="b" direction="in" name="include_frame"/>
|
||||
<arg type="b" direction="in" name="include_cursor"/>
|
||||
<arg type="b" direction="in" name="flash"/>
|
||||
<arg type="s" direction="in" name="filename"/>
|
||||
<arg type="b" direction="out" name="success"/>
|
||||
<arg type="s" direction="out" name="filename_used"/>
|
||||
</method>
|
||||
<method name="Screenshot">
|
||||
<arg type="b" direction="in" name="include_cursor"/>
|
||||
<arg type="b" direction="in" name="flash"/>
|
||||
<arg type="s" direction="in" name="filename"/>
|
||||
<arg type="b" direction="out" name="success"/>
|
||||
<arg type="s" direction="out" name="filename_used"/>
|
||||
</method>
|
||||
<method name="SelectArea">
|
||||
<arg type="i" direction="out" name="x"/>
|
||||
<arg type="i" direction="out" name="y"/>
|
||||
<arg type="i" direction="out" name="width"/>
|
||||
<arg type="i" direction="out" name="height"/>
|
||||
</method>
|
||||
<method name="FlashArea">
|
||||
<arg type="i" direction="in" name="x"/>
|
||||
<arg type="i" direction="in" name="y"/>
|
||||
<arg type="i" direction="in" name="width"/>
|
||||
<arg type="i" direction="in" name="height"/>
|
||||
</method>
|
||||
</interface>;
|
||||
|
||||
const ScreenshotService = new Lang.Class({
|
||||
Name: 'ScreenshotService',
|
||||
|
||||
_init: function() {
|
||||
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(ScreenshotIface, this);
|
||||
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell/Screenshot');
|
||||
|
||||
Gio.DBus.session.own_name('org.gnome.Shell.Screenshot', Gio.BusNameOwnerFlags.REPLACE, null, null);
|
||||
},
|
||||
|
||||
_onScreenshotComplete: function(obj, result, area, filenameUsed, flash, invocation) {
|
||||
if (flash && result) {
|
||||
let flashspot = new Flashspot(area);
|
||||
flashspot.fire();
|
||||
}
|
||||
|
||||
let retval = GLib.Variant.new('(bs)', [result, filenameUsed]);
|
||||
invocation.return_value(retval);
|
||||
},
|
||||
|
||||
ScreenshotAreaAsync : function (params, invocation) {
|
||||
let [x, y, width, height, flash, filename, callback] = params;
|
||||
if (height <= 0 || width <= 0) {
|
||||
invocation.return_error_literal(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED,
|
||||
"Invalid params");
|
||||
return;
|
||||
}
|
||||
let screenshot = new Shell.Screenshot();
|
||||
screenshot.screenshot_area (x, y, width, height, filename,
|
||||
Lang.bind(this, this._onScreenshotComplete,
|
||||
flash, invocation));
|
||||
},
|
||||
|
||||
ScreenshotWindowAsync : function (params, invocation) {
|
||||
let [include_frame, include_cursor, flash, filename] = params;
|
||||
let screenshot = new Shell.Screenshot();
|
||||
screenshot.screenshot_window (include_frame, include_cursor, filename,
|
||||
Lang.bind(this, this._onScreenshotComplete,
|
||||
flash, invocation));
|
||||
},
|
||||
|
||||
ScreenshotAsync : function (params, invocation) {
|
||||
let [include_cursor, flash, filename] = params;
|
||||
let screenshot = new Shell.Screenshot();
|
||||
screenshot.screenshot(include_cursor, filename,
|
||||
Lang.bind(this, this._onScreenshotComplete,
|
||||
flash, invocation));
|
||||
},
|
||||
|
||||
SelectAreaAsync: function (params, invocation) {
|
||||
let selectArea = new SelectArea();
|
||||
selectArea.show();
|
||||
selectArea.connect('finished', Lang.bind(this,
|
||||
function(selectArea, areaRectangle) {
|
||||
if (areaRectangle) {
|
||||
let retval = GLib.Variant.new('(iiii)',
|
||||
[areaRectangle.x, areaRectangle.y,
|
||||
areaRectangle.width, areaRectangle.height]);
|
||||
invocation.return_value(retval);
|
||||
} else {
|
||||
invocation.return_error_literal(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED,
|
||||
"Operation was cancelled");
|
||||
}
|
||||
}));
|
||||
},
|
||||
|
||||
FlashArea: function(x, y, width, height) {
|
||||
let flashspot = new Flashspot({ x : x, y : y, width: width, height: height});
|
||||
flashspot.fire();
|
||||
}
|
||||
});
|
||||
|
||||
const SelectArea = new Lang.Class({
|
||||
Name: 'SelectArea',
|
||||
|
||||
_init: function() {
|
||||
this._startX = -1;
|
||||
this._startY = -1;
|
||||
this._lastX = 0;
|
||||
this._lastY = 0;
|
||||
|
||||
this._initRubberbandColors();
|
||||
|
||||
this._group = new St.Widget({ visible: false,
|
||||
reactive: true,
|
||||
x: 0,
|
||||
y: 0 });
|
||||
Main.uiGroup.add_actor(this._group);
|
||||
|
||||
this._group.connect('button-press-event',
|
||||
Lang.bind(this, this._onButtonPress));
|
||||
this._group.connect('button-release-event',
|
||||
Lang.bind(this, this._onButtonRelease));
|
||||
this._group.connect('key-press-event',
|
||||
Lang.bind(this, this._onKeyPress));
|
||||
this._group.connect('motion-event',
|
||||
Lang.bind(this, this._onMotionEvent));
|
||||
|
||||
let constraint = new Clutter.BindConstraint({ source: global.stage,
|
||||
coordinate: Clutter.BindCoordinate.ALL });
|
||||
this._group.add_constraint(constraint);
|
||||
|
||||
this._rubberband = new Clutter.Rectangle({ color: this._background,
|
||||
has_border: true,
|
||||
border_width: 1,
|
||||
border_color: this._border });
|
||||
this._group.add_actor(this._rubberband);
|
||||
},
|
||||
|
||||
show: function() {
|
||||
if (!Main.pushModal(this._group) || this._group.visible)
|
||||
return;
|
||||
|
||||
global.set_cursor(Shell.Cursor.CROSSHAIR);
|
||||
this._group.visible = true;
|
||||
},
|
||||
|
||||
_initRubberbandColors: function() {
|
||||
function colorFromRGBA(rgba) {
|
||||
return new Clutter.Color({ red: rgba.red * 255,
|
||||
green: rgba.green * 255,
|
||||
blue: rgba.blue * 255,
|
||||
alpha: rgba.alpha * 255 });
|
||||
}
|
||||
|
||||
let path = new Gtk.WidgetPath();
|
||||
path.append_type(Gtk.IconView);
|
||||
|
||||
let context = new Gtk.StyleContext();
|
||||
context.set_path(path);
|
||||
context.add_class('rubberband');
|
||||
|
||||
this._background = colorFromRGBA(context.get_background_color(Gtk.StateFlags.NORMAL));
|
||||
this._border = colorFromRGBA(context.get_border_color(Gtk.StateFlags.NORMAL));
|
||||
},
|
||||
|
||||
_getGeometry: function() {
|
||||
return { x: Math.min(this._startX, this._lastX),
|
||||
y: Math.min(this._startY, this._lastY),
|
||||
width: Math.abs(this._startX - this._lastX),
|
||||
height: Math.abs(this._startY - this._lastY) };
|
||||
},
|
||||
|
||||
_onKeyPress: function(actor, event) {
|
||||
if (event.get_key_symbol() == Clutter.Escape)
|
||||
this._destroy(null, false);
|
||||
|
||||
return;
|
||||
},
|
||||
|
||||
_onMotionEvent: function(actor, event) {
|
||||
if (this._startX == -1 || this._startY == -1)
|
||||
return false;
|
||||
|
||||
[this._lastX, this._lastY] = event.get_coords();
|
||||
let geometry = this._getGeometry();
|
||||
|
||||
this._rubberband.set_position(geometry.x, geometry.y);
|
||||
this._rubberband.set_size(geometry.width, geometry.height);
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
_onButtonPress: function(actor, event) {
|
||||
[this._startX, this._startY] = event.get_coords();
|
||||
this._rubberband.set_position(this._startX, this._startY);
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
_onButtonRelease: function(actor, event) {
|
||||
this._destroy(this._getGeometry(), true);
|
||||
return false;
|
||||
},
|
||||
|
||||
_destroy: function(geometry, fade) {
|
||||
Tweener.addTween(this._group,
|
||||
{ opacity: 0,
|
||||
time: fade ? 0.2 : 0,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this,
|
||||
function() {
|
||||
Main.popModal(this._group);
|
||||
this._group.destroy();
|
||||
global.unset_cursor();
|
||||
|
||||
this.emit('finished', geometry);
|
||||
})
|
||||
});
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(SelectArea.prototype);
|
||||
|
||||
const FLASHSPOT_ANIMATION_OUT_TIME = 0.5; // seconds
|
||||
|
||||
const Flashspot = new Lang.Class({
|
||||
Name: 'Flashspot',
|
||||
Extends: Lightbox.Lightbox,
|
||||
|
||||
_init: function(area) {
|
||||
this.parent(Main.uiGroup, { inhibitEvents: true,
|
||||
width: area.width,
|
||||
height: area.height });
|
||||
|
||||
this.actor.style_class = 'flashspot';
|
||||
this.actor.set_position(area.x, area.y);
|
||||
},
|
||||
|
||||
fire: function() {
|
||||
this.actor.show();
|
||||
this.actor.opacity = 255;
|
||||
Tweener.addTween(this.actor,
|
||||
{ opacity: 0,
|
||||
time: FLASHSPOT_ANIMATION_OUT_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this, function() {
|
||||
this.destroy();
|
||||
})
|
||||
});
|
||||
}
|
||||
});
|
@ -60,18 +60,28 @@ const SearchSystem = new Lang.Class({
|
||||
this.emit('search-updated', this._previousResults[i]);
|
||||
},
|
||||
|
||||
updateSearch: function(searchString) {
|
||||
searchString = searchString.replace(/^\s+/g, '').replace(/\s+$/g, '');
|
||||
if (searchString == '')
|
||||
return;
|
||||
|
||||
let terms = searchString.split(/\s+/);
|
||||
this.updateSearchResults(terms);
|
||||
},
|
||||
|
||||
updateSearchResults: function(terms) {
|
||||
if (!terms)
|
||||
return;
|
||||
|
||||
let searchString = terms.join(' ');
|
||||
let previousSearchString = this._previousTerms.join(' ');
|
||||
if (searchString == previousSearchString)
|
||||
return;
|
||||
|
||||
let isSubSearch = false;
|
||||
if (this._previousTerms.length > 0)
|
||||
isSubSearch = searchString.indexOf(previousSearchString) == 0;
|
||||
let isSubSearch = terms.length == this._previousTerms.length;
|
||||
if (isSubSearch) {
|
||||
for (let i = 0; i < terms.length; i++) {
|
||||
if (terms[i].indexOf(this._previousTerms[i]) != 0) {
|
||||
isSubSearch = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let previousResultsArr = this._previousResults;
|
||||
|
||||
@ -100,6 +110,6 @@ const SearchSystem = new Lang.Class({
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
Signals.addSignalMethods(SearchSystem.prototype);
|
||||
|
@ -4,7 +4,6 @@ const Clutter = imports.gi.Clutter;
|
||||
const Lang = imports.lang;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
const Atk = imports.gi.Atk;
|
||||
|
||||
@ -14,31 +13,10 @@ const Main = imports.ui.main;
|
||||
const Overview = imports.ui.overview;
|
||||
const Separator = imports.ui.separator;
|
||||
const Search = imports.ui.search;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const MAX_LIST_SEARCH_RESULTS_ROWS = 3;
|
||||
const MAX_GRID_SEARCH_RESULTS_ROWS = 1;
|
||||
|
||||
const MaxWidthBin = new Lang.Class({
|
||||
Name: 'MaxWidthBin',
|
||||
Extends: St.Bin,
|
||||
|
||||
vfunc_allocate: function(box, flags) {
|
||||
let themeNode = this.get_theme_node();
|
||||
let maxWidth = themeNode.get_max_width();
|
||||
let availWidth = box.x2 - box.x1;
|
||||
let adjustedBox = box;
|
||||
|
||||
if (availWidth > maxWidth) {
|
||||
let excessWidth = availWidth - maxWidth;
|
||||
adjustedBox.x1 += Math.floor(excessWidth / 2);
|
||||
adjustedBox.x2 -= Math.floor(excessWidth / 2);
|
||||
}
|
||||
|
||||
this.parent(adjustedBox, flags);
|
||||
}
|
||||
});
|
||||
|
||||
const SearchResult = new Lang.Class({
|
||||
Name: 'SearchResult',
|
||||
|
||||
@ -104,11 +82,11 @@ const ListSearchResult = new Lang.Class({
|
||||
y_fill: false,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.START });
|
||||
this.actor.label_actor = title;
|
||||
|
||||
// TODO: should highlight terms in the description here
|
||||
if (this.metaInfo['description']) {
|
||||
let description = new St.Label({ style_class: 'list-search-result-description' });
|
||||
description.clutter_text.set_markup(this.metaInfo['description']);
|
||||
let description = new St.Label({ style_class: 'list-search-result-description',
|
||||
text: '"' + this.metaInfo['description'] + '"' });
|
||||
details.add(description, { x_fill: false,
|
||||
y_fill: false,
|
||||
x_align: St.Align.START,
|
||||
@ -142,7 +120,6 @@ const GridSearchResult = new Lang.Class({
|
||||
}
|
||||
|
||||
this.actor.set_child(content);
|
||||
this.actor.label_actor = content.label_actor;
|
||||
|
||||
let draggable = DND.makeDraggable(this.actor);
|
||||
draggable.connect('drag-begin',
|
||||
@ -201,7 +178,7 @@ const ListSearchResults = new Lang.Class({
|
||||
|
||||
this._content = new St.BoxLayout({ style_class: 'list-search-results',
|
||||
vertical: true });
|
||||
this.actor.add(this._content, { expand: true });
|
||||
this.actor.add_actor(this._content);
|
||||
|
||||
this._notDisplayedResult = [];
|
||||
this._terms = [];
|
||||
@ -231,14 +208,9 @@ const ListSearchResults = new Lang.Class({
|
||||
this._pendingClear = true;
|
||||
},
|
||||
|
||||
_keyFocusIn: function(icon) {
|
||||
this.emit('key-focus-in', icon);
|
||||
},
|
||||
|
||||
renderResults: function(metas) {
|
||||
for (let i = 0; i < metas.length; i++) {
|
||||
let display = new ListSearchResult(this.provider, metas[i], this._terms);
|
||||
display.actor.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
|
||||
this._content.add_actor(display.actor);
|
||||
}
|
||||
},
|
||||
@ -255,7 +227,6 @@ const ListSearchResults = new Lang.Class({
|
||||
return null;
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(ListSearchResults.prototype);
|
||||
|
||||
const GridSearchResults = new Lang.Class({
|
||||
Name: 'GridSearchResults',
|
||||
@ -298,14 +269,9 @@ const GridSearchResults = new Lang.Class({
|
||||
this._pendingClear = true;
|
||||
},
|
||||
|
||||
_keyFocusIn: function(icon) {
|
||||
this.emit('key-focus-in', icon);
|
||||
},
|
||||
|
||||
renderResults: function(metas) {
|
||||
for (let i = 0; i < metas.length; i++) {
|
||||
let display = new GridSearchResult(this.provider, metas[i], this._terms);
|
||||
display.actor.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
|
||||
this._grid.addItem(display.actor);
|
||||
}
|
||||
},
|
||||
@ -322,7 +288,6 @@ const GridSearchResults = new Lang.Class({
|
||||
return null;
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(GridSearchResults.prototype);
|
||||
|
||||
const SearchResults = new Lang.Class({
|
||||
Name: 'SearchResults',
|
||||
@ -336,20 +301,12 @@ const SearchResults = new Lang.Class({
|
||||
|
||||
this._content = new St.BoxLayout({ name: 'searchResultsContent',
|
||||
vertical: true });
|
||||
this._contentBin = new MaxWidthBin({ name: 'searchResultsBin',
|
||||
x_fill: true,
|
||||
y_fill: true,
|
||||
child: this._content });
|
||||
|
||||
let scrollChild = new St.BoxLayout();
|
||||
scrollChild.add(this._contentBin, { expand: true });
|
||||
|
||||
this._scrollView = new St.ScrollView({ x_fill: true,
|
||||
y_fill: false,
|
||||
overlay_scrollbars: true,
|
||||
style_class: 'search-display vfade' });
|
||||
style_class: 'vfade' });
|
||||
this._scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
|
||||
this._scrollView.add_actor(scrollChild);
|
||||
this._scrollView.add_actor(this._content);
|
||||
let action = new Clutter.PanAction({ interpolate: true });
|
||||
action.connect('pan', Lang.bind(this, this._onPan));
|
||||
this._scrollView.add_action(action);
|
||||
@ -382,10 +339,6 @@ const SearchResults = new Lang.Class({
|
||||
return false;
|
||||
},
|
||||
|
||||
_keyFocusIn: function(provider, icon) {
|
||||
Util.ensureActorVisibleInScrollView(this._scrollView, icon);
|
||||
},
|
||||
|
||||
createProviderMeta: function(provider) {
|
||||
let providerBox = new St.BoxLayout({ style_class: 'search-section',
|
||||
vertical: true });
|
||||
@ -399,8 +352,6 @@ const SearchResults = new Lang.Class({
|
||||
resultDisplay = new GridSearchResults(provider);
|
||||
}
|
||||
|
||||
resultDisplay.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
|
||||
|
||||
let resultDisplayBin = new St.Bin({ child: resultDisplay.actor,
|
||||
x_fill: true,
|
||||
y_fill: true });
|
||||
@ -445,15 +396,18 @@ const SearchResults = new Lang.Class({
|
||||
this._searchSystem.reset();
|
||||
this._statusBin.hide();
|
||||
this._clearDisplay();
|
||||
this._defaultResult = null;
|
||||
},
|
||||
|
||||
startingSearch: function() {
|
||||
this.reset();
|
||||
this._statusText.set_text(_("Searching…"));
|
||||
this._statusText.set_text(_("Searching..."));
|
||||
this._statusBin.show();
|
||||
},
|
||||
|
||||
doSearch: function (searchString) {
|
||||
this._searchSystem.updateSearch(searchString);
|
||||
},
|
||||
|
||||
_metaForProvider: function(provider) {
|
||||
return this._providerMeta[this._providers.indexOf(provider)];
|
||||
},
|
||||
@ -579,7 +533,6 @@ const ProviderIcon = new Lang.Class({
|
||||
this.parent({ style_class: 'search-provider-icon',
|
||||
reactive: true,
|
||||
can_focus: true,
|
||||
accessible_name: provider.appInfo.get_name(),
|
||||
track_hover: true });
|
||||
|
||||
this._content = new St.Widget({ layout_manager: new Clutter.BinLayout() });
|
||||
|
@ -30,6 +30,5 @@ const HorizontalSeparator = new Lang.Class({
|
||||
cr.setSource(pattern);
|
||||
cr.rectangle(margin, gradientOffset, gradientWidth, gradientHeight);
|
||||
cr.fill();
|
||||
cr.$dispose();
|
||||
}
|
||||
});
|
||||
|
@ -15,7 +15,6 @@ const DEFAULT_MODE = 'restrictive';
|
||||
const _modes = {
|
||||
'restrictive': {
|
||||
parentMode: null,
|
||||
stylesheetName: 'gnome-shell.css',
|
||||
hasOverview: false,
|
||||
showCalendarEvents: false,
|
||||
allowSettings: false,
|
||||
@ -47,7 +46,7 @@ const _modes = {
|
||||
panel: {
|
||||
left: ['logo'],
|
||||
center: ['dateMenu'],
|
||||
right: ['a11yGreeter', 'display', 'keyboard',
|
||||
right: ['a11y', 'display', 'keyboard',
|
||||
'volume', 'battery', 'powerMenu']
|
||||
},
|
||||
panelStyle: 'login-screen'
|
||||
@ -79,13 +78,12 @@ const _modes = {
|
||||
},
|
||||
|
||||
'initial-setup': {
|
||||
hasWindows: true,
|
||||
isPrimary: true,
|
||||
components: ['keyring'],
|
||||
panel: {
|
||||
left: [],
|
||||
center: ['dateMenu'],
|
||||
right: ['a11yGreeter', 'keyboard', 'volume', 'battery']
|
||||
right: ['a11y', 'keyboard', 'volume']
|
||||
}
|
||||
},
|
||||
|
||||
@ -159,7 +157,12 @@ function listModes() {
|
||||
const SessionMode = new Lang.Class({
|
||||
Name: 'SessionMode',
|
||||
|
||||
init: function() {
|
||||
_init: function() {
|
||||
global.connect('notify::session-mode', Lang.bind(this, this._sync));
|
||||
this._modes = _modes;
|
||||
this._modeStack = [DEFAULT_MODE];
|
||||
this._sync();
|
||||
|
||||
_getModes(Lang.bind(this, function(modes) {
|
||||
this._modes = modes;
|
||||
let primary = modes[global.session_mode] &&
|
||||
@ -167,8 +170,6 @@ const SessionMode = new Lang.Class({
|
||||
let mode = primary ? global.session_mode : 'user';
|
||||
this._modeStack = [mode];
|
||||
this._sync();
|
||||
|
||||
this.emit('sessions-loaded');
|
||||
}));
|
||||
},
|
||||
|
||||
|
@ -1,18 +1,16 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Lang = imports.lang;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
const Config = imports.misc.config;
|
||||
const ExtensionSystem = imports.ui.extensionSystem;
|
||||
const ExtensionDownloader = imports.ui.extensionDownloader;
|
||||
const ExtensionUtils = imports.misc.extensionUtils;
|
||||
const Hash = imports.misc.hash;
|
||||
const Flashspot = imports.ui.flashspot;
|
||||
const Main = imports.ui.main;
|
||||
const Screenshot = imports.ui.screenshot;
|
||||
|
||||
const GnomeShellIface = <interface name="org.gnome.Shell">
|
||||
<method name="Eval">
|
||||
@ -20,26 +18,34 @@ const GnomeShellIface = <interface name="org.gnome.Shell">
|
||||
<arg type="b" direction="out" name="success" />
|
||||
<arg type="s" direction="out" name="result" />
|
||||
</method>
|
||||
<method name="ShowOSD">
|
||||
<arg type="a{sv}" direction="in" name="params"/>
|
||||
</method>
|
||||
<method name="GrabAccelerator">
|
||||
<arg type="s" direction="in" name="accelerator"/>
|
||||
<arg type="u" direction="in" name="flags"/>
|
||||
<arg type="u" direction="out" name="action"/>
|
||||
</method>
|
||||
<method name="GrabAccelerators">
|
||||
<arg type="a(su)" direction="in" name="accelerators"/>
|
||||
<arg type="au" direction="out" name="actions"/>
|
||||
</method>
|
||||
<method name="UngrabAccelerator">
|
||||
<arg type="u" direction="in" name="action"/>
|
||||
<method name="ScreenshotArea">
|
||||
<arg type="i" direction="in" name="x"/>
|
||||
<arg type="i" direction="in" name="y"/>
|
||||
<arg type="i" direction="in" name="width"/>
|
||||
<arg type="i" direction="in" name="height"/>
|
||||
<arg type="b" direction="in" name="flash"/>
|
||||
<arg type="s" direction="in" name="filename"/>
|
||||
<arg type="b" direction="out" name="success"/>
|
||||
</method>
|
||||
<signal name="AcceleratorActivated">
|
||||
<arg name="action" type="u" />
|
||||
<arg name="deviceid" type="u" />
|
||||
</signal>
|
||||
<method name="ScreenshotWindow">
|
||||
<arg type="b" direction="in" name="include_frame"/>
|
||||
<arg type="b" direction="in" name="include_cursor"/>
|
||||
<arg type="b" direction="in" name="flash"/>
|
||||
<arg type="s" direction="in" name="filename"/>
|
||||
<arg type="b" direction="out" name="success"/>
|
||||
</method>
|
||||
<method name="Screenshot">
|
||||
<arg type="b" direction="in" name="include_cursor"/>
|
||||
<arg type="b" direction="in" name="flash"/>
|
||||
<arg type="s" direction="in" name="filename"/>
|
||||
<arg type="b" direction="out" name="success"/>
|
||||
</method>
|
||||
<method name="FlashArea">
|
||||
<arg type="i" direction="in" name="x"/>
|
||||
<arg type="i" direction="in" name="y"/>
|
||||
<arg type="i" direction="in" name="width"/>
|
||||
<arg type="i" direction="in" name="height"/>
|
||||
</method>
|
||||
<property name="Mode" type="s" access="read" />
|
||||
<property name="OverviewActive" type="b" access="readwrite" />
|
||||
<property name="ShellVersion" type="s" access="read" />
|
||||
@ -69,16 +75,7 @@ const GnomeShell = new Lang.Class({
|
||||
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(GnomeShellIface, this);
|
||||
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell');
|
||||
|
||||
this._extensionsService = new GnomeShellExtensions();
|
||||
this._screenshotService = new Screenshot.ScreenshotService();
|
||||
|
||||
this._grabbedAccelerators = new Hash.Map();
|
||||
this._grabbers = new Hash.Map();
|
||||
|
||||
global.display.connect('accelerator-activated', Lang.bind(this,
|
||||
function(display, action, deviceid) {
|
||||
this._emitAcceleratorActivated(action, deviceid);
|
||||
}));
|
||||
this._extensionsSerivce = new GnomeShellExtensions();
|
||||
},
|
||||
|
||||
/**
|
||||
@ -114,101 +111,81 @@ const GnomeShell = new Lang.Class({
|
||||
return [success, returnValue];
|
||||
},
|
||||
|
||||
ShowOSD: function(params) {
|
||||
for (let param in params)
|
||||
params[param] = params[param].deep_unpack();
|
||||
|
||||
let icon = null;
|
||||
if (params['icon'])
|
||||
icon = Gio.Icon.new_for_string(params['icon']);
|
||||
|
||||
Main.osdWindow.setIcon(icon);
|
||||
Main.osdWindow.setLabel(params['label']);
|
||||
Main.osdWindow.setLevel(params['level']);
|
||||
|
||||
Main.osdWindow.show();
|
||||
},
|
||||
|
||||
GrabAcceleratorAsync: function(params, invocation) {
|
||||
let [accel, flags] = params;
|
||||
let sender = invocation.get_sender();
|
||||
let bindingAction = this._grabAcceleratorForSender(accel, flags, sender);
|
||||
return invocation.return_value(GLib.Variant.new('(u)', [bindingAction]));
|
||||
},
|
||||
|
||||
GrabAcceleratorsAsync: function(params, invocation) {
|
||||
let [accels] = params;
|
||||
let sender = invocation.get_sender();
|
||||
let bindingActions = [];
|
||||
for (let i = 0; i < accels.length; i++) {
|
||||
let [accel, flags] = accels[i];
|
||||
bindingActions.push(this._grabAcceleratorForSender(accel, flags, sender));
|
||||
}
|
||||
return invocation.return_value(GLib.Variant.new('(au)', [bindingActions]));
|
||||
},
|
||||
|
||||
UngrabAcceleratorAsync: function(params, invocation) {
|
||||
let [action] = params;
|
||||
let grabbedBy = this._grabbedAccelerators.get(action);
|
||||
if (invocation.get_sender() != grabbedBy)
|
||||
return invocation.return_value(GLib.Variant.new('(b)', [false]));
|
||||
|
||||
let ungrabSucceeded = global.display.ungrab_accelerator(action);
|
||||
if (ungrabSucceeded)
|
||||
this._grabbedAccelerators.delete(action);
|
||||
return invocation.return_value(GLib.Variant.new('(b)', [ungrabSucceeded]));
|
||||
},
|
||||
|
||||
_emitAcceleratorActivated: function(action, deviceid) {
|
||||
let destination = this._grabbedAccelerators.get(action);
|
||||
if (!destination)
|
||||
return;
|
||||
|
||||
let connection = this._dbusImpl.get_connection();
|
||||
let info = this._dbusImpl.get_info();
|
||||
connection.emit_signal(destination,
|
||||
this._dbusImpl.get_object_path(),
|
||||
info ? info.name : null,
|
||||
'AcceleratorActivated',
|
||||
GLib.Variant.new('(uu)', [action, deviceid]));
|
||||
},
|
||||
|
||||
_grabAcceleratorForSender: function(accelerator, flags, sender) {
|
||||
let bindingAction = global.display.grab_accelerator(accelerator);
|
||||
if (bindingAction == Meta.KeyBindingAction.NONE)
|
||||
return Meta.KeyBindingAction.NONE;
|
||||
|
||||
let bindingName = Meta.external_binding_name_for_action(bindingAction);
|
||||
Main.wm.allowKeybinding(bindingName, flags);
|
||||
|
||||
this._grabbedAccelerators.set(bindingAction, sender);
|
||||
|
||||
if (!this._grabbers.has(sender)) {
|
||||
let id = Gio.bus_watch_name(Gio.BusType.SESSION, sender, 0, null,
|
||||
Lang.bind(this, this._onGrabberBusNameVanished));
|
||||
this._grabbers.set(sender, id);
|
||||
_onScreenshotComplete: function(obj, result, area, flash, invocation) {
|
||||
if (flash && result) {
|
||||
let flashspot = new Flashspot.Flashspot(area);
|
||||
flashspot.fire();
|
||||
}
|
||||
|
||||
return bindingAction;
|
||||
let retval = GLib.Variant.new('(b)', [result]);
|
||||
invocation.return_value(retval);
|
||||
},
|
||||
|
||||
_ungrabAccelerator: function(action) {
|
||||
let ungrabSucceeded = global.display.ungrab_accelerator(action);
|
||||
if (ungrabSucceeded)
|
||||
this._grabbedAccelerators.delete(action);
|
||||
/**
|
||||
* ScreenshotArea:
|
||||
* @x: The X coordinate of the area
|
||||
* @y: The Y coordinate of the area
|
||||
* @width: The width of the area
|
||||
* @height: The height of the area
|
||||
* @flash: Whether to flash the area or not
|
||||
* @filename: The filename for the screenshot
|
||||
*
|
||||
* Takes a screenshot of the passed in area and saves it
|
||||
* in @filename as png image, it returns a boolean
|
||||
* indicating whether the operation was successful or not.
|
||||
*
|
||||
*/
|
||||
ScreenshotAreaAsync : function (params, invocation) {
|
||||
let [x, y, width, height, flash, filename, callback] = params;
|
||||
let screenshot = new Shell.Screenshot();
|
||||
screenshot.screenshot_area (x, y, width, height, filename,
|
||||
Lang.bind(this, this._onScreenshotComplete,
|
||||
flash, invocation));
|
||||
},
|
||||
|
||||
_onGrabberBusNameVanished: function(connection, name) {
|
||||
let grabs = this._grabbedAccelerators.items();
|
||||
for (let i = 0; i < grabs.length; i++) {
|
||||
let [action, sender] = grabs[i];
|
||||
if (sender == name)
|
||||
this._ungrabAccelerator(action);
|
||||
}
|
||||
Gio.bus_unwatch_name(this._grabbers.get(name));
|
||||
this._grabbers.delete(name);
|
||||
/**
|
||||
* ScreenshotWindow:
|
||||
* @include_frame: Whether to include the frame or not
|
||||
* @include_cursor: Whether to include the cursor image or not
|
||||
* @flash: Whether to flash the window area or not
|
||||
* @filename: The filename for the screenshot
|
||||
*
|
||||
* Takes a screenshot of the focused window (optionally omitting the frame)
|
||||
* and saves it in @filename as png image, it returns a boolean
|
||||
* indicating whether the operation was successful or not.
|
||||
*
|
||||
*/
|
||||
ScreenshotWindowAsync : function (params, invocation) {
|
||||
let [include_frame, include_cursor, flash, filename] = params;
|
||||
let screenshot = new Shell.Screenshot();
|
||||
screenshot.screenshot_window (include_frame, include_cursor, filename,
|
||||
Lang.bind(this, this._onScreenshotComplete,
|
||||
flash, invocation));
|
||||
},
|
||||
|
||||
/**
|
||||
* Screenshot:
|
||||
* @filename: The filename for the screenshot
|
||||
* @include_cursor: Whether to include the cursor image or not
|
||||
* @flash: Whether to flash the screen or not
|
||||
*
|
||||
* Takes a screenshot of the whole screen and saves it
|
||||
* in @filename as png image, it returns a boolean
|
||||
* indicating whether the operation was successful or not.
|
||||
*
|
||||
*/
|
||||
ScreenshotAsync : function (params, invocation) {
|
||||
let [include_cursor, flash, filename] = params;
|
||||
let screenshot = new Shell.Screenshot();
|
||||
screenshot.screenshot(include_cursor, filename,
|
||||
Lang.bind(this, this._onScreenshotComplete,
|
||||
flash, invocation));
|
||||
},
|
||||
|
||||
FlashArea: function(x, y, width, height) {
|
||||
let flashspot = new Flashspot.Flashspot({ x : x, y : y, width: width, height: height});
|
||||
flashspot.fire();
|
||||
},
|
||||
|
||||
Mode: global.session_mode,
|
||||
|
||||
@ -373,8 +350,8 @@ const ScreenSaverDBus = new Lang.Class({
|
||||
this.parent();
|
||||
|
||||
this._screenShield = screenShield;
|
||||
screenShield.connect('active-changed', Lang.bind(this, function(shield) {
|
||||
this._dbusImpl.emit_signal('ActiveChanged', GLib.Variant.new('(b)', [shield.active]));
|
||||
screenShield.connect('lock-status-changed', Lang.bind(this, function(shield) {
|
||||
this._dbusImpl.emit_signal('ActiveChanged', GLib.Variant.new('(b)', [shield.locked]));
|
||||
}));
|
||||
|
||||
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(ScreenSaverIface, this);
|
||||
@ -395,13 +372,13 @@ const ScreenSaverDBus = new Lang.Class({
|
||||
|
||||
SetActive: function(active) {
|
||||
if (active)
|
||||
this._screenShield.activate(true);
|
||||
this._screenShield.lock(true);
|
||||
else
|
||||
this._screenShield.deactivate(false);
|
||||
this._screenShield.unlock();
|
||||
},
|
||||
|
||||
GetActive: function() {
|
||||
return this._screenShield.active;
|
||||
return this._screenShield.locked;
|
||||
},
|
||||
|
||||
GetActiveTime: function() {
|
||||
|
@ -1,11 +1,8 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Lang = imports.lang;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const BoxPointer = imports.ui.boxpointer;
|
||||
const Main = imports.ui.main;
|
||||
const Params = imports.misc.params;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
@ -68,33 +65,32 @@ const EntryMenu = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
open: function(animate) {
|
||||
open: function() {
|
||||
this._updatePasteItem();
|
||||
this._updateCopyItem();
|
||||
if (this._passwordItem)
|
||||
this._updatePasswordItem();
|
||||
|
||||
this.parent(animate);
|
||||
this._entry.add_style_pseudo_class('focus');
|
||||
|
||||
let direction = Gtk.DirectionType.TAB_FORWARD;
|
||||
if (!this.actor.navigate_focus(null, direction, false))
|
||||
this.actor.grab_key_focus();
|
||||
|
||||
this.parent();
|
||||
this._entry.add_style_pseudo_class('focus');
|
||||
},
|
||||
|
||||
close: function(animate) {
|
||||
close: function() {
|
||||
this._entry.grab_key_focus();
|
||||
this.parent(animate);
|
||||
this.parent();
|
||||
},
|
||||
|
||||
_updateCopyItem: function() {
|
||||
let selection = this._entry.clutter_text.get_selection();
|
||||
this._copyItem.setSensitive(!this._entry.clutter_text.password_char &&
|
||||
selection && selection != '');
|
||||
this._copyItem.setSensitive(selection && selection != '');
|
||||
},
|
||||
|
||||
_updatePasteItem: function() {
|
||||
this._clipboard.get_text(St.ClipboardType.CLIPBOARD, Lang.bind(this,
|
||||
this._clipboard.get_text(Lang.bind(this,
|
||||
function(clipboard, text) {
|
||||
this._pasteItem.setSensitive(text && text != '');
|
||||
}));
|
||||
@ -110,11 +106,11 @@ const EntryMenu = new Lang.Class({
|
||||
|
||||
_onCopyActivated: function() {
|
||||
let selection = this._entry.clutter_text.get_selection();
|
||||
this._clipboard.set_text(St.ClipboardType.CLIPBOARD, selection);
|
||||
this._clipboard.set_text(selection);
|
||||
},
|
||||
|
||||
_onPasteActivated: function() {
|
||||
this._clipboard.get_text(St.ClipboardType.CLIPBOARD, Lang.bind(this,
|
||||
this._clipboard.get_text(Lang.bind(this,
|
||||
function(clipboard, text) {
|
||||
if (!text)
|
||||
return;
|
||||
@ -138,12 +134,12 @@ function _setMenuAlignment(entry, stageX) {
|
||||
|
||||
function _onButtonPressEvent(actor, event, entry) {
|
||||
if (entry.menu.isOpen) {
|
||||
entry.menu.close(BoxPointer.PopupAnimation.FULL);
|
||||
entry.menu.close();
|
||||
return true;
|
||||
} else if (event.get_button() == 3) {
|
||||
let [stageX, stageY] = event.get_coords();
|
||||
_setMenuAlignment(entry, stageX);
|
||||
entry.menu.open(BoxPointer.PopupAnimation.FULL);
|
||||
entry.menu.open();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -153,7 +149,7 @@ function _onPopup(actor, entry) {
|
||||
let [success, textX, textY, lineHeight] = entry.clutter_text.position_to_coords(-1);
|
||||
if (success)
|
||||
entry.menu.setSourceAlignment(textX / entry.width);
|
||||
entry.menu.open(BoxPointer.PopupAnimation.FULL);
|
||||
entry.menu.open();
|
||||
};
|
||||
|
||||
function addContextMenu(entry, params) {
|
||||
@ -171,10 +167,4 @@ function addContextMenu(entry, params) {
|
||||
entry.connect('button-press-event', Lang.bind(null, _onButtonPressEvent, entry));
|
||||
|
||||
entry.connect('popup-menu', Lang.bind(null, _onPopup, entry));
|
||||
|
||||
entry.connect('destroy', function() {
|
||||
entry.menu.destroy();
|
||||
entry.menu = null;
|
||||
entry._menuManager = null;
|
||||
});
|
||||
}
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
@ -15,7 +14,12 @@ const KEY_MOUSE_KEYS_ENABLED = 'mousekeys-enable';
|
||||
|
||||
const APPLICATIONS_SCHEMA = 'org.gnome.desktop.a11y.applications';
|
||||
|
||||
const DPI_LOW_REASONABLE_VALUE = 50;
|
||||
const DPI_HIGH_REASONABLE_VALUE = 500;
|
||||
|
||||
const DPI_FACTOR_LARGE = 1.25;
|
||||
const DPI_FACTOR_LARGER = 1.5;
|
||||
const DPI_FACTOR_LARGEST = 2.0;
|
||||
|
||||
const WM_SCHEMA = 'org.gnome.desktop.wm.preferences';
|
||||
const KEY_VISUAL_BELL = 'visual-bell';
|
||||
@ -70,25 +74,6 @@ const ATIndicator = new Lang.Class({
|
||||
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
this.menu.addSettingsAction(_("Universal Access Settings"), 'gnome-universal-access-panel.desktop');
|
||||
|
||||
this._syncMenuVisibility();
|
||||
},
|
||||
|
||||
_syncMenuVisibility: function() {
|
||||
this._syncMenuVisibilityIdle = 0;
|
||||
|
||||
let items = this.menu._getMenuItems();
|
||||
|
||||
this.actor.visible = items.some(function(f) { return !!f.state; });
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
_queueSyncMenuVisibility: function() {
|
||||
if (this._syncMenuVisibilityIdle)
|
||||
return;
|
||||
|
||||
this._syncMenuVisbilityIdle = Mainloop.idle_add(Lang.bind(this, this._syncMenuVisibility));
|
||||
},
|
||||
|
||||
_buildItemExtended: function(string, initial_value, writable, on_set) {
|
||||
@ -110,11 +95,9 @@ const ATIndicator = new Lang.Class({
|
||||
function(enabled) {
|
||||
return settings.set_boolean(key, enabled);
|
||||
});
|
||||
settings.connect('changed::'+key, Lang.bind(this, function() {
|
||||
settings.connect('changed::'+key, function() {
|
||||
widget.setToggleState(settings.get_boolean(key));
|
||||
|
||||
this._queueSyncMenuVisibility();
|
||||
}));
|
||||
});
|
||||
return widget;
|
||||
},
|
||||
|
||||
@ -146,7 +129,7 @@ const ATIndicator = new Lang.Class({
|
||||
wmSettings.reset(KEY_WM_THEME);
|
||||
}
|
||||
});
|
||||
interfaceSettings.connect('changed::' + KEY_GTK_THEME, Lang.bind(this, function() {
|
||||
interfaceSettings.connect('changed::' + KEY_GTK_THEME, function() {
|
||||
let value = interfaceSettings.get_string(KEY_GTK_THEME);
|
||||
if (value == HIGH_CONTRAST_THEME) {
|
||||
highContrast.setToggleState(true);
|
||||
@ -154,9 +137,7 @@ const ATIndicator = new Lang.Class({
|
||||
highContrast.setToggleState(false);
|
||||
gtkTheme = value;
|
||||
}
|
||||
|
||||
this._queueSyncMenuVisibility();
|
||||
}));
|
||||
});
|
||||
interfaceSettings.connect('changed::' + KEY_ICON_THEME, function() {
|
||||
let value = interfaceSettings.get_string(KEY_ICON_THEME);
|
||||
if (value != HIGH_CONTRAST_THEME)
|
||||
@ -185,22 +166,11 @@ const ATIndicator = new Lang.Class({
|
||||
else
|
||||
settings.reset(KEY_TEXT_SCALING_FACTOR);
|
||||
});
|
||||
settings.connect('changed::' + KEY_TEXT_SCALING_FACTOR, Lang.bind(this, function() {
|
||||
settings.connect('changed::' + KEY_TEXT_SCALING_FACTOR, function() {
|
||||
let factor = settings.get_double(KEY_TEXT_SCALING_FACTOR);
|
||||
let active = (factor > 1.0);
|
||||
widget.setToggleState(active);
|
||||
|
||||
this._queueSyncMenuVisibility();
|
||||
}));
|
||||
});
|
||||
return widget;
|
||||
}
|
||||
});
|
||||
|
||||
const ATGreeterIndicator = new Lang.Class({
|
||||
Name: 'ATGreeterIndicator',
|
||||
Extends: ATIndicator,
|
||||
|
||||
// Override visibility handling to be always visible
|
||||
_syncMenuVisibility: function() { },
|
||||
_queueSyncMenuVisibility: function() { }
|
||||
});
|
||||
|
@ -9,7 +9,6 @@ const St = imports.gi.St;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
const NotificationDaemon = imports.ui.notificationDaemon;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
|
||||
@ -56,8 +55,8 @@ const Indicator = new Lang.Class({
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
|
||||
this._fullMenuItems = [new PopupMenu.PopupSeparatorMenuItem(),
|
||||
new PopupMenu.PopupMenuItem(_("Send Files to Device…")),
|
||||
new PopupMenu.PopupMenuItem(_("Set Up a New Device…")),
|
||||
new PopupMenu.PopupMenuItem(_("Send Files to Device...")),
|
||||
new PopupMenu.PopupMenuItem(_("Set up a New Device...")),
|
||||
new PopupMenu.PopupSeparatorMenuItem()];
|
||||
this._hasDevices = false;
|
||||
|
||||
@ -236,7 +235,7 @@ const Indicator = new Lang.Class({
|
||||
}
|
||||
|
||||
if (device.capabilities & GnomeBluetoothApplet.Capabilities.OBEX_PUSH) {
|
||||
item.menu.addAction(_("Send Files…"), Lang.bind(this, function() {
|
||||
item.menu.addAction(_("Send Files..."), Lang.bind(this, function() {
|
||||
this._applet.send_to_address(device.bdaddr, device.alias);
|
||||
}));
|
||||
}
|
||||
@ -287,7 +286,6 @@ const Indicator = new Lang.Class({
|
||||
_ensureSource: function() {
|
||||
if (!this._source) {
|
||||
this._source = new MessageTray.Source(_("Bluetooth"), 'bluetooth-active');
|
||||
this._source.policy = new NotificationDaemon.NotificationApplicationPolicy('gnome-bluetooth-panel');
|
||||
Main.messageTray.add(this._source);
|
||||
}
|
||||
},
|
||||
@ -448,5 +446,10 @@ const PinNotification = new Lang.Class({
|
||||
return this._entry.clutter_text.text.length == 6;
|
||||
else
|
||||
return true;
|
||||
},
|
||||
|
||||
grabFocus: function(lockTray) {
|
||||
this.parent(lockTray);
|
||||
global.stage.set_key_focus(this._entry);
|
||||
}
|
||||
});
|
||||
|
@ -33,36 +33,6 @@ const KEY_INPUT_SOURCES = 'sources';
|
||||
const INPUT_SOURCE_TYPE_XKB = 'xkb';
|
||||
const INPUT_SOURCE_TYPE_IBUS = 'ibus';
|
||||
|
||||
// This is longest we'll keep the keyboard frozen until an input
|
||||
// source is active.
|
||||
const MAX_INPUT_SOURCE_ACTIVATION_TIME = 2000; // ms
|
||||
|
||||
const BUS_NAME = 'org.gnome.SettingsDaemon.Keyboard';
|
||||
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Keyboard';
|
||||
|
||||
const KeyboardManagerInterface =
|
||||
<interface name="org.gnome.SettingsDaemon.Keyboard">
|
||||
<method name="SetInputSource">
|
||||
<arg type="u" direction="in" />
|
||||
</method>
|
||||
</interface>;
|
||||
|
||||
const KeyboardManagerProxy = Gio.DBusProxy.makeProxyWrapper(KeyboardManagerInterface);
|
||||
|
||||
function releaseKeyboard() {
|
||||
if (Main.modalCount > 0)
|
||||
global.display.unfreeze_keyboard(global.get_current_time());
|
||||
else
|
||||
global.display.ungrab_keyboard(global.get_current_time());
|
||||
}
|
||||
|
||||
function holdKeyboard() {
|
||||
if (Main.modalCount > 0)
|
||||
global.display.freeze_keyboard(global.get_current_time());
|
||||
else
|
||||
global.display.grab_keyboard(global.get_current_time());
|
||||
}
|
||||
|
||||
const IBusManager = new Lang.Class({
|
||||
Name: 'IBusManager',
|
||||
|
||||
@ -145,14 +115,9 @@ const IBusManager = new Lang.Class({
|
||||
this._panelService.connect('update-property', Lang.bind(this, this._updateProperty));
|
||||
// If an engine is already active we need to get its properties
|
||||
this._ibus.get_global_engine_async(-1, null, Lang.bind(this, function(i, result) {
|
||||
let engine;
|
||||
try {
|
||||
engine = this._ibus.get_global_engine_async_finish(result);
|
||||
if (!engine)
|
||||
return;
|
||||
} catch(e) {
|
||||
let engine = this._ibus.get_global_engine_async_finish(result);
|
||||
if (!engine)
|
||||
return;
|
||||
}
|
||||
this._engineChanged(this._ibus, engine.get_name());
|
||||
}));
|
||||
this._updateReadiness();
|
||||
@ -260,7 +225,6 @@ const InputSource = new Lang.Class({
|
||||
},
|
||||
|
||||
activate: function() {
|
||||
holdKeyboard();
|
||||
this.emit('activate');
|
||||
},
|
||||
});
|
||||
@ -293,11 +257,11 @@ const InputSourcePopup = new Lang.Class({
|
||||
this._select(this._selectedIndex);
|
||||
},
|
||||
|
||||
_keyPressHandler: function(keysym, _ignored, action) {
|
||||
_keyPressHandler: function(keysym, backwards, action) {
|
||||
if (action == this._action)
|
||||
this._select(this._next());
|
||||
this._select(backwards ? this._previous() : this._next());
|
||||
else if (action == this._actionBackward)
|
||||
this._select(this._previous());
|
||||
this._select(backwards ? this._next() : this._previous());
|
||||
else if (keysym == Clutter.Left)
|
||||
this._select(this._previous());
|
||||
else if (keysym == Clutter.Right)
|
||||
@ -367,14 +331,15 @@ const InputSourceIndicator = new Lang.Class({
|
||||
this._keybindingAction =
|
||||
Main.wm.addKeybinding('switch-input-source',
|
||||
new Gio.Settings({ schema: "org.gnome.desktop.wm.keybindings" }),
|
||||
Meta.KeyBindingFlags.NONE,
|
||||
Shell.KeyBindingMode.ALL,
|
||||
Meta.KeyBindingFlags.REVERSES,
|
||||
Main.KeybindingMode.ALL,
|
||||
Lang.bind(this, this._switchInputSource));
|
||||
this._keybindingActionBackward =
|
||||
Main.wm.addKeybinding('switch-input-source-backward',
|
||||
new Gio.Settings({ schema: "org.gnome.desktop.wm.keybindings" }),
|
||||
Meta.KeyBindingFlags.NONE,
|
||||
Shell.KeyBindingMode.ALL,
|
||||
Meta.KeyBindingFlags.REVERSES |
|
||||
Meta.KeyBindingFlags.REVERSED,
|
||||
Main.KeybindingMode.ALL,
|
||||
Lang.bind(this, this._switchInputSource));
|
||||
this._settings = new Gio.Settings({ schema: DESKTOP_INPUT_SOURCES_SCHEMA });
|
||||
this._settings.connect('changed::' + KEY_CURRENT_INPUT_SOURCE, Lang.bind(this, this._currentInputSourceChanged));
|
||||
@ -394,27 +359,13 @@ const InputSourceIndicator = new Lang.Class({
|
||||
this._ibusManager.connect('property-updated', Lang.bind(this, this._ibusPropertyUpdated));
|
||||
this._inputSourcesChanged();
|
||||
|
||||
this._keyboardManager = new KeyboardManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH,
|
||||
function(proxy, error) {
|
||||
if (error)
|
||||
log(error.message);
|
||||
});
|
||||
global.display.connect('modifiers-accelerator-activated', Lang.bind(this, this._modifiersSwitcher));
|
||||
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
this._showLayoutItem = this.menu.addAction(_("Show Keyboard Layout"), Lang.bind(this, this._showLayout));
|
||||
|
||||
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
|
||||
this._sessionUpdated();
|
||||
|
||||
this.menu.addSettingsAction(_("Region & Language Settings"), 'gnome-region-panel.desktop');
|
||||
|
||||
this._sourcesPerWindow = false;
|
||||
this._focusWindowNotifyId = 0;
|
||||
this._overviewShowingId = 0;
|
||||
this._overviewHiddenId = 0;
|
||||
this._settings.connect('changed::per-window', Lang.bind(this, this._sourcesPerWindowChanged));
|
||||
this._sourcesPerWindowChanged();
|
||||
this.menu.addSettingsAction(_("Region and Language Settings"), 'gnome-region-panel.desktop');
|
||||
},
|
||||
|
||||
_sessionUpdated: function() {
|
||||
@ -434,44 +385,10 @@ const InputSourceIndicator = new Lang.Class({
|
||||
this._inputSourcesChanged();
|
||||
},
|
||||
|
||||
_modifiersSwitcher: function() {
|
||||
let sourceIndexes = Object.keys(this._inputSources);
|
||||
if (sourceIndexes.length == 0) {
|
||||
releaseKeyboard();
|
||||
return;
|
||||
}
|
||||
|
||||
let is = this._currentSource;
|
||||
if (!is)
|
||||
is = this._inputSources[sourceIndexes[0]];
|
||||
|
||||
let nextIndex = is.index + 1;
|
||||
if (nextIndex > sourceIndexes[sourceIndexes.length - 1])
|
||||
nextIndex = 0;
|
||||
|
||||
while (!(is = this._inputSources[nextIndex]))
|
||||
nextIndex += 1;
|
||||
|
||||
is.activate();
|
||||
},
|
||||
|
||||
_switchInputSource: function(display, screen, window, binding) {
|
||||
if (this._mruSources.length < 2)
|
||||
return;
|
||||
|
||||
// HACK: Fall back on simple input source switching since we
|
||||
// can't show a popup switcher while the message tray is up
|
||||
// without considerable work to consolidate the usage of
|
||||
// pushModal/popModal and grabHelper. See
|
||||
// https://bugzilla.gnome.org/show_bug.cgi?id=695143 .
|
||||
if (Main.keybindingMode == Shell.KeyBindingMode.MESSAGE_TRAY) {
|
||||
this._modifiersSwitcher();
|
||||
return;
|
||||
}
|
||||
|
||||
let popup = new InputSourcePopup(this._mruSources, this._keybindingAction, this._keybindingActionBackward);
|
||||
let modifiers = binding.get_modifiers();
|
||||
let backwards = false; // Not using this
|
||||
let backwards = modifiers & Meta.VirtualModifier.SHIFT_MASK;
|
||||
if (!popup.show(backwards, binding.get_name(), binding.get_mask()))
|
||||
popup.destroy();
|
||||
},
|
||||
@ -481,14 +398,6 @@ const InputSourceIndicator = new Lang.Class({
|
||||
let newSourceIndex = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE);
|
||||
let newSource = this._inputSources[newSourceIndex];
|
||||
|
||||
let oldSource;
|
||||
[oldSource, this._currentSource] = [this._currentSource, newSource];
|
||||
|
||||
if (oldSource) {
|
||||
oldSource.menuItem.setShowDot(false);
|
||||
oldSource.indicatorLabel.hide();
|
||||
}
|
||||
|
||||
if (!newSource || (nVisibleSources < 2 && !newSource.properties)) {
|
||||
// This source index might be invalid if we weren't able
|
||||
// to build a menu item for it, so we hide ourselves since
|
||||
@ -503,8 +412,16 @@ const InputSourceIndicator = new Lang.Class({
|
||||
|
||||
this.actor.show();
|
||||
|
||||
let oldSource;
|
||||
[oldSource, this._currentSource] = [this._currentSource, newSource];
|
||||
|
||||
if (oldSource) {
|
||||
oldSource.menuItem.setShowDot(false);
|
||||
this._container.set_skip_paint(oldSource.indicatorLabel, true);
|
||||
}
|
||||
|
||||
newSource.menuItem.setShowDot(true);
|
||||
newSource.indicatorLabel.show();
|
||||
this._container.set_skip_paint(newSource.indicatorLabel, false);
|
||||
|
||||
this._buildPropSection(newSource.properties);
|
||||
|
||||
@ -514,8 +431,6 @@ const InputSourceIndicator = new Lang.Class({
|
||||
this._mruSources = currentSource.concat(this._mruSources);
|
||||
break;
|
||||
}
|
||||
|
||||
this._changePerWindowSource();
|
||||
},
|
||||
|
||||
_inputSourcesChanged: function() {
|
||||
@ -555,10 +470,8 @@ const InputSourceIndicator = new Lang.Class({
|
||||
let is = new InputSource(type, id, displayName, shortName, i);
|
||||
|
||||
is.connect('activate', Lang.bind(this, function() {
|
||||
let inVariant = new GLib.Variant('(u)', [is.index]);
|
||||
this._keyboardManager.call('SetInputSource', inVariant, 0,
|
||||
MAX_INPUT_SOURCE_ACTIVATION_TIME,
|
||||
null, releaseKeyboard);
|
||||
this._settings.set_value(KEY_CURRENT_INPUT_SOURCE,
|
||||
GLib.Variant.new_uint32(is.index));
|
||||
}));
|
||||
|
||||
if (!(is.shortName in inputSourcesByShortName))
|
||||
@ -581,8 +494,8 @@ const InputSourceIndicator = new Lang.Class({
|
||||
|
||||
this.menu.addMenuItem(is.menuItem, menuIndex++);
|
||||
|
||||
is.indicatorLabel.hide();
|
||||
this._container.add_actor(is.indicatorLabel);
|
||||
this._container.set_skip_paint(is.indicatorLabel, true);
|
||||
}
|
||||
|
||||
let sourcesList = [];
|
||||
@ -771,7 +684,7 @@ const InputSourceIndicator = new Lang.Class({
|
||||
item.prop = prop;
|
||||
item.connect('activate', Lang.bind(this, function() {
|
||||
this._ibusManager.activateProperty(item.prop.get_key(),
|
||||
item.prop.get_state());
|
||||
IBus.PropState.CHECKED);
|
||||
}));
|
||||
break;
|
||||
|
||||
@ -789,82 +702,6 @@ const InputSourceIndicator = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
_getNewInputSource: function(current) {
|
||||
for (let i in this._inputSources) {
|
||||
let is = this._inputSources[i];
|
||||
if (is.type == current.type &&
|
||||
is.id == current.id)
|
||||
return is;
|
||||
}
|
||||
return this._currentSource;
|
||||
},
|
||||
|
||||
_getCurrentWindow: function() {
|
||||
if (Main.overview.visible)
|
||||
return Main.overview;
|
||||
else
|
||||
return global.display.focus_window;
|
||||
},
|
||||
|
||||
_setPerWindowInputSource: function() {
|
||||
let window = this._getCurrentWindow();
|
||||
if (!window)
|
||||
return;
|
||||
|
||||
if (!window._inputSources) {
|
||||
window._inputSources = this._inputSources;
|
||||
window._currentSource = this._currentSource;
|
||||
} else if (window._inputSources == this._inputSources) {
|
||||
window._currentSource.activate();
|
||||
} else {
|
||||
window._inputSources = this._inputSources;
|
||||
window._currentSource = this._getNewInputSource(window._currentSource);
|
||||
window._currentSource.activate();
|
||||
}
|
||||
},
|
||||
|
||||
_sourcesPerWindowChanged: function() {
|
||||
this._sourcesPerWindow = this._settings.get_boolean('per-window');
|
||||
|
||||
if (this._sourcesPerWindow && this._focusWindowNotifyId == 0) {
|
||||
this._focusWindowNotifyId = global.display.connect('notify::focus-window',
|
||||
Lang.bind(this, this._setPerWindowInputSource));
|
||||
this._overviewShowingId = Main.overview.connect('showing',
|
||||
Lang.bind(this, this._setPerWindowInputSource));
|
||||
this._overviewHiddenId = Main.overview.connect('hidden',
|
||||
Lang.bind(this, this._setPerWindowInputSource));
|
||||
} else if (!this._sourcesPerWindow && this._focusWindowNotifyId != 0) {
|
||||
global.display.disconnect(this._focusWindowNotifyId);
|
||||
this._focusWindowNotifyId = 0;
|
||||
Main.overview.disconnect(this._overviewShowingId);
|
||||
this._overviewShowingId = 0;
|
||||
Main.overview.disconnect(this._overviewHiddenId);
|
||||
this._overviewHiddenId = 0;
|
||||
|
||||
let windows = global.get_window_actors().map(function(w) {
|
||||
return w.meta_window;
|
||||
});
|
||||
for (let i = 0; i < windows.length; ++i) {
|
||||
delete windows[i]._inputSources;
|
||||
delete windows[i]._currentSource;
|
||||
}
|
||||
delete Main.overview._inputSources;
|
||||
delete Main.overview._currentSource;
|
||||
}
|
||||
},
|
||||
|
||||
_changePerWindowSource: function() {
|
||||
if (!this._sourcesPerWindow)
|
||||
return;
|
||||
|
||||
let window = this._getCurrentWindow();
|
||||
if (!window)
|
||||
return;
|
||||
|
||||
window._inputSources = this._inputSources;
|
||||
window._currentSource = this._currentSource;
|
||||
},
|
||||
|
||||
_containerGetPreferredWidth: function(container, for_height, alloc) {
|
||||
// Here, and in _containerGetPreferredHeight, we need to query
|
||||
// for the height of all children, but we ignore the results
|
||||
|
@ -10,31 +10,6 @@ const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const VolumeMenu = imports.ui.status.volume;
|
||||
|
||||
const FakeStatusIcon = new Lang.Class({
|
||||
Name: 'FakeStatusIcon',
|
||||
|
||||
_init: function(button) {
|
||||
this.actor = new St.BoxLayout({ style_class: 'panel-status-button-box' });
|
||||
this._button = button;
|
||||
this._button.connect('icons-updated', Lang.bind(this, this._reconstructIcons));
|
||||
this._button.actor.bind_property('visible', this.actor, 'visible',
|
||||
GObject.BindingFlags.SYNC_CREATE);
|
||||
this._reconstructIcons();
|
||||
},
|
||||
|
||||
_reconstructIcons: function() {
|
||||
this.actor.destroy_all_children();
|
||||
this._button.icons.forEach(Lang.bind(this, function(icon) {
|
||||
let newIcon = new St.Icon({ style_class: 'system-status-icon' });
|
||||
icon.bind_property('gicon', newIcon, 'gicon',
|
||||
GObject.BindingFlags.SYNC_CREATE);
|
||||
icon.bind_property('visible', newIcon, 'visible',
|
||||
GObject.BindingFlags.SYNC_CREATE);
|
||||
this.actor.add_actor(newIcon);
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
const Indicator = new Lang.Class({
|
||||
Name: 'LockScreenMenuIndicator',
|
||||
Extends: PanelMenu.SystemStatusButton,
|
||||
@ -43,20 +18,41 @@ const Indicator = new Lang.Class({
|
||||
this.parent(null, _("Volume, network, battery"));
|
||||
this._box.style_class = 'lock-screen-status-button-box';
|
||||
|
||||
this._volumeControl = VolumeMenu.getMixerControl();
|
||||
this._volumeMenu = new VolumeMenu.VolumeMenu(this._volumeControl);
|
||||
this.menu.addMenuItem(this._volumeMenu);
|
||||
this._volume = Main.panel.statusArea.volume;
|
||||
if (this._volume) {
|
||||
this._volumeIcon = this.addIcon(null);
|
||||
this._volume.mainIcon.bind_property('gicon', this._volumeIcon, 'gicon',
|
||||
GObject.BindingFlags.SYNC_CREATE);
|
||||
this._volume.mainIcon.bind_property('visible', this._volumeIcon, 'visible',
|
||||
GObject.BindingFlags.SYNC_CREATE);
|
||||
|
||||
this._volume = new FakeStatusIcon(Main.panel.statusArea.volume);
|
||||
this._box.add_child(this._volume.actor);
|
||||
|
||||
// Network may not exist if the user doesn't have NetworkManager
|
||||
if (Main.panel.statusArea.network) {
|
||||
this._network = new FakeStatusIcon(Main.panel.statusArea.network);
|
||||
this._box.add_child(this._network.actor);
|
||||
this._volumeControl = VolumeMenu.getMixerControl();
|
||||
this._volumeMenu = new VolumeMenu.VolumeMenu(this._volumeControl);
|
||||
this.menu.addMenuItem(this._volumeMenu);
|
||||
}
|
||||
|
||||
this._battery = new FakeStatusIcon(Main.panel.statusArea.battery);
|
||||
this._box.add_child(this._battery.actor);
|
||||
this._network = Main.panel.statusArea.network;
|
||||
if (this._network) {
|
||||
this._networkIcon = this.addIcon(null);
|
||||
this._network.mainIcon.bind_property('gicon', this._networkIcon, 'gicon',
|
||||
GObject.BindingFlags.SYNC_CREATE);
|
||||
this._network.mainIcon.bind_property('visible', this._networkIcon, 'visible',
|
||||
GObject.BindingFlags.SYNC_CREATE);
|
||||
|
||||
this._networkSecondaryIcon = this.addIcon(null);
|
||||
this._network.secondaryIcon.bind_property('gicon', this._networkSecondaryIcon, 'gicon',
|
||||
GObject.BindingFlags.SYNC_CREATE);
|
||||
this._network.secondaryIcon.bind_property('visible', this._networkSecondaryIcon, 'visible',
|
||||
GObject.BindingFlags.SYNC_CREATE);
|
||||
}
|
||||
|
||||
this._battery = Main.panel.statusArea.battery;
|
||||
if (this._battery) {
|
||||
this._batteryIcon = this.addIcon(null);
|
||||
this._battery.mainIcon.bind_property('gicon', this._batteryIcon, 'gicon',
|
||||
GObject.BindingFlags.SYNC_CREATE);
|
||||
this._battery.mainIcon.bind_property('visible', this._batteryIcon, 'visible',
|
||||
GObject.BindingFlags.SYNC_CREATE);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -8,26 +8,16 @@ const NMClient = imports.gi.NMClient;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
|
||||
// Some of the new code depends on as-yet-unreleased NM
|
||||
var NMGtk;
|
||||
try {
|
||||
NMGtk = imports.gi.NMGtk;
|
||||
} catch(e) {
|
||||
NMGtk = null;
|
||||
}
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
const NotificationDaemon = imports.ui.notificationDaemon;
|
||||
const ModemManager = imports.misc.modemManager;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const NMConnectionCategory = {
|
||||
INVALID: 'invalid',
|
||||
WIRED: 'wired',
|
||||
VIRTUAL: 'virtual',
|
||||
WIRELESS: 'wireless',
|
||||
WWAN: 'wwan',
|
||||
VPN: 'vpn'
|
||||
@ -49,7 +39,7 @@ const NM80211ApFlags = NetworkManager['80211ApFlags'];
|
||||
const NM80211ApSecurityFlags = NetworkManager['80211ApSecurityFlags'];
|
||||
|
||||
// number of wireless networks that should be visible
|
||||
// (the remaining are placed into More…)
|
||||
// (the remaining are placed into More...)
|
||||
const NUM_VISIBLE_NETWORKS = 5;
|
||||
|
||||
function macToArray(string) {
|
||||
@ -149,6 +139,46 @@ const NMNetworkMenuItem = new Lang.Class({
|
||||
}
|
||||
});
|
||||
|
||||
const NMWiredSectionTitleMenuItem = new Lang.Class({
|
||||
Name: 'NMWiredSectionTitleMenuItem',
|
||||
Extends: PopupMenu.PopupSwitchMenuItem,
|
||||
|
||||
_init: function(label, params) {
|
||||
params = params || { };
|
||||
params.style_class = 'popup-subtitle-menu-item';
|
||||
this.parent(label, false, params);
|
||||
},
|
||||
|
||||
updateForDevice: function(device) {
|
||||
if (device) {
|
||||
this._device = device;
|
||||
this.setStatus(device.getStatusLabel());
|
||||
this.setToggleState(device.connected);
|
||||
} else
|
||||
this.setStatus('');
|
||||
},
|
||||
|
||||
activate: function(event) {
|
||||
this.parent(event);
|
||||
|
||||
if (!this._device) {
|
||||
log('Section title activated when there is more than one device, should be non reactive');
|
||||
return;
|
||||
}
|
||||
|
||||
let newState = this._switch.state;
|
||||
|
||||
let ok;
|
||||
if (newState)
|
||||
ok = this._device.activate();
|
||||
else
|
||||
ok = this._device.deactivate();
|
||||
|
||||
if (!ok)
|
||||
this._switch.setToggleState(false);
|
||||
}
|
||||
});
|
||||
|
||||
const NMWirelessSectionTitleMenuItem = new Lang.Class({
|
||||
Name: 'NMWirelessSectionTitleMenuItem',
|
||||
Extends: PopupMenu.PopupSwitchMenuItem,
|
||||
@ -309,16 +339,19 @@ const NMDevice = new Lang.Class({
|
||||
Extends: NMConnectionBased,
|
||||
|
||||
_init: function(client, device, connections) {
|
||||
this._client = client;
|
||||
this._setDevice(device);
|
||||
this.parent(connections);
|
||||
this.device = device;
|
||||
this.device._delegate = this;
|
||||
this._stateChangedId = this.device.connect('state-changed', Lang.bind(this, this._deviceStateChanged));
|
||||
|
||||
// protected
|
||||
this._client = client;
|
||||
this.parent(connections);
|
||||
this._activeConnection = null;
|
||||
this._activeConnectionItem = null;
|
||||
this._autoConnectionItem = null;
|
||||
this._overflowItem = null;
|
||||
|
||||
this.statusItem = new PopupMenu.PopupSwitchMenuItem('', this.connected, { style_class: 'popup-subtitle-menu-item' });
|
||||
this.statusItem = new PopupMenu.PopupSwitchMenuItem(this._getDescription(), this.connected, { style_class: 'popup-subtitle-menu-item' });
|
||||
this._statusChanged = this.statusItem.connect('toggled', Lang.bind(this, function(item, state) {
|
||||
let ok;
|
||||
if (state)
|
||||
@ -337,12 +370,23 @@ const NMDevice = new Lang.Class({
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this._setDevice(null);
|
||||
if (this.device)
|
||||
this.device._delegate = null;
|
||||
|
||||
if (this._deferredWorkId) {
|
||||
// Just clear out, the actual removal is handled when the
|
||||
// actor is destroyed
|
||||
this._deferredWorkId = 0;
|
||||
if (this._stateChangedId) {
|
||||
// Need to go through GObject.Object.prototype because
|
||||
// nm_device_disconnect conflicts with g_signal_disconnect
|
||||
GObject.Object.prototype.disconnect.call(this.device, this._stateChangedId);
|
||||
this._stateChangedId = 0;
|
||||
}
|
||||
if (this._carrierChangedId) {
|
||||
// see above for why this is needed
|
||||
GObject.Object.prototype.disconnect.call(this.device, this._carrierChangedId);
|
||||
this._carrierChangedId = 0;
|
||||
}
|
||||
if (this._firmwareChangedId) {
|
||||
GObject.Object.prototype.disconnect.call(this.device, this._firmwareChangedId);
|
||||
this._firmwareChangedId = 0;
|
||||
}
|
||||
|
||||
this._clearSection();
|
||||
@ -351,33 +395,6 @@ const NMDevice = new Lang.Class({
|
||||
this.section.destroy();
|
||||
},
|
||||
|
||||
_setDevice: function(device) {
|
||||
if (device) {
|
||||
this.device = device;
|
||||
this.device._delegate = this;
|
||||
this._stateChangedId = this.device.connect('state-changed', Lang.bind(this, this._deviceStateChanged));
|
||||
} else if (this.device) {
|
||||
this.device._delegate = null;
|
||||
|
||||
if (this._stateChangedId) {
|
||||
// Need to go through GObject.Object.prototype because
|
||||
// nm_device_disconnect conflicts with g_signal_disconnect
|
||||
GObject.Object.prototype.disconnect.call(this.device, this._stateChangedId);
|
||||
this._stateChangedId = 0;
|
||||
}
|
||||
if (this._carrierChangedId) {
|
||||
GObject.Object.prototype.disconnect.call(this.device, this._carrierChangedId);
|
||||
this._carrierChangedId = 0;
|
||||
}
|
||||
if (this._firmwareChangedId) {
|
||||
GObject.Object.prototype.disconnect.call(this.device, this._firmwareChangedId);
|
||||
this._firmwareChangedId = 0;
|
||||
}
|
||||
|
||||
this.device = null;
|
||||
}
|
||||
},
|
||||
|
||||
deactivate: function() {
|
||||
this.device.disconnect(null);
|
||||
return true;
|
||||
@ -392,7 +409,7 @@ const NMDevice = new Lang.Class({
|
||||
// Otherwise, if no connection is currently configured,
|
||||
// try automatic configuration (or summon the config dialog)
|
||||
if (this._connections.length == 1) {
|
||||
this._client.activate_connection(this._connections[0].connection, this.device || null, null, null);
|
||||
this._client.activate_connection(this._connections[0].connection, this.device, null, null);
|
||||
return true;
|
||||
} else if (this._connections.length == 0) {
|
||||
return this._activateAutomaticConnection();
|
||||
@ -412,7 +429,7 @@ const NMDevice = new Lang.Class({
|
||||
},
|
||||
|
||||
get connected() {
|
||||
return this.device && this.device.state == NetworkManager.DeviceState.ACTIVATED;
|
||||
return this.device.state == NetworkManager.DeviceState.ACTIVATED;
|
||||
},
|
||||
|
||||
clearActiveConnection: function(activeConnection) {
|
||||
@ -432,6 +449,7 @@ const NMDevice = new Lang.Class({
|
||||
|
||||
this._activeConnection = activeConnection;
|
||||
|
||||
this._clearSection();
|
||||
this._queueCreateSection();
|
||||
},
|
||||
|
||||
@ -445,9 +463,6 @@ const NMDevice = new Lang.Class({
|
||||
},
|
||||
|
||||
getStatusLabel: function() {
|
||||
if (!this.device)
|
||||
return null;
|
||||
|
||||
switch(this.device.state) {
|
||||
case NetworkManager.DeviceState.DISCONNECTED:
|
||||
case NetworkManager.DeviceState.ACTIVATED:
|
||||
@ -497,21 +512,14 @@ const NMDevice = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
syncDescription: function() {
|
||||
if (this.device && this.device._description)
|
||||
this.statusItem.label.text = this.device._description;
|
||||
},
|
||||
|
||||
// protected
|
||||
_createAutomaticConnection: function() {
|
||||
throw new TypeError('Invoking pure virtual function NMDevice.createAutomaticConnection');
|
||||
},
|
||||
|
||||
_queueCreateSection: function() {
|
||||
if (this._deferredWorkId) {
|
||||
this._clearSection();
|
||||
Main.queueDeferredWork(this._deferredWorkId);
|
||||
}
|
||||
this._clearSection();
|
||||
Main.queueDeferredWork(this._deferredWorkId);
|
||||
},
|
||||
|
||||
_clearSection: function() {
|
||||
@ -549,7 +557,7 @@ const NMDevice = new Lang.Class({
|
||||
|
||||
if (j + activeOffset >= NUM_VISIBLE_NETWORKS) {
|
||||
if (!this._overflowItem) {
|
||||
this._overflowItem = new PopupMenu.PopupSubMenuMenuItem(_("More…"));
|
||||
this._overflowItem = new PopupMenu.PopupSubMenuMenuItem(_("More..."));
|
||||
this.section.addMenuItem(this._overflowItem);
|
||||
}
|
||||
this._overflowItem.menu.addMenuItem(obj.item);
|
||||
@ -611,6 +619,7 @@ const NMDevice = new Lang.Class({
|
||||
|
||||
this._updateStatusItem();
|
||||
|
||||
this._clearSection();
|
||||
this._queueCreateSection();
|
||||
this.emit('state-changed');
|
||||
},
|
||||
@ -634,6 +643,25 @@ const NMDevice = new Lang.Class({
|
||||
this.statusItem.setStatus(this.getStatusLabel());
|
||||
|
||||
this.emit('state-changed');
|
||||
},
|
||||
|
||||
_getDescription: function() {
|
||||
let dev_product = this.device.get_product();
|
||||
let dev_vendor = this.device.get_vendor();
|
||||
if (!dev_product || !dev_vendor)
|
||||
return '';
|
||||
|
||||
let product = Util.fixupPCIDescription(dev_product);
|
||||
let vendor = Util.fixupPCIDescription(dev_vendor);
|
||||
let out = '';
|
||||
|
||||
// Another quick hack; if all of the fixed up vendor string
|
||||
// is found in product, ignore the vendor.
|
||||
if (product.indexOf(vendor) == -1)
|
||||
out += vendor + ' ';
|
||||
out += product;
|
||||
|
||||
return out;
|
||||
}
|
||||
});
|
||||
|
||||
@ -664,7 +692,6 @@ const NMDeviceWired = new Lang.Class({
|
||||
Extends: NMDeviceSimple,
|
||||
|
||||
_init: function(client, device, connections) {
|
||||
device._description = _("Wired");
|
||||
this._autoConnectionName = _("Auto Ethernet");
|
||||
this.category = NMConnectionCategory.WIRED;
|
||||
|
||||
@ -692,24 +719,12 @@ const NMDeviceModem = new Lang.Class({
|
||||
_init: function(client, device, connections) {
|
||||
let is_wwan = false;
|
||||
|
||||
device._description = _("Mobile broadband");
|
||||
this._enabled = true;
|
||||
this.mobileDevice = null;
|
||||
this._connectionType = 'ppp';
|
||||
|
||||
this._capabilities = device.current_capabilities;
|
||||
// Support new ModemManager1 devices
|
||||
if (device.udi.indexOf('/org/freedesktop/ModemManager1/Modem') == 0) {
|
||||
is_wwan = true;
|
||||
this.mobileDevice = new ModemManager.BroadbandModem(device.udi, device.current_capabilities);
|
||||
if (this._capabilities & NetworkManager.DeviceModemCapabilities.GSM_UMTS) {
|
||||
this._connectionType = NetworkManager.SETTING_GSM_SETTING_NAME;
|
||||
} else if (this._capabilities & NetworkManager.DeviceModemCapabilities.LTE) {
|
||||
this._connectionType = NetworkManager.SETTING_GSM_SETTING_NAME;
|
||||
} else if (this._capabilities & NetworkManager.DeviceModemCapabilities.CDMA_EVDO) {
|
||||
this._connectionType = NetworkManager.SETTING_CDMA_SETTING_NAME;
|
||||
}
|
||||
} else if (this._capabilities & NetworkManager.DeviceModemCapabilities.GSM_UMTS) {
|
||||
if (this._capabilities & NetworkManager.DeviceModemCapabilities.GSM_UMTS) {
|
||||
is_wwan = true;
|
||||
this.mobileDevice = new ModemManager.ModemGsm(device.udi);
|
||||
this._connectionType = NetworkManager.SETTING_GSM_SETTING_NAME;
|
||||
@ -719,8 +734,7 @@ const NMDeviceModem = new Lang.Class({
|
||||
this._connectionType = NetworkManager.SETTING_CDMA_SETTING_NAME;
|
||||
} else if (this._capabilities & NetworkManager.DeviceModemCapabilities.LTE) {
|
||||
is_wwan = true;
|
||||
this.mobileDevice = new ModemManager.ModemGsm(device.udi);
|
||||
this._connectionType = NetworkManager.SETTING_GSM_SETTING_NAME;
|
||||
// FIXME: support signal quality
|
||||
}
|
||||
|
||||
if (is_wwan) {
|
||||
@ -823,7 +837,6 @@ const NMDeviceBluetooth = new Lang.Class({
|
||||
Extends: NMDevice,
|
||||
|
||||
_init: function(client, device, connections) {
|
||||
device._description = _("Bluetooth");
|
||||
this._autoConnectionName = this._makeConnectionName(device);
|
||||
device.connect('notify::name', Lang.bind(this, this._updateAutoConnectionName));
|
||||
|
||||
@ -866,8 +879,13 @@ const NMDeviceBluetooth = new Lang.Class({
|
||||
_updateAutoConnectionName: function() {
|
||||
this._autoConnectionName = this._makeConnectionName(this.device);
|
||||
|
||||
this._clearSection();
|
||||
this._queueCreateSection();
|
||||
this._updateStatusItem();
|
||||
},
|
||||
|
||||
_getDescription: function() {
|
||||
return this.device.name || _("Bluetooth");
|
||||
}
|
||||
});
|
||||
|
||||
@ -1127,8 +1145,10 @@ const NMDeviceWireless = new Lang.Class({
|
||||
this._networks.splice(res.network, 1);
|
||||
let newPos = Util.insertSorted(this._networks, network, Lang.bind(this, this._networkSortFunction));
|
||||
|
||||
if (newPos != res.network)
|
||||
if (newPos != res.network) {
|
||||
this._clearSection();
|
||||
this._queueCreateSection();
|
||||
}
|
||||
},
|
||||
|
||||
_accessPointAdded: function(device, accessPoint) {
|
||||
@ -1181,8 +1201,10 @@ const NMDeviceWireless = new Lang.Class({
|
||||
let newPos = Util.insertSorted(this._networks, apObj, this._networkSortFunction);
|
||||
|
||||
// Queue an update of the UI if we changed the order
|
||||
if (newPos != pos)
|
||||
if (newPos != pos) {
|
||||
this._clearSection();
|
||||
this._queueCreateSection();
|
||||
}
|
||||
},
|
||||
|
||||
_accessPointRemoved: function(device, accessPoint) {
|
||||
@ -1242,10 +1264,12 @@ const NMDeviceWireless = new Lang.Class({
|
||||
if (res.network < this._networks.length-1)
|
||||
okNext = this._networkSortFunction(this._networks[res.network + 1], apObj) <= 0;
|
||||
|
||||
if (!okPrev || !okNext)
|
||||
if (!okPrev || !okNext) {
|
||||
this._clearSection();
|
||||
this._queueCreateSection();
|
||||
else if (apObj.item)
|
||||
} else if (apObj.item) {
|
||||
apObj.item.updateBestAP(apObj.accessPoints[0]);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -1321,6 +1345,7 @@ const NMDeviceWireless = new Lang.Class({
|
||||
|
||||
if (forceupdate) {
|
||||
this._networks.sort(this._networkSortFunction);
|
||||
this._clearSection();
|
||||
this._queueCreateSection();
|
||||
}
|
||||
},
|
||||
@ -1353,6 +1378,7 @@ const NMDeviceWireless = new Lang.Class({
|
||||
|
||||
if (forceupdate) {
|
||||
this._networks.sort(this._networkSortFunction);
|
||||
this._clearSection();
|
||||
this._queueCreateSection();
|
||||
}
|
||||
},
|
||||
@ -1432,7 +1458,7 @@ const NMDeviceWireless = new Lang.Class({
|
||||
this.section.addMenuItem(apObj.item, position);
|
||||
} else {
|
||||
if (!this._overflowItem) {
|
||||
this._overflowItem = new PopupMenu.PopupSubMenuMenuItem(_("More…"));
|
||||
this._overflowItem = new PopupMenu.PopupSubMenuMenuItem(_("More..."));
|
||||
this.section.addMenuItem(this._overflowItem);
|
||||
}
|
||||
this._overflowItem.menu.addMenuItem(apObj.item, position - NUM_VISIBLE_NETWORKS);
|
||||
@ -1463,56 +1489,6 @@ const NMDeviceWireless = new Lang.Class({
|
||||
},
|
||||
});
|
||||
|
||||
const NMDeviceVirtual = new Lang.Class({
|
||||
Name: 'NMDeviceVirtual',
|
||||
Extends: NMDeviceSimple,
|
||||
|
||||
_init: function(client, iface, connections) {
|
||||
this.iface = iface;
|
||||
this.parent(client, null, connections);
|
||||
this.category = NMConnectionCategory.VIRTUAL;
|
||||
},
|
||||
|
||||
_shouldShowConnectionList: function() {
|
||||
return this.hasConnections();
|
||||
},
|
||||
|
||||
connectionValid: function(connection) {
|
||||
return connection.get_virtual_iface_name() == this.iface;
|
||||
},
|
||||
|
||||
addConnection: function(connection) {
|
||||
if (!this.device && !this.hasConnections())
|
||||
this.statusItem.label.text = NMGtk.utils_get_connection_device_name(connection);
|
||||
|
||||
this.parent(connection);
|
||||
},
|
||||
|
||||
adoptDevice: function(device) {
|
||||
if (device.get_iface() == this.iface) {
|
||||
this._setDevice(device);
|
||||
if (device._description)
|
||||
this.syncDescription();
|
||||
this._updateStatusItem();
|
||||
this.emit('state-changed');
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
},
|
||||
|
||||
removeDevice: function(device) {
|
||||
if (device == this.device) {
|
||||
this._setDevice(null);
|
||||
this._updateStatusItem();
|
||||
this.emit('state-changed');
|
||||
}
|
||||
},
|
||||
|
||||
hasConnections: function() {
|
||||
return this._connections.length != 0;
|
||||
}
|
||||
});
|
||||
|
||||
const NMVPNSection = new Lang.Class({
|
||||
Name: 'NMVPNSection',
|
||||
Extends: NMConnectionBased,
|
||||
@ -1624,7 +1600,7 @@ const NMVPNSection = new Lang.Class({
|
||||
|
||||
if (j >= NUM_VISIBLE_NETWORKS) {
|
||||
if (!this._overflowItem) {
|
||||
this._overflowItem = new PopupMenu.PopupSubMenuMenuItem(_("More…"));
|
||||
this._overflowItem = new PopupMenu.PopupSubMenuMenuItem(_("More..."));
|
||||
this.section.addMenuItem(this._overflowItem);
|
||||
}
|
||||
this._overflowItem.menu.addMenuItem(obj.item);
|
||||
@ -1669,59 +1645,7 @@ const NMApplet = new Lang.Class({
|
||||
this.secondaryIcon = this.addIcon(new Gio.ThemedIcon({ name: 'network-vpn-symbolic' }));
|
||||
this.secondaryIcon.hide();
|
||||
|
||||
// Device types
|
||||
this._dtypes = { };
|
||||
this._dtypes[NetworkManager.DeviceType.ETHERNET] = NMDeviceWired;
|
||||
this._dtypes[NetworkManager.DeviceType.WIFI] = NMDeviceWireless;
|
||||
this._dtypes[NetworkManager.DeviceType.MODEM] = NMDeviceModem;
|
||||
this._dtypes[NetworkManager.DeviceType.BT] = NMDeviceBluetooth;
|
||||
this._dtypes[NetworkManager.DeviceType.INFINIBAND] = NMDeviceSimple;
|
||||
// TODO: WiMax support
|
||||
|
||||
// Virtual device types
|
||||
this._vtypes = { };
|
||||
if (NMGtk) {
|
||||
this._vtypes[NetworkManager.SETTING_VLAN_SETTING_NAME] = NMDeviceVirtual;
|
||||
this._vtypes[NetworkManager.SETTING_BOND_SETTING_NAME] = NMDeviceVirtual;
|
||||
this._vtypes[NetworkManager.SETTING_BRIDGE_SETTING_NAME] = NMDeviceVirtual;
|
||||
}
|
||||
|
||||
// Connection types
|
||||
this._ctypes = { };
|
||||
this._ctypes[NetworkManager.SETTING_WIRELESS_SETTING_NAME] = NMConnectionCategory.WIRELESS;
|
||||
this._ctypes[NetworkManager.SETTING_WIRED_SETTING_NAME] = NMConnectionCategory.WIRED;
|
||||
this._ctypes[NetworkManager.SETTING_PPPOE_SETTING_NAME] = NMConnectionCategory.WIRED;
|
||||
this._ctypes[NetworkManager.SETTING_PPP_SETTING_NAME] = NMConnectionCategory.WIRED;
|
||||
this._ctypes[NetworkManager.SETTING_BLUETOOTH_SETTING_NAME] = NMConnectionCategory.WWAN;
|
||||
this._ctypes[NetworkManager.SETTING_CDMA_SETTING_NAME] = NMConnectionCategory.WWAN;
|
||||
this._ctypes[NetworkManager.SETTING_GSM_SETTING_NAME] = NMConnectionCategory.WWAN;
|
||||
this._ctypes[NetworkManager.SETTING_INFINIBAND_SETTING_NAME] = NMConnectionCategory.WIRED;
|
||||
if (NMGtk) {
|
||||
this._ctypes[NetworkManager.SETTING_VLAN_SETTING_NAME] = NMConnectionCategory.VIRTUAL;
|
||||
this._ctypes[NetworkManager.SETTING_BOND_SETTING_NAME] = NMConnectionCategory.VIRTUAL;
|
||||
this._ctypes[NetworkManager.SETTING_BRIDGE_SETTING_NAME] = NMConnectionCategory.VIRTUAL;
|
||||
}
|
||||
this._ctypes[NetworkManager.SETTING_VPN_SETTING_NAME] = NMConnectionCategory.VPN;
|
||||
|
||||
NMClient.Client.new_async(null, Lang.bind(this, this._clientGot));
|
||||
NMClient.RemoteSettings.new_async(null, null, Lang.bind(this, this._remoteSettingsGot));
|
||||
},
|
||||
|
||||
_clientGot: function(obj, result) {
|
||||
this._client = NMClient.Client.new_finish(result);
|
||||
|
||||
this._tryLateInit();
|
||||
},
|
||||
|
||||
_remoteSettingsGot: function(obj, result) {
|
||||
this._settings = NMClient.RemoteSettings.new_finish(result);
|
||||
|
||||
this._tryLateInit();
|
||||
},
|
||||
|
||||
_tryLateInit: function() {
|
||||
if (!this._client || !this._settings)
|
||||
return;
|
||||
this._client = NMClient.Client.new();
|
||||
|
||||
this._statusSection = new PopupMenu.PopupMenuSection();
|
||||
this._statusItem = new PopupMenu.PopupMenuItem('', { reactive: false });
|
||||
@ -1743,32 +1667,23 @@ const NMApplet = new Lang.Class({
|
||||
this._mobileUpdateId = 0;
|
||||
this._mobileUpdateDevice = null;
|
||||
|
||||
this._nmDevices = [];
|
||||
this._devices = { };
|
||||
this._virtualDevices = [ ];
|
||||
|
||||
this._devices.wired = {
|
||||
section: new PopupMenu.PopupMenuSection(),
|
||||
devices: [ ],
|
||||
item: new NMWiredSectionTitleMenuItem(_("Wired"))
|
||||
};
|
||||
|
||||
this._devices.wired.section.addMenuItem(this._devices.wired.item);
|
||||
this._devices.wired.section.actor.hide();
|
||||
this.menu.addMenuItem(this._devices.wired.section);
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
|
||||
this._devices.virtual = {
|
||||
section: new PopupMenu.PopupMenuSection(),
|
||||
devices: [ ],
|
||||
};
|
||||
|
||||
this._devices.virtual.section.actor.hide();
|
||||
this.menu.addMenuItem(this._devices.virtual.section);
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
|
||||
this._devices.wireless = {
|
||||
section: new PopupMenu.PopupMenuSection(),
|
||||
devices: [ ],
|
||||
item: this._makeToggleItem('wireless', _("Wi-Fi"))
|
||||
item: this._makeToggleItem('wireless', _("Wireless"))
|
||||
};
|
||||
this._devices.wireless.section.addMenuItem(this._devices.wireless.item);
|
||||
this._devices.wireless.section.actor.hide();
|
||||
@ -1778,7 +1693,9 @@ const NMApplet = new Lang.Class({
|
||||
this._devices.wwan = {
|
||||
section: new PopupMenu.PopupMenuSection(),
|
||||
devices: [ ],
|
||||
item: this._makeToggleItem('wwan', _("Mobile broadband"))
|
||||
};
|
||||
this._devices.wwan.section.addMenuItem(this._devices.wwan.item);
|
||||
this._devices.wwan.section.actor.hide();
|
||||
this.menu.addMenuItem(this._devices.wwan.section);
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
@ -1789,24 +1706,52 @@ const NMApplet = new Lang.Class({
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
this.menu.addSettingsAction(_("Network Settings"), 'gnome-network-panel.desktop');
|
||||
|
||||
this._readConnections();
|
||||
this._readDevices();
|
||||
this._syncNMState();
|
||||
// Device types
|
||||
this._dtypes = { };
|
||||
this._dtypes[NetworkManager.DeviceType.ETHERNET] = NMDeviceWired;
|
||||
this._dtypes[NetworkManager.DeviceType.WIFI] = NMDeviceWireless;
|
||||
this._dtypes[NetworkManager.DeviceType.MODEM] = NMDeviceModem;
|
||||
this._dtypes[NetworkManager.DeviceType.BT] = NMDeviceBluetooth;
|
||||
this._dtypes[NetworkManager.DeviceType.INFINIBAND] = NMDeviceSimple;
|
||||
// TODO: WiMax support
|
||||
|
||||
this._client.connect('notify::manager-running', Lang.bind(this, this._syncNMState));
|
||||
this._client.connect('notify::networking-enabled', Lang.bind(this, this._syncNMState));
|
||||
this._client.connect('notify::state', Lang.bind(this, this._syncNMState));
|
||||
this._client.connect('notify::active-connections', Lang.bind(this, this._updateIcon));
|
||||
this._client.connect('device-added', Lang.bind(this, this._deviceAdded));
|
||||
this._client.connect('device-removed', Lang.bind(this, this._deviceRemoved));
|
||||
this._settings.connect('new-connection', Lang.bind(this, this._newConnection));
|
||||
// Connection types
|
||||
this._ctypes = { };
|
||||
this._ctypes[NetworkManager.SETTING_WIRELESS_SETTING_NAME] = NMConnectionCategory.WIRELESS;
|
||||
this._ctypes[NetworkManager.SETTING_WIRED_SETTING_NAME] = NMConnectionCategory.WIRED;
|
||||
this._ctypes[NetworkManager.SETTING_PPPOE_SETTING_NAME] = NMConnectionCategory.WIRED;
|
||||
this._ctypes[NetworkManager.SETTING_PPP_SETTING_NAME] = NMConnectionCategory.WIRED;
|
||||
this._ctypes[NetworkManager.SETTING_BLUETOOTH_SETTING_NAME] = NMConnectionCategory.WWAN;
|
||||
this._ctypes[NetworkManager.SETTING_CDMA_SETTING_NAME] = NMConnectionCategory.WWAN;
|
||||
this._ctypes[NetworkManager.SETTING_GSM_SETTING_NAME] = NMConnectionCategory.WWAN;
|
||||
this._ctypes[NetworkManager.SETTING_INFINIBAND_SETTING_NAME] = NMConnectionCategory.WIRED;
|
||||
this._ctypes[NetworkManager.SETTING_VPN_SETTING_NAME] = NMConnectionCategory.VPN;
|
||||
|
||||
this._settings = NMClient.RemoteSettings.new(null);
|
||||
this._connectionsReadId = this._settings.connect('connections-read', Lang.bind(this, function() {
|
||||
this._readConnections();
|
||||
this._readDevices();
|
||||
this._syncNMState();
|
||||
|
||||
// Connect to signals late so that early signals don't find in inconsistent state
|
||||
// and connect only once (this signal handler can be called again if NetworkManager goes up and down)
|
||||
if (!this._inited) {
|
||||
this._inited = true;
|
||||
this._client.connect('notify::manager-running', Lang.bind(this, this._syncNMState));
|
||||
this._client.connect('notify::networking-enabled', Lang.bind(this, this._syncNMState));
|
||||
this._client.connect('notify::state', Lang.bind(this, this._syncNMState));
|
||||
this._client.connect('notify::active-connections', Lang.bind(this, this._updateIcon));
|
||||
this._client.connect('device-added', Lang.bind(this, this._deviceAdded));
|
||||
this._client.connect('device-removed', Lang.bind(this, this._deviceRemoved));
|
||||
this._settings.connect('new-connection', Lang.bind(this, this._newConnection));
|
||||
}
|
||||
}));
|
||||
},
|
||||
|
||||
_ensureSource: function() {
|
||||
if (!this._source) {
|
||||
this._source = new MessageTray.Source(_("Network Manager"),
|
||||
'network-transmit-receive');
|
||||
this._source.policy = new NotificationDaemon.NotificationApplicationPolicy('gnome-network-panel');
|
||||
|
||||
this._source.connect('destroy', Lang.bind(this, function() {
|
||||
this._source = null;
|
||||
@ -1831,34 +1776,20 @@ const NMApplet = new Lang.Class({
|
||||
let devices = this._devices[category].devices;
|
||||
let item = this._devices[category].item;
|
||||
let section = this._devices[category].section;
|
||||
|
||||
let visible;
|
||||
if (category == NMConnectionCategory.VIRTUAL)
|
||||
visible = !section.isEmpty();
|
||||
else
|
||||
visible = devices.length > 0;
|
||||
|
||||
if (!visible)
|
||||
if (devices.length == 0)
|
||||
section.actor.hide();
|
||||
else {
|
||||
section.actor.show();
|
||||
|
||||
// Sync the relation between the section title
|
||||
// item (the one with the airplane mode switch)
|
||||
// and the individual device switches
|
||||
if (item) {
|
||||
if (devices.length == 1) {
|
||||
let dev = devices[0];
|
||||
dev.statusItem.actor.hide();
|
||||
item.updateForDevice(dev);
|
||||
} else {
|
||||
devices.forEach(function(dev) {
|
||||
dev.statusItem.actor.show();
|
||||
});
|
||||
|
||||
// remove status text from the section title item
|
||||
item.updateForDevice(null);
|
||||
}
|
||||
if (devices.length == 1) {
|
||||
let dev = devices[0];
|
||||
dev.statusItem.actor.hide();
|
||||
item.updateForDevice(dev);
|
||||
} else {
|
||||
devices.forEach(function(dev) {
|
||||
dev.statusItem.actor.show();
|
||||
});
|
||||
// remove status text from the section title item
|
||||
item.updateForDevice(null);
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -1866,9 +1797,8 @@ const NMApplet = new Lang.Class({
|
||||
_readDevices: function() {
|
||||
let devices = this._client.get_devices() || [ ];
|
||||
for (let i = 0; i < devices.length; ++i) {
|
||||
this._deviceAdded(this._client, devices[i], true);
|
||||
this._deviceAdded(this._client, devices[i]);
|
||||
}
|
||||
this._syncDeviceNames();
|
||||
},
|
||||
|
||||
_notifyForDevice: function(device, iconName, title, text, urgency) {
|
||||
@ -1899,50 +1829,9 @@ const NMApplet = new Lang.Class({
|
||||
MessageTray.Urgency.HIGH);
|
||||
},
|
||||
|
||||
_syncDeviceNames: function() {
|
||||
if (NMGtk) {
|
||||
let names = NMGtk.utils_disambiguate_device_names(this._nmDevices);
|
||||
for (let i = 0; i < this._nmDevices.length; i++) {
|
||||
let device = this._nmDevices[i];
|
||||
device._description = names[i];
|
||||
if (device._delegate)
|
||||
device._delegate.syncDescription();
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < this._nmDevices.length; i++) {
|
||||
let device = this._nmDevices[i];
|
||||
device._delegate.syncDescription();
|
||||
}
|
||||
}
|
||||
},
|
||||
_makeWrapperDevice: function(wrapperClass, device) {
|
||||
let wrapper = new wrapperClass(this._client, device, this._connections);
|
||||
|
||||
_deviceAdded: function(client, device, skipSyncDeviceNames) {
|
||||
if (device._delegate) {
|
||||
// already seen, not adding again
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < this._virtualDevices.length; i++) {
|
||||
if (this._virtualDevices[i].adoptDevice(device)) {
|
||||
this._nmDevices.push(device);
|
||||
if (!skipSyncDeviceNames)
|
||||
this._syncDeviceNames();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let wrapperClass = this._dtypes[device.get_device_type()];
|
||||
if (wrapperClass) {
|
||||
let wrapper = new wrapperClass(this._client, device, this._connections);
|
||||
this._addDeviceWrapper(wrapper);
|
||||
|
||||
this._nmDevices.push(device);
|
||||
if (!skipSyncDeviceNames)
|
||||
this._syncDeviceNames();
|
||||
}
|
||||
},
|
||||
|
||||
_addDeviceWrapper: function(wrapper) {
|
||||
wrapper._activationFailedId = wrapper.connect('activation-failed',
|
||||
Lang.bind(this, this._onActivationFailed));
|
||||
wrapper._deviceStateChangedId = wrapper.connect('state-changed', Lang.bind(this, function(dev) {
|
||||
@ -1954,36 +1843,35 @@ const NMApplet = new Lang.Class({
|
||||
wrapper.disconnect(wrapper._destroyId);
|
||||
});
|
||||
|
||||
let section = this._devices[wrapper.category].section;
|
||||
section.addMenuItem(wrapper.statusItem);
|
||||
section.addMenuItem(wrapper.section);
|
||||
return wrapper;
|
||||
},
|
||||
|
||||
let devices = this._devices[wrapper.category].devices;
|
||||
devices.push(wrapper);
|
||||
_deviceAdded: function(client, device) {
|
||||
if (device._delegate) {
|
||||
// already seen, not adding again
|
||||
return;
|
||||
}
|
||||
let wrapperClass = this._dtypes[device.get_device_type()];
|
||||
if (wrapperClass) {
|
||||
let wrapper = this._makeWrapperDevice(wrapperClass, device);
|
||||
let section = this._devices[wrapper.category].section;
|
||||
let devices = this._devices[wrapper.category].devices;
|
||||
|
||||
this._syncSectionTitle(wrapper.category);
|
||||
section.addMenuItem(wrapper.section, 1);
|
||||
section.addMenuItem(wrapper.statusItem, 1);
|
||||
devices.push(wrapper);
|
||||
|
||||
this._syncSectionTitle(wrapper.category);
|
||||
}
|
||||
},
|
||||
|
||||
_deviceRemoved: function(client, device) {
|
||||
let pos = this._nmDevices.indexOf(device);
|
||||
if (pos != -1) {
|
||||
this._nmDevices.splice(pos, 1);
|
||||
this._syncDeviceNames();
|
||||
}
|
||||
|
||||
let wrapper = device._delegate;
|
||||
if (!wrapper) {
|
||||
if (!device._delegate) {
|
||||
log('Removing a network device that was not added');
|
||||
return;
|
||||
}
|
||||
|
||||
if (wrapper instanceof NMDeviceVirtual)
|
||||
wrapper.removeDevice(device);
|
||||
else
|
||||
this._removeDeviceWrapper(wrapper);
|
||||
},
|
||||
|
||||
_removeDeviceWrapper: function(wrapper) {
|
||||
let wrapper = device._delegate;
|
||||
wrapper.destroy();
|
||||
|
||||
let devices = this._devices[wrapper.category].devices;
|
||||
@ -2184,13 +2072,6 @@ const NMApplet = new Lang.Class({
|
||||
devices[i].removeConnection(connection);
|
||||
}
|
||||
|
||||
if (section == NMConnectionCategory.VIRTUAL) {
|
||||
let iface = connection.get_virtual_iface_name();
|
||||
let wrapper = this._findVirtualDevice(iface);
|
||||
if (wrapper && !wrapper.hasConnections())
|
||||
this._removeDeviceWrapper(wrapper);
|
||||
}
|
||||
|
||||
connection.disconnect(connection._removedId);
|
||||
connection.disconnect(connection._updatedId);
|
||||
connection._removedId = connection._updatedId = 0;
|
||||
@ -2204,27 +2085,6 @@ const NMApplet = new Lang.Class({
|
||||
|
||||
let section = connection._section;
|
||||
|
||||
if (section == NMConnectionCategory.VIRTUAL) {
|
||||
let wrapperClass = this._vtypes[connection._type];
|
||||
if (!wrapperClass)
|
||||
return;
|
||||
|
||||
let iface = connection.get_virtual_iface_name();
|
||||
let wrapper = this._findVirtualDevice(iface);
|
||||
if (!wrapper) {
|
||||
wrapper = new wrapperClass(this._client, iface, this._connections);
|
||||
this._addDeviceWrapper(wrapper);
|
||||
this._virtualDevices.push(wrapper);
|
||||
|
||||
// We might already have a device for this connection
|
||||
for (let i = 0; i < this._nmDevices.length; i++) {
|
||||
let device = this._nmDevices[i];
|
||||
if (wrapper.adoptDevice(device))
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (section == NMConnectionCategory.INVALID)
|
||||
return;
|
||||
if (section == NMConnectionCategory.VPN) {
|
||||
@ -2237,15 +2097,6 @@ const NMApplet = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
_findVirtualDevice: function(iface) {
|
||||
for (let i = 0; i < this._virtualDevices.length; i++) {
|
||||
if (this._virtualDevices[i].iface == iface)
|
||||
return this._virtualDevices[i];
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
_hideDevices: function() {
|
||||
this._devicesHidden = true;
|
||||
|
||||
@ -2261,7 +2112,6 @@ const NMApplet = new Lang.Class({
|
||||
this._statusSection.actor.hide();
|
||||
|
||||
this._syncSectionTitle(NMConnectionCategory.WIRED);
|
||||
this._syncSectionTitle(NMConnectionCategory.VIRTUAL);
|
||||
this._syncSectionTitle(NMConnectionCategory.WIRELESS);
|
||||
this._syncSectionTitle(NMConnectionCategory.WWAN);
|
||||
},
|
||||
@ -2299,7 +2149,6 @@ const NMApplet = new Lang.Class({
|
||||
this.setIcon('network-wireless-acquiring-symbolic');
|
||||
break;
|
||||
case NMConnectionCategory.WIRED:
|
||||
case NMConnectionCategory.VIRTUAL:
|
||||
this.setIcon('network-wired-acquiring-symbolic');
|
||||
break;
|
||||
default:
|
||||
@ -2339,7 +2188,6 @@ const NMApplet = new Lang.Class({
|
||||
break;
|
||||
}
|
||||
case NMConnectionCategory.WIRED:
|
||||
case NMConnectionCategory.VIRTUAL:
|
||||
this.setIcon('network-wired-symbolic');
|
||||
break;
|
||||
case NMConnectionCategory.WWAN:
|
||||
|
@ -7,7 +7,7 @@ const St = imports.gi.St;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
|
||||
const BUS_NAME = 'org.gnome.SettingsDaemon.Power';
|
||||
const BUS_NAME = 'org.gnome.SettingsDaemon';
|
||||
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Power';
|
||||
|
||||
const UPDeviceType = {
|
||||
@ -54,23 +54,14 @@ const Indicator = new Lang.Class({
|
||||
_init: function() {
|
||||
this.parent('battery-missing-symbolic', _("Battery"));
|
||||
|
||||
this._proxy = new PowerManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH,
|
||||
Lang.bind(this, function(proxy, error) {
|
||||
if (error) {
|
||||
log(error.message);
|
||||
return;
|
||||
}
|
||||
this._proxy.connect('g-properties-changed',
|
||||
Lang.bind(this, this._devicesChanged));
|
||||
this._devicesChanged();
|
||||
}));
|
||||
this._proxy = new PowerManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH);
|
||||
|
||||
this._deviceItems = [ ];
|
||||
this._hasPrimary = false;
|
||||
this._primaryDeviceId = null;
|
||||
|
||||
this._batteryItem = new PopupMenu.PopupMenuItem('', { reactive: false });
|
||||
this._primaryPercentage = new St.Label({ style_class: 'popup-battery-percentage' });
|
||||
this._primaryPercentage = new St.Label();
|
||||
this._batteryItem.addActor(this._primaryPercentage, { align: St.Align.END });
|
||||
this.menu.addMenuItem(this._batteryItem);
|
||||
|
||||
@ -79,6 +70,10 @@ const Indicator = new Lang.Class({
|
||||
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
this.menu.addSettingsAction(_("Power Settings"), 'gnome-power-panel.desktop');
|
||||
|
||||
this._proxy.connect('g-properties-changed',
|
||||
Lang.bind(this, this._devicesChanged));
|
||||
this._devicesChanged();
|
||||
},
|
||||
|
||||
_readPrimaryDevice: function() {
|
||||
@ -96,7 +91,7 @@ const Indicator = new Lang.Class({
|
||||
if (time == 0) {
|
||||
// 0 is reported when UPower does not have enough data
|
||||
// to estimate battery life
|
||||
this._batteryItem.label.text = _("Estimating…");
|
||||
this._batteryItem.label.text = _("Estimating...");
|
||||
} else {
|
||||
let minutes = time % 60;
|
||||
let hours = Math.floor(time / 60);
|
||||
@ -188,8 +183,7 @@ const DeviceItem = new Lang.Class({
|
||||
this._box.add_actor(this._label);
|
||||
this.addActor(this._box);
|
||||
|
||||
let percentLabel = new St.Label({ text: C_("percent of battery remaining", "%d%%").format(Math.round(percentage)),
|
||||
style_class: 'popup-battery-percentage' });
|
||||
let percentLabel = new St.Label({ text: C_("percent of battery remaining", "%d%%").format(Math.round(percentage)) });
|
||||
this.addActor(percentLabel, { align: St.Align.END });
|
||||
//FIXME: ideally we would like to expose this._label and percentLabel
|
||||
this.actor.label_actor = percentLabel;
|
||||
@ -198,9 +192,9 @@ const DeviceItem = new Lang.Class({
|
||||
_deviceTypeToString: function(type) {
|
||||
switch (type) {
|
||||
case UPDeviceType.AC_POWER:
|
||||
return _("AC Adapter");
|
||||
return _("AC adapter");
|
||||
case UPDeviceType.BATTERY:
|
||||
return _("Laptop Battery");
|
||||
return _("Laptop battery");
|
||||
case UPDeviceType.UPS:
|
||||
return _("UPS");
|
||||
case UPDeviceType.MONITOR:
|
||||
@ -212,9 +206,9 @@ const DeviceItem = new Lang.Class({
|
||||
case UPDeviceType.PDA:
|
||||
return _("PDA");
|
||||
case UPDeviceType.PHONE:
|
||||
return _("Cell Phone");
|
||||
return _("Cell phone");
|
||||
case UPDeviceType.MEDIA_PLAYER:
|
||||
return _("Media Player");
|
||||
return _("Media player");
|
||||
case UPDeviceType.TABLET:
|
||||
return _("Tablet");
|
||||
case UPDeviceType.COMPUTER:
|
||||
|
@ -5,7 +5,6 @@ const Lang = imports.lang;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Gvc = imports.gi.Gvc;
|
||||
const St = imports.gi.St;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
@ -27,210 +26,6 @@ function getMixerControl() {
|
||||
return _mixerControl;
|
||||
}
|
||||
|
||||
const StreamSlider = new Lang.Class({
|
||||
Name: 'StreamSlider',
|
||||
|
||||
_init: function(control, title) {
|
||||
this._control = control;
|
||||
|
||||
this.item = new PopupMenu.PopupMenuSection();
|
||||
|
||||
this._title = new PopupMenu.PopupMenuItem(title, { reactive: false });
|
||||
this._slider = new PopupMenu.PopupSliderMenuItem(0);
|
||||
this._slider.connect('value-changed', Lang.bind(this, this._sliderChanged));
|
||||
this._slider.connect('drag-end', Lang.bind(this, this._notifyVolumeChange));
|
||||
|
||||
this.item.addMenuItem(this._title);
|
||||
this.item.addMenuItem(this._slider);
|
||||
|
||||
this._stream = null;
|
||||
this._shouldShow = true;
|
||||
},
|
||||
|
||||
get stream() {
|
||||
return this._stream;
|
||||
},
|
||||
|
||||
set stream(stream) {
|
||||
if (this._stream) {
|
||||
this._disconnectStream(this._stream);
|
||||
}
|
||||
|
||||
this._stream = stream;
|
||||
|
||||
if (this._stream) {
|
||||
this._connectStream(this._stream);
|
||||
this._updateVolume();
|
||||
} else {
|
||||
this.emit('stream-updated');
|
||||
}
|
||||
|
||||
this._updateVisibility();
|
||||
},
|
||||
|
||||
_disconnectStream: function(stream) {
|
||||
stream.disconnect(this._mutedChangedId);
|
||||
this._mutedChangedId = 0;
|
||||
stream.disconnect(this._volumeChangedId);
|
||||
this._volumeChangedId = 0;
|
||||
},
|
||||
|
||||
_connectStream: function(stream) {
|
||||
this._mutedChangedId = stream.connect('notify::is-muted', Lang.bind(this, this._updateVolume));
|
||||
this._volumeChangedId = stream.connect('notify::volume', Lang.bind(this, this._updateVolume));
|
||||
},
|
||||
|
||||
_shouldBeVisible: function() {
|
||||
return this._stream != null;
|
||||
},
|
||||
|
||||
_updateVisibility: function() {
|
||||
let visible = this._shouldBeVisible();
|
||||
this._title.actor.visible = visible;
|
||||
this._slider.actor.visible = visible;
|
||||
},
|
||||
|
||||
scroll: function(event) {
|
||||
this._slider.scroll(event);
|
||||
},
|
||||
|
||||
setValue: function(value) {
|
||||
// piggy-back off of sliderChanged
|
||||
this._slider.setValue(value);
|
||||
},
|
||||
|
||||
_sliderChanged: function(slider, value, property) {
|
||||
if (!this._stream)
|
||||
return;
|
||||
|
||||
let volume = value * this._control.get_vol_max_norm();
|
||||
let prevMuted = this._stream.is_muted;
|
||||
if (volume < 1) {
|
||||
this._stream.volume = 0;
|
||||
if (!prevMuted)
|
||||
this._stream.change_is_muted(true);
|
||||
} else {
|
||||
this._stream.volume = volume;
|
||||
if (prevMuted)
|
||||
this._stream.change_is_muted(false);
|
||||
}
|
||||
this._stream.push_volume();
|
||||
},
|
||||
|
||||
_notifyVolumeChange: function() {
|
||||
global.cancel_theme_sound(VOLUME_NOTIFY_ID);
|
||||
global.play_theme_sound(VOLUME_NOTIFY_ID,
|
||||
'audio-volume-change',
|
||||
_("Volume changed"),
|
||||
Clutter.get_current_event ());
|
||||
},
|
||||
|
||||
_updateVolume: function() {
|
||||
let muted = this._stream.is_muted;
|
||||
this._slider.setValue(muted ? 0 : (this._stream.volume / this._control.get_vol_max_norm()));
|
||||
this.emit('stream-updated');
|
||||
},
|
||||
|
||||
getIcon: function() {
|
||||
if (!this._stream)
|
||||
return null;
|
||||
|
||||
let volume = this._stream.volume;
|
||||
if (this._stream.is_muted || volume <= 0) {
|
||||
return 'audio-volume-muted-symbolic';
|
||||
} else {
|
||||
let n = Math.floor(3 * volume / this._control.get_vol_max_norm()) + 1;
|
||||
if (n < 2)
|
||||
return 'audio-volume-low-symbolic';
|
||||
if (n >= 3)
|
||||
return 'audio-volume-high-symbolic';
|
||||
return 'audio-volume-medium-symbolic';
|
||||
}
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(StreamSlider.prototype);
|
||||
|
||||
const OutputStreamSlider = new Lang.Class({
|
||||
Name: 'OutputStreamSlider',
|
||||
Extends: StreamSlider,
|
||||
|
||||
_connectStream: function(stream) {
|
||||
this.parent(stream);
|
||||
this._portChangedId = stream.connect('notify::port', Lang.bind(this, this._portChanged));
|
||||
this._portChanged();
|
||||
},
|
||||
|
||||
_findHeadphones: function(sink) {
|
||||
// This only works for external headphones (e.g. bluetooth)
|
||||
if (sink.get_form_factor() == 'headset' ||
|
||||
sink.get_form_factor() == 'headphone')
|
||||
return true;
|
||||
|
||||
// a bit hackish, but ALSA/PulseAudio have a number
|
||||
// of different identifiers for headphones, and I could
|
||||
// not find the complete list
|
||||
if (sink.get_ports().length > 0)
|
||||
return sink.get_port().port.indexOf('headphone') >= 0;
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
_disconnectStream: function(stream) {
|
||||
this.parent(stream);
|
||||
stream.disconnect(this._portChangedId);
|
||||
this._portChangedId = 0;
|
||||
},
|
||||
|
||||
_portChanged: function() {
|
||||
let hasHeadphones = this._findHeadphones(this._stream);
|
||||
if (hasHeadphones != this._hasHeadphones) {
|
||||
this._hasHeadphones = hasHeadphones;
|
||||
this.emit('headphones-changed', this._hasHeadphones);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const InputStreamSlider = new Lang.Class({
|
||||
Name: 'InputStreamSlider',
|
||||
Extends: StreamSlider,
|
||||
|
||||
_init: function(control, title) {
|
||||
this.parent(control, title);
|
||||
this._control.connect('stream-added', Lang.bind(this, this._maybeShowInput));
|
||||
this._control.connect('stream-removed', Lang.bind(this, this._maybeShowInput));
|
||||
},
|
||||
|
||||
_connectStream: function(stream) {
|
||||
this.parent(stream);
|
||||
this._maybeShowInput();
|
||||
},
|
||||
|
||||
_maybeShowInput: function() {
|
||||
// only show input widgets if any application is recording audio
|
||||
let showInput = false;
|
||||
let recordingApps = this._control.get_source_outputs();
|
||||
if (this._stream && recordingApps) {
|
||||
for (let i = 0; i < recordingApps.length; i++) {
|
||||
let outputStream = recordingApps[i];
|
||||
let id = outputStream.get_application_id();
|
||||
// but skip gnome-volume-control and pavucontrol
|
||||
// (that appear as recording because they show the input level)
|
||||
if (!id || (id != 'org.gnome.VolumeControl' && id != 'org.PulseAudio.pavucontrol')) {
|
||||
showInput = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._showInput = showInput;
|
||||
this._updateVisibility();
|
||||
},
|
||||
|
||||
_shouldBeVisible: function() {
|
||||
return this.parent() && this._showInput;
|
||||
}
|
||||
});
|
||||
|
||||
const VolumeMenu = new Lang.Class({
|
||||
Name: 'VolumeMenu',
|
||||
Extends: PopupMenu.PopupMenuSection,
|
||||
@ -244,48 +39,207 @@ const VolumeMenu = new Lang.Class({
|
||||
this._control.connect('state-changed', Lang.bind(this, this._onControlStateChanged));
|
||||
this._control.connect('default-sink-changed', Lang.bind(this, this._readOutput));
|
||||
this._control.connect('default-source-changed', Lang.bind(this, this._readInput));
|
||||
this._control.connect('stream-added', Lang.bind(this, this._maybeShowInput));
|
||||
this._control.connect('stream-removed', Lang.bind(this, this._maybeShowInput));
|
||||
this._volumeMax = this._control.get_vol_max_norm();
|
||||
|
||||
this._output = null;
|
||||
this._outputVolumeId = 0;
|
||||
this._outputMutedId = 0;
|
||||
/* Translators: This is the label for audio volume */
|
||||
this._output = new OutputStreamSlider(this._control, _("Volume"));
|
||||
this._output.connect('stream-updated', Lang.bind(this, function() {
|
||||
this.emit('icon-changed');
|
||||
}));
|
||||
this._output.connect('headphones-changed', Lang.bind(this, function(stream, value) {
|
||||
this.emit('headphones-changed', value);
|
||||
}));
|
||||
this.addMenuItem(this._output.item);
|
||||
|
||||
this._input = new InputStreamSlider(this._control, _("Microphone"));
|
||||
this.addMenuItem(this._input.item);
|
||||
this._outputTitle = new PopupMenu.PopupMenuItem(_("Volume"), { reactive: false });
|
||||
this._outputSlider = new PopupMenu.PopupSliderMenuItem(0);
|
||||
this._outputSlider.connect('value-changed', Lang.bind(this, this._sliderChanged, '_output'));
|
||||
this._outputSlider.connect('drag-end', Lang.bind(this, this._notifyVolumeChange));
|
||||
this.addMenuItem(this._outputTitle);
|
||||
this.addMenuItem(this._outputSlider);
|
||||
|
||||
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
|
||||
this._input = null;
|
||||
this._inputVolumeId = 0;
|
||||
this._inputMutedId = 0;
|
||||
this._inputTitle = new PopupMenu.PopupMenuItem(_("Microphone"), { reactive: false });
|
||||
this._inputSlider = new PopupMenu.PopupSliderMenuItem(0);
|
||||
this._inputSlider.connect('value-changed', Lang.bind(this, this._sliderChanged, '_input'));
|
||||
this._inputSlider.connect('drag-end', Lang.bind(this, this._notifyVolumeChange));
|
||||
this.addMenuItem(this._inputTitle);
|
||||
this.addMenuItem(this._inputSlider);
|
||||
|
||||
this._onControlStateChanged();
|
||||
},
|
||||
|
||||
scroll: function(event) {
|
||||
this._output.scroll(event);
|
||||
scroll: function(direction) {
|
||||
let currentVolume = this._output.volume;
|
||||
|
||||
if (direction == Clutter.ScrollDirection.DOWN) {
|
||||
let prev_muted = this._output.is_muted;
|
||||
this._output.volume = Math.max(0, currentVolume - this._volumeMax * VOLUME_ADJUSTMENT_STEP);
|
||||
if (this._output.volume < 1) {
|
||||
this._output.volume = 0;
|
||||
if (!prev_muted)
|
||||
this._output.change_is_muted(true);
|
||||
}
|
||||
this._output.push_volume();
|
||||
}
|
||||
else if (direction == Clutter.ScrollDirection.UP) {
|
||||
this._output.volume = Math.min(this._volumeMax, currentVolume + this._volumeMax * VOLUME_ADJUSTMENT_STEP);
|
||||
this._output.change_is_muted(false);
|
||||
this._output.push_volume();
|
||||
}
|
||||
|
||||
this._notifyVolumeChange();
|
||||
},
|
||||
|
||||
_onControlStateChanged: function() {
|
||||
if (this._control.get_state() == Gvc.MixerControlState.READY) {
|
||||
this._readInput();
|
||||
this._readOutput();
|
||||
this._readInput();
|
||||
this._maybeShowInput();
|
||||
} else {
|
||||
this.emit('icon-changed');
|
||||
this.emit('icon-changed', null);
|
||||
}
|
||||
},
|
||||
|
||||
_findHeadphones: function(sink) {
|
||||
// This only works for external headphones (e.g. bluetooth)
|
||||
if (sink.get_form_factor() == 'headset' ||
|
||||
sink.get_form_factor() == 'headphone')
|
||||
return true;
|
||||
|
||||
// a bit hackish, but ALSA/PulseAudio have a number
|
||||
// of different identifiers for headphones, and I could
|
||||
// not find the complete list
|
||||
let port = sink.get_port();
|
||||
if (port)
|
||||
return port.port.indexOf('headphone') >= 0;
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
_portChanged: function() {
|
||||
this.hasHeadphones = this._findHeadphones(this._output);
|
||||
this.emit('headphones-changed');
|
||||
},
|
||||
|
||||
_readOutput: function() {
|
||||
this._output.stream = this._control.get_default_sink();
|
||||
if (this._outputVolumeId) {
|
||||
this._output.disconnect(this._outputVolumeId);
|
||||
this._output.disconnect(this._outputMutedId);
|
||||
this._output.disconnect(this._outputPortId);
|
||||
this._outputVolumeId = 0;
|
||||
this._outputMutedId = 0;
|
||||
this._outputPortId = 0;
|
||||
}
|
||||
this._output = this._control.get_default_sink();
|
||||
if (this._output) {
|
||||
this._outputMutedId = this._output.connect('notify::is-muted', Lang.bind(this, this._mutedChanged, '_output'));
|
||||
this._outputVolumeId = this._output.connect('notify::volume', Lang.bind(this, this._volumeChanged, '_output'));
|
||||
this._outputPortId = this._output.connect('notify::port', Lang.bind(this, this._portChanged));
|
||||
|
||||
this._mutedChanged(null, null, '_output');
|
||||
this._volumeChanged(null, null, '_output');
|
||||
this._portChanged();
|
||||
} else {
|
||||
this.hasHeadphones = false;
|
||||
this._outputSlider.setValue(0);
|
||||
this.emit('icon-changed', 'audio-volume-muted-symbolic');
|
||||
}
|
||||
},
|
||||
|
||||
_readInput: function() {
|
||||
this._input.stream = this._control.get_default_source();
|
||||
if (this._inputVolumeId) {
|
||||
this._input.disconnect(this._inputVolumeId);
|
||||
this._input.disconnect(this._inputMutedId);
|
||||
this._inputVolumeId = 0;
|
||||
this._inputMutedId = 0;
|
||||
}
|
||||
this._input = this._control.get_default_source();
|
||||
if (this._input) {
|
||||
this._inputMutedId = this._input.connect('notify::is-muted', Lang.bind(this, this._mutedChanged, '_input'));
|
||||
this._inputVolumeId = this._input.connect('notify::volume', Lang.bind(this, this._volumeChanged, '_input'));
|
||||
this._mutedChanged (null, null, '_input');
|
||||
this._volumeChanged (null, null, '_input');
|
||||
} else {
|
||||
this._inputTitle.actor.hide();
|
||||
this._inputSlider.actor.hide();
|
||||
}
|
||||
},
|
||||
|
||||
getIcon: function() {
|
||||
return this._output.getIcon();
|
||||
_maybeShowInput: function() {
|
||||
// only show input widgets if any application is recording audio
|
||||
let showInput = false;
|
||||
let recordingApps = this._control.get_source_outputs();
|
||||
if (this._input && recordingApps) {
|
||||
for (let i = 0; i < recordingApps.length; i++) {
|
||||
let outputStream = recordingApps[i];
|
||||
let id = outputStream.get_application_id();
|
||||
// but skip gnome-volume-control and pavucontrol
|
||||
// (that appear as recording because they show the input level)
|
||||
if (!id || (id != 'org.gnome.VolumeControl' && id != 'org.PulseAudio.pavucontrol')) {
|
||||
showInput = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._inputTitle.actor.visible = showInput;
|
||||
this._inputSlider.actor.visible = showInput;
|
||||
},
|
||||
|
||||
_volumeToIcon: function(volume) {
|
||||
if (volume <= 0) {
|
||||
return 'audio-volume-muted-symbolic';
|
||||
} else {
|
||||
let n = Math.floor(3 * volume / this._volumeMax) + 1;
|
||||
if (n < 2)
|
||||
return 'audio-volume-low-symbolic';
|
||||
if (n >= 3)
|
||||
return 'audio-volume-high-symbolic';
|
||||
return 'audio-volume-medium-symbolic';
|
||||
}
|
||||
},
|
||||
|
||||
_sliderChanged: function(slider, value, property) {
|
||||
if (this[property] == null) {
|
||||
log ('Volume slider changed for %s, but %s does not exist'.format(property, property));
|
||||
return;
|
||||
}
|
||||
let volume = value * this._volumeMax;
|
||||
let prev_muted = this[property].is_muted;
|
||||
if (volume < 1) {
|
||||
this[property].volume = 0;
|
||||
if (!prev_muted)
|
||||
this[property].change_is_muted(true);
|
||||
} else {
|
||||
this[property].volume = volume;
|
||||
if (prev_muted)
|
||||
this[property].change_is_muted(false);
|
||||
}
|
||||
this[property].push_volume();
|
||||
},
|
||||
|
||||
_notifyVolumeChange: function() {
|
||||
global.cancel_theme_sound(VOLUME_NOTIFY_ID);
|
||||
global.play_theme_sound(VOLUME_NOTIFY_ID, 'audio-volume-change');
|
||||
},
|
||||
|
||||
_mutedChanged: function(object, param_spec, property) {
|
||||
let muted = this[property].is_muted;
|
||||
let slider = this[property+'Slider'];
|
||||
slider.setValue(muted ? 0 : (this[property].volume / this._volumeMax));
|
||||
if (property == '_output') {
|
||||
if (muted)
|
||||
this.emit('icon-changed', 'audio-volume-muted-symbolic');
|
||||
else
|
||||
this.emit('icon-changed', this._volumeToIcon(this._output.volume));
|
||||
}
|
||||
},
|
||||
|
||||
_volumeChanged: function(object, param_spec, property) {
|
||||
this[property+'Slider'].setValue(this[property].volume / this._volumeMax);
|
||||
if (property == '_output' && !this._output.is_muted)
|
||||
this.emit('icon-changed', this._volumeToIcon(this._output.volume));
|
||||
}
|
||||
});
|
||||
|
||||
@ -298,13 +252,13 @@ const Indicator = new Lang.Class({
|
||||
|
||||
this._control = getMixerControl();
|
||||
this._volumeMenu = new VolumeMenu(this._control);
|
||||
this._volumeMenu.connect('icon-changed', Lang.bind(this, function(menu) {
|
||||
let icon = this._volumeMenu.getIcon();
|
||||
this.actor.visible = (icon != null);
|
||||
this._volumeMenu.connect('icon-changed', Lang.bind(this, function(menu, icon) {
|
||||
this._hasPulseAudio = (icon != null);
|
||||
this.setIcon(icon);
|
||||
this._syncVisibility();
|
||||
}));
|
||||
this._volumeMenu.connect('headphones-changed', Lang.bind(this, function(menu, value) {
|
||||
this._headphoneIcon.visible = value;
|
||||
this._volumeMenu.connect('headphones-changed', Lang.bind(this, function() {
|
||||
this._syncVisibility();
|
||||
}));
|
||||
|
||||
this._headphoneIcon = this.addIcon(new Gio.ThemedIcon({ name: 'headphones-symbolic' }));
|
||||
@ -318,7 +272,13 @@ const Indicator = new Lang.Class({
|
||||
this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
|
||||
},
|
||||
|
||||
_syncVisibility: function() {
|
||||
this.actor.visible = this._hasPulseAudio;
|
||||
this.mainIcon.visible = this._hasPulseAudio;
|
||||
this._headphoneIcon.visible = this._hasPulseAudio && this._volumeMenu.hasHeadphones;
|
||||
},
|
||||
|
||||
_onScrollEvent: function(actor, event) {
|
||||
this._volumeMenu.scroll(event);
|
||||
this._volumeMenu.scroll(event.get_scroll_direction());
|
||||
}
|
||||
});
|
||||
|
@ -46,8 +46,7 @@ const SwitcherPopup = new Lang.Class({
|
||||
this._selectedIndex = 0;
|
||||
|
||||
this.actor = new Shell.GenericContainer({ style_class: 'switcher-popup',
|
||||
reactive: true,
|
||||
visible: false });
|
||||
reactive: true });
|
||||
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
|
||||
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
|
||||
this.actor.connect('allocate', Lang.bind(this, this._allocate));
|
||||
@ -160,7 +159,6 @@ const SwitcherPopup = new Lang.Class({
|
||||
// disturbed by the popup briefly flashing.
|
||||
this._initialDelayTimeoutId = Mainloop.timeout_add(POPUP_DELAY_TIMEOUT,
|
||||
Lang.bind(this, function () {
|
||||
Main.osdWindow.cancel();
|
||||
this.actor.opacity = 255;
|
||||
this._initialDelayTimeoutId = 0;
|
||||
}));
|
||||
@ -305,7 +303,7 @@ const SwitcherList = new Lang.Class({
|
||||
this.actor.connect('allocate', Lang.bind(this, this._allocateTop));
|
||||
|
||||
// Here we use a GenericContainer so that we can force all the
|
||||
// children to have the same width.
|
||||
// children except the separator to have the same width.
|
||||
this._list = new Shell.GenericContainer({ style_class: 'switcher-list-item-container' });
|
||||
this._list.spacing = 0;
|
||||
this._list.connect('style-changed', Lang.bind(this, function() {
|
||||
@ -340,6 +338,7 @@ const SwitcherList = new Lang.Class({
|
||||
|
||||
this._items = [];
|
||||
this._highlighted = -1;
|
||||
this._separator = null;
|
||||
this._squareItems = squareItems;
|
||||
this._minSize = 0;
|
||||
this._scrollableRight = true;
|
||||
@ -402,6 +401,12 @@ const SwitcherList = new Lang.Class({
|
||||
this._itemEntered(index);
|
||||
},
|
||||
|
||||
addSeparator: function () {
|
||||
let box = new St.Bin({ style_class: 'separator' });
|
||||
this._separator = box;
|
||||
this._list.add_actor(box);
|
||||
},
|
||||
|
||||
highlight: function(index, justOutline) {
|
||||
if (this._highlighted != -1) {
|
||||
this._items[this._highlighted].remove_style_pseudo_class('outlined');
|
||||
@ -509,8 +514,14 @@ const SwitcherList = new Lang.Class({
|
||||
_getPreferredWidth: function (actor, forHeight, alloc) {
|
||||
let [maxChildMin, maxChildNat] = this._maxChildWidth(forHeight);
|
||||
|
||||
let separatorWidth = 0;
|
||||
if (this._separator) {
|
||||
let [sepMin, sepNat] = this._separator.get_preferred_width(forHeight);
|
||||
separatorWidth = sepNat + this._list.spacing;
|
||||
}
|
||||
|
||||
let totalSpacing = this._list.spacing * (this._items.length - 1);
|
||||
alloc.min_size = this._items.length * maxChildMin + totalSpacing;
|
||||
alloc.min_size = this._items.length * maxChildMin + separatorWidth + totalSpacing;
|
||||
alloc.natural_size = alloc.min_size;
|
||||
this._minSize = alloc.min_size;
|
||||
},
|
||||
@ -541,7 +552,14 @@ const SwitcherList = new Lang.Class({
|
||||
let [maxChildMin, maxChildNat] = this._maxChildWidth(childHeight);
|
||||
let totalSpacing = this._list.spacing * (this._items.length - 1);
|
||||
|
||||
let childWidth = Math.floor(Math.max(0, box.x2 - box.x1 - totalSpacing) / this._items.length);
|
||||
let separatorWidth = 0;
|
||||
if (this._separator) {
|
||||
let [sepMin, sepNat] = this._separator.get_preferred_width(childHeight);
|
||||
separatorWidth = sepNat;
|
||||
totalSpacing += this._list.spacing;
|
||||
}
|
||||
|
||||
let childWidth = Math.floor(Math.max(0, box.x2 - box.x1 - totalSpacing - separatorWidth) / this._items.length);
|
||||
|
||||
let x = 0;
|
||||
let children = this._list.get_children();
|
||||
@ -561,6 +579,14 @@ const SwitcherList = new Lang.Class({
|
||||
children[i].allocate(childBox, flags);
|
||||
|
||||
x += this._list.spacing + childWidth;
|
||||
} else if (children[i] == this._separator) {
|
||||
// We want the separator to be more compact than the rest.
|
||||
childBox.x1 = x;
|
||||
childBox.y1 = 0;
|
||||
childBox.x2 = x + separatorWidth;
|
||||
childBox.y2 = childHeight;
|
||||
children[i].allocate(childBox, flags);
|
||||
x += this._list.spacing + separatorWidth;
|
||||
} else {
|
||||
// Something else, eg, AppSwitcher's arrows;
|
||||
// we don't allocate it.
|
||||
@ -611,6 +637,5 @@ function drawArrow(area, side) {
|
||||
|
||||
Clutter.cairo_set_source_color(cr, bodyColor);
|
||||
cr.fill();
|
||||
cr.$dispose();
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Shell = imports.gi.Shell;
|
||||
@ -74,9 +73,6 @@ function _wrapTweening(target, tweeningParameters) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!Gtk.Settings.get_default().gtk_enable_animations)
|
||||
tweeningParameters['time'] = 0.000001;
|
||||
|
||||
_addHandler(target, tweeningParameters, 'onStart', _tweenStarted);
|
||||
_addHandler(target, tweeningParameters, 'onComplete', _tweenCompleted);
|
||||
}
|
||||
|
@ -18,7 +18,6 @@ const Panel = imports.ui.panel;
|
||||
const ShellEntry = imports.ui.shellEntry;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const UserMenu = imports.ui.userMenu;
|
||||
const UserWidget = imports.ui.userWidget;
|
||||
|
||||
const Batch = imports.gdm.batch;
|
||||
const GdmUtil = imports.gdm.util;
|
||||
@ -27,6 +26,88 @@ const LoginDialog = imports.gdm.loginDialog;
|
||||
// The timeout before going back automatically to the lock screen (in seconds)
|
||||
const IDLE_TIMEOUT = 2 * 60;
|
||||
|
||||
function versionCompare(required, reference) {
|
||||
required = required.split('.');
|
||||
reference = reference.split('.');
|
||||
|
||||
for (let i = 0; i < required.length; i++) {
|
||||
if (required[i] != reference[i])
|
||||
return required[i] < reference[i];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function isSupported() {
|
||||
try {
|
||||
let params = GLib.Variant.new('(ss)', ['org.gnome.DisplayManager.Manager', 'Version']);
|
||||
let result = Gio.DBus.system.call_sync('org.gnome.DisplayManager',
|
||||
'/org/gnome/DisplayManager/Manager',
|
||||
'org.freedesktop.DBus.Properties',
|
||||
'Get', params, null,
|
||||
Gio.DBusCallFlags.NONE,
|
||||
-1, null);
|
||||
|
||||
let version = result.deep_unpack()[0].deep_unpack();
|
||||
return versionCompare('3.5.91', version);
|
||||
} catch(e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// A widget showing the user avatar and name
|
||||
const UserWidget = new Lang.Class({
|
||||
Name: 'UserWidget',
|
||||
|
||||
_init: function(user) {
|
||||
this._user = user;
|
||||
|
||||
this.actor = new St.BoxLayout({ style_class: 'unlock-dialog-user-name-container',
|
||||
vertical: false });
|
||||
|
||||
this._avatar = new UserMenu.UserAvatarWidget(user);
|
||||
this.actor.add(this._avatar.actor,
|
||||
{ x_fill: true, y_fill: true });
|
||||
|
||||
this._label = new St.Label({ style_class: 'login-dialog-username' });
|
||||
this.actor.add(this._label,
|
||||
{ expand: true,
|
||||
x_fill: true,
|
||||
y_fill: false,
|
||||
y_align: St.Align.MIDDLE });
|
||||
|
||||
this._userLoadedId = this._user.connect('notify::is-loaded',
|
||||
Lang.bind(this, this._updateUser));
|
||||
this._userChangedId = this._user.connect('changed',
|
||||
Lang.bind(this, this._updateUser));
|
||||
if (this._user.is_loaded)
|
||||
this._updateUser();
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
if (this._userLoadedId != 0) {
|
||||
this._user.disconnect(this._userLoadedId);
|
||||
this._userLoadedId = 0;
|
||||
}
|
||||
|
||||
if (this._userChangedId != 0) {
|
||||
this._user.disconnect(this._userChangedId);
|
||||
this._userChangedId = 0;
|
||||
}
|
||||
|
||||
this.actor.destroy();
|
||||
},
|
||||
|
||||
_updateUser: function() {
|
||||
if (this._user.is_loaded)
|
||||
this._label.text = this._user.get_real_name();
|
||||
else
|
||||
this._label.text = '';
|
||||
|
||||
this._avatar.update();
|
||||
}
|
||||
});
|
||||
|
||||
const UnlockDialog = new Lang.Class({
|
||||
Name: 'UnlockDialog',
|
||||
Extends: ModalDialog.ModalDialog,
|
||||
@ -34,7 +115,7 @@ const UnlockDialog = new Lang.Class({
|
||||
_init: function(parentActor) {
|
||||
this.parent({ shellReactive: true,
|
||||
styleClass: 'login-dialog',
|
||||
keybindingMode: Shell.KeyBindingMode.UNLOCK_SCREEN,
|
||||
keybindingMode: Main.KeybindingMode.UNLOCK_SCREEN,
|
||||
parentActor: parentActor
|
||||
});
|
||||
|
||||
@ -47,7 +128,6 @@ const UnlockDialog = new Lang.Class({
|
||||
|
||||
this._greeterClient = new Gdm.Client();
|
||||
this._userVerifier = new GdmUtil.ShellUserVerifier(this._greeterClient, { reauthenticationOnly: true });
|
||||
this._userVerified = false;
|
||||
|
||||
this._userVerifier.connect('ask-question', Lang.bind(this, this._onAskQuestion));
|
||||
this._userVerifier.connect('show-message', Lang.bind(this, this._showMessage));
|
||||
@ -58,7 +138,7 @@ const UnlockDialog = new Lang.Class({
|
||||
this._userVerifier.connect('show-login-hint', Lang.bind(this, this._showLoginHint));
|
||||
this._userVerifier.connect('hide-login-hint', Lang.bind(this, this._hideLoginHint));
|
||||
|
||||
this._userWidget = new UserWidget.UserWidget(this._user);
|
||||
this._userWidget = new UserWidget(this._user);
|
||||
this.contentLayout.add_actor(this._userWidget.actor);
|
||||
|
||||
this._promptLayout = new St.BoxLayout({ style_class: 'login-dialog-prompt-layout',
|
||||
@ -70,7 +150,6 @@ const UnlockDialog = new Lang.Class({
|
||||
|
||||
this._promptEntry = new St.Entry({ style_class: 'login-dialog-prompt-entry',
|
||||
can_focus: true });
|
||||
this._promptEntry.clutter_text.connect('activate', Lang.bind(this, this._doUnlock));
|
||||
this._promptEntry.clutter_text.set_password_char('\u25cf');
|
||||
ShellEntry.addContextMenu(this._promptEntry, { isPassword: true });
|
||||
this.setInitialKeyFocus(this._promptEntry);
|
||||
@ -119,43 +198,41 @@ const UnlockDialog = new Lang.Class({
|
||||
x_align: St.Align.END,
|
||||
y_align: St.Align.MIDDLE });
|
||||
|
||||
let screenSaverSettings = new Gio.Settings({ schema: 'org.gnome.desktop.screensaver' });
|
||||
if (screenSaverSettings.get_boolean('user-switch-enabled')) {
|
||||
let otherUserLabel = new St.Label({ text: _("Log in as another user"),
|
||||
style_class: 'login-dialog-not-listed-label' });
|
||||
this._otherUserButton = new St.Button({ style_class: 'login-dialog-not-listed-button',
|
||||
can_focus: true,
|
||||
child: otherUserLabel,
|
||||
reactive: true,
|
||||
x_align: St.Align.START,
|
||||
x_fill: true });
|
||||
this._otherUserButton.connect('clicked', Lang.bind(this, this._otherUserClicked));
|
||||
this.dialogLayout.add(this._otherUserButton,
|
||||
{ x_align: St.Align.START,
|
||||
x_fill: false });
|
||||
} else {
|
||||
this._otherUserButton = null;
|
||||
}
|
||||
let otherUserLabel = new St.Label({ text: _("Log in as another user"),
|
||||
style_class: 'login-dialog-not-listed-label' });
|
||||
this._otherUserButton = new St.Button({ style_class: 'login-dialog-not-listed-button',
|
||||
can_focus: true,
|
||||
child: otherUserLabel,
|
||||
reactive: true,
|
||||
x_align: St.Align.START,
|
||||
x_fill: true });
|
||||
this._otherUserButton.connect('clicked', Lang.bind(this, this._otherUserClicked));
|
||||
this.dialogLayout.add(this._otherUserButton,
|
||||
{ x_align: St.Align.START,
|
||||
x_fill: false });
|
||||
|
||||
this._updateSensitivity(true);
|
||||
|
||||
let batch = new Batch.Hold();
|
||||
this._userVerifier.begin(this._userName, batch);
|
||||
|
||||
GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
|
||||
this.emit('loaded');
|
||||
return false;
|
||||
}));
|
||||
|
||||
Main.ctrlAltTabManager.addGroup(this.dialogLayout, _("Unlock Window"), 'dialog-password-symbolic');
|
||||
|
||||
this._idleMonitor = new GnomeDesktop.IdleMonitor();
|
||||
this._idleWatchId = this._idleMonitor.add_idle_watch(IDLE_TIMEOUT * 1000, Lang.bind(this, this._escape));
|
||||
this._idleWatchId = this._idleMonitor.add_watch(IDLE_TIMEOUT * 1000, Lang.bind(this, this._escape));
|
||||
},
|
||||
|
||||
_updateSensitivity: function(sensitive) {
|
||||
this._promptEntry.reactive = sensitive;
|
||||
this._promptEntry.clutter_text.editable = sensitive;
|
||||
this._updateOkButtonSensitivity(sensitive && this._promptEntry.text.length > 0);
|
||||
if (this._otherUserButton) {
|
||||
this._otherUserButton.reactive = sensitive;
|
||||
this._otherUserButton.can_focus = sensitive;
|
||||
}
|
||||
this._otherUserButton.reactive = sensitive;
|
||||
this._otherUserButton.can_focus = sensitive;
|
||||
},
|
||||
|
||||
_updateOkButtonSensitivity: function(sensitive) {
|
||||
@ -250,35 +327,18 @@ const UnlockDialog = new Lang.Class({
|
||||
this._userVerifier.answerQuery(query, this._promptEntry.text);
|
||||
},
|
||||
|
||||
_finishUnlock: function() {
|
||||
_onVerificationComplete: function() {
|
||||
this._userVerifier.clear();
|
||||
this.emit('unlocked');
|
||||
},
|
||||
|
||||
_onVerificationComplete: function() {
|
||||
this._userVerified = true;
|
||||
if (!this._userVerifier.hasPendingMessages) {
|
||||
this._finishUnlock();
|
||||
} else {
|
||||
let signalId = this._userVerifier.connect('no-more-messages',
|
||||
Lang.bind(this, function() {
|
||||
this._userVerifier.disconnect(signalId);
|
||||
this._finishUnlock();
|
||||
}));
|
||||
}
|
||||
},
|
||||
|
||||
_onReset: function() {
|
||||
if (!this._userVerified) {
|
||||
this._userVerifier.clear();
|
||||
this.emit('failed');
|
||||
}
|
||||
this.emit('failed');
|
||||
},
|
||||
|
||||
_onVerificationFailed: function() {
|
||||
this._currentQuery = null;
|
||||
this._firstQuestion = true;
|
||||
this._userVerified = false;
|
||||
|
||||
this._promptEntry.text = '';
|
||||
this._promptEntry.clutter_text.set_password_char('\u25cf');
|
||||
@ -318,8 +378,4 @@ const UnlockDialog = new Lang.Class({
|
||||
|
||||
this.destroy();
|
||||
},
|
||||
|
||||
addCharacter: function(unichar) {
|
||||
this._promptEntry.clutter_text.insert_unichar(unichar);
|
||||
},
|
||||
});
|
||||
|
@ -4,20 +4,17 @@ const AccountsService = imports.gi.AccountsService;
|
||||
const Gdm = imports.gi.Gdm;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Lang = imports.lang;
|
||||
const Pango = imports.gi.Pango;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
const Tp = imports.gi.TelepathyGLib;
|
||||
const Atk = imports.gi.Atk;
|
||||
const Clutter = imports.gi.Clutter;
|
||||
|
||||
const BoxPointer = imports.ui.boxpointer;
|
||||
const GnomeSession = imports.misc.gnomeSession;
|
||||
const LoginManager = imports.misc.loginManager;
|
||||
const Main = imports.ui.main;
|
||||
const ModalDialog = imports.ui.modalDialog;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const Params = imports.misc.params;
|
||||
@ -25,17 +22,15 @@ const Util = imports.misc.util;
|
||||
|
||||
const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown';
|
||||
const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver';
|
||||
const PRIVACY_SCHEMA = 'org.gnome.desktop.privacy'
|
||||
const DISABLE_USER_SWITCH_KEY = 'disable-user-switching';
|
||||
const DISABLE_LOCK_SCREEN_KEY = 'disable-lock-screen';
|
||||
const DISABLE_LOG_OUT_KEY = 'disable-log-out';
|
||||
const LOCK_ENABLED_KEY = 'lock-enabled';
|
||||
const ALWAYS_SHOW_LOG_OUT_KEY = 'always-show-log-out';
|
||||
const SHOW_FULL_NAME_IN_TOP_BAR_KEY = 'show-full-name-in-top-bar';
|
||||
const SHOW_FULL_NAME_KEY = 'show-full-name';
|
||||
|
||||
const DIALOG_ICON_SIZE = 64;
|
||||
|
||||
const MAX_USERS_IN_SESSION_DIALOG = 5;
|
||||
|
||||
const IMStatus = {
|
||||
AVAILABLE: 0,
|
||||
BUSY: 1,
|
||||
@ -46,17 +41,6 @@ const IMStatus = {
|
||||
LAST: 6
|
||||
};
|
||||
|
||||
|
||||
const SystemdLoginSessionIface = <interface name='org.freedesktop.login1.Session'>
|
||||
<property name="Id" type="s" access="read"/>
|
||||
<property name="Remote" type="b" access="read"/>
|
||||
<property name="Class" type="s" access="read"/>
|
||||
<property name="Type" type="s" access="read"/>
|
||||
<property name="State" type="s" access="read"/>
|
||||
</interface>;
|
||||
|
||||
const SystemdLoginSession = Gio.DBusProxy.makeProxyWrapper(SystemdLoginSessionIface);
|
||||
|
||||
// Adapted from gdm/gui/user-switch-applet/applet.c
|
||||
//
|
||||
// Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
|
||||
@ -84,7 +68,7 @@ const UserAvatarWidget = new Lang.Class({
|
||||
|
||||
update: function() {
|
||||
let iconFile = this._user.get_icon_file();
|
||||
if (iconFile && !GLib.file_test(iconFile, GLib.FileTest.EXISTS))
|
||||
if (!GLib.file_test(iconFile, GLib.FileTest.EXISTS))
|
||||
iconFile = null;
|
||||
|
||||
if (iconFile) {
|
||||
@ -493,7 +477,6 @@ const UserMenuButton = new Lang.Class({
|
||||
|
||||
this._screenSaverSettings = new Gio.Settings({ schema: SCREENSAVER_SCHEMA });
|
||||
this._lockdownSettings = new Gio.Settings({ schema: LOCKDOWN_SCHEMA });
|
||||
this._privacySettings = new Gio.Settings({ schema: PRIVACY_SCHEMA });
|
||||
|
||||
this._userManager = AccountsService.UserManager.get_default();
|
||||
|
||||
@ -570,12 +553,10 @@ const UserMenuButton = new Lang.Class({
|
||||
Lang.bind(this, this._updateLogout));
|
||||
this._lockdownSettings.connect('changed::' + DISABLE_LOCK_SCREEN_KEY,
|
||||
Lang.bind(this, this._updateLockScreen));
|
||||
global.settings.connect('changed::' + ALWAYS_SHOW_LOG_OUT_KEY,
|
||||
Lang.bind(this, this._updateLogout));
|
||||
this._screenSaverSettings.connect('changed::' + SHOW_FULL_NAME_IN_TOP_BAR_KEY,
|
||||
this._screenSaverSettings.connect('changed::' + SHOW_FULL_NAME_KEY,
|
||||
Lang.bind(this, this._updateUserName));
|
||||
this._privacySettings.connect('changed::' + SHOW_FULL_NAME_IN_TOP_BAR_KEY,
|
||||
Lang.bind(this, this._updateUserName));
|
||||
global.settings.connect('changed::' + SHOW_FULL_NAME_KEY,
|
||||
Lang.bind(this, this._updateUserName));
|
||||
this._updateSwitchUser();
|
||||
this._updateLogout();
|
||||
this._updateLockScreen();
|
||||
@ -600,8 +581,6 @@ const UserMenuButton = new Lang.Class({
|
||||
Lang.bind(this, this._updateHaveShutdown));
|
||||
|
||||
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
|
||||
if (Main.screenShield)
|
||||
Main.screenShield.connect('locked-changed', Lang.bind(this, this._updatePresenceIcon));
|
||||
this._sessionUpdated();
|
||||
},
|
||||
|
||||
@ -623,10 +602,10 @@ const UserMenuButton = new Lang.Class({
|
||||
},
|
||||
|
||||
_updateUserName: function() {
|
||||
let settings = this._privacySettings;
|
||||
let settings = global.settings;
|
||||
if (Main.sessionMode.isLocked)
|
||||
settings = this._screenSaverSettings;
|
||||
if (this._user.is_loaded && settings.get_boolean(SHOW_FULL_NAME_IN_TOP_BAR_KEY))
|
||||
if (this._user.is_loaded && settings.get_boolean(SHOW_FULL_NAME_KEY))
|
||||
this._name.set_text(this._user.get_real_name());
|
||||
else
|
||||
this._name.set_text("");
|
||||
@ -657,7 +636,7 @@ const UserMenuButton = new Lang.Class({
|
||||
|
||||
_updateLockScreen: function() {
|
||||
let allowLockScreen = !this._lockdownSettings.get_boolean(DISABLE_LOCK_SCREEN_KEY);
|
||||
this._lockScreenItem.actor.visible = allowLockScreen && LoginManager.canLock();
|
||||
this._lockScreenItem.actor.visible = allowLockScreen;
|
||||
},
|
||||
|
||||
_updateInstallUpdates: function() {
|
||||
@ -721,11 +700,6 @@ const UserMenuButton = new Lang.Class({
|
||||
this._iconBox.child = this._idleIcon;
|
||||
else
|
||||
this._iconBox.child = this._offlineIcon;
|
||||
|
||||
if (Main.sessionMode.isLocked)
|
||||
this._iconBox.visible = Main.screenShield.locked;
|
||||
else
|
||||
this._iconBox.visible = true;
|
||||
},
|
||||
|
||||
_setupAccounts: function() {
|
||||
@ -846,7 +820,7 @@ const UserMenuButton = new Lang.Class({
|
||||
|
||||
_onMyAccountActivate: function() {
|
||||
Main.overview.hide();
|
||||
let app = Shell.AppSystem.get_default().lookup_app('gnome-user-accounts-panel.desktop');
|
||||
let app = Shell.AppSystem.get_default().lookup_setting('gnome-user-accounts-panel.desktop');
|
||||
app.activate();
|
||||
},
|
||||
|
||||
@ -865,8 +839,7 @@ const UserMenuButton = new Lang.Class({
|
||||
_onLoginScreenActivate: function() {
|
||||
this.menu.close(BoxPointer.PopupAnimation.NONE);
|
||||
Main.overview.hide();
|
||||
if (Main.screenShield)
|
||||
Main.screenShield.lock(false);
|
||||
Main.screenShield.lock(false);
|
||||
Gdm.goto_login_session_sync(null);
|
||||
},
|
||||
|
||||
@ -882,117 +855,25 @@ const UserMenuButton = new Lang.Class({
|
||||
this._session.RebootRemote();
|
||||
},
|
||||
|
||||
_openSessionWarnDialog: function(sessions) {
|
||||
let dialog = new ModalDialog.ModalDialog();
|
||||
let subjectLabel = new St.Label({ style_class: 'end-session-dialog-subject',
|
||||
text: _("Other users are logged in.") });
|
||||
dialog.contentLayout.add(subjectLabel, { y_fill: true,
|
||||
y_align: St.Align.START });
|
||||
|
||||
let descriptionLabel = new St.Label({ style_class: 'end-session-dialog-description'});
|
||||
descriptionLabel.set_text(_("Shutting down might cause them to lose unsaved work."));
|
||||
descriptionLabel.clutter_text.line_wrap = true;
|
||||
dialog.contentLayout.add(descriptionLabel, { x_fill: true,
|
||||
y_fill: true,
|
||||
y_align: St.Align.START });
|
||||
|
||||
let scrollView = new St.ScrollView({ style_class: 'end-session-dialog-app-list' });
|
||||
scrollView.add_style_class_name('vfade');
|
||||
scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
|
||||
dialog.contentLayout.add(scrollView, { x_fill: true, y_fill: true });
|
||||
|
||||
let userList = new St.BoxLayout({ vertical: true });
|
||||
scrollView.add_actor(userList);
|
||||
|
||||
for (let i = 0; i < sessions.length; i++) {
|
||||
let session = sessions[i];
|
||||
let userEntry = new St.BoxLayout({ style_class: 'login-dialog-user-list-item',
|
||||
vertical: false });
|
||||
let avatar = new UserAvatarWidget(session.user);
|
||||
avatar.update();
|
||||
userEntry.add(avatar.actor);
|
||||
|
||||
let userLabelText = "";;
|
||||
let userName = session.user.get_real_name() ?
|
||||
session.user.get_real_name() : session.username;
|
||||
|
||||
if (session.info.remote)
|
||||
/* Translators: Remote here refers to a remote session, like a ssh login */
|
||||
userLabelText = _("%s (remote)").format(userName);
|
||||
else if (session.info.type == "tty")
|
||||
/* Translators: Console here refers to a tty like a VT console */
|
||||
userLabelText = _("%s (console)").format(userName);
|
||||
else
|
||||
userLabelText = userName;
|
||||
|
||||
let textLayout = new St.BoxLayout({ style_class: 'login-dialog-user-list-item-text-box',
|
||||
vertical: true });
|
||||
textLayout.add(new St.Label({ text: userLabelText }),
|
||||
{ y_fill: false,
|
||||
y_align: St.Align.MIDDLE,
|
||||
expand: true });
|
||||
userEntry.add(textLayout, { expand: true });
|
||||
userList.add(userEntry, { x_fill: true });
|
||||
}
|
||||
|
||||
let cancelButton = { label: _("Cancel"),
|
||||
action: function() { dialog.close(); },
|
||||
key: Clutter.Escape };
|
||||
|
||||
let powerOffButton = { label: _("Power Off"), action: Lang.bind(this, function() {
|
||||
dialog.close();
|
||||
this._session.ShutdownRemote();
|
||||
}), default: true };
|
||||
|
||||
dialog.setButtons([cancelButton, powerOffButton]);
|
||||
|
||||
dialog.open();
|
||||
},
|
||||
|
||||
_onSuspendOrPowerOffActivate: function() {
|
||||
Main.overview.hide();
|
||||
|
||||
if (this._haveShutdown &&
|
||||
this._suspendOrPowerOffItem.state == PopupMenu.PopupAlternatingMenuItemState.DEFAULT) {
|
||||
this._loginManager.listSessions(Lang.bind(this,
|
||||
function(result) {
|
||||
let sessions = [];
|
||||
let n = 0;
|
||||
for (let i = 0; i < result.length; i++) {
|
||||
let[id, uid, userName, seat, sessionPath] = result[i];
|
||||
let proxy = new SystemdLoginSession(Gio.DBus.system,
|
||||
'org.freedesktop.login1',
|
||||
sessionPath);
|
||||
|
||||
if (proxy.Class != 'user')
|
||||
continue;
|
||||
|
||||
if (proxy.State == 'closing')
|
||||
continue;
|
||||
|
||||
if (proxy.Id == GLib.getenv('XDG_SESSION_ID'))
|
||||
continue;
|
||||
|
||||
sessions.push({ user: this._userManager.get_user(userName),
|
||||
username: userName,
|
||||
info: { type: proxy.Type,
|
||||
remote: proxy.Remote }
|
||||
});
|
||||
|
||||
// limit the number of entries
|
||||
n++;
|
||||
if (n == MAX_USERS_IN_SESSION_DIALOG)
|
||||
break;
|
||||
}
|
||||
|
||||
if (n != 0)
|
||||
this._openSessionWarnDialog(sessions);
|
||||
else
|
||||
this._session.ShutdownRemote();
|
||||
}));
|
||||
this._session.ShutdownRemote();
|
||||
} else {
|
||||
this.menu.close(BoxPointer.PopupAnimation.NONE);
|
||||
this._loginManager.suspend();
|
||||
if (this._screenSaverSettings.get_boolean(LOCK_ENABLED_KEY)) {
|
||||
let tmpId = Main.screenShield.connect('lock-screen-shown', Lang.bind(this, function() {
|
||||
Main.screenShield.disconnect(tmpId);
|
||||
|
||||
this._loginManager.suspend();
|
||||
}));
|
||||
|
||||
this.menu.close(BoxPointer.PopupAnimation.NONE);
|
||||
Main.screenShield.lock(true);
|
||||
} else {
|
||||
this._loginManager.suspend();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -1,61 +0,0 @@
|
||||
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
//
|
||||
// A widget showing the user avatar and name
|
||||
const AccountsService = imports.gi.AccountsService;
|
||||
const Lang = imports.lang;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const UserMenu = imports.ui.userMenu;
|
||||
|
||||
const UserWidget = new Lang.Class({
|
||||
Name: 'UserWidget',
|
||||
|
||||
_init: function(user) {
|
||||
this._user = user;
|
||||
|
||||
this.actor = new St.BoxLayout({ style_class: 'user-widget',
|
||||
vertical: false });
|
||||
|
||||
this._avatar = new UserMenu.UserAvatarWidget(user);
|
||||
this.actor.add(this._avatar.actor,
|
||||
{ x_fill: true, y_fill: true });
|
||||
|
||||
this._label = new St.Label({ style_class: 'user-widget-label' });
|
||||
this.actor.add(this._label,
|
||||
{ expand: true,
|
||||
x_fill: true,
|
||||
y_fill: false,
|
||||
y_align: St.Align.MIDDLE });
|
||||
|
||||
this._userLoadedId = this._user.connect('notify::is-loaded',
|
||||
Lang.bind(this, this._updateUser));
|
||||
this._userChangedId = this._user.connect('changed',
|
||||
Lang.bind(this, this._updateUser));
|
||||
if (this._user.is_loaded)
|
||||
this._updateUser();
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
if (this._userLoadedId != 0) {
|
||||
this._user.disconnect(this._userLoadedId);
|
||||
this._userLoadedId = 0;
|
||||
}
|
||||
|
||||
if (this._userChangedId != 0) {
|
||||
this._user.disconnect(this._userChangedId);
|
||||
this._userChangedId = 0;
|
||||
}
|
||||
|
||||
this.actor.destroy();
|
||||
},
|
||||
|
||||
_updateUser: function() {
|
||||
if (this._user.is_loaded)
|
||||
this._label.text = this._user.get_real_name();
|
||||
else
|
||||
this._label.text = '';
|
||||
|
||||
this._avatar.update();
|
||||
}
|
||||
});
|
@ -12,8 +12,6 @@ const St = imports.gi.St;
|
||||
|
||||
const AppDisplay = imports.ui.appDisplay;
|
||||
const Main = imports.ui.main;
|
||||
const OverviewControls = imports.ui.overviewControls;
|
||||
const Params = imports.misc.params;
|
||||
const RemoteSearch = imports.ui.remoteSearch;
|
||||
const Search = imports.ui.search;
|
||||
const SearchDisplay = imports.ui.searchDisplay;
|
||||
@ -24,12 +22,6 @@ const WorkspacesView = imports.ui.workspacesView;
|
||||
|
||||
const SHELL_KEYBINDINGS_SCHEMA = 'org.gnome.shell.keybindings';
|
||||
|
||||
const ViewPage = {
|
||||
WINDOWS: 1,
|
||||
APPS: 2,
|
||||
SEARCH: 3
|
||||
};
|
||||
|
||||
const FocusTrap = new Lang.Class({
|
||||
Name: 'FocusTrap',
|
||||
Extends: St.Widget,
|
||||
@ -42,14 +34,6 @@ const FocusTrap = new Lang.Class({
|
||||
}
|
||||
});
|
||||
|
||||
function getTermsForSearchString(searchString) {
|
||||
searchString = searchString.replace(/^\s+/g, '').replace(/\s+$/g, '');
|
||||
if (searchString == '')
|
||||
return [];
|
||||
|
||||
let terms = searchString.split(/\s+/);
|
||||
return terms;
|
||||
}
|
||||
|
||||
const ViewSelector = new Lang.Class({
|
||||
Name: 'ViewSelector',
|
||||
@ -57,13 +41,13 @@ const ViewSelector = new Lang.Class({
|
||||
_init : function(searchEntry, showAppsButton) {
|
||||
this.actor = new Shell.Stack({ name: 'viewSelector' });
|
||||
|
||||
this._showAppsBlocked = false;
|
||||
this._showAppsButton = showAppsButton;
|
||||
this._showAppsButton.connect('notify::checked', Lang.bind(this, this._onShowAppsButtonToggled));
|
||||
|
||||
this._activePage = null;
|
||||
|
||||
this._searchActive = false;
|
||||
this.active = false;
|
||||
this._searchPending = false;
|
||||
this._searchTimeoutId = 0;
|
||||
|
||||
this._searchSystem = new Search.SearchSystem();
|
||||
@ -83,26 +67,26 @@ const ViewSelector = new Lang.Class({
|
||||
this._entry.connect('notify::mapped', Lang.bind(this, this._onMapped));
|
||||
global.stage.connect('notify::key-focus', Lang.bind(this, this._onStageKeyFocusChanged));
|
||||
|
||||
this._entry.set_primary_icon(new St.Icon({ style_class: 'search-entry-icon',
|
||||
icon_name: 'edit-find-symbolic' }));
|
||||
this._clearIcon = new St.Icon({ style_class: 'search-entry-icon',
|
||||
icon_name: 'edit-clear-symbolic' });
|
||||
this._inactiveIcon = new St.Icon({ style_class: 'search-entry-icon',
|
||||
icon_name: 'edit-find-symbolic' });
|
||||
this._activeIcon = new St.Icon({ style_class: 'search-entry-icon',
|
||||
icon_name: 'edit-clear-symbolic' });
|
||||
this._entry.set_secondary_icon(this._inactiveIcon);
|
||||
|
||||
this._iconClickedId = 0;
|
||||
this._capturedEventId = 0;
|
||||
|
||||
this._workspacesDisplay = new WorkspacesView.WorkspacesDisplay();
|
||||
this._workspacesPage = this._addPage(this._workspacesDisplay.actor,
|
||||
this._workspacesPage = this._addPage(this._workspacesDisplay.actor, null,
|
||||
_("Windows"), 'emblem-documents-symbolic');
|
||||
|
||||
this._appDisplay = new AppDisplay.AppDisplay();
|
||||
this._appsPage = this._addPage(this._appDisplay.actor,
|
||||
this._appDisplay = new AppDisplay.AllAppDisplay();
|
||||
this._appsPage = this._addPage(this._appDisplay.actor, null,
|
||||
_("Applications"), 'view-grid-symbolic');
|
||||
|
||||
this._searchResults = new SearchDisplay.SearchResults(this._searchSystem);
|
||||
this._searchPage = this._addPage(this._searchResults.actor,
|
||||
_("Search"), 'edit-find-symbolic',
|
||||
{ a11yFocus: this._entry });
|
||||
this._searchPage = this._addPage(this._searchResults.actor, this._entry,
|
||||
_("Search"), 'edit-find-symbolic');
|
||||
|
||||
this._searchSettings = new Gio.Settings({ schema: Search.SEARCH_PROVIDERS_SCHEMA });
|
||||
this._searchSettings.connect('changed::disabled', Lang.bind(this, this._reloadRemoteProviders));
|
||||
@ -113,6 +97,7 @@ const ViewSelector = new Lang.Class({
|
||||
// Wanda comes obviously first
|
||||
this.addSearchProvider(new Wanda.WandaSearchProvider());
|
||||
this.addSearchProvider(new AppDisplay.AppSearchProvider());
|
||||
this.addSearchProvider(new AppDisplay.SettingsSearchProvider());
|
||||
|
||||
// Load remote search providers provided by applications
|
||||
RemoteSearch.loadRemoteSearchProviders(Lang.bind(this, this.addSearchProvider));
|
||||
@ -128,6 +113,9 @@ const ViewSelector = new Lang.Class({
|
||||
|
||||
global.focus_manager.add_group(this._searchResults.actor);
|
||||
|
||||
Main.overview.connect('item-drag-begin',
|
||||
Lang.bind(this, this._resetShowAppsButton));
|
||||
|
||||
this._stageKeyPressId = 0;
|
||||
Main.overview.connect('showing', Lang.bind(this,
|
||||
function () {
|
||||
@ -147,8 +135,8 @@ const ViewSelector = new Lang.Class({
|
||||
Main.wm.addKeybinding('toggle-application-view',
|
||||
new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }),
|
||||
Meta.KeyBindingFlags.NONE,
|
||||
Shell.KeyBindingMode.NORMAL |
|
||||
Shell.KeyBindingMode.OVERVIEW,
|
||||
Main.KeybindingMode.NORMAL |
|
||||
Main.KeybindingMode.OVERVIEW,
|
||||
Lang.bind(this, this._toggleAppsPage));
|
||||
},
|
||||
|
||||
@ -160,7 +148,6 @@ const ViewSelector = new Lang.Class({
|
||||
show: function() {
|
||||
this._activePage = this._workspacesPage;
|
||||
|
||||
this.reset();
|
||||
this._appsPage.hide();
|
||||
this._searchPage.hide();
|
||||
this._workspacesDisplay.show();
|
||||
@ -168,7 +155,7 @@ const ViewSelector = new Lang.Class({
|
||||
if (!this._workspacesDisplay.activeWorkspaceHasMaximizedWindows())
|
||||
Main.overview.fadeOutDesktop();
|
||||
|
||||
this._showPage(this._workspacesPage, true);
|
||||
this._showPage(this._workspacesPage);
|
||||
},
|
||||
|
||||
zoomFromOverview: function() {
|
||||
@ -182,16 +169,14 @@ const ViewSelector = new Lang.Class({
|
||||
this._workspacesDisplay.hide();
|
||||
},
|
||||
|
||||
_addPage: function(actor, name, a11yIcon, params) {
|
||||
params = Params.parse(params, { a11yFocus: null });
|
||||
|
||||
_addPage: function(actor, a11yFocus, name, a11yIcon) {
|
||||
let page = new St.Bin({ child: actor,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.START,
|
||||
x_fill: true,
|
||||
y_fill: true });
|
||||
if (params.a11yFocus)
|
||||
Main.ctrlAltTabManager.addGroup(params.a11yFocus, name, a11yIcon);
|
||||
if (a11yFocus)
|
||||
Main.ctrlAltTabManager.addGroup(a11yFocus, name, a11yIcon);
|
||||
else
|
||||
Main.ctrlAltTabManager.addGroup(actor, name, a11yIcon,
|
||||
{ proxy: this.actor,
|
||||
@ -204,40 +189,29 @@ const ViewSelector = new Lang.Class({
|
||||
return page;
|
||||
},
|
||||
|
||||
_fadePageIn: function(oldPage) {
|
||||
if (oldPage)
|
||||
oldPage.hide();
|
||||
|
||||
this.emit('page-empty');
|
||||
|
||||
this._activePage.show();
|
||||
Tweener.addTween(this._activePage,
|
||||
{ opacity: 255,
|
||||
time: OverviewControls.SIDE_CONTROLS_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
},
|
||||
|
||||
_showPage: function(page, noFade) {
|
||||
if (page == this._activePage)
|
||||
_showPage: function(page) {
|
||||
if(page == this._activePage)
|
||||
return;
|
||||
|
||||
let oldPage = this._activePage;
|
||||
this._activePage = page;
|
||||
this.emit('page-changed');
|
||||
|
||||
if (oldPage && !noFade)
|
||||
Tweener.addTween(oldPage,
|
||||
if(this._activePage) {
|
||||
Tweener.addTween(this._activePage,
|
||||
{ opacity: 0,
|
||||
time: OverviewControls.SIDE_CONTROLS_ANIMATION_TIME,
|
||||
time: 0.1,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: Lang.bind(this,
|
||||
function() {
|
||||
this._fadePageIn(oldPage);
|
||||
this._activePage.hide();
|
||||
this._activePage = page;
|
||||
})
|
||||
});
|
||||
else
|
||||
this._fadePageIn(oldPage);
|
||||
}
|
||||
|
||||
page.show();
|
||||
Tweener.addTween(page,
|
||||
{ opacity: 255,
|
||||
time: 0.1,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
},
|
||||
|
||||
_a11yFocusPage: function(page) {
|
||||
@ -246,19 +220,15 @@ const ViewSelector = new Lang.Class({
|
||||
},
|
||||
|
||||
_onShowAppsButtonToggled: function() {
|
||||
if (this._showAppsBlocked)
|
||||
return;
|
||||
|
||||
this._showPage(this._showAppsButton.checked ?
|
||||
this._appsPage : this._workspacesPage);
|
||||
if (this.active)
|
||||
this.reset();
|
||||
else
|
||||
this._showPage(this._showAppsButton.checked ? this._appsPage
|
||||
: this._workspacesPage);
|
||||
},
|
||||
|
||||
_resetShowAppsButton: function() {
|
||||
this._showAppsBlocked = true;
|
||||
this._showAppsButton.checked = false;
|
||||
this._showAppsBlocked = false;
|
||||
|
||||
this._showPage(this._workspacesPage, true);
|
||||
},
|
||||
|
||||
_onStageKeyPress: function(actor, event) {
|
||||
@ -271,16 +241,17 @@ const ViewSelector = new Lang.Class({
|
||||
let symbol = event.get_key_symbol();
|
||||
|
||||
if (symbol == Clutter.Escape) {
|
||||
if (this._searchActive)
|
||||
if (this.active)
|
||||
this.reset();
|
||||
else if (this._showAppsButton.checked)
|
||||
this._showAppsButton.checked = false;
|
||||
this._resetShowAppsButton();
|
||||
else
|
||||
Main.overview.hide();
|
||||
return true;
|
||||
} else if (this._shouldTriggerSearch(symbol)) {
|
||||
} else if (Clutter.keysym_to_unicode(symbol) ||
|
||||
(symbol == Clutter.BackSpace && this.active)) {
|
||||
this.startSearch(event);
|
||||
} else if (!this._searchActive) {
|
||||
} else if (!this.active) {
|
||||
if (symbol == Clutter.Tab || symbol == Clutter.Down) {
|
||||
this._activePage.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
|
||||
return true;
|
||||
@ -343,17 +314,6 @@ const ViewSelector = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
_shouldTriggerSearch: function(symbol) {
|
||||
let unicode = Clutter.keysym_to_unicode(symbol);
|
||||
if (unicode == 0)
|
||||
return false;
|
||||
|
||||
if (getTermsForSearchString(String.fromCharCode(unicode)).length > 0)
|
||||
return true;
|
||||
|
||||
return symbol == Clutter.BackSpace && this._searchActive;
|
||||
},
|
||||
|
||||
startSearch: function(event) {
|
||||
global.stage.set_key_focus(this._text);
|
||||
this._text.event(event, true);
|
||||
@ -365,39 +325,39 @@ const ViewSelector = new Lang.Class({
|
||||
},
|
||||
|
||||
_onTextChanged: function (se, prop) {
|
||||
let terms = getTermsForSearchString(this._entry.get_text());
|
||||
|
||||
let searchPreviouslyActive = this._searchActive;
|
||||
this._searchActive = (terms.length > 0);
|
||||
|
||||
let startSearch = this._searchActive && !searchPreviouslyActive;
|
||||
if (startSearch)
|
||||
let searchPreviouslyActive = this.active;
|
||||
this.active = this._entry.get_text() != '';
|
||||
this._searchPending = this.active && !searchPreviouslyActive;
|
||||
if (this._searchPending) {
|
||||
this._searchResults.startingSearch();
|
||||
}
|
||||
if (this.active) {
|
||||
this._entry.set_secondary_icon(this._activeIcon);
|
||||
|
||||
if (this._searchActive) {
|
||||
this._entry.set_secondary_icon(this._clearIcon);
|
||||
|
||||
if (this._iconClickedId == 0)
|
||||
if (this._iconClickedId == 0) {
|
||||
this._iconClickedId = this._entry.connect('secondary-icon-clicked',
|
||||
Lang.bind(this, this.reset));
|
||||
|
||||
if (this._searchTimeoutId == 0)
|
||||
this._searchTimeoutId = Mainloop.timeout_add(150,
|
||||
Lang.bind(this, this._doSearch));
|
||||
} else {
|
||||
if (this._iconClickedId > 0) {
|
||||
this._entry.disconnect(this._iconClickedId);
|
||||
this._iconClickedId = 0;
|
||||
Lang.bind(this, function() {
|
||||
this.reset();
|
||||
}));
|
||||
}
|
||||
} else {
|
||||
if (this._iconClickedId > 0)
|
||||
this._entry.disconnect(this._iconClickedId);
|
||||
this._iconClickedId = 0;
|
||||
|
||||
this._entry.set_secondary_icon(this._inactiveIcon);
|
||||
this._searchCancelled();
|
||||
}
|
||||
if (!this.active) {
|
||||
if (this._searchTimeoutId > 0) {
|
||||
Mainloop.source_remove(this._searchTimeoutId);
|
||||
this._searchTimeoutId = 0;
|
||||
}
|
||||
|
||||
this._entry.set_secondary_icon(null);
|
||||
this._searchCancelled();
|
||||
return;
|
||||
}
|
||||
if (this._searchTimeoutId > 0)
|
||||
return;
|
||||
this._searchTimeoutId = Mainloop.timeout_add(150, Lang.bind(this, this._doSearch));
|
||||
},
|
||||
|
||||
_onKeyPress: function(entry, event) {
|
||||
@ -407,7 +367,16 @@ const ViewSelector = new Lang.Class({
|
||||
this.reset();
|
||||
return true;
|
||||
}
|
||||
} else if (this._searchActive) {
|
||||
} else if (symbol == Clutter.Return || symbol == Clutter.KP_Enter) {
|
||||
// We can't connect to 'activate' here because search providers
|
||||
// might want to do something with the modifiers in activateDefault.
|
||||
if (this._searchTimeoutId > 0) {
|
||||
Mainloop.source_remove(this._searchTimeoutId);
|
||||
this._doSearch();
|
||||
}
|
||||
this._searchResults.activateDefault();
|
||||
return true;
|
||||
} else if (this.active) {
|
||||
let arrowNext, nextDirection;
|
||||
if (entry.get_text_direction() == Clutter.TextDirection.RTL) {
|
||||
arrowNext = Clutter.Left;
|
||||
@ -431,15 +400,6 @@ const ViewSelector = new Lang.Class({
|
||||
} else if (symbol == arrowNext && this._text.position == -1) {
|
||||
this._searchResults.navigateFocus(nextDirection);
|
||||
return true;
|
||||
} else if (symbol == Clutter.Return || symbol == Clutter.KP_Enter) {
|
||||
// We can't connect to 'activate' here because search providers
|
||||
// might want to do something with the modifiers in activateDefault.
|
||||
if (this._searchTimeoutId > 0) {
|
||||
Mainloop.source_remove(this._searchTimeoutId);
|
||||
this._doSearch();
|
||||
}
|
||||
this._searchResults.activateDefault();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@ -462,10 +422,9 @@ const ViewSelector = new Lang.Class({
|
||||
|
||||
_doSearch: function () {
|
||||
this._searchTimeoutId = 0;
|
||||
let text = this._text.get_text().replace(/^\s+/g, '').replace(/\s+$/g, '');
|
||||
this._searchResults.doSearch(text);
|
||||
|
||||
let terms = getTermsForSearchString(this._entry.get_text());
|
||||
|
||||
this._searchSystem.updateSearchResults(terms);
|
||||
this._showPage(this._searchPage);
|
||||
},
|
||||
|
||||
@ -502,31 +461,6 @@ const ViewSelector = new Lang.Class({
|
||||
removeSearchProvider: function(provider) {
|
||||
this._searchSystem.unregisterProvider(provider);
|
||||
this._searchResults.destroyProviderMeta(provider);
|
||||
},
|
||||
|
||||
getActivePage: function() {
|
||||
if (this._activePage == this._workspacesPage)
|
||||
return ViewPage.WINDOWS;
|
||||
else if (this._activePage == this._appsPage)
|
||||
return ViewPage.APPS;
|
||||
else
|
||||
return ViewPage.SEARCH;
|
||||
},
|
||||
|
||||
fadeIn: function() {
|
||||
let actor = this._activePage;
|
||||
Tweener.addTween(actor, { opacity: 255,
|
||||
time: OverviewControls.SIDE_CONTROLS_ANIMATION_TIME / 2,
|
||||
transition: 'easeInQuad'
|
||||
});
|
||||
},
|
||||
|
||||
fadeHalf: function() {
|
||||
let actor = this._activePage;
|
||||
Tweener.addTween(actor, { opacity: 128,
|
||||
time: OverviewControls.SIDE_CONTROLS_ANIMATION_TIME / 2,
|
||||
transition: 'easeOutQuad'
|
||||
});
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(ViewSelector.prototype);
|
||||
|
@ -11,14 +11,12 @@ const Layout = imports.ui.layout;
|
||||
const Main = imports.ui.main;
|
||||
const Panel = imports.ui.panel;
|
||||
|
||||
// we could make these gsettings
|
||||
const FISH_NAME = 'wanda';
|
||||
const FISH_FILENAME = 'wanda.png';
|
||||
const FISH_SPEED = 300;
|
||||
const FISH_COMMAND = 'fortune';
|
||||
// The size of an individual frame in the animation
|
||||
const FISH_HEIGHT = 22;
|
||||
const FISH_WIDTH = 36;
|
||||
|
||||
const GNOME_PANEL_PIXMAPDIR = '../gnome-panel/fish';
|
||||
const FISH_GROUP = 'Fish Animation';
|
||||
|
||||
const MAGIC_FISH_KEY = 'free the fish';
|
||||
@ -28,16 +26,33 @@ const WandaIcon = new Lang.Class({
|
||||
Extends: IconGrid.BaseIcon,
|
||||
|
||||
_init : function(fish, label, params) {
|
||||
this.parent(label, params);
|
||||
|
||||
this._fish = fish;
|
||||
this._imageFile = GLib.build_filenamev([global.datadir, fish + '.png']);
|
||||
let file = GLib.build_filenamev([global.datadir, GNOME_PANEL_PIXMAPDIR, fish + '.fish']);
|
||||
|
||||
this._imgHeight = FISH_HEIGHT;
|
||||
this._imgWidth = FISH_WIDTH;
|
||||
if (GLib.file_test(file, GLib.FileTest.EXISTS)) {
|
||||
this._keyfile = new GLib.KeyFile();
|
||||
this._keyfile.load_from_file(file, GLib.KeyFileFlags.NONE);
|
||||
|
||||
this._imageFile = GLib.build_filenamev([global.datadir, GNOME_PANEL_PIXMAPDIR,
|
||||
this._keyfile.get_string(FISH_GROUP, 'image')]);
|
||||
|
||||
let tmpPixbuf = GdkPixbuf.Pixbuf.new_from_file(this._imageFile);
|
||||
|
||||
this._imgHeight = tmpPixbuf.height;
|
||||
this._imgWidth = tmpPixbuf.width / this._keyfile.get_integer(FISH_GROUP, 'frames');
|
||||
} else {
|
||||
this._imageFile = null;
|
||||
}
|
||||
|
||||
this.parent(label, params);
|
||||
},
|
||||
|
||||
createIcon: function(iconSize) {
|
||||
if (!this._imageFile) {
|
||||
return new St.Icon({ icon_name: 'face-smile',
|
||||
icon_size: iconSize });
|
||||
}
|
||||
|
||||
this._animations = new Panel.Animation(this._imageFile, this._imgWidth, this._imgHeight, FISH_SPEED);
|
||||
this._animations.play();
|
||||
return this._animations.actor;
|
||||
|
@ -105,74 +105,74 @@ const WindowManager = new Lang.Class({
|
||||
|
||||
this._workspaceSwitcherPopup = null;
|
||||
this.setCustomKeybindingHandler('switch-to-workspace-left',
|
||||
Shell.KeyBindingMode.NORMAL |
|
||||
Shell.KeyBindingMode.OVERVIEW,
|
||||
Main.KeybindingMode.NORMAL |
|
||||
Main.KeybindingMode.OVERVIEW,
|
||||
Lang.bind(this, this._showWorkspaceSwitcher));
|
||||
this.setCustomKeybindingHandler('switch-to-workspace-right',
|
||||
Shell.KeyBindingMode.NORMAL |
|
||||
Shell.KeyBindingMode.OVERVIEW,
|
||||
Main.KeybindingMode.NORMAL |
|
||||
Main.KeybindingMode.OVERVIEW,
|
||||
Lang.bind(this, this._showWorkspaceSwitcher));
|
||||
this.setCustomKeybindingHandler('switch-to-workspace-up',
|
||||
Shell.KeyBindingMode.NORMAL |
|
||||
Shell.KeyBindingMode.OVERVIEW,
|
||||
Main.KeybindingMode.NORMAL |
|
||||
Main.KeybindingMode.OVERVIEW,
|
||||
Lang.bind(this, this._showWorkspaceSwitcher));
|
||||
this.setCustomKeybindingHandler('switch-to-workspace-down',
|
||||
Shell.KeyBindingMode.NORMAL |
|
||||
Shell.KeyBindingMode.OVERVIEW,
|
||||
Main.KeybindingMode.NORMAL |
|
||||
Main.KeybindingMode.OVERVIEW,
|
||||
Lang.bind(this, this._showWorkspaceSwitcher));
|
||||
this.setCustomKeybindingHandler('move-to-workspace-left',
|
||||
Shell.KeyBindingMode.NORMAL |
|
||||
Shell.KeyBindingMode.OVERVIEW,
|
||||
Main.KeybindingMode.NORMAL |
|
||||
Main.KeybindingMode.OVERVIEW,
|
||||
Lang.bind(this, this._showWorkspaceSwitcher));
|
||||
this.setCustomKeybindingHandler('move-to-workspace-right',
|
||||
Shell.KeyBindingMode.NORMAL |
|
||||
Shell.KeyBindingMode.OVERVIEW,
|
||||
Main.KeybindingMode.NORMAL |
|
||||
Main.KeybindingMode.OVERVIEW,
|
||||
Lang.bind(this, this._showWorkspaceSwitcher));
|
||||
this.setCustomKeybindingHandler('move-to-workspace-up',
|
||||
Shell.KeyBindingMode.NORMAL |
|
||||
Shell.KeyBindingMode.OVERVIEW,
|
||||
Main.KeybindingMode.NORMAL |
|
||||
Main.KeybindingMode.OVERVIEW,
|
||||
Lang.bind(this, this._showWorkspaceSwitcher));
|
||||
this.setCustomKeybindingHandler('move-to-workspace-down',
|
||||
Shell.KeyBindingMode.NORMAL |
|
||||
Shell.KeyBindingMode.OVERVIEW,
|
||||
Main.KeybindingMode.NORMAL |
|
||||
Main.KeybindingMode.OVERVIEW,
|
||||
Lang.bind(this, this._showWorkspaceSwitcher));
|
||||
this.setCustomKeybindingHandler('switch-applications',
|
||||
Shell.KeyBindingMode.NORMAL,
|
||||
Main.KeybindingMode.NORMAL,
|
||||
Lang.bind(this, this._startAppSwitcher));
|
||||
this.setCustomKeybindingHandler('switch-group',
|
||||
Shell.KeyBindingMode.NORMAL,
|
||||
Main.KeybindingMode.NORMAL,
|
||||
Lang.bind(this, this._startAppSwitcher));
|
||||
this.setCustomKeybindingHandler('switch-applications-backward',
|
||||
Shell.KeyBindingMode.NORMAL,
|
||||
Main.KeybindingMode.NORMAL,
|
||||
Lang.bind(this, this._startAppSwitcher));
|
||||
this.setCustomKeybindingHandler('switch-group-backward',
|
||||
Shell.KeyBindingMode.NORMAL,
|
||||
Main.KeybindingMode.NORMAL,
|
||||
Lang.bind(this, this._startAppSwitcher));
|
||||
this.setCustomKeybindingHandler('switch-windows',
|
||||
Shell.KeyBindingMode.NORMAL,
|
||||
Main.KeybindingMode.NORMAL,
|
||||
Lang.bind(this, this._startWindowSwitcher));
|
||||
this.setCustomKeybindingHandler('switch-windows-backward',
|
||||
Shell.KeyBindingMode.NORMAL,
|
||||
Main.KeybindingMode.NORMAL,
|
||||
Lang.bind(this, this._startWindowSwitcher));
|
||||
this.setCustomKeybindingHandler('switch-panels',
|
||||
Shell.KeyBindingMode.NORMAL |
|
||||
Shell.KeyBindingMode.OVERVIEW |
|
||||
Shell.KeyBindingMode.LOCK_SCREEN |
|
||||
Shell.KeyBindingMode.UNLOCK_SCREEN |
|
||||
Shell.KeyBindingMode.LOGIN_SCREEN,
|
||||
Main.KeybindingMode.NORMAL |
|
||||
Main.KeybindingMode.OVERVIEW |
|
||||
Main.KeybindingMode.LOCK_SCREEN |
|
||||
Main.KeybindingMode.UNLOCK_SCREEN |
|
||||
Main.KeybindingMode.LOGIN_SCREEN,
|
||||
Lang.bind(this, this._startA11ySwitcher));
|
||||
this.setCustomKeybindingHandler('switch-panels-backward',
|
||||
Shell.KeyBindingMode.NORMAL |
|
||||
Shell.KeyBindingMode.OVERVIEW |
|
||||
Shell.KeyBindingMode.LOCK_SCREEN |
|
||||
Shell.KeyBindingMode.UNLOCK_SCREEN |
|
||||
Shell.KeyBindingMode.LOGIN_SCREEN,
|
||||
Main.KeybindingMode.NORMAL |
|
||||
Main.KeybindingMode.OVERVIEW |
|
||||
Main.KeybindingMode.LOCK_SCREEN |
|
||||
Main.KeybindingMode.UNLOCK_SCREEN |
|
||||
Main.KeybindingMode.LOGIN_SCREEN,
|
||||
Lang.bind(this, this._startA11ySwitcher));
|
||||
|
||||
this.addKeybinding('open-application-menu',
|
||||
new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }),
|
||||
Meta.KeyBindingFlags.NONE,
|
||||
Shell.KeyBindingMode.NORMAL,
|
||||
Main.KeybindingMode.NORMAL,
|
||||
Lang.bind(this, this._openAppMenu));
|
||||
|
||||
Main.overview.connect('showing', Lang.bind(this, function() {
|
||||
@ -199,7 +199,7 @@ const WindowManager = new Lang.Class({
|
||||
|
||||
removeKeybinding: function(name) {
|
||||
if (global.display.remove_keybinding(name))
|
||||
this.allowKeybinding(name, Shell.KeyBindingMode.NONE);
|
||||
this.allowKeybinding(name, Main.KeybindingMode.NONE);
|
||||
},
|
||||
|
||||
allowKeybinding: function(name, modes) {
|
||||
@ -242,12 +242,24 @@ const WindowManager = new Lang.Class({
|
||||
}
|
||||
|
||||
actor.set_scale(1.0, 1.0);
|
||||
actor.move_anchor_point_from_gravity(Clutter.Gravity.CENTER);
|
||||
|
||||
/* scale window down to 0x0.
|
||||
* maybe TODO: get icon geometry passed through and move the window towards it?
|
||||
*/
|
||||
this._minimizing.push(actor);
|
||||
|
||||
if (actor.meta_window.is_monitor_sized()) {
|
||||
Tweener.addTween(actor,
|
||||
{ opacity: 0,
|
||||
let monitor = Main.layoutManager.findMonitorForWindow(actor.meta_window);
|
||||
let xDest = monitor.x;
|
||||
let yDest = monitor.y;
|
||||
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
|
||||
xDest += monitor.width;
|
||||
|
||||
Tweener.addTween(actor,
|
||||
{ scale_x: 0.0,
|
||||
scale_y: 0.0,
|
||||
x: xDest,
|
||||
y: yDest,
|
||||
time: WINDOW_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: this._minimizeWindowDone,
|
||||
@ -257,46 +269,12 @@ const WindowManager = new Lang.Class({
|
||||
onOverwriteScope: this,
|
||||
onOverwriteParams: [shellwm, actor]
|
||||
});
|
||||
} else {
|
||||
let xDest, yDest, xScale, yScale;
|
||||
let [success, geom] = actor.meta_window.get_icon_geometry();
|
||||
if (success) {
|
||||
xDest = geom.x;
|
||||
yDest = geom.y;
|
||||
xScale = geom.width / actor.width;
|
||||
yScale = geom.height / actor.height;
|
||||
} else {
|
||||
let monitor = Main.layoutManager.monitors[actor.meta_window.get_monitor()];
|
||||
xDest = monitor.x;
|
||||
yDest = monitor.y;
|
||||
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
|
||||
xDest += monitor.width;
|
||||
xScale = 0;
|
||||
yScale = 0;
|
||||
}
|
||||
|
||||
Tweener.addTween(actor,
|
||||
{ scale_x: xScale,
|
||||
scale_y: yScale,
|
||||
x: xDest,
|
||||
y: yDest,
|
||||
time: WINDOW_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: this._minimizeWindowDone,
|
||||
onCompleteScope: this,
|
||||
onCompleteParams: [shellwm, actor],
|
||||
onOverwrite: this._minimizeWindowOverwritten,
|
||||
onOverwriteScope: this,
|
||||
onOverwriteParams: [shellwm, actor]
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_minimizeWindowDone : function(shellwm, actor) {
|
||||
if (this._removeEffect(this._minimizing, actor)) {
|
||||
Tweener.removeTweens(actor);
|
||||
actor.set_scale(1.0, 1.0);
|
||||
actor.set_opacity(255);
|
||||
actor.move_anchor_point_from_gravity(Clutter.Gravity.NORTH_WEST);
|
||||
|
||||
shellwm.completed_minimize(actor);
|
||||
@ -516,14 +494,14 @@ const WindowManager = new Lang.Class({
|
||||
},
|
||||
|
||||
_filterKeybinding: function(shellwm, binding) {
|
||||
if (Main.keybindingMode == Shell.KeyBindingMode.NONE)
|
||||
if (Main.keybindingMode == Main.KeybindingMode.NONE)
|
||||
return true;
|
||||
|
||||
// There's little sense in implementing a keybinding in mutter and
|
||||
// not having it work in NORMAL mode; handle this case generically
|
||||
// so we don't have to explicitly allow all builtin keybindings in
|
||||
// NORMAL mode.
|
||||
if (Main.keybindingMode == Shell.KeyBindingMode.NORMAL &&
|
||||
if (Main.keybindingMode == Main.KeybindingMode.NORMAL &&
|
||||
binding.is_builtin())
|
||||
return false;
|
||||
|
||||
@ -547,11 +525,11 @@ const WindowManager = new Lang.Class({
|
||||
if (direction == Meta.MotionDirection.UP ||
|
||||
direction == Meta.MotionDirection.UP_LEFT ||
|
||||
direction == Meta.MotionDirection.UP_RIGHT)
|
||||
yDest = global.screen_height - Main.panel.actor.height;
|
||||
yDest = global.screen_height;
|
||||
else if (direction == Meta.MotionDirection.DOWN ||
|
||||
direction == Meta.MotionDirection.DOWN_LEFT ||
|
||||
direction == Meta.MotionDirection.DOWN_RIGHT)
|
||||
yDest = -global.screen_height + Main.panel.actor.height;
|
||||
yDest = -global.screen_height;
|
||||
|
||||
if (direction == Meta.MotionDirection.LEFT ||
|
||||
direction == Meta.MotionDirection.UP_LEFT ||
|
||||
@ -564,9 +542,9 @@ const WindowManager = new Lang.Class({
|
||||
|
||||
let switchData = {};
|
||||
this._switchData = switchData;
|
||||
switchData.inGroup = new Clutter.Actor();
|
||||
switchData.outGroup = new Clutter.Actor();
|
||||
switchData.movingWindowBin = new Clutter.Actor();
|
||||
switchData.inGroup = new Clutter.Group();
|
||||
switchData.outGroup = new Clutter.Group();
|
||||
switchData.movingWindowBin = new Clutter.Group();
|
||||
switchData.windows = [];
|
||||
|
||||
let wgroup = global.window_group;
|
||||
@ -593,7 +571,7 @@ const WindowManager = new Lang.Class({
|
||||
switchData.windows.push({ window: window,
|
||||
parent: window.get_parent() });
|
||||
window.reparent(switchData.inGroup);
|
||||
window.show();
|
||||
window.show_all();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Meta = imports.gi.Meta;
|
||||
@ -29,6 +30,9 @@ const CLOSE_BUTTON_FADE_TIME = 0.1;
|
||||
|
||||
const DRAGGING_WINDOW_OPACITY = 100;
|
||||
|
||||
const BUTTON_LAYOUT_SCHEMA = 'org.gnome.shell.overrides';
|
||||
const BUTTON_LAYOUT_KEY = 'button-layout';
|
||||
|
||||
// When calculating a layout, we calculate the scale of windows and the percent
|
||||
// of the available area the new layout uses. If the values for the new layout,
|
||||
// when weighted with the values as below, are worse than the previous layout's,
|
||||
@ -41,6 +45,46 @@ function _interpolate(start, end, step) {
|
||||
return start + (end - start) * step;
|
||||
}
|
||||
|
||||
function _clamp(value, min, max) {
|
||||
return Math.max(min, Math.min(max, value));
|
||||
}
|
||||
|
||||
|
||||
const ScaledPoint = new Lang.Class({
|
||||
Name: 'ScaledPoint',
|
||||
|
||||
_init: function(x, y, scaleX, scaleY) {
|
||||
[this.x, this.y, this.scaleX, this.scaleY] = arguments;
|
||||
},
|
||||
|
||||
getPosition : function() {
|
||||
return [this.x, this.y];
|
||||
},
|
||||
|
||||
getScale : function() {
|
||||
return [this.scaleX, this.scaleY];
|
||||
},
|
||||
|
||||
setPosition : function(x, y) {
|
||||
[this.x, this.y] = arguments;
|
||||
},
|
||||
|
||||
setScale : function(scaleX, scaleY) {
|
||||
[this.scaleX, this.scaleY] = arguments;
|
||||
},
|
||||
|
||||
interpPosition : function(other, step) {
|
||||
return [_interpolate(this.x, other.x, step),
|
||||
_interpolate(this.y, other.y, step)];
|
||||
},
|
||||
|
||||
interpScale : function(other, step) {
|
||||
return [_interpolate(this.scaleX, other.scaleX, step),
|
||||
_interpolate(this.scaleY, other.scaleY, step)];
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
const WindowClone = new Lang.Class({
|
||||
Name: 'WindowClone',
|
||||
|
||||
@ -68,15 +112,15 @@ const WindowClone = new Lang.Class({
|
||||
|
||||
// The MetaShapedTexture that we clone has a size that includes
|
||||
// the invisible border; this is inconvenient; rather than trying
|
||||
// to compensate all over the place we insert a ClutterActor into
|
||||
// to compensate all over the place we insert a ClutterGroup into
|
||||
// the hierarchy that is sized to only the visible portion.
|
||||
this.actor = new Clutter.Actor({ reactive: true,
|
||||
this.actor = new Clutter.Group({ reactive: true,
|
||||
x: this.origX,
|
||||
y: this.origY,
|
||||
width: outerRect.width,
|
||||
height: outerRect.height });
|
||||
|
||||
this.actor.add_child(this._windowClone);
|
||||
this.actor.add_actor(this._windowClone);
|
||||
|
||||
this.actor._delegate = this;
|
||||
|
||||
@ -95,7 +139,12 @@ const WindowClone = new Lang.Class({
|
||||
|
||||
this.actor.add_action(clickAction);
|
||||
|
||||
this.actor.connect('scroll-event',
|
||||
Lang.bind(this, this._onScroll));
|
||||
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
this.actor.connect('leave-event',
|
||||
Lang.bind(this, this._onLeave));
|
||||
|
||||
this._draggable = DND.makeDraggable(this.actor,
|
||||
{ restoreOnSuccess: true,
|
||||
@ -107,6 +156,8 @@ const WindowClone = new Lang.Class({
|
||||
this._draggable.connect('drag-end', Lang.bind(this, this._onDragEnd));
|
||||
this.inDrag = false;
|
||||
|
||||
this._windowIsZooming = false;
|
||||
this._zooming = false;
|
||||
this._selected = false;
|
||||
},
|
||||
|
||||
@ -121,39 +172,35 @@ const WindowClone = new Lang.Class({
|
||||
return this._slot;
|
||||
},
|
||||
|
||||
// Find the actor just below us, respecting reparenting done
|
||||
// by DND code
|
||||
getActualStackAbove: function() {
|
||||
if (this._stackAbove == null)
|
||||
return null;
|
||||
|
||||
if (this.inDrag || this._zooming) {
|
||||
if (this._stackAbove._delegate)
|
||||
return this._stackAbove._delegate.getActualStackAbove();
|
||||
else
|
||||
return null;
|
||||
} else {
|
||||
return this._stackAbove;
|
||||
}
|
||||
},
|
||||
|
||||
setStackAbove: function (actor) {
|
||||
this._stackAbove = actor;
|
||||
if (this.inDrag)
|
||||
// We'll fix up the stack after the drag
|
||||
if (this.inDrag || this._zooming)
|
||||
// We'll fix up the stack after the drag/zooming
|
||||
return;
|
||||
|
||||
let actualAbove = this.getActualStackAbove();
|
||||
if (actualAbove == null)
|
||||
if (this._stackAbove == null)
|
||||
this.actor.lower_bottom();
|
||||
else
|
||||
this.actor.raise(actualAbove);
|
||||
this.actor.raise(this._stackAbove);
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
this.actor.destroy();
|
||||
},
|
||||
|
||||
zoomFromOverview: function() {
|
||||
if (this._zooming) {
|
||||
// If the user clicked on the zoomed window, or we are
|
||||
// returning there anyways, then we can zoom right to the
|
||||
// window, but if we are going to some other window, then
|
||||
// we need to cancel the zoom before animating, or it
|
||||
// will look funny.
|
||||
|
||||
if (!this._selected &&
|
||||
this.metaWindow != global.display.focus_window)
|
||||
this._zoomEnd();
|
||||
}
|
||||
},
|
||||
|
||||
_disconnectRealWindowSignals: function() {
|
||||
if (this._sizeChangedId > 0)
|
||||
this.realWindow.disconnect(this._sizeChangedId);
|
||||
@ -195,6 +242,8 @@ const WindowClone = new Lang.Class({
|
||||
|
||||
this.metaWindow._delegate = null;
|
||||
this.actor._delegate = null;
|
||||
if (this._zoomLightbox)
|
||||
this._zoomLightbox.destroy();
|
||||
|
||||
if (this.inDrag) {
|
||||
this.emit('drag-end');
|
||||
@ -204,6 +253,112 @@ const WindowClone = new Lang.Class({
|
||||
this.disconnectAll();
|
||||
},
|
||||
|
||||
_onLeave: function (actor, event) {
|
||||
if (this._zoomStep)
|
||||
this._zoomEnd();
|
||||
},
|
||||
|
||||
_onScroll : function (actor, event) {
|
||||
let direction = event.get_scroll_direction();
|
||||
if (direction == Clutter.ScrollDirection.UP) {
|
||||
if (this._zoomStep == undefined)
|
||||
this._zoomStart();
|
||||
if (this._zoomStep < 100) {
|
||||
this._zoomStep += SCROLL_SCALE_AMOUNT;
|
||||
this._zoomUpdate();
|
||||
}
|
||||
} else if (direction == Clutter.ScrollDirection.DOWN) {
|
||||
if (this._zoomStep > 0) {
|
||||
this._zoomStep -= SCROLL_SCALE_AMOUNT;
|
||||
this._zoomStep = Math.max(0, this._zoomStep);
|
||||
this._zoomUpdate();
|
||||
}
|
||||
if (this._zoomStep <= 0.0)
|
||||
this._zoomEnd();
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
_zoomUpdate : function () {
|
||||
[this.actor.x, this.actor.y] = this._zoomGlobalOrig.interpPosition(this._zoomTarget, this._zoomStep / 100);
|
||||
[this.actor.scale_x, this.actor.scale_y] = this._zoomGlobalOrig.interpScale(this._zoomTarget, this._zoomStep / 100);
|
||||
|
||||
let [width, height] = this.actor.get_transformed_size();
|
||||
|
||||
let monitorIndex = this.metaWindow.get_monitor();
|
||||
let monitor = Main.layoutManager.monitors[monitorIndex];
|
||||
let availArea = new Meta.Rectangle({ x: monitor.x,
|
||||
y: monitor.y,
|
||||
width: monitor.width,
|
||||
height: monitor.height });
|
||||
if (monitorIndex == Main.layoutManager.primaryIndex) {
|
||||
availArea.y += Main.panel.actor.height;
|
||||
availArea.height -= Main.panel.actor.height;
|
||||
}
|
||||
|
||||
this.actor.x = _clamp(this.actor.x, availArea.x, availArea.x + availArea.width - width);
|
||||
this.actor.y = _clamp(this.actor.y, availArea.y, availArea.y + availArea.height - height);
|
||||
},
|
||||
|
||||
_zoomStart : function () {
|
||||
this._zooming = true;
|
||||
this.emit('zoom-start');
|
||||
|
||||
if (!this._zoomLightbox)
|
||||
this._zoomLightbox = new Lightbox.Lightbox(Main.uiGroup,
|
||||
{ fadeInTime: LIGHTBOX_FADE_TIME,
|
||||
fadeOutTime: LIGHTBOX_FADE_TIME });
|
||||
this._zoomLightbox.show();
|
||||
|
||||
this._zoomLocalOrig = new ScaledPoint(this.actor.x, this.actor.y, this.actor.scale_x, this.actor.scale_y);
|
||||
this._zoomGlobalOrig = new ScaledPoint();
|
||||
let parent = this._origParent = this.actor.get_parent();
|
||||
let [width, height] = this.actor.get_transformed_size();
|
||||
this._zoomGlobalOrig.setPosition.apply(this._zoomGlobalOrig, this.actor.get_transformed_position());
|
||||
this._zoomGlobalOrig.setScale(width / this.actor.width, height / this.actor.height);
|
||||
|
||||
this.actor.reparent(Main.uiGroup);
|
||||
this._zoomLightbox.highlight(this.actor);
|
||||
|
||||
[this.actor.x, this.actor.y] = this._zoomGlobalOrig.getPosition();
|
||||
[this.actor.scale_x, this.actor.scale_y] = this._zoomGlobalOrig.getScale();
|
||||
|
||||
this.actor.raise_top();
|
||||
|
||||
this._zoomTarget = new ScaledPoint(0, 0, 1.0, 1.0);
|
||||
this._zoomTarget.setPosition(this.actor.x - (this.actor.width - width) / 2, this.actor.y - (this.actor.height - height) / 2);
|
||||
this._zoomStep = 0;
|
||||
|
||||
this._zoomUpdate();
|
||||
},
|
||||
|
||||
_zoomEnd : function () {
|
||||
this._zooming = false;
|
||||
this.emit('zoom-end');
|
||||
|
||||
this.actor.reparent(this._origParent);
|
||||
if (this._stackAbove == null)
|
||||
this.actor.lower_bottom();
|
||||
// If the workspace has been destroyed while we were reparented to
|
||||
// the stage, _stackAbove will be unparented and we can't raise our
|
||||
// actor above it - as we are bound to be destroyed anyway in that
|
||||
// case, we can skip that step
|
||||
else if (this._stackAbove.get_parent())
|
||||
this.actor.raise(this._stackAbove);
|
||||
|
||||
[this.actor.x, this.actor.y] = this._zoomLocalOrig.getPosition();
|
||||
[this.actor.scale_x, this.actor.scale_y] = this._zoomLocalOrig.getScale();
|
||||
|
||||
this._zoomLightbox.hide();
|
||||
|
||||
this._zoomLocalPosition = undefined;
|
||||
this._zoomLocalScale = undefined;
|
||||
this._zoomGlobalPosition = undefined;
|
||||
this._zoomGlobalScale = undefined;
|
||||
this._zoomTargetPosition = undefined;
|
||||
this._zoomStep = undefined;
|
||||
},
|
||||
|
||||
_onClicked: function(action, actor) {
|
||||
this._selected = true;
|
||||
this.emit('selected', global.get_current_time());
|
||||
@ -229,6 +384,9 @@ const WindowClone = new Lang.Class({
|
||||
},
|
||||
|
||||
_onDragBegin : function (draggable, time) {
|
||||
if (this._zooming)
|
||||
this._zoomEnd();
|
||||
|
||||
this._dragSlot = this._slot;
|
||||
[this.dragOrigX, this.dragOrigY] = this.actor.get_position();
|
||||
this.dragOrigScale = this.actor.scale_x;
|
||||
@ -283,6 +441,8 @@ const WindowOverlay = new Lang.Class({
|
||||
this._parentActor = parentActor;
|
||||
this._hidden = false;
|
||||
|
||||
this._settings = new Gio.Settings({ schema: BUTTON_LAYOUT_SCHEMA });
|
||||
|
||||
this.borderSize = 0;
|
||||
this.border = new St.Bin({ style_class: 'window-clone-border' });
|
||||
|
||||
@ -310,6 +470,8 @@ const WindowOverlay = new Lang.Class({
|
||||
Lang.bind(this, this._onLeave));
|
||||
|
||||
this._windowAddedId = 0;
|
||||
windowClone.connect('zoom-start', Lang.bind(this, this.hide));
|
||||
windowClone.connect('zoom-end', Lang.bind(this, this.show));
|
||||
|
||||
button.hide();
|
||||
|
||||
@ -364,23 +526,24 @@ const WindowOverlay = new Lang.Class({
|
||||
},
|
||||
|
||||
chromeWidths: function () {
|
||||
return [this.borderSize,
|
||||
Math.max(this.borderSize, this.closeButton.width - this.closeButton._overlap)];
|
||||
return [this.borderSize, this.borderSize];
|
||||
},
|
||||
|
||||
relayout: function(animate) {
|
||||
let button = this.closeButton;
|
||||
let title = this.title;
|
||||
let border = this.border;
|
||||
|
||||
Tweener.removeTweens(button);
|
||||
Tweener.removeTweens(title);
|
||||
Tweener.removeTweens(border);
|
||||
|
||||
let [cloneX, cloneY, cloneWidth, cloneHeight] = this._windowClone.slot;
|
||||
|
||||
let layout = Meta.prefs_get_button_layout();
|
||||
let side = layout.left_buttons.indexOf(Meta.ButtonFunction.CLOSE) > -1 ? St.Side.LEFT : St.Side.RIGHT;
|
||||
let button = this.closeButton;
|
||||
let title = this.title;
|
||||
|
||||
let layout = this._settings.get_string(BUTTON_LAYOUT_KEY);
|
||||
let rtl = Clutter.get_default_text_direction() == Clutter.TextDirection.RTL;
|
||||
|
||||
let split = layout.split(":");
|
||||
let side;
|
||||
if (split[0].indexOf("close") > -1)
|
||||
side = rtl ? St.Side.RIGHT : St.Side.LEFT;
|
||||
else
|
||||
side = rtl ? St.Side.LEFT : St.Side.RIGHT;
|
||||
|
||||
let buttonX;
|
||||
let buttonY = cloneY - (button.height - button._overlap);
|
||||
@ -574,93 +737,19 @@ const WindowOverlay = new Lang.Class({
|
||||
Signals.addSignalMethods(WindowOverlay.prototype);
|
||||
|
||||
const WindowPositionFlags = {
|
||||
NONE: 0,
|
||||
INITIAL: 1 << 0,
|
||||
ANIMATE: 1 << 1
|
||||
};
|
||||
|
||||
// Window Thumbnail Layout Algorithm
|
||||
// =================================
|
||||
//
|
||||
// General overview
|
||||
// ----------------
|
||||
//
|
||||
// The window thumbnail layout algorithm calculates some optimal layout
|
||||
// by computing layouts with some number of rows, calculating how good
|
||||
// each layout is, and stopping iterating when it finds one that is worse
|
||||
// than the previous layout. A layout consists of which windows are in
|
||||
// which rows, row sizes and other general state tracking that would make
|
||||
// calculating window positions from this information fairly easy.
|
||||
//
|
||||
// We don't compute some global order of windows right now for optimal
|
||||
// travel when animating into the overview; windows are assumed to be
|
||||
// in some stable order.
|
||||
//
|
||||
// After a layout is computed that's considered the best layout, we
|
||||
// compute the layout scale to fit it in the area, and then compute
|
||||
// slots (sizes and positions) for each thumbnail.
|
||||
//
|
||||
// Layout generation
|
||||
// -----------------
|
||||
//
|
||||
// Layout generation is naive and simple: we simply add windows to a row
|
||||
// until we've added too many windows to a row, and then make a new row,
|
||||
// until we have our required N rows. The potential issue with this strategy
|
||||
// is that we may have too many windows at the bottom in some pathological
|
||||
// cases, which tends to make the thumbnails have the shape of a pile of
|
||||
// sand with a peak, with one window at the top.
|
||||
//
|
||||
// Scaling factors
|
||||
// ---------------
|
||||
//
|
||||
// Thumbnail position is mostly straightforward -- the main issue is
|
||||
// computing an optimal scale for each window that fits the constraints,
|
||||
// and doesn't make the thumbnail too small to see. There are two factors
|
||||
// involved in thumbnail scale to make sure that these two goals are met:
|
||||
// the window scale (calculated by _computeWindowScale) and the layout
|
||||
// scale (calculated by computeSizeAndScale).
|
||||
//
|
||||
// The calculation logic becomes slightly more complicated because row
|
||||
// and column spacing are not scaled, they're constant, so we can't
|
||||
// simply generate a bunch of window positions and then scale it. In
|
||||
// practice, it's not too bad -- we can simply try to fit the layout
|
||||
// in the input area minus whatever spacing we have, and then add
|
||||
// it back afterwards.
|
||||
//
|
||||
// The window scale is constant for the window's size regardless of the
|
||||
// input area or the layout scale or rows or anything else, and right
|
||||
// now just enlarges the window if it's too small. The fact that this
|
||||
// factor is stable makes it easy to calculate, so there's no sense
|
||||
// in not applying it in most calculations.
|
||||
//
|
||||
// The layout scale depends on the input area, the rows, etc, but is the
|
||||
// same for the entire layout, rather than being per-window. After
|
||||
// generating the rows of windows, we basically do some basic math to
|
||||
// fit the full, unscaled layout to the input area, as described above.
|
||||
//
|
||||
// With these two factors combined, the final scale of each thumbnail is
|
||||
// simply windowScale * layoutScale... almost.
|
||||
//
|
||||
// There's one additional constraint: the thumbnail scale must never be
|
||||
// larger than WINDOW_CLONE_MAXIMUM_SCALE, which means that the inequality:
|
||||
//
|
||||
// windowScale * layoutScale <= WINDOW_CLONE_MAXIMUM_SCALE
|
||||
//
|
||||
// must always be true. This is for each individual window -- while we
|
||||
// could adjust layoutScale to make the largest thumbnail smaller than
|
||||
// WINDOW_CLONE_MAXIMUM_SCALE, it would shrink windows which are already
|
||||
// under the inequality. To solve this, we simply cheat: we simply keep
|
||||
// each window's "cell" area to be the same, but we shrink the thumbnail
|
||||
// and center it horizontally, and align it to the bottom vertically.
|
||||
|
||||
const LayoutStrategy = new Lang.Class({
|
||||
Name: 'LayoutStrategy',
|
||||
Abstract: true,
|
||||
|
||||
_init: function(monitor, rowSpacing, columnSpacing) {
|
||||
_init: function(monitor, rowSpacing, columnSpacing, bottomPadding) {
|
||||
this._monitor = monitor;
|
||||
this._rowSpacing = rowSpacing;
|
||||
this._columnSpacing = columnSpacing;
|
||||
this._bottomPadding = bottomPadding;
|
||||
},
|
||||
|
||||
_newRow: function() {
|
||||
@ -675,34 +764,40 @@ const LayoutStrategy = new Lang.Class({
|
||||
// meant to be scaled
|
||||
//
|
||||
// * neither height/fullHeight have any sort of spacing or padding
|
||||
//
|
||||
// * if cellWidth is present, all windows in the row will occupy
|
||||
// the space of cellWidth, centered.
|
||||
return { x: 0, y: 0,
|
||||
width: 0, height: 0,
|
||||
fullWidth: 0, fullHeight: 0,
|
||||
cellWidth: 0,
|
||||
windows: [] };
|
||||
},
|
||||
|
||||
// Computes and returns an individual scaling factor for @window,
|
||||
// to be applied in addition to the overal layout scale.
|
||||
_computeWindowScale: function(window) {
|
||||
// Since we align windows next to each other, the height of the
|
||||
// thumbnails is much more important to preserve than the width of
|
||||
// them, so two windows with equal height, but maybe differering
|
||||
// widths line up.
|
||||
let ratio = window.actor.height / this._monitor.height;
|
||||
// Compute the size and fancy scale for @window using the
|
||||
// base scale, @scale.
|
||||
//
|
||||
// Returns a list structure: [ scaledWidth, scaledHeight, fancyScale ]
|
||||
// where scaledWidth and scaledHeight are the window's
|
||||
// width and height, scaled by fancyScale for convenience.
|
||||
_computeWindowSizeAndScale: function(window, scale) {
|
||||
let width = window.actor.width;
|
||||
let height = window.actor.height;
|
||||
let ratio;
|
||||
|
||||
// The purpose of this manipulation here is to prevent windows
|
||||
// from getting too small. For something like a calculator window,
|
||||
// we need to bump up the size just a bit to make sure it looks
|
||||
// good. We'll use a multiplier of 1.5 for this.
|
||||
if (width > height)
|
||||
ratio = width / this._monitor.width;
|
||||
else
|
||||
ratio = height / this._monitor.height;
|
||||
|
||||
// Map from [0, 1] to [1.5, 1]
|
||||
return _interpolate(1.5, 1, ratio);
|
||||
let fancyScale = (2 / (1 + ratio)) * scale;
|
||||
return [width * fancyScale, height * fancyScale, fancyScale];
|
||||
},
|
||||
|
||||
// Compute the size of each row, by assigning to the properties
|
||||
// row.width, row.height, row.fullWidth, row.fullHeight, and
|
||||
// (optionally) for each row in @layout.rows. This method is
|
||||
// intended to be called by subclasses.
|
||||
// (optionally) row.cellWidth, for each row in @layout.rows.
|
||||
// This method is intended to be called by subclasses.
|
||||
_computeRowSizes: function(layout) {
|
||||
throw new Error('_computeRowSizes not implemented');
|
||||
},
|
||||
@ -734,7 +829,7 @@ const LayoutStrategy = new Lang.Class({
|
||||
let area = layout.area;
|
||||
|
||||
let hspacing = (layout.maxColumns - 1) * this._columnSpacing;
|
||||
let vspacing = (layout.numRows - 1) * this._rowSpacing;
|
||||
let vspacing = (layout.numRows - 1) * this._rowSpacing + this._bottomPadding;
|
||||
|
||||
let spacedWidth = area.width - hspacing;
|
||||
let spacedHeight = area.height - vspacing;
|
||||
@ -753,17 +848,10 @@ const LayoutStrategy = new Lang.Class({
|
||||
layout.space = space;
|
||||
},
|
||||
|
||||
_getDistance: function (row, actor) {
|
||||
let dist_x = actor.x - row.x;
|
||||
let dist_y = actor.y - row.y;
|
||||
|
||||
return Math.sqrt(Math.pow(dist_x, 2) + Math.pow(dist_y, 2));
|
||||
},
|
||||
|
||||
computeWindowSlots: function(layout, area) {
|
||||
this._computeRowSizes(layout);
|
||||
|
||||
let { rows: rows, scale: scale } = layout;
|
||||
let { rows: rows, scale: scale, state: state } = layout;
|
||||
|
||||
let slots = [];
|
||||
|
||||
@ -773,33 +861,29 @@ const LayoutStrategy = new Lang.Class({
|
||||
row.x = area.x + (area.width - row.width) / 2;
|
||||
row.y = area.y + y;
|
||||
y += row.height + this._rowSpacing;
|
||||
row.windows.sort(Lang.bind(this, function(a, b) {
|
||||
return this._getDistance(row, a.realWindow) - this._getDistance(row, b.realWindow);
|
||||
}));
|
||||
}
|
||||
|
||||
let height = y - this._rowSpacing;
|
||||
let height = y - this._rowSpacing + this._bottomPadding;
|
||||
let baseY = (area.height - height) / 2;
|
||||
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
let row = rows[i];
|
||||
row.y += baseY;
|
||||
let x = row.x;
|
||||
let baseX = row.x;
|
||||
for (let j = 0; j < row.windows.length; j++) {
|
||||
let window = row.windows[j];
|
||||
|
||||
let s = scale * this._computeWindowScale(window);
|
||||
let cellWidth = window.actor.width * s;
|
||||
let cellHeight = window.actor.height * s;
|
||||
let [width, height, s] = this._computeWindowSizeAndScale(window, scale);
|
||||
let y = row.y + row.height - height;
|
||||
|
||||
s = Math.min(s, WINDOW_CLONE_MAXIMUM_SCALE);
|
||||
let cloneWidth = window.actor.width * s;
|
||||
let x = baseX;
|
||||
if (row.cellWidth) {
|
||||
x += (row.cellWidth - width) / 2;
|
||||
width = row.cellWidth;
|
||||
}
|
||||
|
||||
let cloneX = x + (cellWidth - cloneWidth) / 2;
|
||||
let cloneY = row.y + row.height - cellHeight;
|
||||
|
||||
slots.push([cloneX, cloneY, s, window]);
|
||||
x += cellWidth + this._columnSpacing;
|
||||
slots.push([x, y, s]);
|
||||
baseX += width + this._columnSpacing;
|
||||
}
|
||||
}
|
||||
return slots;
|
||||
@ -838,9 +922,7 @@ const UnalignedLayoutStrategy = new Lang.Class({
|
||||
let rows = [];
|
||||
let totalWidth = 0;
|
||||
for (let i = 0; i < windows.length; i++) {
|
||||
let window = windows[i];
|
||||
let s = this._computeWindowScale(window);
|
||||
totalWidth += window.actor.width * s;
|
||||
totalWidth += windows[i].actor.width;
|
||||
}
|
||||
|
||||
let idealRowWidth = totalWidth / numRows;
|
||||
@ -852,9 +934,7 @@ const UnalignedLayoutStrategy = new Lang.Class({
|
||||
|
||||
for (; windowIdx < windows.length; windowIdx++) {
|
||||
let window = windows[windowIdx];
|
||||
let s = this._computeWindowScale(window);
|
||||
let width = window.actor.width * s;
|
||||
let height = window.actor.height * s;
|
||||
let [width, height] = this._computeWindowSizeAndScale(window, 1);
|
||||
row.fullHeight = Math.max(row.fullHeight, height);
|
||||
|
||||
// either new width is < idealWidth or new width is nearer from idealWidth then oldWidth
|
||||
@ -883,6 +963,57 @@ const UnalignedLayoutStrategy = new Lang.Class({
|
||||
}
|
||||
});
|
||||
|
||||
const GridLayoutStrategy = new Lang.Class({
|
||||
Name: 'GridLayoutStrategy',
|
||||
Extends: LayoutStrategy,
|
||||
|
||||
_computeRowSizes: function(layout) {
|
||||
let { rows: rows, scale: scale } = layout;
|
||||
|
||||
let gridWidth = layout.numColumns * layout.maxWindowWidth;
|
||||
let hspacing = (layout.numColumns - 1) * this._columnSpacing;
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
let row = rows[i];
|
||||
row.fullWidth = layout.gridWidth;
|
||||
row.fullHeight = layout.maxWindowHeight;
|
||||
|
||||
row.width = row.fullWidth * scale + hspacing;
|
||||
row.height = row.fullHeight * scale;
|
||||
row.cellWidth = layout.maxWindowWidth * scale;
|
||||
}
|
||||
},
|
||||
|
||||
computeLayout: function(windows, layout) {
|
||||
let { numRows: numRows, numColumns: numColumns } = layout;
|
||||
let rows = [];
|
||||
let windowIdx = 0;
|
||||
|
||||
let maxWindowWidth = 0;
|
||||
let maxWindowHeight = 0;
|
||||
for (let i = 0; i < numRows; i++) {
|
||||
let row = this._newRow();
|
||||
rows.push(row);
|
||||
for (; windowIdx < windows.length; windowIdx++) {
|
||||
if (row.windows.length >= numColumns)
|
||||
break;
|
||||
|
||||
let window = windows[windowIdx];
|
||||
row.windows.push(window);
|
||||
|
||||
let [width, height] = this._computeWindowSizeAndScale(window, 1);
|
||||
maxWindowWidth = Math.max(maxWindowWidth, width);
|
||||
maxWindowHeight = Math.max(maxWindowHeight, height);
|
||||
}
|
||||
}
|
||||
|
||||
layout.rows = rows;
|
||||
layout.maxColumns = numColumns;
|
||||
layout.gridWidth = numColumns * maxWindowWidth;
|
||||
layout.gridHeight = numRows * maxWindowHeight;
|
||||
layout.maxWindowWidth = maxWindowWidth;
|
||||
layout.maxWindowHeight = maxWindowHeight;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @metaWorkspace: a #Meta.Workspace, or null
|
||||
@ -901,13 +1032,11 @@ const Workspace = new Lang.Class({
|
||||
|
||||
this.monitorIndex = monitorIndex;
|
||||
this._monitor = Main.layoutManager.monitors[this.monitorIndex];
|
||||
this._windowOverlaysGroup = new Clutter.Actor();
|
||||
this._windowOverlaysGroup = new Clutter.Group();
|
||||
// Without this the drop area will be overlapped.
|
||||
this._windowOverlaysGroup.set_size(0, 0);
|
||||
|
||||
this.actor = new St.Widget({ style_class: 'window-picker' });
|
||||
if (monitorIndex != Main.layoutManager.primaryIndex)
|
||||
this.actor.add_style_class_name('external-monitor');
|
||||
this.actor.set_size(0, 0);
|
||||
|
||||
this._dropRect = new Clutter.Rectangle({ opacity: 0 });
|
||||
@ -926,7 +1055,7 @@ const Workspace = new Lang.Class({
|
||||
this._windowOverlays = [];
|
||||
for (let i = 0; i < windows.length; i++) {
|
||||
if (this._isOverviewWindow(windows[i])) {
|
||||
this._addWindowClone(windows[i], true);
|
||||
this._addWindowClone(windows[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -963,7 +1092,7 @@ const Workspace = new Lang.Class({
|
||||
return false;
|
||||
}));
|
||||
|
||||
this.positionWindows(WindowPositionFlags.NONE);
|
||||
this.positionWindows(WindowPositionFlags.ANIMATE);
|
||||
},
|
||||
|
||||
_lookupIndex: function (metaWindow) {
|
||||
@ -1039,9 +1168,9 @@ const Workspace = new Lang.Class({
|
||||
let currentWorkspace = global.screen.get_active_workspace();
|
||||
let isOnCurrentWorkspace = this.metaWorkspace == null || this.metaWorkspace == currentWorkspace;
|
||||
|
||||
for (let i = 0; i < slots.length; i++) {
|
||||
for (let i = 0; i < clones.length; i++) {
|
||||
let slot = slots[i];
|
||||
let [x, y, scale, clone] = slot;
|
||||
let clone = clones[i];
|
||||
let metaWindow = clone.metaWindow;
|
||||
let overlay = clone.overlay;
|
||||
clone.slotId = i;
|
||||
@ -1051,23 +1180,12 @@ const Workspace = new Lang.Class({
|
||||
if (clone.inDrag)
|
||||
continue;
|
||||
|
||||
let cloneWidth = clone.actor.width * scale;
|
||||
let cloneHeight = clone.actor.height * scale;
|
||||
clone.slot = [x, y, cloneWidth, cloneHeight];
|
||||
let [x, y, scale] = slot;
|
||||
clone.slot = [x, y, clone.actor.width * scale, clone.actor.height * scale];
|
||||
|
||||
if (overlay && (initialPositioning || !clone.positioned))
|
||||
if (overlay && initialPositioning)
|
||||
overlay.hide();
|
||||
|
||||
if (!clone.positioned) {
|
||||
// This window appeared after the overview was already up
|
||||
// Grow the clone from the center of the slot
|
||||
clone.actor.x = x + cloneWidth / 2;
|
||||
clone.actor.y = y + cloneHeight / 2;
|
||||
clone.actor.scale_x = 0;
|
||||
clone.actor.scale_y = 0;
|
||||
clone.positioned = true;
|
||||
}
|
||||
|
||||
if (animate && isOnCurrentWorkspace) {
|
||||
if (!metaWindow.showing_on_its_workspace()) {
|
||||
/* Hidden windows should fade in and grow
|
||||
@ -1081,6 +1199,7 @@ const Workspace = new Lang.Class({
|
||||
clone.actor.y = y;
|
||||
}
|
||||
|
||||
// Make the window slightly transparent to indicate it's hidden
|
||||
Tweener.addTween(clone.actor,
|
||||
{ opacity: 255,
|
||||
time: Overview.ANIMATION_TIME,
|
||||
@ -1145,6 +1264,9 @@ const Workspace = new Lang.Class({
|
||||
},
|
||||
|
||||
_delayedWindowRepositioning: function() {
|
||||
if (this._windowIsZooming)
|
||||
return true;
|
||||
|
||||
let [x, y, mask] = global.get_pointer();
|
||||
|
||||
let pointerHasMoved = (this._cursorX != x && this._cursorY != y);
|
||||
@ -1253,7 +1375,7 @@ const Workspace = new Lang.Class({
|
||||
if (!this._isMyWindow(win) || !this._isOverviewWindow(win))
|
||||
return;
|
||||
|
||||
let [clone, overlay] = this._addWindowClone(win, false);
|
||||
let [clone, overlay] = this._addWindowClone(win);
|
||||
|
||||
if (win._overviewHint) {
|
||||
let x = win._overviewHint.x - this.actor.x;
|
||||
@ -1262,10 +1384,16 @@ const Workspace = new Lang.Class({
|
||||
delete win._overviewHint;
|
||||
|
||||
clone.slot = [x, y, clone.actor.width * scale, clone.actor.height * scale];
|
||||
clone.positioned = true;
|
||||
clone.actor.set_position (x, y);
|
||||
clone.actor.set_scale (scale, scale);
|
||||
clone.overlay.relayout(false);
|
||||
} else {
|
||||
// Position new windows at the top corner of the workspace rather
|
||||
// than where they were placed for real to avoid the window
|
||||
// being clipped to the workspaceView. Its not really more
|
||||
// natural for the window to suddenly appear in the overview
|
||||
// on some seemingly random location anyway.
|
||||
clone.actor.set_position (this._x, this._y);
|
||||
}
|
||||
|
||||
this._currentLayout = null;
|
||||
@ -1344,6 +1472,8 @@ const Workspace = new Lang.Class({
|
||||
if (overlay)
|
||||
overlay.hide();
|
||||
|
||||
clone.zoomFromOverview();
|
||||
|
||||
if (clone.metaWindow.showing_on_its_workspace()) {
|
||||
Tweener.addTween(clone.actor,
|
||||
{ x: clone.origX,
|
||||
@ -1390,6 +1520,13 @@ const Workspace = new Lang.Class({
|
||||
|
||||
if (this._positionWindowsId > 0)
|
||||
Meta.later_remove(this._positionWindowsId);
|
||||
|
||||
// Usually, the windows will be destroyed automatically with
|
||||
// their parent (this.actor), but we might have a zoomed window
|
||||
// which has been reparented to the stage - _windows[0] holds
|
||||
// the desktop window, which is never reparented
|
||||
for (let w = 0; w < this._windows.length; w++)
|
||||
this._windows[w].destroy();
|
||||
this._windows = [];
|
||||
},
|
||||
|
||||
@ -1398,11 +1535,10 @@ const Workspace = new Lang.Class({
|
||||
this.leavingOverview = false;
|
||||
},
|
||||
|
||||
// Tests if @actor belongs to this workspaces and monitor
|
||||
_isMyWindow : function (actor) {
|
||||
let win = actor.meta_window;
|
||||
return (this.metaWorkspace == null || win.located_on_workspace(this.metaWorkspace)) &&
|
||||
(win.get_monitor() == this.monitorIndex);
|
||||
// Tests if @win belongs to this workspaces and monitor
|
||||
_isMyWindow : function (win) {
|
||||
return (this.metaWorkspace == null || Main.isWindowActorDisplayedOnWorkspace(win, this.metaWorkspace.index())) &&
|
||||
(!win.get_meta_window() || win.get_meta_window().get_monitor() == this.monitorIndex);
|
||||
},
|
||||
|
||||
// Tests if @win should be shown in the Overview
|
||||
@ -1412,11 +1548,10 @@ const Workspace = new Lang.Class({
|
||||
},
|
||||
|
||||
// Create a clone of a (non-desktop) window and add it to the window list
|
||||
_addWindowClone : function(win, positioned) {
|
||||
_addWindowClone : function(win) {
|
||||
let clone = new WindowClone(win, this);
|
||||
let overlay = new WindowOverlay(clone, this._windowOverlaysGroup);
|
||||
clone.overlay = overlay;
|
||||
clone.positioned = positioned;
|
||||
|
||||
clone.connect('selected',
|
||||
Lang.bind(this, this._onCloneSelected));
|
||||
@ -1434,6 +1569,14 @@ const Workspace = new Lang.Class({
|
||||
Main.overview.endWindowDrag();
|
||||
overlay.show();
|
||||
}));
|
||||
clone.connect('zoom-start',
|
||||
Lang.bind(this, function() {
|
||||
this._windowIsZooming = true;
|
||||
}));
|
||||
clone.connect('zoom-end',
|
||||
Lang.bind(this, function() {
|
||||
this._windowIsZooming = false;
|
||||
}));
|
||||
clone.connect('size-changed',
|
||||
Lang.bind(this, function() {
|
||||
this.positionWindows(0);
|
||||
@ -1443,11 +1586,6 @@ const Workspace = new Lang.Class({
|
||||
|
||||
overlay.connect('show-close-button', Lang.bind(this, this._onShowOverlayClose));
|
||||
|
||||
if (this._windows.length == 0)
|
||||
clone.setStackAbove(null);
|
||||
else
|
||||
clone.setStackAbove(this._windows[this._windows.length - 1].actor);
|
||||
|
||||
this._windows.push(clone);
|
||||
this._windowOverlays.push(overlay);
|
||||
|
||||
@ -1485,7 +1623,7 @@ const Workspace = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
_computeLayout: function(windows, area, rowSpacing, columnSpacing) {
|
||||
_computeLayout: function(windows, area, rowSpacing, columnSpacing, bottomPadding) {
|
||||
// We look for the largest scale that allows us to fit the
|
||||
// largest row/tallest column on the workspace.
|
||||
|
||||
@ -1500,7 +1638,8 @@ const Workspace = new Lang.Class({
|
||||
if (numColumns == lastLayout.numColumns)
|
||||
break;
|
||||
|
||||
let strategy = new UnalignedLayoutStrategy(this._monitor, rowSpacing, columnSpacing);
|
||||
let strategyClass = numRows > 2 ? GridLayoutStrategy : UnalignedLayoutStrategy;
|
||||
let strategy = new strategyClass(this._monitor, rowSpacing, columnSpacing, bottomPadding);
|
||||
|
||||
let layout = { area: area, strategy: strategy, numRows: numRows, numColumns: numColumns };
|
||||
strategy.computeLayout(windows, layout);
|
||||
@ -1532,12 +1671,6 @@ const Workspace = new Lang.Class({
|
||||
// Window grid spacing
|
||||
let columnSpacing = node.get_length('-horizontal-spacing');
|
||||
let rowSpacing = node.get_length('-vertical-spacing');
|
||||
let padding = {
|
||||
left: node.get_padding(St.Side.LEFT),
|
||||
top: node.get_padding(St.Side.TOP),
|
||||
bottom: node.get_padding(St.Side.BOTTOM),
|
||||
right: node.get_padding(St.Side.RIGHT),
|
||||
};
|
||||
|
||||
if (!totalWindows)
|
||||
return [];
|
||||
@ -1552,25 +1685,19 @@ const Workspace = new Lang.Class({
|
||||
[leftBorder, rightBorder] = overlay.chromeWidths();
|
||||
} else {
|
||||
[closeButtonHeight, captionHeight] = [0, 0];
|
||||
[leftBorder, rightBorder] = [0, 0];
|
||||
}
|
||||
|
||||
rowSpacing += captionHeight;
|
||||
columnSpacing += (rightBorder + leftBorder) / 2;
|
||||
padding.top += closeButtonHeight;
|
||||
padding.bottom += captionHeight;
|
||||
padding.left += leftBorder;
|
||||
padding.right += rightBorder;
|
||||
columnSpacing += rightBorder;
|
||||
|
||||
let area = {
|
||||
x: this._x + padding.left,
|
||||
y: this._y + padding.top,
|
||||
width: this._width - padding.left - padding.right,
|
||||
height: this._height - padding.top - padding.bottom,
|
||||
};
|
||||
let area = { x: this._x, y: this._y, width: this._width, height: this._height };
|
||||
area.y += closeButtonHeight;
|
||||
area.height -= closeButtonHeight;
|
||||
area.x += leftBorder;
|
||||
area.width -= leftBorder;
|
||||
|
||||
if (!this._currentLayout)
|
||||
this._currentLayout = this._computeLayout(windows, area, rowSpacing, columnSpacing);
|
||||
this._currentLayout = this._computeLayout(windows, area, rowSpacing, columnSpacing, captionHeight);
|
||||
|
||||
let layout = this._currentLayout;
|
||||
let strategy = layout.strategy;
|
||||
|
@ -55,9 +55,10 @@ const WorkspaceSwitcherPopup = new Lang.Class({
|
||||
|
||||
_getPreferredHeight : function (actor, forWidth, alloc) {
|
||||
let children = this._list.get_children();
|
||||
let workArea = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex);
|
||||
let primary = Main.layoutManager.primaryMonitor;
|
||||
|
||||
let availHeight = workArea.height;
|
||||
let availHeight = primary.height;
|
||||
availHeight -= Main.panel.actor.height;
|
||||
availHeight -= this.actor.get_theme_node().get_vertical_padding();
|
||||
availHeight -= this._container.get_theme_node().get_vertical_padding();
|
||||
availHeight -= this._list.get_theme_node().get_vertical_padding();
|
||||
@ -66,7 +67,7 @@ const WorkspaceSwitcherPopup = new Lang.Class({
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
let [childMinHeight, childNaturalHeight] = children[i].get_preferred_height(-1);
|
||||
let [childMinWidth, childNaturalWidth] = children[i].get_preferred_width(childNaturalHeight);
|
||||
height += childNaturalHeight * workArea.width / workArea.height;
|
||||
height += childNaturalHeight * primary.width / primary.height;
|
||||
}
|
||||
|
||||
let spacing = this._itemSpacing * (global.screen.n_workspaces - 1);
|
||||
@ -80,8 +81,8 @@ const WorkspaceSwitcherPopup = new Lang.Class({
|
||||
},
|
||||
|
||||
_getPreferredWidth : function (actor, forHeight, alloc) {
|
||||
let workArea = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex);
|
||||
this._childWidth = Math.round(this._childHeight * workArea.width / workArea.height);
|
||||
let primary = Main.layoutManager.primaryMonitor;
|
||||
this._childWidth = Math.round(this._childHeight * primary.width / primary.height);
|
||||
|
||||
alloc.min_size = this._childWidth;
|
||||
alloc.natural_size = this._childWidth;
|
||||
@ -121,11 +122,12 @@ const WorkspaceSwitcherPopup = new Lang.Class({
|
||||
|
||||
}
|
||||
|
||||
let workArea = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex);
|
||||
let primary = Main.layoutManager.primaryMonitor;
|
||||
let [containerMinHeight, containerNatHeight] = this._container.get_preferred_height(global.screen_width);
|
||||
let [containerMinWidth, containerNatWidth] = this._container.get_preferred_width(containerNatHeight);
|
||||
this._container.x = workArea.x + Math.floor((workArea.width - containerNatWidth) / 2);
|
||||
this._container.y = workArea.y + Math.floor((workArea.height - containerNatHeight) / 2);
|
||||
this._container.x = primary.x + Math.floor((primary.width - containerNatWidth) / 2);
|
||||
this._container.y = primary.y + Main.panel.actor.height +
|
||||
Math.floor(((primary.height - Main.panel.actor.height) - containerNatHeight) / 2);
|
||||
},
|
||||
|
||||
_show : function() {
|
||||
|
@ -9,7 +9,6 @@ const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Background = imports.ui.background;
|
||||
const DND = imports.ui.dnd;
|
||||
const Main = imports.ui.main;
|
||||
const Tweener = imports.ui.tweener;
|
||||
@ -62,35 +61,12 @@ const WindowClone = new Lang.Class({
|
||||
this.inDrag = false;
|
||||
},
|
||||
|
||||
// Find the actor just below us, respecting reparenting done
|
||||
// by DND code
|
||||
getActualStackAbove: function() {
|
||||
if (this._stackAbove == null)
|
||||
return null;
|
||||
|
||||
if (this.inDrag) {
|
||||
if (this._stackAbove._delegate)
|
||||
return this._stackAbove._delegate.getActualStackAbove();
|
||||
else
|
||||
return null;
|
||||
} else {
|
||||
return this._stackAbove;
|
||||
}
|
||||
},
|
||||
|
||||
setStackAbove: function (actor) {
|
||||
this._stackAbove = actor;
|
||||
|
||||
// Don't apply the new stacking now, it will be applied
|
||||
// when dragging ends and window are stacked again
|
||||
if (actor.inDrag)
|
||||
return;
|
||||
|
||||
let actualAbove = this.getActualStackAbove();
|
||||
if (actualAbove == null)
|
||||
if (this._stackAbove == null)
|
||||
this.actor.lower_bottom();
|
||||
else
|
||||
this.actor.raise(actualAbove);
|
||||
this.actor.raise(this._stackAbove);
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
@ -189,20 +165,18 @@ const WorkspaceThumbnail = new Lang.Class({
|
||||
style_class: 'workspace-thumbnail' });
|
||||
this.actor._delegate = this;
|
||||
|
||||
this._contents = new Clutter.Actor();
|
||||
this.actor.add_child(this._contents);
|
||||
this._contents = new Clutter.Group();
|
||||
this.actor.add_actor(this._contents);
|
||||
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
|
||||
this._createBackground();
|
||||
this._background = Meta.BackgroundActor.new_for_screen(global.screen);
|
||||
this._contents.add_actor(this._background);
|
||||
|
||||
let monitor = Main.layoutManager.primaryMonitor;
|
||||
this.setPorthole(monitor.x, monitor.y, monitor.width, monitor.height);
|
||||
|
||||
let windows = global.get_window_actors().filter(Lang.bind(this, function(actor) {
|
||||
let win = actor.meta_window;
|
||||
return win.located_on_workspace(metaWorkspace);
|
||||
}));
|
||||
let windows = global.get_window_actors().filter(this._isWorkspaceWindow, this);
|
||||
|
||||
// Create clones for windows that should be visible in the Overview
|
||||
this._windows = [];
|
||||
@ -236,12 +210,6 @@ const WorkspaceThumbnail = new Lang.Class({
|
||||
this._collapseFraction = 0; // Not collapsed
|
||||
},
|
||||
|
||||
_createBackground: function() {
|
||||
this._bgManager = new Background.BackgroundManager({ monitorIndex: Main.layoutManager.primaryIndex,
|
||||
container: this._contents,
|
||||
effects: Meta.BackgroundEffects.NONE });
|
||||
},
|
||||
|
||||
setPorthole: function(x, y, width, height) {
|
||||
this._portholeX = x;
|
||||
this._portholeY = y;
|
||||
@ -265,7 +233,7 @@ const WorkspaceThumbnail = new Lang.Class({
|
||||
let clone = this._windows[i];
|
||||
let metaWindow = clone.metaWindow;
|
||||
if (i == 0) {
|
||||
clone.setStackAbove(this._bgManager.background.actor);
|
||||
clone.setStackAbove(this._background);
|
||||
} else {
|
||||
let previousClone = this._windows[i - 1];
|
||||
clone.setStackAbove(previousClone.actor);
|
||||
@ -384,8 +352,7 @@ const WorkspaceThumbnail = new Lang.Class({
|
||||
},
|
||||
|
||||
destroy : function() {
|
||||
if (this.actor)
|
||||
this.actor.destroy();
|
||||
this.actor.destroy();
|
||||
},
|
||||
|
||||
workspaceRemoved : function() {
|
||||
@ -406,20 +373,19 @@ const WorkspaceThumbnail = new Lang.Class({
|
||||
_onDestroy: function(actor) {
|
||||
this.workspaceRemoved();
|
||||
|
||||
if (this._bgManager) {
|
||||
this._bgManager.destroy();
|
||||
this._bgManager = null;
|
||||
}
|
||||
|
||||
this._windows = [];
|
||||
this.actor = null;
|
||||
},
|
||||
|
||||
// Tests if @actor belongs to this workspace and monitor
|
||||
_isMyWindow : function (actor) {
|
||||
let win = actor.meta_window;
|
||||
return win.located_on_workspace(this.metaWorkspace) &&
|
||||
(win.get_monitor() == this.monitorIndex);
|
||||
// Tests if @win belongs to this workspace
|
||||
_isWorkspaceWindow : function (win) {
|
||||
return Main.isWindowActorDisplayedOnWorkspace(win, this.metaWorkspace.index());
|
||||
},
|
||||
|
||||
// Tests if @win belongs to this workspace and monitor
|
||||
_isMyWindow : function (win) {
|
||||
return this._isWorkspaceWindow(win) &&
|
||||
(!win.get_meta_window() || win.get_meta_window().get_monitor() == this.monitorIndex);
|
||||
},
|
||||
|
||||
// Tests if @win should be shown in the Overview
|
||||
@ -452,7 +418,7 @@ const WorkspaceThumbnail = new Lang.Class({
|
||||
this._contents.add_actor(clone.actor);
|
||||
|
||||
if (this._windows.length == 0)
|
||||
clone.setStackAbove(this._bgManager.background.actor);
|
||||
clone.setStackAbove(this._background);
|
||||
else
|
||||
clone.setStackAbove(this._windows[this._windows.length - 1].actor);
|
||||
|
||||
@ -562,7 +528,6 @@ const ThumbnailsBox = new Lang.Class({
|
||||
this._dropPlaceholderPos = -1;
|
||||
this._dropPlaceholder = new St.Bin({ style_class: 'placeholder' });
|
||||
this.actor.add_actor(this._dropPlaceholder);
|
||||
this._spliceIndex = -1;
|
||||
|
||||
this._targetScale = 0;
|
||||
this._scale = 0;
|
||||
@ -579,6 +544,7 @@ const ThumbnailsBox = new Lang.Class({
|
||||
|
||||
this.actor.connect('button-press-event', function() { return true; });
|
||||
this.actor.connect('button-release-event', Lang.bind(this, this._onButtonRelease));
|
||||
this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
|
||||
|
||||
Main.overview.connect('showing',
|
||||
Lang.bind(this, this._createThumbnails));
|
||||
@ -617,7 +583,7 @@ const ThumbnailsBox = new Lang.Class({
|
||||
let thumbnail = this._thumbnails[i]
|
||||
let [w, h] = thumbnail.actor.get_transformed_size();
|
||||
if (y >= thumbnail.actor.y && y <= thumbnail.actor.y + h) {
|
||||
thumbnail.activate(event.get_time());
|
||||
thumbnail.activate(event.time);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -745,8 +711,6 @@ const ThumbnailsBox = new Lang.Class({
|
||||
return win.get_workspace() >= newWorkspaceIndex;
|
||||
});
|
||||
|
||||
this._spliceIndex = newWorkspaceIndex;
|
||||
|
||||
// ... move them down one.
|
||||
windows.forEach(function(win) {
|
||||
win.meta_window.change_workspace_by_index(win.get_workspace() + 1,
|
||||
@ -768,14 +732,6 @@ const ThumbnailsBox = new Lang.Class({
|
||||
WORKSPACE_KEEP_ALIVE_TIME);
|
||||
}
|
||||
|
||||
// Start the animation on the workspace (which is actually
|
||||
// an old one which just became empty)
|
||||
let thumbnail = this._thumbnails[newWorkspaceIndex];
|
||||
this._setThumbnailState(thumbnail, ThumbnailState.NEW);
|
||||
thumbnail.slidePosition = 1;
|
||||
|
||||
this._queueUpdateStates();
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@ -803,7 +759,14 @@ const ThumbnailsBox = new Lang.Class({
|
||||
this._stateCounts[ThumbnailState[key]] = 0;
|
||||
|
||||
// The "porthole" is the portion of the screen that we show in the workspaces
|
||||
this._porthole = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex);
|
||||
let panelHeight = Main.panel.actor.height;
|
||||
let monitor = Main.layoutManager.primaryMonitor;
|
||||
this._porthole = {
|
||||
x: monitor.x,
|
||||
y: monitor.y + panelHeight,
|
||||
width: monitor.width,
|
||||
height: monitor.height - panelHeight
|
||||
};
|
||||
|
||||
this.addThumbnails(0, global.screen.n_workspaces);
|
||||
|
||||
@ -863,8 +826,7 @@ const ThumbnailsBox = new Lang.Class({
|
||||
this._thumbnails.push(thumbnail);
|
||||
this.actor.add_actor(thumbnail.actor);
|
||||
|
||||
if (start > 0 && this._spliceIndex == -1) {
|
||||
// not the initial fill, and not splicing via DND
|
||||
if (start > 0) { // not the initial fill
|
||||
thumbnail.state = ThumbnailState.NEW;
|
||||
thumbnail.slidePosition = 1; // start slid out
|
||||
this._haveNewThumbnails = true;
|
||||
@ -879,9 +841,6 @@ const ThumbnailsBox = new Lang.Class({
|
||||
|
||||
// The thumbnails indicator actually needs to be on top of the thumbnails
|
||||
this._indicator.raise_top();
|
||||
|
||||
// Clear the splice index, we got the message
|
||||
this._spliceIndex = -1;
|
||||
},
|
||||
|
||||
removeThumbnails: function(start, count) {
|
||||
@ -1041,6 +1000,8 @@ const ThumbnailsBox = new Lang.Class({
|
||||
// See comment about this._background in _init()
|
||||
let themeNode = this._background.get_theme_node();
|
||||
|
||||
forWidth = themeNode.adjust_for_width(forWidth);
|
||||
|
||||
// Note that for getPreferredWidth/Height we cheat a bit and skip propagating
|
||||
// the size request to our children because we know how big they are and know
|
||||
// that the actors aren't depending on the virtual functions being called.
|
||||
@ -1256,5 +1217,16 @@ const ThumbnailsBox = new Lang.Class({
|
||||
},
|
||||
onCompleteScope: this
|
||||
});
|
||||
},
|
||||
|
||||
_onScrollEvent: function (actor, event) {
|
||||
switch (event.get_scroll_direction()) {
|
||||
case Clutter.ScrollDirection.UP:
|
||||
Main.wm.actionMoveWorkspace(Meta.MotionDirection.UP);
|
||||
break;
|
||||
case Clutter.ScrollDirection.DOWN:
|
||||
Main.wm.actionMoveWorkspace(Meta.MotionDirection.DOWN);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -23,13 +23,14 @@ const MAX_WORKSPACES = 16;
|
||||
|
||||
const OVERRIDE_SCHEMA = 'org.gnome.shell.overrides';
|
||||
|
||||
const CONTROLS_POP_IN_TIME = 0.1;
|
||||
|
||||
|
||||
const WorkspacesView = new Lang.Class({
|
||||
Name: 'WorkspacesView',
|
||||
|
||||
_init: function(workspaces) {
|
||||
this.actor = new St.Widget({ style_class: 'workspaces-view',
|
||||
reactive: true });
|
||||
this.actor = new St.Widget({ style_class: 'workspaces-view' });
|
||||
|
||||
// The actor itself isn't a drop target, so we don't want to pick on its area
|
||||
this.actor.set_size(0, 0);
|
||||
@ -47,6 +48,10 @@ const WorkspacesView = new Lang.Class({
|
||||
this._height = 0;
|
||||
this._x = 0;
|
||||
this._y = 0;
|
||||
this._clipX = 0;
|
||||
this._clipY = 0;
|
||||
this._clipWidth = 0;
|
||||
this._clipHeight = 0;
|
||||
this._spacing = 0;
|
||||
this._animating = false; // tweening
|
||||
this._scrolling = false; // swipe-scrolling
|
||||
@ -85,8 +90,8 @@ const WorkspacesView = new Lang.Class({
|
||||
this._overviewShownId =
|
||||
Main.overview.connect('shown',
|
||||
Lang.bind(this, function() {
|
||||
this.actor.set_clip(this._x, this._y,
|
||||
this._width, this._height);
|
||||
this.actor.set_clip(this._clipX, this._clipY,
|
||||
this._clipWidth, this._clipHeight);
|
||||
}));
|
||||
|
||||
this.scrollAdjustment = new St.Adjustment({ value: activeWorkspaceIndex,
|
||||
@ -153,6 +158,13 @@ const WorkspacesView = new Lang.Class({
|
||||
this._workspaces[i].setGeometry(x, y, width, height);
|
||||
},
|
||||
|
||||
setClipRect: function(x, y, width, height) {
|
||||
this._clipX = x;
|
||||
this._clipY = y;
|
||||
this._clipWidth = width;
|
||||
this._clipHeight = height;
|
||||
},
|
||||
|
||||
_lookupWorkspaceForMetaWindow: function (metaWindow) {
|
||||
for (let i = 0; i < this._workspaces.length; i++) {
|
||||
if (this._workspaces[i].containsMetaWindow(metaWindow))
|
||||
@ -191,6 +203,11 @@ const WorkspacesView = new Lang.Class({
|
||||
this._extraWorkspaces[i].syncStacking(stackIndices);
|
||||
},
|
||||
|
||||
updateWindowPositions: function() {
|
||||
for (let w = 0; w < this._workspaces.length; w++)
|
||||
this._workspaces[w].positionWindows(Workspace.WindowPositionFlags.ANIMATE);
|
||||
},
|
||||
|
||||
_scrollToActive: function() {
|
||||
let active = global.screen.get_active_workspace_index();
|
||||
|
||||
@ -386,15 +403,6 @@ const WorkspacesView = new Lang.Class({
|
||||
let current = Math.round(adj.value);
|
||||
|
||||
if (active != current) {
|
||||
if (!this._workspaces[current]) {
|
||||
// The current workspace was destroyed. This could happen
|
||||
// when you are on the last empty workspace, and consolidate
|
||||
// windows using the thumbnail bar.
|
||||
// In that case, the intended behavior is to stay on the empty
|
||||
// workspace, which is the last one, so pick it.
|
||||
current = this._workspaces.length - 1;
|
||||
}
|
||||
|
||||
let metaWorkspace = this._workspaces[current].metaWorkspace;
|
||||
metaWorkspace.activate(global.get_current_time());
|
||||
}
|
||||
@ -429,9 +437,23 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
Name: 'WorkspacesDisplay',
|
||||
|
||||
_init: function() {
|
||||
this.actor = new St.Widget({ clip_to_allocation: true });
|
||||
this.actor.connect('notify::allocation', Lang.bind(this, this._updateWorkspacesGeometry));
|
||||
this.actor = new Shell.GenericContainer({ style_class: 'workspaces-display' });
|
||||
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('parent-set', Lang.bind(this, this._parentSet));
|
||||
this.actor.set_clip_to_allocation(true);
|
||||
|
||||
this._spacing = 0;
|
||||
this.actor.connect('style-changed', Lang.bind(this,
|
||||
function() {
|
||||
let node = this.actor.get_theme_node();
|
||||
let spacing = node.get_length('spacing');
|
||||
if (spacing != this._spacing) {
|
||||
this._spacing = spacing;
|
||||
this._updateWorkspacesGeometry();
|
||||
}
|
||||
}));
|
||||
|
||||
let clickAction = new Clutter.ClickAction()
|
||||
clickAction.connect('clicked', Lang.bind(this, function(action) {
|
||||
@ -452,11 +474,6 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
this._workspacesViews[i].startSwipeScroll();
|
||||
return true;
|
||||
}));
|
||||
panAction.connect('gesture-cancel', Lang.bind(this, function() {
|
||||
clickAction.release();
|
||||
for (let i = 0; i < this._workspacesViews.length; i++)
|
||||
this._workspacesViews[i].endSwipeScroll();
|
||||
}));
|
||||
panAction.connect('gesture-end', Lang.bind(this, function() {
|
||||
clickAction.release();
|
||||
for (let i = 0; i < this._workspacesViews.length; i++)
|
||||
@ -465,8 +482,23 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
Main.overview.addAction(panAction);
|
||||
this.actor.bind_property('mapped', panAction, 'enabled', GObject.BindingFlags.SYNC_CREATE);
|
||||
|
||||
let controls = new St.Bin({ style_class: 'workspace-controls',
|
||||
request_mode: Clutter.RequestMode.WIDTH_FOR_HEIGHT,
|
||||
y_align: St.Align.START,
|
||||
y_fill: true });
|
||||
this._controls = controls;
|
||||
this.actor.add_actor(controls);
|
||||
|
||||
controls.reactive = true;
|
||||
controls.track_hover = true;
|
||||
controls.connect('notify::hover',
|
||||
Lang.bind(this, this._onControlsHoverChanged));
|
||||
|
||||
this._primaryIndex = Main.layoutManager.primaryIndex;
|
||||
|
||||
this._thumbnailsBox = new WorkspaceThumbnail.ThumbnailsBox();
|
||||
controls.add_actor(this._thumbnailsBox.actor);
|
||||
|
||||
this._workspacesViews = [];
|
||||
this._workspaces = [];
|
||||
this._primaryScrollAdjustment = null;
|
||||
@ -477,13 +509,43 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
this._workspacesOnlyOnPrimaryChanged));
|
||||
this._workspacesOnlyOnPrimaryChanged();
|
||||
|
||||
this._inDrag = false;
|
||||
this._cancelledDrag = false;
|
||||
|
||||
this._controlsInitiallyHovered = false;
|
||||
this._alwaysZoomOut = false;
|
||||
this._zoomOut = false;
|
||||
this._zoomFraction = 0;
|
||||
|
||||
this._updateAlwaysZoom();
|
||||
|
||||
// If we stop hiding the overview on layout changes, we will need to
|
||||
// update the _workspacesViews here
|
||||
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._updateAlwaysZoom));
|
||||
|
||||
Main.xdndHandler.connect('drag-begin', Lang.bind(this, function(){
|
||||
this._alwaysZoomOut = true;
|
||||
}));
|
||||
|
||||
Main.xdndHandler.connect('drag-end', Lang.bind(this, function(){
|
||||
this._alwaysZoomOut = false;
|
||||
this._updateAlwaysZoom();
|
||||
}));
|
||||
|
||||
global.screen.connect('notify::n-workspaces',
|
||||
Lang.bind(this, this._workspacesChanged));
|
||||
|
||||
this._switchWorkspaceNotifyId = 0;
|
||||
|
||||
this._itemDragBeginId = 0;
|
||||
this._itemDragCancelledId = 0;
|
||||
this._itemDragEndId = 0;
|
||||
this._windowDragBeginId = 0;
|
||||
this._windowDragCancelledId = 0;
|
||||
this._windowDragEndId = 0;
|
||||
this._notifyOpacityId = 0;
|
||||
this._scrollEventId = 0;
|
||||
this._swipeScrollBeginId = 0;
|
||||
this._swipeScrollEndId = 0;
|
||||
},
|
||||
|
||||
_onPan: function(action) {
|
||||
@ -494,13 +556,49 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
},
|
||||
|
||||
show: function() {
|
||||
if(!this._alwaysZoomOut) {
|
||||
let [mouseX, mouseY] = global.get_pointer();
|
||||
let [x, y] = this._controls.get_transformed_position();
|
||||
let [width, height] = this._controls.get_transformed_size();
|
||||
let visibleWidth = this._controls.get_theme_node().get_length('visible-width');
|
||||
let rtl = (Clutter.get_default_text_direction () == Clutter.TextDirection.RTL);
|
||||
if(rtl)
|
||||
x = x + width - visibleWidth;
|
||||
if(mouseX > x - 0.5 && mouseX < x + visibleWidth + 0.5 &&
|
||||
mouseY > y - 0.5 && mouseY < y + height + 0.5)
|
||||
this._controlsInitiallyHovered = true;
|
||||
}
|
||||
|
||||
this._zoomOut = this._alwaysZoomOut;
|
||||
this._zoomFraction = this._alwaysZoomOut ? 1 : 0;
|
||||
this._updateZoom();
|
||||
|
||||
this._controls.show();
|
||||
|
||||
this._updateWorkspacesViews();
|
||||
|
||||
this._restackedNotifyId =
|
||||
Main.overview.connect('windows-restacked',
|
||||
Lang.bind(this, this._onRestacked));
|
||||
if (this._scrollEventId == 0)
|
||||
this._scrollEventId = Main.overview.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
|
||||
|
||||
if (this._itemDragBeginId == 0)
|
||||
this._itemDragBeginId = Main.overview.connect('item-drag-begin',
|
||||
Lang.bind(this, this._dragBegin));
|
||||
if (this._itemDragCancelledId == 0)
|
||||
this._itemDragCancelledId = Main.overview.connect('item-drag-cancelled',
|
||||
Lang.bind(this, this._dragCancelled));
|
||||
if (this._itemDragEndId == 0)
|
||||
this._itemDragEndId = Main.overview.connect('item-drag-end',
|
||||
Lang.bind(this, this._dragEnd));
|
||||
if (this._windowDragBeginId == 0)
|
||||
this._windowDragBeginId = Main.overview.connect('window-drag-begin',
|
||||
Lang.bind(this, this._dragBegin));
|
||||
if (this._windowDragCancelledId == 0)
|
||||
this._windowDragCancelledId = Main.overview.connect('window-drag-cancelled',
|
||||
Lang.bind(this, this._dragCancelled));
|
||||
if (this._windowDragEndId == 0)
|
||||
this._windowDragEndId = Main.overview.connect('window-drag-end',
|
||||
Lang.bind(this, this._dragEnd));
|
||||
},
|
||||
|
||||
zoomFromOverview: function() {
|
||||
@ -510,13 +608,38 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
this._controls.hide();
|
||||
|
||||
if (!this._alwaysZoomOut)
|
||||
this.zoomFraction = 0;
|
||||
|
||||
if (this._restackedNotifyId > 0){
|
||||
Main.overview.disconnect(this._restackedNotifyId);
|
||||
this._restackedNotifyId = 0;
|
||||
}
|
||||
if (this._scrollEventId > 0) {
|
||||
Main.overview.disconnect(this._scrollEventId);
|
||||
this._scrollEventId = 0;
|
||||
if (this._itemDragBeginId > 0) {
|
||||
Main.overview.disconnect(this._itemDragBeginId);
|
||||
this._itemDragBeginId = 0;
|
||||
}
|
||||
if (this._itemDragCancelledId > 0) {
|
||||
Main.overview.disconnect(this._itemDragCancelledId);
|
||||
this._itemDragCancelledId = 0;
|
||||
}
|
||||
if (this._itemDragEndId > 0) {
|
||||
Main.overview.disconnect(this._itemDragEndId);
|
||||
this._itemDragEndId = 0;
|
||||
}
|
||||
if (this._windowDragBeginId > 0) {
|
||||
Main.overview.disconnect(this._windowDragBeginId);
|
||||
this._windowDragBeginId = 0;
|
||||
}
|
||||
if (this._windowDragCancelledId > 0) {
|
||||
Main.overview.disconnect(this._windowDragCancelledId);
|
||||
this._windowDragCancelledId = 0;
|
||||
}
|
||||
if (this._windowDragEndId > 0) {
|
||||
Main.overview.disconnect(this._windowDragEndId);
|
||||
this._windowDragEndId = 0;
|
||||
}
|
||||
|
||||
for (let i = 0; i < this._workspacesViews.length; i++)
|
||||
@ -563,7 +686,6 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
this._workspaces.push(monitorWorkspaces);
|
||||
|
||||
let view = new WorkspacesView(monitorWorkspaces);
|
||||
view.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
|
||||
if (this._workspacesOnlyOnPrimary || i == this._primaryIndex) {
|
||||
this._scrollAdjustment = view.scrollAdjustment;
|
||||
this._scrollAdjustment.connect('notify::value',
|
||||
@ -606,6 +728,76 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
return this._getPrimaryView().getActiveWorkspace().hasMaximizedWindows();
|
||||
},
|
||||
|
||||
// zoomFraction property allows us to tween the controls sliding in and out
|
||||
set zoomFraction(fraction) {
|
||||
this._zoomFraction = fraction;
|
||||
this.actor.queue_relayout();
|
||||
},
|
||||
|
||||
get zoomFraction() {
|
||||
return this._zoomFraction;
|
||||
},
|
||||
|
||||
_updateAlwaysZoom: function() {
|
||||
// Always show the pager if workspaces are actually used,
|
||||
// e.g. there are windows on more than one
|
||||
this._alwaysZoomOut = global.screen.n_workspaces > 2;
|
||||
|
||||
if (this._alwaysZoomOut)
|
||||
return;
|
||||
|
||||
let monitors = Main.layoutManager.monitors;
|
||||
let primary = Main.layoutManager.primaryMonitor;
|
||||
|
||||
/* Look for any monitor to the right of the primary, if there is
|
||||
* one, we always keep zoom out, otherwise its hard to reach
|
||||
* the thumbnail area without passing into the next monitor. */
|
||||
for (let i = 0; i < monitors.length; i++) {
|
||||
if (monitors[i].x >= primary.x + primary.width) {
|
||||
this._alwaysZoomOut = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_getPreferredWidth: function (actor, forHeight, alloc) {
|
||||
// pass through the call in case the child needs it, but report 0x0
|
||||
this._controls.get_preferred_width(forHeight);
|
||||
},
|
||||
|
||||
_getPreferredHeight: function (actor, forWidth, alloc) {
|
||||
// pass through the call in case the child needs it, but report 0x0
|
||||
this._controls.get_preferred_height(forWidth);
|
||||
},
|
||||
|
||||
_allocate: function (actor, box, flags) {
|
||||
let childBox = new Clutter.ActorBox();
|
||||
|
||||
let totalWidth = box.x2 - box.x1;
|
||||
|
||||
// width of the controls
|
||||
let [controlsMin, controlsNatural] = this._controls.get_preferred_width(box.y2 - box.y1);
|
||||
|
||||
// Amount of space on the screen we reserve for the visible control
|
||||
let controlsVisible = this._controls.get_theme_node().get_length('visible-width');
|
||||
let controlsReserved = controlsVisible * (1 - this._zoomFraction) + controlsNatural * this._zoomFraction;
|
||||
|
||||
let rtl = (Clutter.get_default_text_direction () == Clutter.TextDirection.RTL);
|
||||
if (rtl) {
|
||||
childBox.x2 = controlsReserved;
|
||||
childBox.x1 = childBox.x2 - controlsNatural;
|
||||
} else {
|
||||
childBox.x1 = totalWidth - controlsReserved;
|
||||
childBox.x2 = childBox.x1 + controlsNatural;
|
||||
}
|
||||
|
||||
childBox.y1 = 0;
|
||||
childBox.y2 = box.y2- box.y1;
|
||||
this._controls.allocate(childBox, flags);
|
||||
|
||||
this._updateWorkspacesGeometry();
|
||||
},
|
||||
|
||||
_parentSet: function(actor, oldParent) {
|
||||
if (oldParent && this._notifyOpacityId)
|
||||
oldParent.disconnect(this._notifyOpacityId);
|
||||
@ -642,17 +834,37 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
let width = fullWidth;
|
||||
let height = fullHeight;
|
||||
|
||||
let [controlsMin, controlsNatural] = this._controls.get_preferred_width(height);
|
||||
let controlsVisible = this._controls.get_theme_node().get_length('visible-width');
|
||||
|
||||
let [x, y] = this.actor.get_transformed_position();
|
||||
|
||||
let rtl = (Clutter.get_default_text_direction () == Clutter.TextDirection.RTL);
|
||||
|
||||
let clipWidth = width - controlsVisible;
|
||||
let clipHeight = fullHeight;
|
||||
let clipX = rtl ? x + controlsVisible : x;
|
||||
let clipY = y + (fullHeight - clipHeight) / 2;
|
||||
|
||||
let widthAdjust = this._zoomOut ? controlsNatural : controlsVisible;
|
||||
widthAdjust += this._spacing;
|
||||
width -= widthAdjust;
|
||||
if (rtl)
|
||||
x += widthAdjust;
|
||||
|
||||
let monitors = Main.layoutManager.monitors;
|
||||
let m = 0;
|
||||
for (let i = 0; i < monitors.length; i++) {
|
||||
if (i == this._primaryIndex) {
|
||||
this._workspacesViews[m].setClipRect(clipX, clipY,
|
||||
clipWidth, clipHeight);
|
||||
this._workspacesViews[m].setGeometry(x, y, width, height);
|
||||
m++;
|
||||
} else if (!this._workspacesOnlyOnPrimary) {
|
||||
this._workspacesViews[m].setClipRect(monitors[i].x,
|
||||
monitors[i].y,
|
||||
monitors[i].width,
|
||||
monitors[i].height);
|
||||
this._workspacesViews[m].setGeometry(monitors[i].x,
|
||||
monitors[i].y,
|
||||
monitors[i].width,
|
||||
@ -668,6 +880,9 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
},
|
||||
|
||||
_workspacesChanged: function() {
|
||||
this._updateAlwaysZoom();
|
||||
this._updateZoom();
|
||||
|
||||
if (!this._workspacesViews.length)
|
||||
return;
|
||||
|
||||
@ -721,18 +936,66 @@ const WorkspacesDisplay = new Lang.Class({
|
||||
newNumWorkspaces);
|
||||
},
|
||||
|
||||
_onScrollEvent: function(actor, event) {
|
||||
if (!this.actor.mapped)
|
||||
return false;
|
||||
switch (event.get_scroll_direction()) {
|
||||
case Clutter.ScrollDirection.UP:
|
||||
Main.wm.actionMoveWorkspace(Meta.MotionDirection.UP);
|
||||
return true;
|
||||
case Clutter.ScrollDirection.DOWN:
|
||||
Main.wm.actionMoveWorkspace(Meta.MotionDirection.DOWN);
|
||||
return true;
|
||||
_updateZoom : function() {
|
||||
if (Main.overview.animationInProgress)
|
||||
return;
|
||||
|
||||
let shouldZoom = this._alwaysZoomOut || this._controls.hover;
|
||||
if (shouldZoom != this._zoomOut) {
|
||||
this._zoomOut = shouldZoom;
|
||||
this._updateWorkspacesGeometry();
|
||||
|
||||
if (!this._workspacesViews.length)
|
||||
return;
|
||||
|
||||
Tweener.addTween(this,
|
||||
{ zoomFraction: this._zoomOut ? 1 : 0,
|
||||
time: WORKSPACE_SWITCH_TIME,
|
||||
transition: 'easeOutQuad' });
|
||||
|
||||
for (let i = 0; i < this._workspacesViews.length; i++)
|
||||
this._workspacesViews[i].updateWindowPositions();
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
_onControlsHoverChanged: function() {
|
||||
if(!this._controls.hover)
|
||||
this._controlsInitiallyHovered = false;
|
||||
if(!this._controlsInitiallyHovered)
|
||||
this._updateZoom();
|
||||
},
|
||||
|
||||
_dragBegin: function() {
|
||||
this._inDrag = true;
|
||||
this._cancelledDrag = false;
|
||||
this._dragMonitor = {
|
||||
dragMotion: Lang.bind(this, this._onDragMotion)
|
||||
};
|
||||
DND.addDragMonitor(this._dragMonitor);
|
||||
},
|
||||
|
||||
_dragCancelled: function() {
|
||||
this._cancelledDrag = true;
|
||||
DND.removeDragMonitor(this._dragMonitor);
|
||||
},
|
||||
|
||||
_onDragMotion: function(dragEvent) {
|
||||
let controlsHovered = this._controls.contains(dragEvent.targetActor);
|
||||
this._controls.set_hover(controlsHovered);
|
||||
|
||||
return DND.DragMotionResult.CONTINUE;
|
||||
},
|
||||
|
||||
_dragEnd: function() {
|
||||
this._inDrag = false;
|
||||
|
||||
// We do this deferred because drag-end is emitted before dnd.js emits
|
||||
// event/leave events that were suppressed during the drag. If we didn't
|
||||
// defer this, we'd zoom out then immediately zoom in because of the
|
||||
// enter event we received. That would normally be invisible but we
|
||||
// might as well avoid it.
|
||||
Meta.later_add(Meta.LaterType.BEFORE_REDRAW,
|
||||
Lang.bind(this, this._updateZoom));
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(WorkspacesDisplay.prototype);
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Lang = imports.lang;
|
||||
const Main = imports.ui.main;
|
||||
const Shell = imports.gi.Shell;
|
||||
const Signals = imports.signals;
|
||||
const DND = imports.ui.dnd;
|
||||
@ -17,11 +16,18 @@ const XdndHandler = new Lang.Class({
|
||||
|
||||
// Used as a drag actor in case we don't have a cursor window clone
|
||||
this._dummy = new Clutter.Rectangle({ width: 1, height: 1, opacity: 0 });
|
||||
Main.uiGroup.add_actor(this._dummy);
|
||||
global.stage.add_actor(this._dummy);
|
||||
Shell.util_set_hidden_from_pick(this._dummy, true);
|
||||
this._dummy.hide();
|
||||
|
||||
global.init_xdnd();
|
||||
// Mutter delays the creation of the output window as long
|
||||
// as possible to avoid flicker. In case a plugin wants to
|
||||
// access it directly it has to connect to the stage's show
|
||||
// signal. (see comment in compositor.c:meta_compositor_manage_screen)
|
||||
global.stage.connect('show', function () {
|
||||
global.init_xdnd();
|
||||
return false;
|
||||
});
|
||||
|
||||
global.connect('xdnd-enter', Lang.bind(this, this._onEnter));
|
||||
global.connect('xdnd-position-changed', Lang.bind(this, this._onPositionChanged));
|
||||
@ -68,7 +74,7 @@ const XdndHandler = new Lang.Class({
|
||||
source: cursorWindow});
|
||||
|
||||
this._cursorWindowClone = new Clutter.Clone({ source: cursorWindow });
|
||||
Main.uiGroup.add_actor(this._cursorWindowClone);
|
||||
global.overlay_group.add_actor(this._cursorWindowClone);
|
||||
Shell.util_set_hidden_from_pick(this._cursorWindowClone, true);
|
||||
|
||||
// Make sure that the clone has the same position as the source
|
||||
@ -82,7 +88,7 @@ const XdndHandler = new Lang.Class({
|
||||
},
|
||||
|
||||
_onPositionChanged: function(obj, x, y) {
|
||||
let pickedActor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y);
|
||||
let pickedActor = global.stage.get_actor_at_pos(Clutter.PickMode.ALL, x, y);
|
||||
|
||||
// Make sure that the cursor window is on top
|
||||
if (this._cursorWindowClone)
|
||||
|
@ -21,7 +21,6 @@ eu
|
||||
fa
|
||||
fi
|
||||
fr
|
||||
fur
|
||||
ga
|
||||
gl
|
||||
gu
|
||||
@ -60,7 +59,6 @@ sr@latin
|
||||
sv
|
||||
ta
|
||||
te
|
||||
tg
|
||||
th
|
||||
tr
|
||||
ug
|
||||
|
@ -1,6 +1,3 @@
|
||||
# List of source files containing translatable strings.
|
||||
# Please keep this file sorted alphabetically.
|
||||
[encoding: UTF-8]
|
||||
data/50-gnome-shell-screenshot.xml.in
|
||||
data/50-gnome-shell-system.xml.in
|
||||
data/gnome-shell.desktop.in.in
|
||||
@ -13,9 +10,7 @@ js/gdm/util.js
|
||||
js/misc/util.js
|
||||
js/ui/appDisplay.js
|
||||
js/ui/appFavorites.js
|
||||
js/ui/backgroundMenu.js
|
||||
js/ui/calendar.js
|
||||
js/ui/components/automountManager.js
|
||||
js/ui/components/autorunManager.js
|
||||
js/ui/components/keyring.js
|
||||
js/ui/components/networkAgent.js
|
||||
@ -33,7 +28,6 @@ js/ui/lookingGlass.js
|
||||
js/ui/main.js
|
||||
js/ui/messageTray.js
|
||||
js/ui/notificationDaemon.js
|
||||
js/ui/overviewControls.js
|
||||
js/ui/overview.js
|
||||
js/ui/panel.js
|
||||
js/ui/popupMenu.js
|
||||
@ -61,5 +55,6 @@ src/shell-app.c
|
||||
src/shell-app-system.c
|
||||
src/shell-global.c
|
||||
src/shell-keyring-prompt.c
|
||||
src/shell-mobile-providers.c
|
||||
src/shell-polkit-authentication-agent.c
|
||||
src/shell-util.c
|
||||
|
1116
po/ca@valencia.po
1116
po/ca@valencia.po
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user