Compare commits

..

2 Commits

Author SHA1 Message Date
7d598b7725 Bump version to 3.7.3.1
Update NEWS.
2012-12-20 17:47:36 +01:00
9f890982b2 Revert "NetworkMenu: rework multiple NIC support"
This reverts commit 490206b5b2.

Conflicts:
	configure.ac
	src/gvc
2012-12-20 17:46:31 +01:00
175 changed files with 22951 additions and 33070 deletions

152
NEWS
View File

@ -1,154 +1,6 @@
3.7.90 3.7.3.1
======
* 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] * Revert 490206b to not depend on NMGTK-0.9.7, which hasn't been released yet
* dateMenu: Add "Open Clocks" entry [Mathieu; #644390]
* screenshot: Immediately show the flash spot [Jasper; #691875]
* Misc. bug fixes [Rico, Jeremy]
Contributors:
Jeremy Bicha, Mathieu Bridon, Bastien Nocera, Jasper St. Pierre,
Rico Tzschichholz
Translations:
Ihar Hrachyshka [be]
3.7.4
=====
* Make menu separators crisp [Giovanni, Allan; #641745]
* power: Update for new D-Bus name [Bastien; #690506]
* Add smooth scrolling support [Jasper; #687573]
* Tweak notification layout [Allan; #688506]
* Ping the active window when using the app menu [Giovanni; #684340]
* Make password entries insensitive after submission [Jasper; #690594, #690895]
* Honor lock-delay GSettings key [Giovanni, Matthias; #690766, #691170]
* Use text/calendar preferred app as the calendar app [Giovanni; #690767]
* lookingGlass: Move to an inspect() function [Jasper; #690726]
* Make OSK animation quicker, snappier [Rui; #688642]
* Allow to close chat notifications with Escape [Jasper; #690897]
* Honor org.gnome.desktop.screensaver.user-switch-enabled [Giovanni; #691042]
* Add a SelectArea() DBus method [Cosimo; #687954]
* Support non-absolute paths when saving screenshots [Cosimo; #688004]
* OSK: Fix extended keys popups [Rui; #674955]
* Don't hide or show the keyboard immediately [Rui; #688646]
* Improve padding in power menu [Giovanni; #689297]
* Add per-window input source switching [Rui; #691414]
* Misc bug fixes and cleanups [Rico, Jasper, Giovanni, Rui, Florian, Dan;
#690608, #690589, #690539, #687081, #690667, #690665, #690666, #685856,
#690858, #690895, #680414, #690965, #691019, #690590, #681376, #690180,
#685513, #689263, #691553, #691720, #691743, #691750]
Contributors:
Giovanni Campagna, Cosimo Cecchi, Matthias Clasen, Allan Day, Rui Matos,
Florian Müllner, Bastien Nocera, Jasper St. Pierre, Rico Tzschichholz,
Dan Winship
Translations:
Matej Urbančič [sl], Kjartan Maraas [nb], Mattias Põldaru [et],
Yaron Shahrabani [he], Aurimas Černius [lt], Khaled Hosny [ar],
Fran Diéguez [gl], Daniel Mustieles [es], Piotr Drąg [pl], Balázs Úr [hu],
Baurzhan Muftakhidinov [kk], Tobias Endrigkeit [de], Dušan Kazik [sk],
Aron Xu [zh_CN], Gheyret Kenji [ug]
3.7.3 3.7.3
===== =====

View File

@ -1,5 +1,5 @@
AC_PREREQ(2.63) AC_PREREQ(2.63)
AC_INIT([gnome-shell],[3.7.90],[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_HEADERS([config.h])
AC_CONFIG_SRCDIR([src/shell-global.c]) AC_CONFIG_SRCDIR([src/shell-global.c])
@ -16,6 +16,8 @@ m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
# Checks for programs. # Checks for programs.
AC_PROG_CC AC_PROG_CC
# Needed for per-target cflags, like in gnomeshell-taskpanel
AM_PROG_CC_C_O
# Initialize libtool # Initialize libtool
LT_PREREQ([2.2.6]) LT_PREREQ([2.2.6])
@ -53,28 +55,29 @@ if $PKG_CONFIG --exists gstreamer-1.0 '>=' $GSTREAMER_MIN_VERSION ; then
AC_MSG_RESULT(yes) AC_MSG_RESULT(yes)
build_recorder=true build_recorder=true
recorder_modules="gstreamer-1.0 gstreamer-base-1.0 x11 gtk+-3.0" recorder_modules="gstreamer-1.0 gstreamer-base-1.0 x11 gtk+-3.0"
PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-1.0 xfixes) PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-1.0 xfixes gl)
else else
AC_MSG_RESULT(no) AC_MSG_RESULT(no)
fi fi
AM_CONDITIONAL(BUILD_RECORDER, $build_recorder) 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 GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
GJS_MIN_VERSION=1.35.4 GJS_MIN_VERSION=1.33.2
MUTTER_MIN_VERSION=3.7.90 MUTTER_MIN_VERSION=3.7.3
GTK_MIN_VERSION=3.7.9 GTK_MIN_VERSION=3.3.9
GIO_MIN_VERSION=2.35.0 GIO_MIN_VERSION=2.35.0
LIBECAL_MIN_VERSION=3.5.3 LIBECAL_MIN_VERSION=3.5.3
LIBEDATASERVER_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_GLIB_MIN_VERSION=0.17.5
TELEPATHY_LOGGER_MIN_VERSION=0.2.4
POLKIT_MIN_VERSION=0.100 POLKIT_MIN_VERSION=0.100
STARTUP_NOTIFICATION_MIN_VERSION=0.11 STARTUP_NOTIFICATION_MIN_VERSION=0.11
GCR_MIN_VERSION=3.3.90 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 GNOME_MENUS_REQUIRED_VERSION=3.5.3
NETWORKMANAGER_MIN_VERSION=0.9.6
PULSE_MIN_VERS=2.0 PULSE_MIN_VERS=2.0
# Collect more than 20 libraries for a prize! # Collect more than 20 libraries for a prize!
@ -87,26 +90,42 @@ PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
libgnome-menu-3.0 >= $GNOME_MENUS_REQUIRED_VERSION libgnome-menu-3.0 >= $GNOME_MENUS_REQUIRED_VERSION
$recorder_modules $recorder_modules
gdk-x11-3.0 libsoup-2.4 gdk-x11-3.0 libsoup-2.4
gl
clutter-x11-1.0 >= $CLUTTER_MIN_VERSION clutter-x11-1.0 >= $CLUTTER_MIN_VERSION
clutter-glx-1.0 >= $CLUTTER_MIN_VERSION clutter-glx-1.0 >= $CLUTTER_MIN_VERSION
libstartup-notification-1.0 >= $STARTUP_NOTIFICATION_MIN_VERSION libstartup-notification-1.0 >= $STARTUP_NOTIFICATION_MIN_VERSION
gobject-introspection-1.0 >= $GOBJECT_INTROSPECTION_MIN_VERSION gobject-introspection-1.0 >= $GOBJECT_INTROSPECTION_MIN_VERSION
libcanberra libcanberra-gtk3 libcanberra
telepathy-glib >= $TELEPATHY_GLIB_MIN_VERSION telepathy-glib >= $TELEPATHY_GLIB_MIN_VERSION
telepathy-logger-0.2 >= $TELEPATHY_LOGGER_MIN_VERSION
polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes
libnm-glib libnm-util >= $NETWORKMANAGER_MIN_VERSION libnm-glib libnm-util gnome-keyring-1
libnm-gtk >= $NETWORKMANAGER_MIN_VERSION gcr-3 >= $GCR_MIN_VERSION)
gnome-keyring-1 gcr-3 >= $GCR_MIN_VERSION)
PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0)
PKG_CHECK_MODULES(SHELL_HOTPLUG_SNIFFER, gio-2.0 gdk-pixbuf-2.0)
PKG_CHECK_MODULES(BROWSER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION json-glib-1.0 >= 0.13.2)
GNOME_KEYBINDINGS_KEYSDIR=`$PKG_CONFIG --variable keysdir gnome-keybindings`
AC_SUBST([GNOME_KEYBINDINGS_KEYSDIR])
GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION])
saved_CFLAGS=$CFLAGS
saved_LIBS=$LIBS
CFLAGS=$GNOME_SHELL_CFLAGS
LIBS=$GNOME_SHELL_LIBS
AC_CHECK_FUNCS(JS_NewGlobalObject XFixesCreatePointerBarrier)
CFLAGS=$saved_CFLAGS
LIBS=$saved_LIBS
PKG_CHECK_MODULES(GNOME_SHELL_JS, gio-2.0 gjs-internals-1.0 >= $GJS_MIN_VERSION) PKG_CHECK_MODULES(GNOME_SHELL_JS, gio-2.0 gjs-internals-1.0 >= $GJS_MIN_VERSION)
PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 >= 0.6.8 x11) PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 >= 0.6.8 x11)
PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0)
PKG_CHECK_MODULES(SHELL_HOTPLUG_SNIFFER, gio-2.0 gdk-pixbuf-2.0)
PKG_CHECK_MODULES(BROWSER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION json-glib-1.0 >= 0.13.2)
PKG_CHECK_MODULES(TRAY, gtk+-3.0) PKG_CHECK_MODULES(TRAY, gtk+-3.0)
PKG_CHECK_MODULES(GVC, libpulse >= $PULSE_MIN_VERS libpulse-mainloop-glib gobject-2.0) PKG_CHECK_MODULES(GVC, libpulse >= $PULSE_MIN_VERS libpulse-mainloop-glib gobject-2.0)
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.7.4) PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.7.2.2)
PKG_CHECK_MODULES(CARIBOU, caribou-1.0 >= 0.4.8)
AC_MSG_CHECKING([for bluetooth support]) AC_MSG_CHECKING([for bluetooth support])
PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.1.0], 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_CFLAGS)
AC_SUBST(CALENDAR_SERVER_LIBS) AC_SUBST(CALENDAR_SERVER_LIBS)
GNOME_KEYBINDINGS_KEYSDIR=`$PKG_CONFIG --variable keysdir gnome-keybindings` AC_ARG_WITH(systemd,
AC_SUBST([GNOME_KEYBINDINGS_KEYSDIR]) 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_GIR_DIR=`$PKG_CONFIG --variable=girdir libmutter`
MUTTER_TYPELIB_DIR=`$PKG_CONFIG --variable=typelibdir libmutter` MUTTER_TYPELIB_DIR=`$PKG_CONFIG --variable=typelibdir libmutter`
@ -156,6 +197,16 @@ fi
# Sets GLIB_GENMARSHAL and GLIB_MKENUMS # Sets GLIB_GENMARSHAL and GLIB_MKENUMS
AM_PATH_GLIB_2_0() 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]) GTK_DOC_CHECK([1.15], [--flavour no-tmpl])
@ -171,7 +222,32 @@ if test "$enable_man" != no; then
fi fi
AM_CONDITIONAL(ENABLE_MAN, test "$enable_man" != no) 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, 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) AS_HELP_STRING([--enable-jhbuild-wrapper-script],[Make "gnome-shell" script work for jhbuild]),,enable_jhbuild_wrapper_script=no)

View File

@ -12,7 +12,6 @@ desktop_DATA = gnome-shell.desktop gnome-shell-extension-prefs.desktop
introspectiondir = $(datadir)/dbus-1/interfaces introspectiondir = $(datadir)/dbus-1/interfaces
introspection_DATA = \ introspection_DATA = \
org.gnome.Shell.Screenshot.xml \
org.gnome.ShellSearchProvider.xml \ org.gnome.ShellSearchProvider.xml \
org.gnome.ShellSearchProvider2.xml org.gnome.ShellSearchProvider2.xml

View File

@ -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>

View File

@ -46,7 +46,7 @@
<!-- <!--
GetResultMetas: GetResultMetas:
@identifiers: An array of result identifiers as returned by GetInitialResultSet() or GetSubsearchResultSet() @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 Return an array of meta data used to display each given result
--> -->

View File

@ -46,7 +46,7 @@
<!-- <!--
GetResultMetas: GetResultMetas:
@identifiers: An array of result identifiers as returned by GetInitialResultSet() or GetSubsearchResultSet() @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 Return an array of meta data used to display each given result
--> -->

View File

@ -39,14 +39,6 @@
will be displayed in the favorites area. will be displayed in the favorites area.
</_description> </_description>
</key> </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"> <key name="command-history" type="as">
<default>[]</default> <default>[]</default>
<_summary>History for command (Alt-F2) dialog</_summary> <_summary>History for command (Alt-F2) dialog</_summary>
@ -73,6 +65,11 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
menuitem in single-user, single-session situations. menuitem in single-user, single-session situations.
</_description> </_description>
</key> </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"> <key name="remember-mount-password" type="b">
<default>false</default> <default>false</default>
<_summary>Whether to remember password for mounting encrypted or remote filesystems</_summary> <_summary>Whether to remember password for mounting encrypted or remote filesystems</_summary>

View File

@ -38,6 +38,7 @@ stage {
/* small */ /* small */
.app-well-menu, .app-well-menu,
.contact-details-status,
.run-dialog-error-label { .run-dialog-error-label {
font-size: 9pt; font-size: 9pt;
} }
@ -127,7 +128,7 @@ StScrollBar StButton#vhandle:active {
.popup-menu-boxpointer { .popup-menu-boxpointer {
-arrow-border-radius: 8px; -arrow-border-radius: 8px;
-arrow-background-color: rgba(0,0,0,0.9); -arrow-background-color: black;
-arrow-border-width: 2px; -arrow-border-width: 2px;
-arrow-border-color: #a5a5a5; -arrow-border-color: #a5a5a5;
-arrow-base: 24px; -arrow-base: 24px;
@ -205,12 +206,11 @@ StScrollBar StButton#vhandle:active {
} }
.popup-separator-menu-item { .popup-separator-menu-item {
-gradient-height: 1px; -gradient-height: 2px;
-gradient-start: rgba(255,255,255,0.0); -gradient-start: rgba(8,8,8,0);
-gradient-end: rgba(255,255,255,0.3); -gradient-end: #333333;
-margin-horizontal: 24px; -margin-horizontal: 1.5em;
height: 1px; height: 1em;
padding: 8px 0px;
} }
.popup-alternating-menu-item:alternate { .popup-alternating-menu-item:alternate {
@ -251,10 +251,6 @@ StScrollBar StButton#vhandle:active {
icon-size: 1.09em; icon-size: 1.09em;
} }
.popup-battery-percentage {
padding-left: 24px;
}
/* Switches */ /* Switches */
.toggle-switch { .toggle-switch {
width: 65px; width: 65px;
@ -285,24 +281,20 @@ StScrollBar StButton#vhandle:active {
/* Buttons */ /* Buttons */
.candidate-page-button, .dash-search-button,
.notification-button, .notification-button,
.notification-icon-button, .notification-icon-button,
.hotplug-notification-item, .hotplug-notification-item,
.hotplug-resident-eject-button, .hotplug-resident-eject-button,
.modal-dialog-button, .modal-dialog-button {
.app-view-control { font-weight: bold;
border: 1px solid #8b8b8b; border: 1px solid #8b8b8b;
background-gradient-direction: vertical; background-gradient-direction: vertical;
background-gradient-start: rgba(255, 255, 255, 0.2); background-gradient-start: rgba(255, 255, 255, 0.2);
background-gradient-end: rgba(255, 255, 255, 0); background-gradient-end: rgba(255, 255, 255, 0);
} }
.modal-dialog-button { .dash-search-button:hover,
font-weight: bold;
}
.candidate-page-button:hover,
.notification-button:hover, .notification-button:hover,
.notification-icon-button:hover, .notification-icon-button:hover,
.hotplug-notification-item:hover, .hotplug-notification-item:hover,
@ -312,6 +304,8 @@ StScrollBar StButton#vhandle:active {
background-gradient-end: rgba(255, 255, 255, 0.1); background-gradient-end: rgba(255, 255, 255, 0.1);
} }
.dash-search-button:selected,
.dash-search-button:focus,
.notification-button:focus, .notification-button:focus,
.notification-icon-button:focus, .notification-icon-button:focus,
.hotplug-notification-item:focus, .hotplug-notification-item:focus,
@ -319,20 +313,18 @@ StScrollBar StButton#vhandle:active {
border-width: 2px; border-width: 2px;
} }
.candidate-page-button:active, .dash-search-button:active,
.candidate-page-button:pressed, .dash-search-button:pressed,
.notification-button:active, .notification-button:active,
.notification-icon-button:active, .notification-icon-button:active,
.hotplug-notification-item:active, .hotplug-notification-item:active,
.hotplug-resident-eject-button:active, .hotplug-resident-eject-button:active,
.modal-dialog-button:active, .modal-dialog-button:active,
.modal-dialog-button:pressed, .modal-dialog-button:pressed {
.app-view-control:checked {
background-gradient-start: rgba(255, 255, 255, 0); background-gradient-start: rgba(255, 255, 255, 0);
background-gradient-end: rgba(255, 255, 255, 0.2); background-gradient-end: rgba(255, 255, 255, 0.2);
} }
.candidate-page-button:insensitive,
.notification-button:insensitive, .notification-button:insensitive,
.notification-icon-button:insensitive, .notification-icon-button:insensitive,
.modal-dialog-button:insensitive { .modal-dialog-button:insensitive {
@ -342,27 +334,6 @@ StScrollBar StButton#vhandle:active {
background-color: rgba(102, 102, 102, 0.15); 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 */ /* Entries */
#searchEntry, #searchEntry,
@ -383,7 +354,7 @@ StScrollBar StButton#vhandle:active {
background-gradient-start: rgba(5,5,6,0.1); background-gradient-start: rgba(5,5,6,0.1);
background-gradient-end: rgba(254,254,254,0.1); background-gradient-end: rgba(254,254,254,0.1);
background-gradient-direction: vertical; background-gradient-direction: vertical;
transition-duration: 300ms; transition-duration: 300;
box-shadow: inset 0px 2px 4px rgba(0,0,0,0.6); box-shadow: inset 0px 2px 4px rgba(0,0,0,0.6);
} }
@ -418,7 +389,7 @@ StScrollBar StButton#vhandle:active {
color: rgb(64, 64, 64); color: rgb(64, 64, 64);
caret-color: rgb(64, 64, 64); caret-color: rgb(64, 64, 64);
font-weight: bold; font-weight: bold;
transition-duration: 0ms; transition-duration: 0;
} }
.notification StEntry, .notification StEntry,
@ -449,14 +420,17 @@ StScrollBar StButton#vhandle:active {
background-color: black; background-color: black;
font-weight: bold; font-weight: bold;
height: 1.86em; height: 1.86em;
transition-duration: 250;
} }
#panel.lock-screen { #panel.lock-screen {
height: 2em;
background-color: rgba(0,0,0,0.3); background-color: rgba(0,0,0,0.3);
} }
#panel.unlock-screen, #panel.unlock-screen,
#panel.login-screen { #panel.login-screen {
height: 2em;
background-color: transparent; background-color: transparent;
} }
@ -529,7 +503,7 @@ StScrollBar StButton#vhandle:active {
-minimum-hpadding: 6px; -minimum-hpadding: 6px;
font-weight: bold; font-weight: bold;
color: #ccc; color: #ccc;
transition-duration: 100ms; transition-duration: 100;
} }
#panel.unlock-screen .panel-button, #panel.unlock-screen .panel-button,
@ -577,7 +551,7 @@ StScrollBar StButton#vhandle:active {
} }
.panel-menu { .panel-menu {
-boxpointer-gap: 4px; -boxpointer-gap: 4px
} }
.panel-status-button-box { .panel-status-button-box {
@ -646,8 +620,20 @@ StScrollBar StButton#vhandle:active {
spacing: 24px; spacing: 24px;
} }
.overview-controls { #overview-group {
padding-bottom: 32px; 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 { .workspace-thumbnails-background {
@ -667,7 +653,6 @@ StScrollBar StButton#vhandle:active {
.workspace-thumbnails { .workspace-thumbnails {
spacing: 11px; spacing: 11px;
visible-width: 32px; /* Amount visible before hovering */
} }
.workspace-thumbnail-indicator { .workspace-thumbnail-indicator {
@ -677,7 +662,6 @@ StScrollBar StButton#vhandle:active {
} }
.window-caption { .window-caption {
spacing: 25px;
background: rgba(0,0,0,0.5); background: rgba(0,0,0,0.5);
border-radius: 8px; border-radius: 8px;
padding: 4px 12px; padding: 4px 12px;
@ -685,10 +669,6 @@ StScrollBar StButton#vhandle:active {
border: 2px solid rgba(0, 0, 0, 0); border: 2px solid rgba(0, 0, 0, 0);
} }
.window-caption:hover {
border: 2px solid rgba(255, 255, 255, 0);
}
.window-close, .notification-close { .window-close, .notification-close {
background-image: url("close-window.svg"); background-image: url("close-window.svg");
background-size: 32px; background-size: 32px;
@ -700,6 +680,10 @@ StScrollBar StButton#vhandle:active {
-shell-close-overlap: 16px; -shell-close-overlap: 16px;
} }
.window-caption:hover {
border: 2px solid rgba(255, 255, 255, 0);
}
.window-clone-border { .window-clone-border {
border: 4px solid rgba(255, 255, 255, 0.5); border: 4px solid rgba(255, 255, 255, 0.5);
border-radius: 4px; border-radius: 4px;
@ -732,36 +716,6 @@ StScrollBar StButton#vhandle:active {
.window-picker { .window-picker {
-horizontal-spacing: 32px; -horizontal-spacing: 32px;
-vertical-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 */ /* Dash */
@ -788,9 +742,14 @@ StScrollBar StButton#vhandle:active {
height: 24px; height: 24px;
} }
#viewSelector {
spacing: 1em;
}
/* Search Box */ /* Search Box */
#searchEntry { #searchEntry {
border-radius: 17px;
width: 320px; width: 320px;
} }
@ -807,20 +766,20 @@ StScrollBar StButton#vhandle:active {
/* Search Results */ /* Search Results */
#searchResults { #searchResults {
padding: 20px 10px 0px 10px; padding: 20px 10px 10px 10px;
spacing: 18px; spacing: 18px;
} }
#searchResultsBin {
max-width: 1000px;
}
#searchResultsContent { #searchResultsContent {
padding-left: 20px;
padding-right: 20px; padding-right: 20px;
spacing: 16px; spacing: 16px;
} }
#searchResultsContent:rtl {
padding-right: 0px;
padding-left: 20px;
}
.search-section { .search-section {
/* This should be equal to #searchResultsContent spacing */ /* This should be equal to #searchResultsContent spacing */
spacing: 16px; spacing: 16px;
@ -849,6 +808,24 @@ StScrollBar StButton#vhandle:active {
spacing: 3px; 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 { .dash-label {
border-radius: 7px; border-radius: 7px;
padding: 4px 12px; padding: 4px 12px;
@ -869,29 +846,40 @@ StScrollBar StButton#vhandle:active {
icon-size: 96px; icon-size: 96px;
} }
.app-display { .all-app {
padding: 8px; padding: 16px 25px 16px 16px;
spacing: 20px; spacing: 20px;
} }
.app-view-controls { .all-app:rtl {
width: 250px; padding-right: 16px;
padding-bottom: 32px; padding-left: 25px;
} }
.app-view-control { .app-filter {
padding: 4px 16px; font-weight: bold;
height: 2.85em;
color: #aaa;
width: 200px;
} }
.search-display > StBoxLayout, .app-filter:hover {
.all-apps > StBoxLayout, color: #eee;
.frequent-apps > StBoxLayout {
/* horizontal padding to make sure the scrollbar doesn't overlap content */
padding: 0px 18px;
} }
.app-folder-icon { .app-filter:selected {
padding: 5px; 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 { .dash-item-container > StButton {
@ -910,6 +898,8 @@ StScrollBar StButton#vhandle:active {
} }
.list-search-result-description { .list-search-result-description {
font-weight: bold;
font-size: 12pt;
color: #eeeeec; color: #eeeeec;
} }
@ -927,31 +917,16 @@ StScrollBar StButton#vhandle:active {
border-radius: 4px; border-radius: 4px;
padding: 3px; padding: 3px;
border: 1px rgba(0,0,0,0); border: 1px rgba(0,0,0,0);
transition-duration: 100ms; transition-duration: 100;
text-align: center; 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 { .app-well-app.running > .overview-icon {
text-shadow: black 0px 2px 2px; text-shadow: black 0px 2px 2px;
background-image: url("running-indicator.svg"); background-image: url("running-indicator.svg");
background-size: contain; background-size: contain;
} }
.app-well-app.app-folder > .overview-icon {
background-color: rgba(0,0,0,0.5);
}
.app-well-app:hover > .overview-icon, .app-well-app:hover > .overview-icon,
.show-apps:hover > .overview-icon, .show-apps:hover > .overview-icon,
.search-provider-icon:hover, .search-provider-icon:hover,
@ -959,14 +934,10 @@ StScrollBar StButton#vhandle:active {
.grid-search-result:hover .overview-icon { .grid-search-result:hover .overview-icon {
background-color: rgba(255,255,255,0.1); background-color: rgba(255,255,255,0.1);
text-shadow: black 0px 2px 2px; text-shadow: black 0px 2px 2px;
transition-duration: 100ms; transition-duration: 100;
color:white; color:white;
} }
.list-search-result:hover .list-search-result-description {
text-shadow: rgba(0,0,0,0.8) 0px 1px 2px;
}
.show-apps { .show-apps {
padding: 4px 0; padding: 4px 0;
} }
@ -979,22 +950,19 @@ StScrollBar StButton#vhandle:active {
color: white; color: white;
} }
.app-well-app:checked > .overview-icon, .show-apps:checked > .overview-icon {
.app-well-app:active > .overview-icon,
.show-apps:checked > .overview-icon,
.show-apps:active > .overview-icon {
background-gradient-start: rgba(255, 255, 255, .05); background-gradient-start: rgba(255, 255, 255, .05);
background-gradient-end: rgba(255, 255, 255, .15); background-gradient-end: rgba(255, 255, 255, .15);
background-gradient-direction: vertical; background-gradient-direction: vertical;
border-radius: 4px; border-radius: 4px;
box-shadow: inset 0px 1px 2px 0px rgba(0, 0, 0, 1); 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:checked .show-apps-icon,
.show-apps:focus .show-apps-icon { .show-apps:focus .show-apps-icon {
color: white; color: white;
transition-duration: 100ms; transition-duration: 100;
} }
.app-well-app:focus > .overview-icon, .app-well-app:focus > .overview-icon,
@ -1033,7 +1001,7 @@ StScrollBar StButton#vhandle:active {
-minimum-hpadding: 6px; -minimum-hpadding: 6px;
font-weight: bold; font-weight: bold;
color: #ccc; color: #ccc;
transition-duration: 100ms; transition-duration: 100;
padding-left: .3em; padding-left: .3em;
padding-right: .3em; padding-right: .3em;
} }
@ -1336,7 +1304,17 @@ StScrollBar StButton#vhandle:active {
#message-tray { #message-tray {
background: #2e3436 url(message-tray-background.png); background: #2e3436 url(message-tray-background.png);
background-repeat: repeat; 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 { .message-tray-summary {
@ -1353,7 +1331,7 @@ StScrollBar StButton#vhandle:active {
border-radius: 10px 10px 0px 0px; border-radius: 10px 10px 0px 0px;
background: rgba(0,0,0,0.9); background: rgba(0,0,0,0.9);
padding: 8px 8px 4px 8px; padding: 8px 8px 4px 8px;
spacing-rows: 4px; spacing-rows: 10px;
spacing-columns: 10px; spacing-columns: 10px;
} }
@ -1433,11 +1411,11 @@ StScrollBar StButton#vhandle:active {
} }
.notification-actions { .notification-actions {
padding-top: 18px;
spacing: 10px; spacing: 10px;
} }
.notification-button { .notification-button {
border-radius: 18px;
padding: 4px 42px 5px; padding: 4px 42px 5px;
} }
@ -1470,6 +1448,7 @@ StScrollBar StButton#vhandle:active {
.hotplug-notification-item { .hotplug-notification-item {
padding: 2px 10px; padding: 2px 10px;
border-radius: 18px;
} }
.hotplug-notification-item:focus { .hotplug-notification-item:focus {
@ -1594,7 +1573,7 @@ StScrollBar StButton#vhandle:active {
.summary-source { .summary-source {
border-radius: 4px; border-radius: 4px;
padding: 0 6px 0 6px; padding: 0 6px 0 6px;
transition-duration: 100ms; transition-duration: 100;
} }
.summary-source-counter { .summary-source-counter {
@ -1608,26 +1587,12 @@ StScrollBar StButton#vhandle:active {
-shell-counter-overlap-y: 13px; -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 */ /* App Switcher */
.switcher-popup { .switcher-popup {
padding: 8px; padding: 8px;
spacing: 16px; spacing: 16px;
} }
.osd-window,
.switcher-list { .switcher-list {
background: rgba(0,0,0,0.8); background: rgba(0,0,0,0.8);
border: 1px solid rgba(128,128,128,0.40); border: 1px solid rgba(128,128,128,0.40);
@ -1779,6 +1744,8 @@ StScrollBar StButton#vhandle:active {
} }
.modal-dialog-button { .modal-dialog-button {
border-radius: 18px;
margin-left: 10px; margin-left: 10px;
margin-right: 10px; margin-right: 10px;
padding: 4px 32px 5px; padding: 4px 32px 5px;
@ -2129,64 +2096,17 @@ StScrollBar StButton#vhandle:active {
} }
/* IBus Candidate Popup */ /* IBus Candidate Popup */
.candidate-popup-boxpointer {
-arrow-border-radius: 8px;
-arrow-background-color: #707070;
-arrow-border-width: 0px;
-arrow-base: 24px;
-arrow-rise: 11px;
}
.candidate-popup-content {
padding: 0.5em;
spacing: 0.3em;
}
.candidate-popup-text {
font-size: 9pt;
}
.candidate-index { .candidate-index {
padding: 0 0.5em 0 0; padding: 0.5em 0.5em 0.5em 0.5em;
color: #cccccc;
} }
.candidate-box { .candidate-label {
padding: 0.3em 0.5em 0.3em 0.5em; padding: 0.5em 0.5em 0.5em 0.5em;
} }
.candidate-box:selected { .candidate-label:selected {
border-radius: 4px; border-radius: 4px;
background-color: rgba(255,255,255,0.2); background-color: rgba(255,255,255,0.33);
}
.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;
} }
/* Login Dialog */ /* Login Dialog */
@ -2422,18 +2342,10 @@ StScrollBar StButton#vhandle:active {
color: orange; color: orange;
} }
.user-widget { .unlock-dialog-user-name-container {
spacing: .4em; 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 */
.screen-shield-background { .screen-shield-background {
@ -2490,7 +2402,6 @@ StScrollBar StButton#vhandle:active {
.screen-shield-notifications-box { .screen-shield-notifications-box {
spacing: 18px; spacing: 18px;
max-width: 34em;
} }
.screen-shield-notification-source { .screen-shield-notification-source {
@ -2525,13 +2436,7 @@ StScrollBar StButton#vhandle:active {
} }
.input-source-switcher-symbol { .input-source-switcher-symbol {
font-size: 34pt; font-size: 42pt;
width: 96px; width: 96px;
height: 96px; height: 96px;
} }
/* Background menu */
.background-menu {
-boxpointer-gap: 4px;
}

View File

@ -88,18 +88,11 @@ doc-gen-org.gnome.Shell.SearchProvider2.xml: $(top_srcdir)/data/org.gnome.ShellS
--generate-docbook doc-gen \ --generate-docbook doc-gen \
$(top_srcdir)/data/org.gnome.ShellSearchProvider2.xml $(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). # Extra SGML files that are included by $(DOC_MAIN_SGML_FILE).
# e.g. content_files=running.sgml building.sgml changes-2.0.sgml # e.g. content_files=running.sgml building.sgml changes-2.0.sgml
content_files= \ content_files= \
doc-gen-org.gnome.Shell.SearchProvider.xml \ doc-gen-org.gnome.Shell.SearchProvider.xml \
doc-gen-org.gnome.Shell.SearchProvider2.xml \ doc-gen-org.gnome.Shell.SearchProvider2.xml
doc-gen-org.gnome.Shell.Screenshot.xml
# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded # SGML files where gtk-doc abbrevations (#GtkWidget) are expanded
# These files must be listed here *and* in content_files # These files must be listed here *and* in content_files

View File

@ -50,6 +50,7 @@
<xi:include href="xml/shell-xfixes-cursor.xml"/> <xi:include href="xml/shell-xfixes-cursor.xml"/>
<xi:include href="xml/shell-util.xml"/> <xi:include href="xml/shell-util.xml"/>
<xi:include href="xml/shell-mount-operation.xml"/> <xi:include href="xml/shell-mount-operation.xml"/>
<xi:include href="xml/shell-mobile-providers.xml"/>
<xi:include href="xml/shell-network-agent.xml"/> <xi:include href="xml/shell-network-agent.xml"/>
<xi:include href="xml/shell-polkit-authentication-agent.xml"/> <xi:include href="xml/shell-polkit-authentication-agent.xml"/>
<xi:include href="xml/shell-tp-client.xml"/> <xi:include href="xml/shell-tp-client.xml"/>

View File

@ -66,11 +66,4 @@ its dependencies to build from tarballs.</description>
<gnome:userid>fmuellner</gnome:userid> <gnome:userid>fmuellner</gnome:userid>
</foaf:Person> </foaf:Person>
</maintainer> </maintainer>
<maintainer>
<foaf:Person>
<foaf:name>Ray Strode</foaf:name>
<foaf:mbox rdf:resource="mailto:halfline@gmail.com" />
<gnome:userid>halfline</gnome:userid>
</foaf:Person>
</maintainer>
</Project> </Project>

View File

@ -28,7 +28,6 @@ nobase_dist_js_DATA = \
misc/extensionUtils.js \ misc/extensionUtils.js \
misc/fileUtils.js \ misc/fileUtils.js \
misc/gnomeSession.js \ misc/gnomeSession.js \
misc/hash.js \
misc/history.js \ misc/history.js \
misc/jsParse.js \ misc/jsParse.js \
misc/loginManager.js \ misc/loginManager.js \
@ -39,8 +38,6 @@ nobase_dist_js_DATA = \
ui/altTab.js \ ui/altTab.js \
ui/appDisplay.js \ ui/appDisplay.js \
ui/appFavorites.js \ ui/appFavorites.js \
ui/backgroundMenu.js \
ui/background.js \
ui/boxpointer.js \ ui/boxpointer.js \
ui/calendar.js \ ui/calendar.js \
ui/checkBox.js \ ui/checkBox.js \
@ -52,6 +49,7 @@ nobase_dist_js_DATA = \
ui/extensionSystem.js \ ui/extensionSystem.js \
ui/extensionDownloader.js \ ui/extensionDownloader.js \
ui/environment.js \ ui/environment.js \
ui/flashspot.js \
ui/ibusCandidatePopup.js\ ui/ibusCandidatePopup.js\
ui/grabHelper.js \ ui/grabHelper.js \
ui/iconGrid.js \ ui/iconGrid.js \
@ -69,16 +67,13 @@ nobase_dist_js_DATA = \
ui/shellEntry.js \ ui/shellEntry.js \
ui/shellMountOperation.js \ ui/shellMountOperation.js \
ui/notificationDaemon.js \ ui/notificationDaemon.js \
ui/osdWindow.js \
ui/overview.js \ ui/overview.js \
ui/overviewControls.js \
ui/panel.js \ ui/panel.js \
ui/panelMenu.js \ ui/panelMenu.js \
ui/pointerWatcher.js \ ui/pointerWatcher.js \
ui/popupMenu.js \ ui/popupMenu.js \
ui/remoteSearch.js \ ui/remoteSearch.js \
ui/runDialog.js \ ui/runDialog.js \
ui/screenshot.js \
ui/screenShield.js \ ui/screenShield.js \
ui/scripting.js \ ui/scripting.js \
ui/search.js \ ui/search.js \
@ -95,7 +90,6 @@ nobase_dist_js_DATA = \
ui/tweener.js \ ui/tweener.js \
ui/unlockDialog.js \ ui/unlockDialog.js \
ui/userMenu.js \ ui/userMenu.js \
ui/userWidget.js \
ui/viewSelector.js \ ui/viewSelector.js \
ui/wanda.js \ ui/wanda.js \
ui/windowAttentionHandler.js \ ui/windowAttentionHandler.js \

View File

@ -45,7 +45,6 @@ const Application = new Lang.Class({
this._extensionPrefsModules = {}; this._extensionPrefsModules = {};
this._extensionIters = {}; this._extensionIters = {};
this._startupUuid = null;
}, },
_buildModel: function() { _buildModel: function() {
@ -204,7 +203,6 @@ const Application = new Lang.Class({
_scanExtensions: function() { _scanExtensions: function() {
let finder = new ExtensionUtils.ExtensionFinder(); let finder = new ExtensionUtils.ExtensionFinder();
finder.connect('extension-found', Lang.bind(this, this._extensionFound)); finder.connect('extension-found', Lang.bind(this, this._extensionFound));
finder.connect('extensions-loaded', Lang.bind(this, this._extensionsLoaded));
finder.scanExtensions(); finder.scanExtensions();
}, },
@ -214,11 +212,6 @@ const Application = new Lang.Class({
this._extensionIters[extension.uuid] = iter; this._extensionIters[extension.uuid] = iter;
}, },
_extensionsLoaded: function() {
if (this._startupUuid && this._extensionAvailable(this._startupUuid))
this._selectExtension(this._startupUuid);
this._startupUuid = null;
},
_onActivate: function() { _onActivate: function() {
this._window.present(); this._window.present();
@ -239,10 +232,10 @@ const Application = new Lang.Class({
// Strip off "extension:///" prefix which fakes a URI, if it exists // Strip off "extension:///" prefix which fakes a URI, if it exists
uuid = stripPrefix(uuid, "extension:///"); uuid = stripPrefix(uuid, "extension:///");
if (this._extensionAvailable(uuid)) if (!this._extensionAvailable(uuid))
this._selectExtension(uuid); return 1;
else
this._startupUuid = uuid; this._selectExtension(uuid);
} }
return 0; return 0;
} }

View File

@ -43,9 +43,8 @@ const Panel = imports.ui.panel;
const PanelMenu = imports.ui.panelMenu; const PanelMenu = imports.ui.panelMenu;
const Tweener = imports.ui.tweener; const Tweener = imports.ui.tweener;
const UserMenu = imports.ui.userMenu; 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 _SCROLL_ANIMATION_TIME = 0.5;
const _TIMED_LOGIN_IDLE_THRESHOLD = 5.0; const _TIMED_LOGIN_IDLE_THRESHOLD = 5.0;
const _LOGO_ICON_HEIGHT = 16; const _LOGO_ICON_HEIGHT = 16;
@ -56,6 +55,39 @@ const WORK_SPINNER_ANIMATION_TIME = 0.3;
let _loginDialog = null; 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({ const LogoMenuButton = new Lang.Class({
Name: 'LogoMenuButton', Name: 'LogoMenuButton',
Extends: PanelMenu.Button, Extends: PanelMenu.Button,
@ -96,7 +128,6 @@ const UserListItem = new Lang.Class({
let layout = new St.BoxLayout({ vertical: false }); let layout = new St.BoxLayout({ vertical: false });
this.actor = new St.Button({ style_class: 'login-dialog-user-list-item', this.actor = new St.Button({ style_class: 'login-dialog-user-list-item',
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
can_focus: true, can_focus: true,
child: layout, child: layout,
reactive: true, 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) { _onItemActivated: function(activatedItem) {
this.emit('activate', 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 = []; let tasks = [];
if (isExpanded) for (let userName in this._items) {
this._box.add_style_pseudo_class('expanded'); let item = this._items[userName];
else
this._box.remove_style_pseudo_class('expanded'); 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) { for (let userName in this._items) {
let item = this._items[userName]; let item = this._items[userName];
item.actor.sync_hover(); item.actor.sync_hover();
item.actor.reactive = true;
item.actor.can_focus = true;
item.syncStyleClasses(); 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) { scrollToItem: function(item) {
@ -329,7 +492,6 @@ const SessionListItem = new Lang.Class({
this.id = id; this.id = id;
this.actor = new St.Button({ style_class: 'login-dialog-session-list-item', this.actor = new St.Button({ style_class: 'login-dialog-session-list-item',
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
can_focus: true, can_focus: true,
reactive: true, reactive: true,
x_fill: true, x_fill: true,
@ -370,7 +532,6 @@ const SessionListItem = new Lang.Class({
color.alpha / 255); color.alpha / 255);
cr.arc(width / 2, height / 2, width / 3, 0, 2 * Math.PI); cr.arc(width / 2, height / 2, width / 3, 0, 2 * Math.PI);
cr.fill(); cr.fill();
cr.$dispose();
}, },
_onClicked: function() { _onClicked: function() {
@ -390,7 +551,6 @@ const SessionList = new Lang.Class({
this.actor.child = this._box; this.actor.child = this._box;
this._button = new St.Button({ style_class: 'login-dialog-session-list-button', this._button = new St.Button({ style_class: 'login-dialog-session-list-button',
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
can_focus: true, can_focus: true,
x_fill: true, x_fill: true,
y_fill: true }); y_fill: true });
@ -402,7 +562,7 @@ const SessionList = new Lang.Class({
box.add_actor(this._triangle); box.add_actor(this._triangle);
let label = new St.Label({ style_class: 'login-dialog-session-list-label', let label = new St.Label({ style_class: 'login-dialog-session-list-label',
text: _("Session") }); text: _("Session...") });
box.add_actor(label); box.add_actor(label);
this._button.connect('clicked', this._button.connect('clicked',
@ -513,7 +673,7 @@ const LoginDialog = new Lang.Class({
this.parent({ shellReactive: true, this.parent({ shellReactive: true,
styleClass: 'login-dialog', styleClass: 'login-dialog',
parentActor: parentActor, parentActor: parentActor,
keybindingMode: Shell.KeyBindingMode.LOGIN_SCREEN, keybindingMode: Main.KeybindingMode.LOGIN_SCREEN,
shouldFadeIn: false }); shouldFadeIn: false });
this.connect('destroy', this.connect('destroy',
Lang.bind(this, this._onDestroy)); Lang.bind(this, this._onDestroy));
@ -553,20 +713,24 @@ const LoginDialog = new Lang.Class({
this._settings.connect('changed::' + GdmUtil.DISABLE_USER_LIST_KEY, this._settings.connect('changed::' + GdmUtil.DISABLE_USER_LIST_KEY,
Lang.bind(this, this._updateDisableUserList)); 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', this._bannerLabel = new St.Label({ style_class: 'login-dialog-banner',
text: '' }); text: '' });
this._userSelectionBox.add(this._bannerLabel); this.contentLayout.add(this._bannerLabel);
this._updateBanner(); 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._userList = new UserList();
this._userSelectionBox.add(this._userList.actor, this.contentLayout.add(this._userList.actor,
{ expand: true, { expand: true,
x_fill: true, x_fill: true,
y_fill: true }); y_fill: true });
this.setInitialKeyFocus(this._userList.actor); this.setInitialKeyFocus(this._userList.actor);
@ -577,13 +741,6 @@ const LoginDialog = new Lang.Class({
x_fill: true, x_fill: true,
y_fill: true, y_fill: true,
x_align: St.Align.START }); 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._promptLabel = new St.Label({ style_class: 'login-dialog-prompt-label' });
this._promptBox.add(this._promptLabel, this._promptBox.add(this._promptLabel,
@ -629,7 +786,6 @@ const LoginDialog = new Lang.Class({
let notListedLabel = new St.Label({ text: _("Not listed?"), let notListedLabel = new St.Label({ text: _("Not listed?"),
style_class: 'login-dialog-not-listed-label' }); style_class: 'login-dialog-not-listed-label' });
this._notListedButton = new St.Button({ style_class: 'login-dialog-not-listed-button', this._notListedButton = new St.Button({ style_class: 'login-dialog-not-listed-button',
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
can_focus: true, can_focus: true,
child: notListedLabel, child: notListedLabel,
reactive: true, reactive: true,
@ -638,10 +794,10 @@ const LoginDialog = new Lang.Class({
this._notListedButton.connect('clicked', Lang.bind(this, this._hideUserListAndLogIn)); this._notListedButton.connect('clicked', Lang.bind(this, this._hideUserListAndLogIn));
this._userSelectionBox.add(this._notListedButton, this.contentLayout.add(this._notListedButton,
{ expand: false, { expand: false,
x_align: St.Align.START, x_align: St.Align.START,
x_fill: true }); x_fill: true });
if (!this._userManager.is_loaded) if (!this._userManager.is_loaded)
this._userManagerLoadedId = this._userManager.connect('notify::is-loaded', this._userManagerLoadedId = this._userManager.connect('notify::is-loaded',
@ -683,9 +839,9 @@ const LoginDialog = new Lang.Class({
if (enabled && text) { if (enabled && text) {
this._bannerLabel.set_text(text); this._bannerLabel.set_text(text);
this._bannerLabel.show(); this._fadeInBanner();
} else { } else {
this._bannerLabel.hide(); this._fadeOutBanner();
} }
}, },
@ -716,50 +872,73 @@ const LoginDialog = new Lang.Class({
if (message) { if (message) {
this._promptMessage.text = message; this._promptMessage.text = message;
this._promptMessage.styleClass = styleClass; this._promptMessage.styleClass = styleClass;
this._promptMessage.show(); GdmUtil.fadeInActor(this._promptMessage);
} else { } else {
this._promptMessage.hide(); GdmUtil.fadeOutActor(this._promptMessage);
} }
}, },
_showLoginHint: function(verifier, message) { _showLoginHint: function(verifier, message) {
this._promptLoginHint.set_text(message) this._promptLoginHint.set_text(message)
this._promptLoginHint.show(); GdmUtil.fadeInActor(this._promptLoginHint);
this._promptLoginHint.opacity = 255;
}, },
_hideLoginHint: function() { _hideLoginHint: function() {
this._promptLoginHint.hide(); GdmUtil.fadeOutActor(this._promptLoginHint);
this._promptLoginHint.set_text(''); this._promptLoginHint.set_text('');
}, },
cancel: function() { cancel: function() {
if (this._verifyingUser) this._userVerifier.cancel();
this._userVerifier.cancel(); },
else
this._reset(); _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) { _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 hold = new Batch.Hold();
let tasks = [function() { let tasks = [function() {
return this._fadeInPrompt();
},
function() {
this._prepareDialog(forSecret, hold); this._prepareDialog(forSecret, hold);
}, },
@ -835,21 +1014,26 @@ const LoginDialog = new Lang.Class({
this._promptEntryTextChangedId = 0; this._promptEntryTextChangedId = 0;
} }
this._setWorking(false); let tasks = [function() {
this._promptBox.hide(); this._setWorking(false);
this._promptLoginHint.hide();
this._promptUser.set_child(null); return GdmUtil.fadeOutActor(this._promptBox);
},
this._updateSensitivity(true); function() {
this._promptEntry.set_text(''); this._promptLoginHint.hide();
this._sessionList.close(); this._updateSensitivity(true);
this._promptLoginHint.hide(); this._promptEntry.set_text('');
this.clearButtons(); this.clearButtons();
this._workSpinner = null; this._workSpinner = null;
this._signInButton = null; this._signInButton = null;
}];
let batch = new Batch.ConsecutiveBatch(this, tasks);
return batch.run();
}, },
_setWorking: function(working) { _setWorking: function(working) {
@ -871,8 +1055,7 @@ const LoginDialog = new Lang.Class({
transition: 'linear', transition: 'linear',
onCompleteScope: this, onCompleteScope: this,
onComplete: function() { onComplete: function() {
if (this._workSpinner) this._workSpinner.stop();
this._workSpinner.stop();
} }
}); });
} }
@ -881,7 +1064,6 @@ const LoginDialog = new Lang.Class({
_askQuestion: function(verifier, serviceName, question, passwordChar) { _askQuestion: function(verifier, serviceName, question, passwordChar) {
this._promptLabel.set_text(question); this._promptLabel.set_text(question);
this._updateSensitivity(true);
this._promptEntry.set_text(''); this._promptEntry.set_text('');
this._promptEntry.clutter_text.set_password_char(passwordChar); this._promptEntry.clutter_text.set_password_char(passwordChar);
@ -918,26 +1100,7 @@ const LoginDialog = new Lang.Class({
}, },
_onSessionOpened: function(client, serviceName) { _onSessionOpened: function(client, serviceName) {
Tweener.addTween(this.dialogLayout, this._greeter.call_start_session_when_ready_sync(serviceName, true, null);
{ 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 });
}, },
_waitForItemForUser: function(userName) { _waitForItemForUser: function(userName) {
@ -1064,21 +1227,75 @@ const LoginDialog = new Lang.Class({
})); }));
}, },
_setUserListExpanded: function(expanded) {
this._userList.updateStyle(expanded);
this._userSelectionBox.visible = expanded;
},
_hideUserListAndLogIn: function() { _hideUserListAndLogIn: function() {
this._setUserListExpanded(false); let tasks = [function() {
GdmUtil.cloneAndFadeOutActor(this._userSelectionBox); return this._userList.hideItems();
this._askForUsernameAndLogIn(); },
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() { _showUserList: function() {
this._hidePrompt(); let tasks = [this._hidePrompt,
this._setUserListExpanded(true);
this._userList.actor.grab_key_focus(); 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) { _beginVerificationForUser: function(userName) {
@ -1089,30 +1306,38 @@ const LoginDialog = new Lang.Class({
return hold; return hold;
}, },
_beginVerificationForItem: function(item) {
let userWidget = new UserWidget.UserWidget(item.user);
this._promptUser.set_child(userWidget.actor);
let tasks = [function() {
let userName = item.user.get_user_name();
return this._beginVerificationForUser(userName);
}];
let batch = new Batch.ConsecutiveBatch(this, tasks);
return batch.run();
},
_onUserListActivated: function(activatedItem) { _onUserListActivated: function(activatedItem) {
let userName;
let tasks = [function() { let tasks = [function() {
return GdmUtil.cloneAndFadeOutActor(this._userSelectionBox); this._userList.actor.reactive = false;
return this._userList.pinInPlace();
}, },
function() { 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; this._user = activatedItem.user;
let batch = new Batch.ConcurrentBatch(this, [new Batch.ConsecutiveBatch(this, tasks), let batch = new Batch.ConsecutiveBatch(this, tasks);
this._beginVerificationForItem(activatedItem)]);
batch.run(); batch.run();
}, },

View File

@ -1,6 +1,5 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio; const Gio = imports.gi.Gio;
const Lang = imports.lang; const Lang = imports.lang;
const Mainloop = imports.mainloop; const Mainloop = imports.mainloop;
@ -16,7 +15,6 @@ const Tweener = imports.ui.tweener;
const PASSWORD_SERVICE_NAME = 'gdm-password'; const PASSWORD_SERVICE_NAME = 'gdm-password';
const FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint'; const FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint';
const FADE_ANIMATION_TIME = 0.16; const FADE_ANIMATION_TIME = 0.16;
const CLONE_FADE_ANIMATION_TIME = 0.25;
const LOGIN_SCREEN_SCHEMA = 'org.gnome.login-screen'; const LOGIN_SCREEN_SCHEMA = 'org.gnome.login-screen';
const FINGERPRINT_AUTHENTICATION_KEY = 'enable-fingerprint-authentication'; const FINGERPRINT_AUTHENTICATION_KEY = 'enable-fingerprint-authentication';
@ -73,34 +71,6 @@ function fadeOutActor(actor) {
return hold; 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({ const ShellUserVerifier = new Lang.Class({
Name: 'ShellUserVerifier', Name: 'ShellUserVerifier',

View File

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

View File

@ -58,7 +58,6 @@ const SessionManagerIface = <interface name="org.gnome.SessionManager">
<arg type="u" direction="in" /> <arg type="u" direction="in" />
<arg type="b" direction="out" /> <arg type="b" direction="out" />
</method> </method>
<property name="SessionIsActive" type="b" access="read"/>
<signal name="InhibitorAdded"> <signal name="InhibitorAdded">
<arg type="o" direction="out"/> <arg type="o" direction="out"/>
</signal> </signal>

View File

@ -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;
},
});

View File

@ -5,7 +5,7 @@ const Gio = imports.gi.Gio;
const Lang = imports.lang; const Lang = imports.lang;
const Mainloop = imports.mainloop; const Mainloop = imports.mainloop;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const Signals = imports.signals; const UPowerGlib = imports.gi.UPowerGlib;
const SystemdLoginManagerIface = <interface name='org.freedesktop.login1.Manager'> const SystemdLoginManagerIface = <interface name='org.freedesktop.login1.Manager'>
<method name='PowerOff'> <method name='PowerOff'>
@ -26,19 +26,6 @@ const SystemdLoginManagerIface = <interface name='org.freedesktop.login1.Manager
<method name='CanSuspend'> <method name='CanSuspend'>
<arg type='s' direction='out'/> <arg type='s' direction='out'/>
</method> </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='ListSessions'>
<arg name='sessions' type='a(susso)' direction='out'/>
</method>
<signal name='PrepareForSleep'>
<arg type='b' direction='out'/>
</signal>
</interface>; </interface>;
const SystemdLoginSessionIface = <interface name='org.freedesktop.login1.Session'> const SystemdLoginSessionIface = <interface name='org.freedesktop.login1.Session'>
@ -64,6 +51,12 @@ const ConsoleKitManagerIface = <interface name='org.freedesktop.ConsoleKit.Manag
</interface>; </interface>;
const ConsoleKitSessionIface = <interface name='org.freedesktop.ConsoleKit.Session'> 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='Lock' />
<signal name='Unlock' /> <signal name='Unlock' />
</interface>; </interface>;
@ -100,8 +93,6 @@ const LoginManagerSystemd = new Lang.Class({
this._proxy = new SystemdLoginManager(Gio.DBus.system, this._proxy = new SystemdLoginManager(Gio.DBus.system,
'org.freedesktop.login1', 'org.freedesktop.login1',
'/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 // Having this function is a bit of a hack since the Systemd and ConsoleKit
@ -118,6 +109,10 @@ const LoginManagerSystemd = new Lang.Class({
return this._currentSession; return this._currentSession;
}, },
get sessionActive() {
return Shell.session_is_active_for_systemd();
},
canPowerOff: function(asyncCallback) { canPowerOff: function(asyncCallback) {
this._proxy.CanPowerOffRemote(function(result, error) { this._proxy.CanPowerOffRemote(function(result, error) {
if (error) if (error)
@ -145,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() { powerOff: function() {
this._proxy.PowerOffRemote(true); this._proxy.PowerOffRemote(true);
}, },
@ -164,33 +150,8 @@ const LoginManagerSystemd = new Lang.Class({
suspend: function() { suspend: function() {
this._proxy.SuspendRemote(true); 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({ const LoginManagerConsoleKit = new Lang.Class({
Name: 'LoginManagerConsoleKit', Name: 'LoginManagerConsoleKit',
@ -199,6 +160,7 @@ const LoginManagerConsoleKit = new Lang.Class({
this._proxy = new ConsoleKitManager(Gio.DBus.system, this._proxy = new ConsoleKitManager(Gio.DBus.system,
'org.freedesktop.ConsoleKit', 'org.freedesktop.ConsoleKit',
'/org/freedesktop/ConsoleKit/Manager'); '/org/freedesktop/ConsoleKit/Manager');
this._upClient = new UPowerGlib.Client();
}, },
// Having this function is a bit of a hack since the Systemd and ConsoleKit // Having this function is a bit of a hack since the Systemd and ConsoleKit
@ -215,6 +177,19 @@ const LoginManagerConsoleKit = new Lang.Class({
return 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) { canPowerOff: function(asyncCallback) {
this._proxy.CanStopRemote(function(result, error) { this._proxy.CanStopRemote(function(result, error) {
if (error) if (error)
@ -234,11 +209,10 @@ const LoginManagerConsoleKit = new Lang.Class({
}, },
canSuspend: function(asyncCallback) { canSuspend: function(asyncCallback) {
asyncCallback(false); Mainloop.idle_add(Lang.bind(this, function() {
}, asyncCallback(this._upClient.get_can_suspend());
return false;
listSessions: function(asyncCallback) { }));
asyncCallback([]);
}, },
powerOff: function() { powerOff: function() {
@ -250,12 +224,6 @@ const LoginManagerConsoleKit = new Lang.Class({
}, },
suspend: function() { suspend: function() {
this.emit('prepare-for-sleep', true); this._upClient.suspend_sync(null);
this.emit('prepare-for-sleep', false);
},
inhibit: function(reason, callback) {
callback(null);
} }
}); });
Signals.addSignalMethods(LoginManagerConsoleKit.prototype);

View File

@ -2,93 +2,9 @@
const Gio = imports.gi.Gio; const Gio = imports.gi.Gio;
const Lang = imports.lang; const Lang = imports.lang;
const NMGtk = imports.gi.NMGtk; const Shell = imports.gi.Shell;
const Signals = imports.signals; 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 // The following are not the complete interfaces, just the methods we need
// (or may need in the future) // (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); 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({ const ModemGsm = new Lang.Class({
Name: 'ModemGsm', Name: 'ModemGsm',
@ -141,7 +127,7 @@ const ModemGsm = new Lang.Class({
this.emit('notify::signal-quality'); this.emit('notify::signal-quality');
})); }));
this._proxy.connectSignal('RegistrationInfo', Lang.bind(this, function(proxy, sender, [status, code, name]) { 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.emit('notify::operator-name');
})); }));
this._proxy.GetRegistrationInfoRemote(Lang.bind(this, function([result], err) { this._proxy.GetRegistrationInfoRemote(Lang.bind(this, function([result], err) {
@ -151,7 +137,7 @@ const ModemGsm = new Lang.Class({
} }
let [status, code, name] = result; let [status, code, name] = result;
this.operator_name = _findProviderForMccMnc(name, code); this.operator_name = this._findOperatorName(name, code);
this.emit('notify::operator-name'); this.emit('notify::operator-name');
})); }));
this._proxy.GetSignalQualityRemote(Lang.bind(this, function(result, err) { this._proxy.GetSignalQualityRemote(Lang.bind(this, function(result, err) {
@ -164,6 +150,32 @@ const ModemGsm = new Lang.Class({
} }
this.emit('notify::signal-quality'); 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); Signals.addSignalMethods(ModemGsm.prototype);
@ -203,99 +215,15 @@ const ModemCdma = new Lang.Class({
// it will return an error if the device is not connected // it will return an error if the device is not connected
this.operator_name = null; this.operator_name = null;
} else { } else {
let [bandClass, band, sid] = result; let [bandClass, band, id] = result;
if (name.length > 0) {
this.operator_name = _findProviderForSid(sid) let table = _getProvidersTable();
this.operator_name = findProviderForSid(table, id);
} else
this.operator_name = null;
} }
this.emit('notify::operator-name'); this.emit('notify::operator-name');
})); }));
} }
}); });
Signals.addSignalMethods(ModemCdma.prototype); 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);

View File

@ -1,8 +1,6 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib; const GLib = imports.gi.GLib;
const St = imports.gi.St;
const Main = imports.ui.main; const Main = imports.ui.main;
@ -161,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: // lowerBound:
// @array: an array or array-like object, already sorted // @array: an array or array-like object, already sorted
// according to @cmp // according to @cmp
@ -210,27 +288,3 @@ function insertSorted(array, val, cmp) {
return pos; 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;
}

View File

@ -98,13 +98,43 @@ const AppSwitcherPopup = new Lang.Class({
} }
}, },
_createSwitcher: function() { _getAppLists: function() {
let apps = Shell.AppSystem.get_default().get_running (); 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; return false;
this._switcherList = new AppSwitcher(apps, this); this._switcherList = new AppSwitcher(localApps, otherApps, this);
this._items = this._switcherList.icons; this._items = this._switcherList.icons;
return true; return true;
@ -235,8 +265,12 @@ const AppSwitcherPopup = new Lang.Class({
this.parent(); this.parent();
let appIcon = this._items[this._selectedIndex]; let appIcon = this._items[this._selectedIndex];
let window = this._currentWindow > 0 ? this._currentWindow : 0; let window;
appIcon.app.activate_window(appIcon.cachedWindows[window], timestamp); if (this._currentWindow >= 0)
window = appIcon.cachedWindows[this._currentWindow];
else
window = null;
appIcon.app.activate_window(window, timestamp);
}, },
_onDestroy : function() { _onDestroy : function() {
@ -427,26 +461,34 @@ const AppSwitcher = new Lang.Class({
Name: 'AppSwitcher', Name: 'AppSwitcher',
Extends: SwitcherPopup.SwitcherList, Extends: SwitcherPopup.SwitcherList,
_init : function(apps, altTabPopup) { _init : function(localApps, otherApps, altTabPopup) {
this.parent(true); 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.icons = [];
this._arrows = []; this._arrows = [];
for (let i = 0; i < workspaceIcons.length; i++)
let windowTracker = Shell.WindowTracker.get_default(); this._addIcon(workspaceIcons[i]);
let allWindows = global.display.get_tab_list(Meta.TabList.NORMAL, if (workspaceIcons.length > 0 && otherIcons.length > 0)
global.screen, null); this.addSeparator();
for (let i = 0; i < otherIcons.length; i++)
// Construct the AppIcons, add to the popup this._addIcon(otherIcons[i]);
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);
}
this._curApp = -1; this._curApp = -1;
this._iconSize = 0; this._iconSize = 0;
@ -472,6 +514,8 @@ const AppSwitcher = new Lang.Class({
let [iconMinHeight, iconNaturalHeight] = this.icons[j].label.get_preferred_height(-1); let [iconMinHeight, iconNaturalHeight] = this.icons[j].label.get_preferred_height(-1);
let iconSpacing = iconNaturalHeight + iconPadding + iconBorder; let iconSpacing = iconNaturalHeight + iconPadding + iconBorder;
let totalSpacing = this._list.spacing * (this._items.length - 1); 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 // We just assume the whole screen here due to weirdness happing with the passed width
let primary = Main.layoutManager.primaryMonitor; let primary = Main.layoutManager.primaryMonitor;
@ -594,12 +638,24 @@ const ThumbnailList = new Lang.Class({
_init : function(windows) { _init : function(windows) {
this.parent(false); 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._labels = new Array();
this._thumbnailBins = new Array(); this._thumbnailBins = new Array();
this._clones = new Array(); this._clones = new Array();
this._windows = windows; this._windows = windows;
for (let i = 0; i < windows.length; i++) { 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', let box = new St.BoxLayout({ style_class: 'thumbnail-box',
vertical: true }); vertical: true });

View File

@ -3,7 +3,6 @@
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio; const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib; const GLib = imports.gi.GLib;
const GObject = imports.gi.GObject;
const Gtk = imports.gi.Gtk; const Gtk = imports.gi.Gtk;
const GMenu = imports.gi.GMenu; const GMenu = imports.gi.GMenu;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
@ -15,12 +14,10 @@ const Mainloop = imports.mainloop;
const Atk = imports.gi.Atk; const Atk = imports.gi.Atk;
const AppFavorites = imports.ui.appFavorites; const AppFavorites = imports.ui.appFavorites;
const BoxPointer = imports.ui.boxpointer;
const DND = imports.ui.dnd; const DND = imports.ui.dnd;
const IconGrid = imports.ui.iconGrid; const IconGrid = imports.ui.iconGrid;
const Main = imports.ui.main; const Main = imports.ui.main;
const Overview = imports.ui.overview; const Overview = imports.ui.overview;
const OverviewControls = imports.ui.overviewControls;
const PopupMenu = imports.ui.popupMenu; const PopupMenu = imports.ui.popupMenu;
const Tweener = imports.ui.tweener; const Tweener = imports.ui.tweener;
const Workspace = imports.ui.workspace; const Workspace = imports.ui.workspace;
@ -30,232 +27,58 @@ const Util = imports.misc.util;
const MAX_APPLICATION_WORK_MILLIS = 75; const MAX_APPLICATION_WORK_MILLIS = 75;
const MENU_POPUP_TIMEOUT = 600; const MENU_POPUP_TIMEOUT = 600;
const SCROLL_TIME = 0.1; const SCROLL_TIME = 0.1;
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 AlphabeticalView = new Lang.Class({ const AlphabeticalView = new Lang.Class({
Name: 'AlphabeticalView', Name: 'AlphabeticalView',
Abstract: true,
_init: function() { _init: function() {
this._grid = new IconGrid.IconGrid({ xAlign: St.Align.MIDDLE, this._grid = new IconGrid.IconGrid({ xAlign: St.Align.START });
columnLimit: MAX_COLUMNS }); this._appSystem = Shell.AppSystem.get_default();
// Standard hack for ClutterBinLayout this._pendingAppLaterId = 0;
this._grid.actor.x_expand = true; this._appIcons = {}; // desktop file id
this._allApps = [];
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 AllView = new Lang.Class({
Name: 'AllView',
Extends: AlphabeticalView,
_init: function() {
this.parent();
let box = new St.BoxLayout({ vertical: true }); let box = new St.BoxLayout({ vertical: true });
this._stack = new St.Widget({ layout_manager: new Clutter.BinLayout() }); box.add(this._grid.actor, { y_align: St.Align.START, expand: true });
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 });
this.actor = new St.ScrollView({ x_fill: true, this.actor = new St.ScrollView({ x_fill: true,
y_fill: false, y_fill: false,
y_align: St.Align.START, y_align: St.Align.START,
x_expand: true, style_class: 'vfade' });
y_expand: true,
overlay_scrollbars: true,
style_class: 'all-apps vfade' });
this.actor.add_actor(box); this.actor.add_actor(box);
this.actor.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC); this.actor.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
let action = new Clutter.PanAction({ interpolate: true }); let action = new Clutter.PanAction({ interpolate: true });
action.connect('pan', Lang.bind(this, this._onPan)); action.connect('pan', Lang.bind(this, this._onPan));
this.actor.add_action(action); 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) { _onPan: function(action) {
this._clickAction.release();
let [dist, dx, dy] = action.get_motion_delta(0); let [dist, dx, dy] = action.get_motion_delta(0);
let adjustment = this.actor.vscroll.adjustment; let adjustment = this.actor.vscroll.adjustment;
adjustment.value -= (dy / this.actor.height) * adjustment.page_size; adjustment.value -= (dy / this.actor.height) * adjustment.page_size;
return false; return false;
}, },
_getItemId: function(item) { removeAll: function() {
if (item instanceof Shell.App) this._grid.removeAll();
return item.get_id(); this._appIcons = {};
else if (item instanceof GMenu.TreeDirectory) this._allApps = [];
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);
}, },
addApp: function(app) { addApp: function(app) {
let appIcon = this._addItem(app); var id = app.get_id();
if (appIcon) if (this._appIcons[id] !== undefined)
appIcon.actor.connect('key-focus-in', return;
Lang.bind(this, this._ensureIconVisible));
},
addFolder: function(dir) { let appIcon = new AppWellIcon(app);
let folderIcon = this._addItem(dir); let pos = Util.insertSorted(this._allApps, app, function(a, b) {
if (folderIcon) return a.compare_by_name(b);
folderIcon.actor.connect('key-focus-in', });
Lang.bind(this, this._ensureIconVisible)); this._grid.addItem(appIcon.actor, pos);
}, appIcon.actor.connect('key-focus-in', Lang.bind(this, this._ensureIconVisible));
addFolderPopup: function(popup) { this._appIcons[id] = appIcon;
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);
}));
}, },
_ensureIconVisible: function(icon) { _ensureIconVisible: function(icon) {
@ -263,9 +86,9 @@ const AllView = new Lang.Class({
let [value, lower, upper, stepIncrement, pageIncrement, pageSize] = adjustment.get_values(); let [value, lower, upper, stepIncrement, pageIncrement, pageSize] = adjustment.get_values();
let offset = 0; let offset = 0;
let vfade = this.actor.get_effect("fade"); let vfade = this.actor.get_effect("vfade");
if (vfade) if (vfade)
offset = vfade.vfade_offset; offset = vfade.fade_offset;
// If this gets called as part of a right-click, the actor // If this gets called as part of a right-click, the actor
// will be needs_allocation, and so "icon.y" would return 0 // will be needs_allocation, and so "icon.y" would return 0
@ -284,170 +107,174 @@ const AllView = new Lang.Class({
transition: 'easeOutQuad' }); transition: 'easeOutQuad' });
}, },
_updateIconOpacities: function(folderOpen) { setVisibleApps: function(apps) {
for (let id in this._items) { if (apps == null) { // null implies "all"
if (folderOpen && !this._items[id].actor.checked) for (var id in this._appIcons) {
this._items[id].actor.opacity = INACTIVE_GRID_OPACITY; var icon = this._appIcons[id];
else icon.actor.visible = true;
this._items[id].actor.opacity = 255; }
} 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({ const ViewByCategories = new Lang.Class({
Name: 'FrequentView', Name: 'ViewByCategories',
_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',
_init: function() { _init: function() {
this._appSystem = Shell.AppSystem.get_default(); this._appSystem = Shell.AppSystem.get_default();
this._appSystem.connect('installed-changed', Lang.bind(this, function() { this.actor = new St.BoxLayout({ style_class: 'all-app' });
Main.queueDeferredWork(this._allAppsWorkId); this.actor._delegate = this;
}));
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._views = []; this._view = new AlphabeticalView();
let view, button; // categories can be -1 (the All view) or 0...n-1, where n
view = new FrequentView(); // is the number of sections
button = new St.Button({ label: _("Frequent"), // -2 is a flag to indicate that nothing is selected
style_class: 'app-view-control', // (used only before the actor is mapped the first time)
can_focus: true, this._currentCategory = -2;
x_expand: true }); this._categories = [];
this._views[Views.FREQUENT] = { 'view': view, 'control': button };
view = new AllView(); this._categoryBox = new St.BoxLayout({ vertical: true,
button = new St.Button({ label: _("All"), reactive: true,
style_class: 'app-view-control', accessible_role: Atk.Role.LIST });
can_focus: true, this._categoryScroll = new St.ScrollView({ x_fill: false,
x_expand: true }); y_fill: false,
this._views[Views.ALL] = { 'view': view, 'control': button }; 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', // Always select the "All" filter when switching to the app view
vertical: true, this.actor.connect('notify::mapped', Lang.bind(this,
x_expand: true, y_expand: true }); function() {
if (this.actor.mapped && this._allCategoryButton)
this._viewStack = new St.Widget({ layout_manager: new Clutter.BinLayout(), this._selectCategory(-1);
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);
// We need a dummy actor to catch the keyboard focus if the // We need a dummy actor to catch the keyboard focus if the
// user Ctrl-Alt-Tabs here before the deferred work creates // user Ctrl-Alt-Tabs here before the deferred work creates
// our real contents // our real contents
this._focusDummy = new St.Bin({ can_focus: true }); this._focusDummy = new St.Bin({ can_focus: true });
this._viewStack.add_actor(this._focusDummy); this.actor.add(this._focusDummy);
this._allAppsWorkId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplayAllApps));
this._frequentAppsWorkId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplayFrequentApps));
}, },
_showView: function(activeIndex) { _selectCategory: function(num) {
for (let i = 0; i < this._views.length; i++) { if (this._currentCategory == num) // nothing to do
let actor = this._views[i].view.actor; return;
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);
if (i == activeIndex) this._currentCategory = num;
this._views[i].control.add_style_pseudo_class('checked');
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 else
this._views[i].control.remove_style_pseudo_class('checked'); this._categories[i].button.remove_style_pseudo_class('selected');
} }
}, },
_redisplay: function() { // Recursively load a GMenuTreeDirectory; we could put this in ShellAppSystem too
this._redisplayFrequentApps(); _loadCategory: function(dir, appList) {
this._redisplayAllApps(); 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() { _addCategory: function(name, index, dir) {
let view = this._views[Views.FREQUENT].view; let apps;
view.removeAll(); if (dir != null) {
view.loadApps(); 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() { _removeAll: function() {
let view = this._views[Views.ALL].view; this._view.removeAll();
this._categories = [];
this._categoryBox.destroy_all_children();
},
view.removeAll(); refresh: function() {
this._removeAll();
let tree = this._appSystem.get_tree(); /* Translators: Filter to display all applications */
let root = tree.get_root_directory(); this._addCategory(_("All"), -1, null);
let iter = root.iter(); var tree = this._appSystem.get_tree();
let nextType; var root = tree.get_root_directory();
let folderCategories = global.settings.get_strv('app-folder-categories');
var iter = root.iter();
var nextType;
var i = 0;
while ((nextType = iter.next()) != GMenu.TreeItemType.INVALID) { while ((nextType = iter.next()) != GMenu.TreeItemType.INVALID) {
if (nextType == GMenu.TreeItemType.DIRECTORY) { if (nextType == GMenu.TreeItemType.DIRECTORY) {
let dir = iter.get_directory(); var dir = iter.get_directory();
if (dir.get_is_nodisplay()) if (dir.get_is_nodisplay())
continue; continue;
if (folderCategories.indexOf(dir.get_menu_id()) != -1) if (this._addCategory(dir.get_name(), i, dir))
view.addFolder(dir); i++;
else
_loadCategory(dir, view);
} }
} }
view.loadGrid();
this._selectCategory(-1);
if (this._focusDummy) { if (this._focusDummy) {
let focused = this._focusDummy.has_key_focus(); let focused = this._focusDummy.has_key_focus();
@ -459,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({ const AppSearchProvider = new Lang.Class({
Name: 'AppSearchProvider', Name: 'AppSearchProvider',
@ -510,163 +360,70 @@ const AppSearchProvider = new Lang.Class({
createResultActor: function (resultMeta, terms) { createResultActor: function (resultMeta, terms) {
let app = resultMeta['id']; let app = resultMeta['id'];
let icon = new AppIcon(app); let icon = new AppWellIcon(app);
return icon.actor; return icon.actor;
} }
}); });
const FolderIcon = new Lang.Class({ const SettingsSearchProvider = new Lang.Class({
Name: 'FolderIcon', Name: 'SettingsSearchProvider',
_init: function(dir, parentView) { _init: function() {
this._dir = dir; this.appInfo = Gio.DesktopAppInfo.new('gnome-control-center.desktop');
this._parentView = parentView; this._appSys = Shell.AppSystem.get_default();
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();
}));
}, },
_createIcon: function(size) { getResultMetas: function(prefs, callback) {
return this.view.createFolderIcon(size); let metas = [];
}, for (let i = 0; i < prefs.length; i++) {
let pref = prefs[i];
_ensurePopup: function() { metas.push({ 'id': pref,
if (this._popup) 'name': pref.get_name(),
return; 'createIcon': function() { return null; }
});
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;
} }
callback(metas);
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();
}));
}, },
toggle: function() { getInitialResultSet: function(terms) {
if (this._isOpen) this.searchSystem.pushResults(this, this._appSys.search_settings(terms));
this.popdown();
else
this.popup();
}, },
popup: function() { getSubsearchResultSet: function(previousResults, terms) {
if (this._isOpen) this.searchSystem.pushResults(this, this._appSys.search_settings(terms));
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);
}, },
popdown: function() { activateResult: function(pref) {
if (!this._isOpen) pref.activate();
return; },
this._boxPointer.hide(BoxPointer.PopupAnimation.FADE | launchSearch: function(terms) {
BoxPointer.PopupAnimation.SLIDE); // FIXME: this should be a remote search provider
this._isOpen = false; this.appInfo.launch([], global.create_app_launch_context());
this.emit('open-state-changed', false);
} }
}); });
Signals.addSignalMethods(AppFolderPopup.prototype);
const AppIcon = new Lang.Class({ const AppIcon = new Lang.Class({
Name: 'AppIcon', 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.app = app;
this.actor = new St.Button({ style_class: 'app-well-app', this.actor = new St.Button({ style_class: 'app-well-app',
reactive: true, reactive: true,
@ -676,15 +433,13 @@ const AppIcon = new Lang.Class({
y_fill: true }); y_fill: true });
this.actor._delegate = this; this.actor._delegate = this;
if (!iconParams) this.icon = new AppIcon(app, iconParams);
iconParams = {};
iconParams['createIcon'] = Lang.bind(this, this._createIcon);
this.icon = new IconGrid.BaseIcon(app.get_name(), iconParams);
this.actor.set_child(this.icon.actor); this.actor.set_child(this.icon.actor);
this.actor.label_actor = this.icon.label; 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('button-press-event', Lang.bind(this, this._onButtonPress));
this.actor.connect('clicked', Lang.bind(this, this._onClicked)); this.actor.connect('clicked', Lang.bind(this, this._onClicked));
this.actor.connect('popup-menu', Lang.bind(this, this._onKeyboardPopupMenu)); this.actor.connect('popup-menu', Lang.bind(this, this._onKeyboardPopupMenu));
@ -723,10 +478,6 @@ const AppIcon = new Lang.Class({
this._removeMenuTimeout(); this._removeMenuTimeout();
}, },
_createIcon: function(iconSize) {
return this.app.create_icon_texture(iconSize);
},
_removeMenuTimeout: function() { _removeMenuTimeout: function() {
if (this._menuTimeoutId > 0) { if (this._menuTimeoutId > 0) {
Mainloop.source_remove(this._menuTimeoutId); Mainloop.source_remove(this._menuTimeoutId);
@ -784,7 +535,6 @@ const AppIcon = new Lang.Class({
popupMenu: function() { popupMenu: function() {
this._removeMenuTimeout(); this._removeMenuTimeout();
this.actor.fake_release(); this.actor.fake_release();
this._draggable.fakeRelease();
if (!this._menu) { if (!this._menu) {
this._menu = new AppIconMenu(this); this._menu = new AppIconMenu(this);
@ -826,13 +576,16 @@ const AppIcon = new Lang.Class({
this.emit('launching'); this.emit('launching');
let modifiers = event.get_state(); let modifiers = event.get_state();
if (modifiers & Clutter.ModifierType.CONTROL_MASK if (this._onActivateOverride) {
&& this.app.state == Shell.AppState.RUNNING) { this._onActivateOverride(event);
this.app.open_new_window(-1);
} else { } 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(); Main.overview.hide();
}, },
@ -853,7 +606,7 @@ const AppIcon = new Lang.Class({
return this.icon.icon; return this.icon.icon;
} }
}); });
Signals.addSignalMethods(AppIcon.prototype); Signals.addSignalMethods(AppWellIcon.prototype);
const AppIconMenu = new Lang.Class({ const AppIconMenu = new Lang.Class({
Name: 'AppIconMenu', Name: 'AppIconMenu',

View File

@ -1,724 +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._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);
},
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;
if (params.onFinished)
params.onFinished(content);
} else {
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;
}
if (params.onFinished)
params.onFinished(content);
}));
}
},
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._animationUpdateTimeoutId) {
GLib.source_remove (this._animationUpdateTimeoutId);
this._animationUpdateTimeoutId = 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;
this.actor.add_child(actor);
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._images[index].content = content;
this._watchCacheFile(filename);
},
_updateAnimationProgress: function() {
if (this._images[1]) {
this._images[1].raise_top();
this._images[1].opacity = this._animation.transitionProgress * 255;
}
this._queueAnimationUpdate();
},
_updateAnimation: function() {
this._animationUpdateTimeoutId = 0;
let files = this._animation.getKeyFrameFiles(this._layoutManager.monitors[this._monitorIndex]);
if (!files) {
this._setLoaded();
this._queueAnimationUpdate();
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) {
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();
}
})
});
}
},
_queueAnimationUpdate: function() {
if (this._animationUpdateTimeoutId != 0)
return;
if (!this._cancellable || this._cancellable.is_cancelled())
return;
if (!this._animation.duration)
return;
let interval = Math.max(ANIMATION_MIN_WAKEUP_INTERVAL * 1000,
ANIMATION_OPACITY_STEP_INCREMENT / this._animation.duration);
this._animationUpdateTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
interval,
Lang.bind(this, function() {
this._animationUpdateTimeoutId = 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._keyFrames = [];
this.duration = 0.0;
this.transitionProgress = 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.duration = this._show.get_total_duration();
this.loaded = true;
if (callback)
callback();
}));
},
getKeyFrameFiles: function(monitor) {
if (!this._show)
return null;
if (this._show.get_num_slides() < 1)
return null;
let [progress, duration, isFixed, file1, file2] = this._show.get_current_slide(monitor.width, monitor.height);
this.transitionProgress = progress;
let files = [];
if (file1)
files.push(file1);
if (file2)
files.push(file2);
return files;
},
});
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;
this._loadedSignalId = 0;
this._changedSignalId = 0;
},
destroy: function() {
if (this._loadedSignalId)
this._newBackground.disconnect(this._loadedSignalId);
if (this._changedSignalId)
this.background.disconnect(this._changedSignalId);
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;
let signalId = newBackground.connect('loaded',
Lang.bind(this, function() {
newBackground.disconnect(signalId);
Tweener.addTween(background.actor,
{ opacity: 0,
time: FADE_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() {
this.background = newBackground;
this._newBackground = null;
background.actor.destroy();
this.emit('changed');
})
});
}));
this._loadedSignalId = signalId;
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();
}
let signalId = background.connect('changed', Lang.bind(this, function() {
background.disconnect(signalId);
this._updateBackground(background, this._monitorIndex);
}));
this._changedSignalId = signalId;
return background;
},
});
Signals.addSignalMethods(BackgroundManager.prototype);

View File

@ -1,58 +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);
}

View File

@ -38,7 +38,6 @@ const BoxPointer = new Lang.Class({
this._arrowSide = arrowSide; this._arrowSide = arrowSide;
this._userArrowSide = arrowSide; this._userArrowSide = arrowSide;
this._arrowOrigin = 0; this._arrowOrigin = 0;
this._arrowActor = null;
this.actor = new St.Bin({ x_fill: true, this.actor = new St.Bin({ x_fill: true,
y_fill: true }); y_fill: true });
this._container = new Shell.GenericContainer(); this._container = new Shell.GenericContainer();
@ -221,27 +220,31 @@ const BoxPointer = new Lang.Class({
this.bin.allocate(childBox, flags); this.bin.allocate(childBox, flags);
if (this._sourceActor && this._sourceActor.mapped) { if (this._sourceActor && this._sourceActor.mapped) {
this._reposition(); this._reposition(this._sourceActor, this._arrowAlignment);
this._updateFlip();
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) { _drawBorder: function(area) {
let themeNode = this.actor.get_theme_node(); 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 borderWidth = themeNode.get_length('-arrow-border-width');
let base = themeNode.get_length('-arrow-base'); let base = themeNode.get_length('-arrow-base');
let rise = themeNode.get_length('-arrow-rise'); let rise = themeNode.get_length('-arrow-rise');
@ -402,11 +405,11 @@ const BoxPointer = new Lang.Class({
cr.setLineWidth(borderWidth); cr.setLineWidth(borderWidth);
cr.stroke(); cr.stroke();
} }
cr.$dispose();
}, },
setPosition: function(sourceActor, alignment) { setPosition: function(sourceActor, alignment) {
this._arrowSide = this._userArrowSide;
// We need to show it now to force an allocation, // We need to show it now to force an allocation,
// so that we can query the correct size. // so that we can query the correct size.
this.actor.show(); this.actor.show();
@ -414,8 +417,7 @@ const BoxPointer = new Lang.Class({
this._sourceActor = sourceActor; this._sourceActor = sourceActor;
this._arrowAlignment = alignment; this._arrowAlignment = alignment;
this._reposition(); this._reposition(sourceActor, alignment);
this._updateFlip();
}, },
setSourceAlignment: function(alignment) { setSourceAlignment: function(alignment) {
@ -427,10 +429,7 @@ const BoxPointer = new Lang.Class({
this.setPosition(this._sourceActor, this._arrowAlignment); this.setPosition(this._sourceActor, this._arrowAlignment);
}, },
_reposition: function() { _reposition: function(sourceActor, alignment) {
let sourceActor = this._sourceActor;
let alignment = this._arrowAlignment;
// Position correctly relative to the sourceActor // Position correctly relative to the sourceActor
let sourceNode = sourceActor.get_theme_node(); let sourceNode = sourceActor.get_theme_node();
let sourceContentBox = sourceNode.get_content_box(sourceActor.get_allocation_box()); 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() { _shiftActor : function() {
// Since the position of the BoxPointer depends on the allocated size // Since the position of the BoxPointer depends on the allocated size
// of the BoxPointer and the position of the source actor, trying // 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 // allocation loops and warnings. Instead we do the positioning via
// the anchor point, which is independent of allocation, and leave // the anchor point, which is independent of allocation, and leave
// x == y == 0. // x == y == 0.
@ -572,47 +561,37 @@ const BoxPointer = new Lang.Class({
-(this._yPosition + this._yOffset)); -(this._yPosition + this._yOffset));
}, },
_calculateArrowSide: function(arrowSide) { _shouldFlip: function() {
let sourceAllocation = Shell.util_get_transformed_allocation(this._sourceActor); 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); let monitor = Main.layoutManager.findMonitorForActor(this.actor);
switch (arrowSide) { switch (this._arrowSide) {
case St.Side.TOP: case St.Side.TOP:
if (sourceAllocation.y2 + boxHeight > monitor.y + monitor.height && if (boxAllocation.y2 > monitor.y + monitor.height &&
boxHeight < sourceAllocation.y1 - monitor.y) boxHeight < sourceAllocation.y1 - monitor.y)
return St.Side.BOTTOM; return true;
break; break;
case St.Side.BOTTOM: case St.Side.BOTTOM:
if (sourceAllocation.y1 - boxHeight < monitor.y && if (boxAllocation.y1 < monitor.y &&
boxHeight < monitor.y + monitor.height - sourceAllocation.y2) boxHeight < monitor.y + monitor.height - sourceAllocation.y2)
return St.Side.TOP; return true;
break; break;
case St.Side.LEFT: case St.Side.LEFT:
if (sourceAllocation.y2 + boxWidth > monitor.x + monitor.width && if (boxAllocation.x2 > monitor.x + monitor.width &&
boxWidth < sourceAllocation.x1 - monitor.x) boxWidth < sourceAllocation.x1 - monitor.x)
return St.Side.RIGHT; return true;
break; break;
case St.Side.RIGHT: case St.Side.RIGHT:
if (sourceAllocation.y1 - boxWidth < monitor.x && if (boxAllocation.x1 < monitor.x &&
boxWidth < monitor.x + monitor.width - sourceAllocation.x2) boxWidth < monitor.x + monitor.width - sourceAllocation.x2)
return St.Side.LEFT; return true;
break; break;
} }
return arrowSide; return false;
},
_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;
}));
}
}, },
set xOffset(offset) { set xOffset(offset) {

View File

@ -2,7 +2,6 @@
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio; const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang; const Lang = imports.lang;
const St = imports.gi.St; const St = imports.gi.St;
const Signals = imports.signals; const Signals = imports.signals;
@ -63,18 +62,15 @@ function _formatEventTime(event, clockFormat) {
} else { } else {
switch (clockFormat) { switch (clockFormat) {
case '24h': case '24h':
/* Translators: Shown in calendar event list, if 24h format, /* 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:%M"));
ret = event.date.toLocaleFormat(C_("event list time", "%H\u2236%M"));
break; break;
default: default:
/* explicit fall-through */ /* explicit fall-through */
case '12h': case '12h':
/* Transators: Shown in calendar event list, if 12h format, /* Transators: Shown in calendar event list, if 12h format */
\u2236 is a ratio character, similar to : and \u2009 is ret = event.date.toLocaleFormat(C_("event list time", "%l:%M %p"));
a thin space */
ret = event.date.toLocaleFormat(C_("event list time", "%l\u2236%M\u2009%p"));
break; break;
} }
} }
@ -197,12 +193,15 @@ const CalendarServerIface = <interface name="org.gnome.Shell.CalendarServer">
const CalendarServerInfo = Gio.DBusInterfaceInfo.new_for_xml(CalendarServerIface); const CalendarServerInfo = Gio.DBusInterfaceInfo.new_for_xml(CalendarServerIface);
function CalendarServer() { function CalendarServer() {
return new Gio.DBusProxy({ g_connection: Gio.DBus.session, var self = new Gio.DBusProxy({ g_connection: Gio.DBus.session,
g_interface_name: CalendarServerInfo.name, g_interface_name: CalendarServerInfo.name,
g_interface_info: CalendarServerInfo, g_interface_info: CalendarServerInfo,
g_name: 'org.gnome.Shell.CalendarServer', g_name: 'org.gnome.Shell.CalendarServer',
g_object_path: '/org/gnome/Shell/CalendarServer', g_object_path: '/org/gnome/Shell/CalendarServer',
g_flags: Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES }); g_flags: Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES });
self.init(null);
return self;
} }
function _datesEqual(a, b) { function _datesEqual(a, b) {
@ -230,27 +229,14 @@ const DBusEventSource = new Lang.Class({
_init: function() { _init: function() {
this._resetCache(); this._resetCache();
this._initialized = false;
this._dbusProxy = new CalendarServer(); this._dbusProxy = new CalendarServer();
this._dbusProxy.init_async(GLib.PRIORITY_DEFAULT, null, Lang.bind(this, function(object, result) { this._dbusProxy.connectSignal('Changed', Lang.bind(this, this._onChanged));
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.connect('notify::g-name-owner', Lang.bind(this, function() {
if (this._dbusProxy.g_name_owner)
this._dbusProxy.connect('notify::g-name-owner', Lang.bind(this, function() { this._onNameAppeared();
if (this._dbusProxy.g_name_owner) else
this._onNameAppeared(); this._onNameVanished();
else
this._onNameVanished();
}));
this._initialized = true;
this._onNameAppeared();
})); }));
}, },
@ -297,10 +283,6 @@ const DBusEventSource = new Lang.Class({
}, },
_loadEvents: function(forceReload) { _loadEvents: function(forceReload) {
// Ignore while loading
if (!this._initialized)
return;
if (this._curRequestBegin && this._curRequestEnd){ if (this._curRequestBegin && this._curRequestEnd){
let callFlags = Gio.DBusCallFlags.NO_AUTO_START; let callFlags = Gio.DBusCallFlags.NO_AUTO_START;
if (forceReload) if (forceReload)

View File

@ -8,6 +8,7 @@ const Params = imports.misc.params;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const GnomeSession = imports.misc.gnomeSession; const GnomeSession = imports.misc.gnomeSession;
const LoginManager = imports.misc.loginManager;
const Main = imports.ui.main; const Main = imports.ui.main;
const ShellMountOperation = imports.ui.shellMountOperation; const ShellMountOperation = imports.ui.shellMountOperation;
@ -32,6 +33,7 @@ const AutomountManager = new Lang.Class({
Lang.bind(this, this._InhibitorsChanged)); Lang.bind(this, this._InhibitorsChanged));
this._inhibited = false; this._inhibited = false;
this._loginManager = LoginManager.getLoginManager();
this._volumeMonitor = Gio.VolumeMonitor.get(); this._volumeMonitor = Gio.VolumeMonitor.get();
}, },
@ -83,29 +85,25 @@ const AutomountManager = new Lang.Class({
_onDriveConnected: function() { _onDriveConnected: function() {
// if we're not in the current ConsoleKit session, // if we're not in the current ConsoleKit session,
// or screensaver is active, don't play sounds // or screensaver is active, don't play sounds
if (!this._session.SessionIsActive) if (!this._loginManager.sessionActive)
return; return;
global.play_theme_sound(0, 'device-added-media', global.play_theme_sound(0, 'device-added-media');
_("External drive connected"),
null);
}, },
_onDriveDisconnected: function() { _onDriveDisconnected: function() {
// if we're not in the current ConsoleKit session, // if we're not in the current ConsoleKit session,
// or screensaver is active, don't play sounds // or screensaver is active, don't play sounds
if (!this._session.SessionIsActive) if (!this._loginManager.sessionActive)
return; return;
global.play_theme_sound(0, 'device-removed-media', global.play_theme_sound(0, 'device-removed-media');
_("External drive disconnected"),
null);
}, },
_onDriveEjectButton: function(monitor, drive) { _onDriveEjectButton: function(monitor, drive) {
// TODO: this code path is not tested, as the GVfs volume monitor // TODO: this code path is not tested, as the GVfs volume monitor
// doesn't emit this signal just yet. // doesn't emit this signal just yet.
if (!this._session.SessionIsActive) if (!this._loginManager.sessionActive)
return; return;
// we force stop/eject in this case, so we don't have to pass a // 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 (params.checkSession) {
// if we're not in the current ConsoleKit session, // if we're not in the current ConsoleKit session,
// don't attempt automount // don't attempt automount
if (!this._session.SessionIsActive) if (!this._loginManager.sessionActive)
return; return;
} }

View File

@ -4,7 +4,7 @@ const Lang = imports.lang;
const Gio = imports.gi.Gio; const Gio = imports.gi.Gio;
const St = imports.gi.St; const St = imports.gi.St;
const GnomeSession = imports.misc.gnomeSession; const LoginManager = imports.misc.loginManager;
const Main = imports.ui.main; const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray; const MessageTray = imports.ui.messageTray;
const ShellMountOperation = imports.ui.shellMountOperation; const ShellMountOperation = imports.ui.shellMountOperation;
@ -162,7 +162,8 @@ const AutorunManager = new Lang.Class({
Name: 'AutorunManager', Name: 'AutorunManager',
_init: function() { _init: function() {
this._session = new GnomeSession.SessionManager(); this._loginManager = LoginManager.getLoginManager();
this._volumeMonitor = Gio.VolumeMonitor.get(); this._volumeMonitor = Gio.VolumeMonitor.get();
this._transDispatcher = new AutorunTransientDispatcher(this); this._transDispatcher = new AutorunTransientDispatcher(this);
@ -214,7 +215,7 @@ const AutorunManager = new Lang.Class({
_onMountAdded: function(monitor, mount) { _onMountAdded: function(monitor, mount) {
// don't do anything if our session is not the currently // don't do anything if our session is not the currently
// active one // active one
if (!this._session.SessionIsActive) if (!this._loginManager.sessionActive)
return; return;
this._processMount(mount, true); this._processMount(mount, true);
@ -292,6 +293,7 @@ const AutorunResidentSource = new Lang.Class({
_init: function(manager) { _init: function(manager) {
this.parent(_("Removable Devices"), 'media-removable'); this.parent(_("Removable Devices"), 'media-removable');
this.showInLockScreen = false;
this._mounts = []; this._mounts = [];
@ -299,10 +301,6 @@ const AutorunResidentSource = new Lang.Class({
this._notification = new AutorunResidentNotification(this._manager, this); this._notification = new AutorunResidentNotification(this._manager, this);
}, },
_createPolicy: function() {
return new MessageTray.NotificationPolicy({ showInLockScreen: false });
},
buildRightClickMenu: function() { buildRightClickMenu: function() {
return null; return null;
}, },

View File

@ -60,14 +60,18 @@ const KeyringDialog = new Lang.Class({
this._controlTable = null; 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: '', this.setButtons(buttons);
action: Lang.bind(this, this._onCancelButton), this._cancelButton = buttons[0].button;
key: Clutter.Escape }); this._continueButton = buttons[1].button;
this._continueButton = this.addButton({ label: '',
action: Lang.bind(this, this._onContinueButton),
default: true },
{ expand: true, x_fill: false, x_align: St.Align.END });
this.prompt.bind_property('cancel-label', this._cancelButton, 'label', GObject.BindingFlags.SYNC_CREATE); 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); 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 }); 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() { _ensureOpen: function() {
// NOTE: ModalDialog.open() is safe to call if the dialog is // NOTE: ModalDialog.open() is safe to call if the dialog is
// already open - it just returns true without side-effects // already open - it just returns true without side-effects
@ -171,14 +167,12 @@ const KeyringDialog = new Lang.Class({
_onShowPassword: function(prompt) { _onShowPassword: function(prompt) {
this._buildControlTable(); this._buildControlTable();
this._ensureOpen(); this._ensureOpen();
this._updateSensitivity(true);
this._passwordEntry.grab_key_focus(); this._passwordEntry.grab_key_focus();
}, },
_onShowConfirm: function(prompt) { _onShowConfirm: function(prompt) {
this._buildControlTable(); this._buildControlTable();
this._ensureOpen(); this._ensureOpen();
this._updateSensitivity(true);
this._continueButton.grab_key_focus(); this._continueButton.grab_key_focus();
}, },
@ -198,7 +192,6 @@ const KeyringDialog = new Lang.Class({
}, },
_onContinueButton: function() { _onContinueButton: function() {
this._updateSensitivity(false);
this.prompt.complete(); this.prompt.complete();
}, },

View File

@ -587,19 +587,18 @@ const NetworkAgent = new Lang.Class({
Name: 'NetworkAgent', Name: 'NetworkAgent',
_init: function() { _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._dialogs = { };
this._vpnRequests = { }; this._vpnRequests = { };
this._native.connect('new-request', Lang.bind(this, this._newRequest)); this._native.connect('new-request', Lang.bind(this, this._newRequest));
this._native.connect('cancel-request', Lang.bind(this, this._cancelRequest)); this._native.connect('cancel-request', Lang.bind(this, this._cancelRequest));
this._enabled = false;
}, },
enable: function() { enable: function() {
this._enabled = true; this._native.auto_register = true;
}, },
disable: function() { disable: function() {
@ -613,15 +612,12 @@ const NetworkAgent = new Lang.Class({
this._vpnRequests[requestId].cancel(true); this._vpnRequests[requestId].cancel(true);
this._vpnRequests = { }; 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) { _newRequest: function(agent, requestId, connection, settingName, hints, flags) {
if (!this._enabled) {
agent.respond(requestId, Shell.NetworkAgentResponse.USER_CANCELED);
return;
}
if (settingName == 'vpn') { if (settingName == 'vpn') {
this._vpnRequest(requestId, connection, hints, flags); this._vpnRequest(requestId, connection, hints, flags);
return; return;

View File

@ -159,13 +159,14 @@ const AuthenticationDialog = new Lang.Class({
messageBox.add(this._nullMessageLabel); messageBox.add(this._nullMessageLabel);
this._nullMessageLabel.show(); this._nullMessageLabel.show();
this._cancelButton = this.addButton({ label: _("Cancel"), this.setButtons([{ label: _("Cancel"),
action: Lang.bind(this, this.cancel), action: Lang.bind(this, this.cancel),
key: Clutter.Escape }); key: Clutter.Escape
this._okButton = this.addButton({ label: _("Authenticate"), },
action: Lang.bind(this, this._onAuthenticateButtonPressed), { label: _("Authenticate"),
default: true }, action: Lang.bind(this, this._onAuthenticateButtonPressed),
{ expand: true, x_fill: false, x_align: St.Align.END }); default: true
}]);
this._doneEmitted = false; 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() { _onEntryActivate: function() {
let response = this._passwordEntry.get_text(); let response = this._passwordEntry.get_text();
this._updateSensitivity(false);
this._session.response(response); this._session.response(response);
// When the user responds, dismiss already shown info and // When the user responds, dismiss already shown info and
// error texts (if any) // error texts (if any)
@ -277,7 +269,6 @@ const AuthenticationDialog = new Lang.Class({
this._passwordBox.show(); this._passwordBox.show();
this._passwordEntry.set_text(''); this._passwordEntry.set_text('');
this._passwordEntry.grab_key_focus(); this._passwordEntry.grab_key_focus();
this._updateSensitivity(true);
this._ensureOpen(); this._ensureOpen();
}, },
@ -335,19 +326,11 @@ const AuthenticationAgent = new Lang.Class({
}, },
enable: function() { enable: function() {
try { this._native.register();
this._native.register();
} catch(e) {
log('Failed to register AuthenticationAgent');
}
}, },
disable: function() { disable: function() {
try { this._native.unregister();
this._native.unregister();
} catch(e) {
log('Failed to unregister AuthenticationAgent');
}
}, },
_onInitiate: function(nativeAgent, actionId, message, iconName, cookie, userNames) { _onInitiate: function(nativeAgent, actionId, message, iconName, cookie, userNames) {

View File

@ -20,8 +20,8 @@ const Recorder = new Lang.Class({
Main.wm.addKeybinding('toggle-recording', Main.wm.addKeybinding('toggle-recording',
this._bindingSettings, this._bindingSettings,
Meta.KeyBindingFlags.NONE, Meta.KeyBindingFlags.NONE,
Shell.KeyBindingMode.NORMAL | Main.KeybindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW, Main.KeybindingMode.OVERVIEW,
Lang.bind(this, this._toggleRecorder)); Lang.bind(this, this._toggleRecorder));
}, },

View File

@ -13,7 +13,6 @@ const Tp = imports.gi.TelepathyGLib;
const History = imports.misc.history; const History = imports.misc.history;
const Main = imports.ui.main; const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray; const MessageTray = imports.ui.messageTray;
const NotificationDaemon = imports.ui.notificationDaemon;
const Params = imports.misc.params; const Params = imports.misc.params;
const PopupMenu = imports.ui.popupMenu; const PopupMenu = imports.ui.popupMenu;
@ -416,8 +415,6 @@ const TelepathyClient = new Lang.Class({
_ensureAppSource: function() { _ensureAppSource: function() {
if (this._appSource == null) { if (this._appSource == null) {
this._appSource = new MessageTray.Source(_("Chat"), 'empathy'); this._appSource = new MessageTray.Source(_("Chat"), 'empathy');
this._appSource.policy = new NotificationDaemon.NotificationApplicationPolicy('empathy');
Main.messageTray.add(this._appSource); Main.messageTray.add(this._appSource);
this._appSource.connect('destroy', Lang.bind(this, function () { this._appSource.connect('destroy', Lang.bind(this, function () {
this._appSource = null; this._appSource = null;
@ -487,10 +484,6 @@ const ChatSource = new Lang.Class({
return rightClickMenu; return rightClickMenu;
}, },
_createPolicy: function() {
return new NotificationDaemon.NotificationApplicationPolicy('empathy');
},
_updateAlias: function() { _updateAlias: function() {
let oldAlias = this.title; let oldAlias = this.title;
let newAlias = this._contact.get_alias(); let newAlias = this._contact.get_alias();
@ -640,10 +633,6 @@ const ChatSource = new Lang.Class({
return this._pendingMessages.length; return this._pendingMessages.length;
}, },
get indicatorCount() {
return this.count;
},
get unseenCount() { get unseenCount() {
return this.count; return this.count;
}, },
@ -1059,10 +1048,6 @@ const ApproverSource = new Lang.Class({
})); }));
}, },
_createPolicy: function() {
return new NotificationDaemon.NotificationApplicationPolicy('empathy');
},
destroy: function() { destroy: function() {
if (this._invalidId != 0) { if (this._invalidId != 0) {
this._dispatchOp.disconnect(this._invalidId); this._dispatchOp.disconnect(this._invalidId);
@ -1361,8 +1346,9 @@ const AccountNotification = new Lang.Class({
this.connect('action-invoked', Lang.bind(this, function(self, action) { this.connect('action-invoked', Lang.bind(this, function(self, action) {
switch (action) { switch (action) {
case 'view': case 'view':
let cmd = 'empathy-accounts --select-account=' + let cmd = '/usr/bin/empathy-accounts'
account.get_path_suffix(); + ' --select-account=%s'
.format(account.get_path_suffix());
let app_info = Gio.app_info_create_from_commandline(cmd, null, 0); let app_info = Gio.app_info_create_from_commandline(cmd, null, 0);
app_info.launch([], global.create_app_launch_context()); app_info.launch([], global.create_app_launch_context());
break; break;

View File

@ -22,8 +22,11 @@ const DASH_ITEM_LABEL_HIDE_TIME = 0.1;
const DASH_ITEM_HOVER_TIMEOUT = 300; const DASH_ITEM_HOVER_TIMEOUT = 300;
function getAppFromSource(source) { function getAppFromSource(source) {
if (source instanceof AppDisplay.AppIcon) { if (source instanceof AppDisplay.AppWellIcon) {
return source.app; return source.app;
} else if (source.metaWindow) {
let tracker = Shell.WindowTracker.get_default();
return tracker.get_window_app(source.metaWindow);
} else { } else {
return null; return null;
} }
@ -33,26 +36,30 @@ function getAppFromSource(source) {
// when requesting a size // when requesting a size
const DashItemContainer = new Lang.Class({ const DashItemContainer = new Lang.Class({
Name: 'DashItemContainer', Name: 'DashItemContainer',
Extends: St.Widget,
_init: function() { _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._labelText = "";
this.label = new St.Label({ style_class: 'dash-label'}); this.label = new St.Label({ style_class: 'dash-label'});
this.label.hide(); this.label.hide();
Main.layoutManager.addChrome(this.label); Main.layoutManager.addChrome(this.label);
this.label_actor = this.label; this.actor.label_actor = this.label;
this.child = null; this.child = null;
this._childScale = 0; this._childScale = 1;
this._childOpacity = 0; this._childOpacity = 255;
this.animatingOut = false; this.animatingOut = false;
}, },
vfunc_allocate: function(box, flags) { _allocate: function(actor, box, flags) {
this.set_allocation(box, flags);
if (this.child == null) if (this.child == null)
return; return;
@ -74,28 +81,28 @@ const DashItemContainer = new Lang.Class({
this.child.allocate(childBox, flags); this.child.allocate(childBox, flags);
}, },
vfunc_get_preferred_height: function(forWidth) { _getPreferredHeight: function(actor, forWidth, alloc) {
let themeNode = this.get_theme_node(); alloc.min_size = 0;
alloc.natural_size = 0;
if (this.child == null) if (this.child == null)
return [0, 0]; return;
forWidth = themeNode.adjust_for_width(forWidth);
let [minHeight, natHeight] = this.child.get_preferred_height(forWidth); let [minHeight, natHeight] = this.child.get_preferred_height(forWidth);
return themeNode.adjust_preferred_height(minHeight * this.child.scale_y, alloc.min_size += minHeight * this.child.scale_y;
natHeight * this.child.scale_y); alloc.natural_size += natHeight * this.child.scale_y;
}, },
vfunc_get_preferred_width: function(forHeight) { _getPreferredWidth: function(actor, forHeight, alloc) {
let themeNode = this.get_theme_node(); alloc.min_size = 0;
alloc.natural_size = 0;
if (this.child == null) if (this.child == null)
return [0, 0]; return;
forHeight = themeNode.adjust_for_height(forHeight);
let [minWidth, natWidth] = this.child.get_preferred_width(forHeight); let [minWidth, natWidth] = this.child.get_preferred_width(forHeight);
return themeNode.adjust_preferred_width(minWidth * this.child.scale_y, alloc.min_size = minWidth * this.child.scale_y;
natWidth * this.child.scale_y); alloc.natural_size = natWidth * this.child.scale_y;
}, },
showLabel: function() { showLabel: function() {
@ -106,9 +113,9 @@ const DashItemContainer = new Lang.Class({
this.label.opacity = 0; this.label.opacity = 0;
this.label.show(); 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 labelHeight = this.label.get_height();
let yOffset = Math.floor((itemHeight - labelHeight) / 2) 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) if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
x = stageX - this.label.get_width() - xOffset; x = stageX - this.label.get_width() - xOffset;
else else
x = stageX + this.get_width() + xOffset; x = stageX + this.actor.get_width() + xOffset;
this.label.set_position(x, y); this.label.set_position(x, y);
Tweener.addTween(this.label, Tweener.addTween(this.label,
@ -152,25 +159,22 @@ const DashItemContainer = new Lang.Class({
if (this.child == actor) if (this.child == actor)
return; return;
this.destroy_all_children(); this.actor.destroy_all_children();
this.child = actor; this.child = actor;
this.add_actor(this.child); this.actor.add_actor(this.child);
this.child.set_scale_with_gravity(this._childScale, this._childScale,
Clutter.Gravity.CENTER);
this.child.set_opacity(this._childOpacity);
}, },
show: function(animate) { animateIn: function() {
if (this.child == null) if (this.child == null)
return; return;
let time = animate ? DASH_ANIMATION_TIME : 0; this.childScale = 0;
this.childOpacity = 0;
Tweener.addTween(this, Tweener.addTween(this,
{ childScale: 1.0, { childScale: 1.0,
childOpacity: 255, childOpacity: 255,
time: time, time: DASH_ANIMATION_TIME,
transition: 'easeOutQuad' transition: 'easeOutQuad'
}); });
}, },
@ -179,7 +183,7 @@ const DashItemContainer = new Lang.Class({
if (this.label) if (this.label)
this.label.destroy(); this.label.destroy();
this.parent(); this.actor.destroy();
}, },
animateOutAndDestroy: function() { animateOutAndDestroy: function() {
@ -187,18 +191,19 @@ const DashItemContainer = new Lang.Class({
this.label.destroy(); this.label.destroy();
if (this.child == null) { if (this.child == null) {
this.destroy(); this.actor.destroy();
return; return;
} }
this.animatingOut = true; this.animatingOut = true;
this.childScale = 1.0;
Tweener.addTween(this, Tweener.addTween(this,
{ childScale: 0.0, { childScale: 0.0,
childOpacity: 0, childOpacity: 0,
time: DASH_ANIMATION_TIME, time: DASH_ANIMATION_TIME,
transition: 'easeOutQuad', transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() { 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, this.child.set_scale_with_gravity(scale, scale,
Clutter.Gravity.CENTER); Clutter.Gravity.CENTER);
this.queue_relayout(); this.actor.queue_relayout();
}, },
get childScale() { get childScale() {
@ -225,7 +230,7 @@ const DashItemContainer = new Lang.Class({
return; return;
this.child.set_opacity(opacity); this.child.set_opacity(opacity);
this.queue_redraw(); this.actor.queue_redraw();
}, },
get childOpacity() { get childOpacity() {
@ -356,23 +361,6 @@ const DashActor = new Lang.Class({
childBox.y1 = contentBox.y2 - showAppsNatHeight; childBox.y1 = contentBox.y2 - showAppsNatHeight;
childBox.y2 = contentBox.y2; childBox.y2 = contentBox.y2;
showAppsButton.allocate(childBox, flags); 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._container.add_actor(this._box);
this._showAppsIcon = new ShowAppsIcon(); this._showAppsIcon = new ShowAppsIcon();
this._showAppsIcon.childScale = 1;
this._showAppsIcon.childOpacity = 255;
this._showAppsIcon.icon.setIconSize(this.iconSize); this._showAppsIcon.icon.setIconSize(this.iconSize);
this._hookUpLabel(this._showAppsIcon); this._hookUpLabel(this._showAppsIcon);
this.showAppsButton = this._showAppsIcon.toggleButton; 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 = new St.Bin({ child: this._container });
this.actor.connect('notify::height', Lang.bind(this, this.actor.connect('notify::height', Lang.bind(this,
@ -429,10 +415,12 @@ const Dash = new Lang.Class({
Lang.bind(this, this._onDragEnd)); Lang.bind(this, this._onDragEnd));
Main.overview.connect('item-drag-cancelled', Main.overview.connect('item-drag-cancelled',
Lang.bind(this, this._onDragCancelled)); Lang.bind(this, this._onDragCancelled));
Main.overview.connect('window-drag-begin',
// Translators: this is the name of the dock/favorites area on Lang.bind(this, this._onDragBegin));
// the left of the overview Main.overview.connect('window-drag-cancelled',
Main.ctrlAltTabManager.addGroup(this.actor, _("Dash"), 'user-bookmarks-symbolic'); Lang.bind(this, this._onDragCancelled));
Main.overview.connect('window-drag-end',
Lang.bind(this, this._onDragEnd));
}, },
_onDragBegin: function() { _onDragBegin: function() {
@ -467,7 +455,7 @@ const Dash = new Lang.Class({
return DND.DragMotionResult.CONTINUE; return DND.DragMotionResult.CONTINUE;
let showAppsHovered = let showAppsHovered =
this._showAppsIcon.contains(dragEvent.targetActor); this._showAppsIcon.actor.contains(dragEvent.targetActor);
if (!this._box.contains(dragEvent.targetActor) || showAppsHovered) if (!this._box.contains(dragEvent.targetActor) || showAppsHovered)
this._clearDragPlaceholder(); this._clearDragPlaceholder();
@ -503,9 +491,9 @@ const Dash = new Lang.Class({
}, },
_createAppItem: function(app) { _createAppItem: function(app) {
let appIcon = new AppDisplay.AppIcon(app, let appIcon = new AppDisplay.AppWellIcon(app,
{ setSizeManually: true, { setSizeManually: true,
showLabel: false }); showLabel: false });
appIcon._draggable.connect('drag-begin', appIcon._draggable.connect('drag-begin',
Lang.bind(this, function() { Lang.bind(this, function() {
appIcon.actor.opacity = 50; appIcon.actor.opacity = 50;
@ -522,7 +510,7 @@ const Dash = new Lang.Class({
let item = new DashItemContainer(); let item = new DashItemContainer();
item.setChild(appIcon.actor); 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 // accessible_name is set at DashItemContainer.setLabelText
appIcon.actor.label_actor = null; appIcon.actor.label_actor = null;
item.setLabelText(app.get_name()); 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 // animating out (which means they will be destroyed at the end of
// the animation) // the animation)
let iconChildren = this._box.get_children().filter(function(actor) { let iconChildren = this._box.get_children().filter(function(actor) {
return actor.child && return actor._delegate.child &&
actor.child._delegate && actor._delegate.child._delegate &&
actor.child._delegate.icon && actor._delegate.child._delegate.icon &&
!actor.animatingOut; !actor._delegate.animatingOut;
}); });
iconChildren.push(this._showAppsIcon); iconChildren.push(this._showAppsIcon.actor);
if (this._maxHeight == -1) if (this._maxHeight == -1)
return; return;
@ -601,18 +589,23 @@ const Dash = new Lang.Class({
let availHeight = maxContent.y2 - maxContent.y1; let availHeight = maxContent.y2 - maxContent.y1;
let spacing = themeNode.get_length('spacing'); 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; let minHeight, natHeight;
// Enforce the current icon size during the size request // Enforce the current icon size during the size request if
let [currentWidth, currentHeight] = firstIcon.icon.get_size(); // the icon is animating
if (firstIcon._animating) {
let [currentWidth, currentHeight] = firstIcon.icon.get_size();
firstIcon.icon.set_size(this.iconSize, this.iconSize); firstIcon.icon.set_size(this.iconSize, this.iconSize);
[minHeight, natHeight] = firstButton.get_preferred_height(-1); [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 // Subtract icon padding and box spacing from the available height
availHeight -= iconChildren.length * (natHeight - this.iconSize) + availHeight -= iconChildren.length * (natHeight - this.iconSize) +
@ -637,7 +630,7 @@ const Dash = new Lang.Class({
let scale = oldIconSize / newIconSize; let scale = oldIconSize / newIconSize;
for (let i = 0; i < iconChildren.length; i++) { 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 // Set the new size immediately, to keep the icons' sizes
// in sync with this.iconSize // in sync with this.iconSize
@ -657,11 +650,15 @@ const Dash = new Lang.Class({
icon.icon.set_size(icon.icon.width * scale, icon.icon.set_size(icon.icon.width * scale,
icon.icon.height * scale); icon.icon.height * scale);
icon._animating = true;
Tweener.addTween(icon.icon, Tweener.addTween(icon.icon,
{ width: targetWidth, { width: targetWidth,
height: targetHeight, height: targetHeight,
time: DASH_ANIMATION_TIME, time: DASH_ANIMATION_TIME,
transition: 'easeOutQuad', transition: 'easeOutQuad',
onComplete: function() {
icon._animating = false;
}
}); });
} }
}, },
@ -672,13 +669,13 @@ const Dash = new Lang.Class({
let running = this._appSystem.get_running(); let running = this._appSystem.get_running();
let children = this._box.get_children().filter(function(actor) { let children = this._box.get_children().filter(function(actor) {
return actor.child && return actor._delegate.child &&
actor.child._delegate && actor._delegate.child._delegate &&
actor.child._delegate.app; actor._delegate.child._delegate.app;
}); });
// Apps currently in the dash // Apps currently in the dash
let oldApps = children.map(function(actor) { let oldApps = children.map(function(actor) {
return actor.child._delegate.app; return actor._delegate.child._delegate.app;
}); });
// Apps supposed to be in the dash // Apps supposed to be in the dash
let newApps = []; let newApps = [];
@ -744,7 +741,7 @@ const Dash = new Lang.Class({
let insertHere = newApps[newIndex + 1] && let insertHere = newApps[newIndex + 1] &&
newApps[newIndex + 1] == oldApps[oldIndex]; newApps[newIndex + 1] == oldApps[oldIndex];
let alreadyRemoved = removedActors.reduce(function(result, actor) { 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]; return result || removedApp == newApps[newIndex];
}, false); }, false);
@ -761,11 +758,11 @@ const Dash = new Lang.Class({
} }
for (let i = 0; i < addedItems.length; i++) 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); addedItems[i].pos);
for (let i = 0; i < removedActors.length; i++) { 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 // Don't animate item removal when the overview is transitioning
// or hidden // or hidden
@ -779,20 +776,18 @@ const Dash = new Lang.Class({
// Skip animations on first run when adding the initial set // Skip animations on first run when adding the initial set
// of items, to avoid all items zooming in at once // of items, to avoid all items zooming in at once
if (!this._shownInitially) {
let animate = this._shownInitially && Main.overview.visible &&
!Main.overview.animationInProgress;
if (!this._shownInitially)
this._shownInitially = true; this._shownInitially = true;
return;
for (let i = 0; i < addedItems.length; i++) {
addedItems[i].item.show(animate);
} }
// Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=692744 // Don't animate item addition when the overview is transitioning
// Without it, StBoxLayout may use a stale size cache // or hidden
this._box.queue_relayout(); if (!Main.overview.visible || Main.overview.animationInProgress)
return;
for (let i = 0; i < addedItems.length; i++)
addedItems[i].item.animateIn();
}, },
_clearDragPlaceholder: function() { _clearDragPlaceholder: function() {
@ -823,7 +818,7 @@ const Dash = new Lang.Class({
// the remove target has the same size as "normal" items, we don't // the remove target has the same size as "normal" items, we don't
// need to do the same adjustment there. // need to do the same adjustment there.
if (this._dragPlaceholder) { if (this._dragPlaceholder) {
boxHeight -= this._dragPlaceholder.height; boxHeight -= this._dragPlaceholder.actor.height;
numChildren--; numChildren--;
} }
@ -837,7 +832,7 @@ const Dash = new Lang.Class({
if (this._dragPlaceholder) { if (this._dragPlaceholder) {
this._dragPlaceholder.animateOutAndDestroy(); this._dragPlaceholder.animateOutAndDestroy();
this._animatingPlaceholdersCount++; this._animatingPlaceholdersCount++;
this._dragPlaceholder.connect('destroy', this._dragPlaceholder.actor.connect('destroy',
Lang.bind(this, function() { Lang.bind(this, function() {
this._animatingPlaceholdersCount--; this._animatingPlaceholdersCount--;
})); }));
@ -852,7 +847,7 @@ const Dash = new Lang.Class({
// an animation // an animation
let fadeIn; let fadeIn;
if (this._dragPlaceholder) { if (this._dragPlaceholder) {
this._dragPlaceholder.destroy(); this._dragPlaceholder.actor.destroy();
fadeIn = false; fadeIn = false;
} else { } else {
fadeIn = true; fadeIn = true;
@ -861,9 +856,10 @@ const Dash = new Lang.Class({
this._dragPlaceholder = new DragPlaceholderItem(); this._dragPlaceholder = new DragPlaceholderItem();
this._dragPlaceholder.child.set_width (this.iconSize); this._dragPlaceholder.child.set_width (this.iconSize);
this._dragPlaceholder.child.set_height (this.iconSize / 2); 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._dragPlaceholderPos);
this._dragPlaceholder.show(fadeIn); if (fadeIn)
this._dragPlaceholder.animateIn();
} }
// Remove the drag placeholder if we are not in the // 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(); let children = this._box.get_children();
for (let i = 0; i < this._dragPlaceholderPos; i++) { for (let i = 0; i < this._dragPlaceholderPos; i++) {
if (this._dragPlaceholder && if (this._dragPlaceholder &&
children[i] == this._dragPlaceholder) children[i] == this._dragPlaceholder.actor)
continue; continue;
let childId = children[i].child._delegate.app.get_id(); let childId = children[i]._delegate.child._delegate.app.get_id();
if (childId == id) if (childId == id)
continue; continue;
if (childId in favorites) if (childId in favorites)

View File

@ -32,7 +32,6 @@ function _onVertSepRepaint (area)
cr.setDash([1, 3], 1); // Hard-code for now cr.setDash([1, 3], 1); // Hard-code for now
cr.setLineWidth(stippleWidth); cr.setLineWidth(stippleWidth);
cr.stroke(); cr.stroke();
cr.$dispose();
}; };
const DateMenuButton = new Lang.Class({ const DateMenuButton = new Lang.Class({
@ -84,26 +83,12 @@ const DateMenuButton = new Lang.Class({
})); }));
vbox.add(this._calendar.actor); vbox.add(this._calendar.actor);
let separator = new PopupMenu.PopupSeparatorMenuItem(); item = this.menu.addSettingsAction(_("Date and Time Settings"), 'gnome-datetime-panel.desktop');
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');
if (item) { 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.show_on_set_parent = false;
item.actor.can_focus = false; item.actor.can_focus = false;
item.actor.reparent(vbox); item.actor.reparent(vbox);
@ -123,6 +108,16 @@ const DateMenuButton = new Lang.Class({
// Event list // Event list
vbox.add(this._eventList.actor, { expand: true }); 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 // Whenever the menu is opened, select today
this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) { this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) {
if (isOpen) { if (isOpen) {
@ -156,9 +151,10 @@ const DateMenuButton = new Lang.Class({
this._sessionUpdated(); this._sessionUpdated();
}, },
_appInstalledChanged: function() { _calendarSettingsChanged: function() {
let app = Shell.AppSystem.get_default().lookup_app('gnome-clocks.desktop'); let exec = this._calendarSettings.get_string('exec');
this._openClocksItem.actor.visible = app !== null; let fullExec = GLib.find_program_in_path(exec);
this._openCalendarItem.actor.visible = fullExec != null;
}, },
_setEventsVisibility: function(visible) { _setEventsVisibility: function(visible) {
@ -209,14 +205,24 @@ const DateMenuButton = new Lang.Class({
_onOpenCalendarActivate: function() { _onOpenCalendarActivate: function() {
this.menu.close(); this.menu.close();
let tool = this._calendarSettings.get_string('exec');
let app = Gio.AppInfo.get_default_for_type('text/calendar', false); if (tool.length == 0 || tool.substr(0, 9) == 'evolution') {
app.launch([], global.create_app_launch_context()); // TODO: pass the selected day
}, let app = Shell.AppSystem.get_default().lookup_app('evolution-calendar.desktop');
app.activate();
_onOpenClocksActivate: function() { } else {
this.menu.close(); let needTerm = this._calendarSettings.get_boolean('needs-term');
let app = Shell.AppSystem.get_default().lookup_app('gnome-clocks.desktop'); if (needTerm) {
app.activate(); 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)
}
}
} }
}); });

View File

@ -136,10 +136,9 @@ const _Draggable = new Lang.Class({
}, },
_ungrabActor: function() { _ungrabActor: function() {
Clutter.ungrab_pointer();
if (!this._onEventId) if (!this._onEventId)
return; return;
Clutter.ungrab_pointer();
this.actor.disconnect(this._onEventId); this.actor.disconnect(this._onEventId);
this._onEventId = null; this._onEventId = null;
}, },
@ -204,19 +203,6 @@ const _Draggable = new Lang.Class({
return false; 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: * startDrag:
* @stageX: X coordinate of event * @stageX: X coordinate of event

View File

@ -19,7 +19,6 @@
*/ */
const Lang = imports.lang; const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Signals = imports.signals; const Signals = imports.signals;
const AccountsService = imports.gi.AccountsService; const AccountsService = imports.gi.AccountsService;
@ -51,7 +50,6 @@ const EndSessionDialogIface = <interface name="org.gnome.SessionManager.EndSessi
<arg type="u" direction="in" /> <arg type="u" direction="in" />
<arg type="ao" direction="in" /> <arg type="ao" direction="in" />
</method> </method>
<method name="Close" />
<signal name="ConfirmedLogout" /> <signal name="ConfirmedLogout" />
<signal name="ConfirmedReboot" /> <signal name="ConfirmedReboot" />
<signal name="ConfirmedShutdown" /> <signal name="ConfirmedShutdown" />
@ -379,12 +377,7 @@ const EndSessionDialog = new Lang.Class({
let signal = dialogContent.confirmButtons[i].signal; let signal = dialogContent.confirmButtons[i].signal;
let label = dialogContent.confirmButtons[i].label; let label = dialogContent.confirmButtons[i].label;
buttons.push({ action: Lang.bind(this, function() { buttons.push({ action: Lang.bind(this, function() {
this.close(true); this._confirm(signal);
let signalId = this.connect('closed',
Lang.bind(this, function() {
this.disconnect(signalId);
this._confirm(signal);
}));
}), }),
label: label }); label: label });
} }
@ -392,17 +385,15 @@ const EndSessionDialog = new Lang.Class({
this.setButtons(buttons); this.setButtons(buttons);
}, },
close: function(skipSignal) { close: function() {
this.parent(); this.parent();
this._dbusImpl.emit_signal('Closed', null);
if (!skipSignal)
this._dbusImpl.emit_signal('Closed', null);
}, },
cancel: function() { cancel: function() {
this._stopTimer(); this._stopTimer();
this._dbusImpl.emit_signal('Canceled', null); this._dbusImpl.emit_signal('Canceled', null);
this.close(); this.close(global.get_current_time());
}, },
_confirm: function(signal) { _confirm: function(signal) {
@ -417,34 +408,22 @@ const EndSessionDialog = new Lang.Class({
}, },
_startTimer: function() { _startTimer: function() {
let startTime = GLib.get_monotonic_time();
this._secondsLeft = this._totalSecondsToStayOpen; this._secondsLeft = this._totalSecondsToStayOpen;
Tweener.addTween(this,
this._timerId = Mainloop.timeout_add_seconds(1, Lang.bind(this, { _secondsLeft: 0,
function() { time: this._secondsLeft,
let currentTime = GLib.get_monotonic_time(); transition: 'linear',
let secondsElapsed = ((currentTime - startTime) / 1000000); onUpdate: Lang.bind(this, this._updateDescription),
onComplete: Lang.bind(this, function() {
this._secondsLeft = this._totalSecondsToStayOpen - secondsElapsed; let dialogContent = DialogContent[this._type];
if (this._secondsLeft > 0) { let button = dialogContent.confirmButtons[dialogContent.confirmButtons.length - 1];
this._updateDescription(); this._confirm(button.signal);
return true; }),
} });
let dialogContent = DialogContent[this._type];
let button = dialogContent.confirmButtons[dialogContent.confirmButtons.length - 1];
this._confirm(button.signal);
return false;
}));
}, },
_stopTimer: function() { _stopTimer: function() {
if (this._timerId != 0) { Tweener.removeTweens(this);
Mainloop.source_remove(this._timerId);
this._timerId = 0;
}
this._secondsLeft = 0; this._secondsLeft = 0;
}, },
@ -461,7 +440,7 @@ const EndSessionDialog = new Lang.Class({
let item = new ListItem(app, reason); let item = new ListItem(app, reason);
item.connect('activate', item.connect('activate',
Lang.bind(this, function() { Lang.bind(this, function() {
this.close(); this.close(global.get_current_time());
})); }));
this._applicationList.add(item.actor, { x_fill: true }); this._applicationList.add(item.actor, { x_fill: true });
this._stopTimer(); this._stopTimer();
@ -509,9 +488,5 @@ const EndSessionDialog = new Lang.Class({
invocation.return_value(null); invocation.return_value(null);
this.disconnect(signalId); this.disconnect(signalId);
})); }));
},
Close: function(parameters, invocation) {
this.close();
} }
}); });

View File

@ -215,7 +215,7 @@ const InstallExtensionDialog = new Lang.Class({
}, },
_onCancelButtonPressed: function(button, event) { _onCancelButtonPressed: function(button, event) {
this.close(); this.close(global.get_current_time());
this._invocation.return_value(GLib.Variant.new('(s)', ['cancelled'])); 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); gotExtensionZipFile(session, message, uuid, dir, callback, errback);
})); }));
this.close(); this.close(global.get_current_time());
} }
}); });

View File

@ -106,15 +106,11 @@ function enableExtension(uuid) {
extensionOrder.push(uuid); extensionOrder.push(uuid);
let stylesheetNames = [global.session_mode + '.css', 'stylesheet.css']; let stylesheetFile = extension.dir.get_child('stylesheet.css');
for (let i = 0; i < stylesheetNames.length; i++) { if (stylesheetFile.query_exists(null)) {
let stylesheetFile = extension.dir.get_child(stylesheetNames[i]); let theme = St.ThemeContext.get_for_stage(global.stage).get_theme();
if (stylesheetFile.query_exists(null)) { theme.load_stylesheet(stylesheetFile.get_path());
let theme = St.ThemeContext.get_for_stage(global.stage).get_theme(); extension.stylesheet = stylesheetFile;
theme.load_stylesheet(stylesheetFile.get_path());
extension.stylesheet = stylesheetFile;
break;
}
} }
extension.stateObj.enable(); extension.stateObj.enable();

45
js/ui/flashspot.js Normal file
View 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();
})
});
}
});

View File

@ -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 Clutter = imports.gi.Clutter;
const Gtk = imports.gi.Gtk; const Gtk = imports.gi.Gtk;
@ -10,6 +10,17 @@ const St = imports.gi.St;
const Main = imports.ui.main; const Main = imports.ui.main;
const Params = imports.misc.params; const Params = imports.misc.params;
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: // GrabHelper:
// @owner: the actor that owns the GrabHelper // @owner: the actor that owns the GrabHelper
// @params: optional parameters to pass to Main.pushModal() // @params: optional parameters to pass to Main.pushModal()
@ -35,7 +46,6 @@ const GrabHelper = new Lang.Class({
this._keyFocusNotifyId = 0; this._keyFocusNotifyId = 0;
this._focusWindowChangedId = 0; this._focusWindowChangedId = 0;
this._ignoreRelease = false; this._ignoreRelease = false;
this._isUngrabbingCount = 0;
this._modalCount = 0; this._modalCount = 0;
this._grabFocusCount = 0; this._grabFocusCount = 0;
@ -67,7 +77,7 @@ const GrabHelper = new Lang.Class({
}, },
_isWithinGrabbedActor: function(actor) { _isWithinGrabbedActor: function(actor) {
let currentActor = this.currentGrab.actor; let currentActor = this.currentGrab.actor;
while (actor) { while (actor) {
if (this._actors.indexOf(actor) != -1) if (this._actors.indexOf(actor) != -1)
return true; return true;
@ -168,18 +178,12 @@ const GrabHelper = new Lang.Class({
if (params.grabFocus && !this._takeFocusGrab(hadFocus)) if (params.grabFocus && !this._takeFocusGrab(hadFocus))
return false; return false;
this._grabStack.push(params); if (params.focus)
if (params.focus) {
params.focus.grab_key_focus(); params.focus.grab_key_focus();
} else if (newFocus && (hadFocus || params.grabFocus)) { else if (hadFocus || params.grabFocus)
if (!newFocus.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false)) _navigateActor(newFocus);
newFocus.grab_key_focus();
}
if ((params.grabFocus || params.modal) && !this._capturedEventId)
this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
this._grabStack.push(params);
return true; return true;
}, },
@ -188,6 +192,8 @@ const GrabHelper = new Lang.Class({
if (firstGrab) { if (firstGrab) {
if (!Main.pushModal(this._owner, this._modalParams)) if (!Main.pushModal(this._owner, this._modalParams))
return false; return false;
this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
} }
this._modalCount++; this._modalCount++;
@ -199,6 +205,11 @@ const GrabHelper = new Lang.Class({
if (this._modalCount > 0) if (this._modalCount > 0)
return; return;
if (this._capturedEventId > 0) {
global.stage.disconnect(this._capturedEventId);
this._capturedEventId = 0;
}
Main.popModal(this._owner); Main.popModal(this._owner);
global.sync_pointer(); global.sync_pointer();
}, },
@ -234,7 +245,7 @@ const GrabHelper = new Lang.Class({
this._keyFocusNotifyId = 0; this._keyFocusNotifyId = 0;
} }
if (this._focusWindowChangedId > 0) { if (!this._focusWindowChanged > 0) {
let metaDisplay = global.screen.get_display(); let metaDisplay = global.screen.get_display();
metaDisplay.disconnect(this._focusWindowChangedId); metaDisplay.disconnect(this._focusWindowChangedId);
this._focusWindowChangedId = 0; this._focusWindowChangedId = 0;
@ -276,14 +287,6 @@ const GrabHelper = new Lang.Class({
if (grabStackIndex < 0) if (grabStackIndex < 0)
return; return;
// We may get key focus changes when calling onUngrab, which
// would cause an extra ungrab() on the next actor in the
// stack, which is wrong. Ignore key focus changes during the
// ungrab, and restore the saved key focus ourselves afterwards.
// We use a count as ungrab() may be re-entrant, as onUngrab()
// may ungrab additional actors.
this._isUngrabbingCount++;
let focus = global.stage.key_focus; let focus = global.stage.key_focus;
let hadFocus = focus && this._isWithinGrabbedActor(focus); let hadFocus = focus && this._isWithinGrabbedActor(focus);
@ -305,29 +308,14 @@ const GrabHelper = new Lang.Class({
this._releaseFocusGrab(); this._releaseFocusGrab();
} }
if (!this.grabbed && this._capturedEventId > 0) {
global.stage.disconnect(this._capturedEventId);
this._capturedEventId = 0;
}
if (hadFocus) { if (hadFocus) {
let poppedGrab = poppedGrabs[0]; let poppedGrab = poppedGrabs[0];
if (poppedGrab.savedFocus) _navigateActor(poppedGrab.savedFocus);
poppedGrab.savedFocus.grab_key_focus();
} }
this._isUngrabbingCount--;
}, },
_onCapturedEvent: function(actor, event) { _onCapturedEvent: function(actor, event) {
let type = event.type(); 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 press = type == Clutter.EventType.BUTTON_PRESS;
let release = type == Clutter.EventType.BUTTON_RELEASE; let release = type == Clutter.EventType.BUTTON_RELEASE;
let button = press || release; let button = press || release;
@ -340,6 +328,12 @@ const GrabHelper = new Lang.Class({
if (!button && this._modalCount == 0) if (!button && this._modalCount == 0)
return false; 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())) if (this._isWithinGrabbedActor(event.get_source()))
return false; return false;
@ -359,9 +353,6 @@ const GrabHelper = new Lang.Class({
}, },
_onKeyFocusChanged: function() { _onKeyFocusChanged: function() {
if (this._isUngrabbingCount > 0)
return;
let focus = global.stage.key_focus; let focus = global.stage.key_focus;
if (!focus || !this._isWithinGrabbedActor(focus)) if (!focus || !this._isWithinGrabbedActor(focus))
this.ungrab({ isUser: true }); this.ungrab({ isUser: true });

View File

@ -3,149 +3,107 @@
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const IBus = imports.gi.IBus; const IBus = imports.gi.IBus;
const Lang = imports.lang; const Lang = imports.lang;
const Signals = imports.signals;
const St = imports.gi.St; const St = imports.gi.St;
const BoxPointer = imports.ui.boxpointer; const BoxPointer = imports.ui.boxpointer;
const Main = imports.ui.main; const Main = imports.ui.main;
const PopupMenu = imports.ui.popupMenu;
const MAX_CANDIDATES_PER_PAGE = 16; const MAX_CANDIDATES_PER_PAGE = 16;
const CandidateArea = new Lang.Class({ const CandidateArea = new Lang.Class({
Name: 'CandidateArea', Name: 'CandidateArea',
Extends: PopupMenu.PopupBaseMenuItem,
_init: function() { _init: function() {
this.actor = new St.BoxLayout({ vertical: true, this.parent({ reactive: false });
visible: false });
this._candidateBoxes = []; // 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) { for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
let box = new St.BoxLayout({ style_class: 'candidate-box', this._indexLabels.push(new St.Label({ style_class: 'candidate-index' }));
reactive: true, this._candidateLabels.push(new St.Label({ style_class: 'candidate-label' }));
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._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._orientation = -1;
this._cursorPosition = 0; this._cursorPosition = 0;
}, },
setOrientation: function(orientation) { _setOrientation: function(orientation) {
if (this._orientation == orientation) if (this._orientation == orientation)
return; return;
this._orientation = orientation; this._orientation = orientation;
if (this._orientation == IBus.Orientation.HORIZONTAL) { this._table.remove_all_children();
this.actor.vertical = false;
this.actor.remove_style_class_name('vertical'); if (this._orientation == IBus.Orientation.HORIZONTAL)
this.actor.add_style_class_name('horizontal'); for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
this._previousButton.child.icon_name = 'go-previous-symbolic'; this._tableLayout.pack(this._indexLabels[i], i*2, 0);
this._nextButton.child.icon_name = 'go-next-symbolic'; this._tableLayout.pack(this._candidateLabels[i], i*2 + 1, 0);
} else { // VERTICAL || SYSTEM }
this.actor.vertical = true; else // VERTICAL || SYSTEM
this.actor.add_style_class_name('vertical'); for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
this.actor.remove_style_class_name('horizontal'); this._tableLayout.pack(this._indexLabels[i], 0, i);
this._previousButton.child.icon_name = 'go-up-symbolic'; this._tableLayout.pack(this._candidateLabels[i], 1, i);
this._nextButton.child.icon_name = 'go-down-symbolic'; }
}
}, },
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) { for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
let visible = i < candidates.length; let visible = i < candidates.length;
let box = this._candidateBoxes[i]; this._indexLabels[i].visible = visible;
box.visible = visible; this._candidateLabels[i].visible = visible;
if (!visible) if (!visible)
continue; continue;
box._indexLabel.text = ((indexes && indexes[i]) ? indexes[i] : '%x'.format(i + 1)); this._indexLabels[i].text = ((indexes && indexes[i]) ? indexes[i] : '%x.'.format(i + 1));
box._candidateLabel.text = candidates[i]; 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; this._cursorPosition = cursorPosition;
if (cursorVisible) if (cursorVisible)
this._candidateBoxes[cursorPosition].add_style_pseudo_class('selected'); this._candidateLabels[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;
}, },
}); });
Signals.addSignalMethods(CandidateArea.prototype);
const CandidatePopup = new Lang.Class({ const CandidatePopup = new Lang.Class({
Name: 'CandidatePopup', Name: 'CandidatePopup',
Extends: PopupMenu.PopupMenu,
_init: function() { _init: function() {
this._cursor = new St.Bin({ opacity: 0 }); this._cursor = new St.Bin({ opacity: 0 });
Main.uiGroup.add_actor(this._cursor); Main.uiGroup.add_actor(this._cursor);
this._boxPointer = new BoxPointer.BoxPointer(St.Side.TOP); this.parent(this._cursor, 0, St.Side.TOP);
this._boxPointer.actor.visible = false; this.actor.hide();
this._boxPointer.actor.style_class = 'candidate-popup-boxpointer'; Main.uiGroup.add_actor(this.actor);
Main.layoutManager.addChrome(this._boxPointer.actor);
let box = new St.BoxLayout({ style_class: 'candidate-popup-content', this._preeditTextItem = new PopupMenu.PopupMenuItem('', { reactive: false });
vertical: true }); this._preeditTextItem.actor.hide();
this._boxPointer.bin.set_child(box); this.addMenuItem(this._preeditTextItem);
this._preeditText = new St.Label({ style_class: 'candidate-popup-text', this._auxTextItem = new PopupMenu.PopupMenuItem('', { reactive: false });
visible: false }); this._auxTextItem.actor.hide();
box.add(this._preeditText); this.addMenuItem(this._auxTextItem);
this._auxText = new St.Label({ style_class: 'candidate-popup-text', this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
visible: false });
box.add(this._auxText);
this._candidateArea = new CandidateArea(); this._lookupTableItem = new CandidateArea();
box.add(this._candidateArea.actor); this._lookupTableItem.actor.hide();
this.addMenuItem(this._lookupTableItem);
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._panelService = null; this._panelService = null;
}, },
@ -159,61 +117,66 @@ const CandidatePopup = new Lang.Class({
Lang.bind(this, function(ps, x, y, w, h) { Lang.bind(this, function(ps, x, y, w, h) {
this._cursor.set_position(x, y); this._cursor.set_position(x, y);
this._cursor.set_size(w, h); this._cursor.set_size(w, h);
if (this._boxPointer.actor.visible)
this._boxPointer.setPosition(this._cursor, 0);
})); }));
panelService.connect('update-preedit-text', panelService.connect('update-preedit-text',
Lang.bind(this, function(ps, text, cursorPosition, visible) { 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._updateVisibility();
this._preeditText.text = text.get_text(); this._preeditTextItem.actor.label_actor.text = text.get_text();
let attrs = text.get_attributes(); let attrs = text.get_attributes();
if (attrs) if (attrs)
this._setTextAttributes(this._preeditText.clutter_text, this._setTextAttributes(this._preeditTextItem.actor.label_actor.clutter_text,
attrs); attrs);
})); }));
panelService.connect('show-preedit-text', panelService.connect('show-preedit-text',
Lang.bind(this, function(ps) { Lang.bind(this, function(ps) {
this._preeditText.show(); this._preeditTextItem.actor.show();
this._updateVisibility(); this._updateVisibility();
})); }));
panelService.connect('hide-preedit-text', panelService.connect('hide-preedit-text',
Lang.bind(this, function(ps) { Lang.bind(this, function(ps) {
this._preeditText.hide(); this._preeditTextItem.actor.hide();
this._updateVisibility(); this._updateVisibility();
})); }));
panelService.connect('update-auxiliary-text', panelService.connect('update-auxiliary-text',
Lang.bind(this, function(ps, text, visible) { 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._updateVisibility();
this._auxText.text = text.get_text(); this._auxTextItem.actor.label_actor.text = text.get_text();
})); }));
panelService.connect('show-auxiliary-text', panelService.connect('show-auxiliary-text',
Lang.bind(this, function(ps) { Lang.bind(this, function(ps) {
this._auxText.show(); this._auxTextItem.actor.show();
this._updateVisibility(); this._updateVisibility();
})); }));
panelService.connect('hide-auxiliary-text', panelService.connect('hide-auxiliary-text',
Lang.bind(this, function(ps) { Lang.bind(this, function(ps) {
this._auxText.hide(); this._auxTextItem.actor.hide();
this._updateVisibility(); this._updateVisibility();
})); }));
panelService.connect('update-lookup-table', panelService.connect('update-lookup-table',
Lang.bind(this, function(ps, lookupTable, visible) { 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(); this._updateVisibility();
let nCandidates = lookupTable.get_number_of_candidates();
let cursorPos = lookupTable.get_cursor_pos(); let cursorPos = lookupTable.get_cursor_pos();
let pageSize = lookupTable.get_page_size(); let pageSize = lookupTable.get_page_size();
let nPages = Math.ceil(nCandidates / pageSize);
let page = ((cursorPos == 0) ? 0 : Math.floor(cursorPos / pageSize)); let page = ((cursorPos == 0) ? 0 : Math.floor(cursorPos / pageSize));
let startIndex = page * 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 indexes = [];
let indexLabel; let indexLabel;
for (let i = 0; indexLabel = lookupTable.get_label(i); ++i) 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) for (let i = startIndex; i < endIndex; ++i)
candidates.push(lookupTable.get_candidate(i).get_text()); candidates.push(lookupTable.get_candidate(i).get_text());
this._candidateArea.setCandidates(indexes, this._lookupTableItem.setCandidates(indexes,
candidates, candidates,
cursorPos % pageSize, lookupTable.get_orientation(),
lookupTable.is_cursor_visible()); cursorPos % pageSize,
this._candidateArea.setOrientation(lookupTable.get_orientation()); lookupTable.is_cursor_visible());
this._candidateArea.updateButtons(lookupTable.is_round(), page, nPages);
})); }));
panelService.connect('show-lookup-table', panelService.connect('show-lookup-table',
Lang.bind(this, function(ps) { Lang.bind(this, function(ps) {
this._candidateArea.actor.show(); this._lookupTableItem.actor.show();
this._updateVisibility(); this._updateVisibility();
})); }));
panelService.connect('hide-lookup-table', panelService.connect('hide-lookup-table',
Lang.bind(this, function(ps) { Lang.bind(this, function(ps) {
this._candidateArea.actor.hide(); this._lookupTableItem.actor.hide();
this._updateVisibility(); this._updateVisibility();
})); }));
panelService.connect('focus-out', panelService.connect('focus-out',
Lang.bind(this, function(ps) { Lang.bind(this, function(ps) {
this._boxPointer.hide(BoxPointer.PopupAnimation.NONE); this.close(BoxPointer.PopupAnimation.NONE);
})); }));
}, },
_updateVisibility: function() { _updateVisibility: function() {
let isVisible = (this._preeditText.visible || let isVisible = (this._preeditTextItem.actor.visible ||
this._auxText.visible || this._auxTextItem.actor.visible ||
this._candidateArea.actor.visible); this._lookupTableItem.actor.visible);
if (isVisible) { if (isVisible)
this._boxPointer.setPosition(this._cursor, 0); this.open(BoxPointer.PopupAnimation.NONE);
this._boxPointer.show(BoxPointer.PopupAnimation.NONE); else
this._boxPointer.actor.raise_top(); this.close(BoxPointer.PopupAnimation.NONE);
} else {
this._boxPointer.hide(BoxPointer.PopupAnimation.NONE);
}
}, },
_setTextAttributes: function(clutterText, ibusAttrList) { _setTextAttributes: function(clutterText, ibusAttrList) {

View File

@ -176,16 +176,13 @@ const IconGrid = new Lang.Class({
_init: function(params) { _init: function(params) {
params = Params.parse(params, { rowLimit: null, params = Params.parse(params, { rowLimit: null,
columnLimit: null, columnLimit: null,
fillParent: false,
xAlign: St.Align.MIDDLE }); xAlign: St.Align.MIDDLE });
this._rowLimit = params.rowLimit; this._rowLimit = params.rowLimit;
this._colLimit = params.columnLimit; this._colLimit = params.columnLimit;
this._xAlign = params.xAlign; this._xAlign = params.xAlign;
this._fillParent = params.fillParent;
this.actor = new St.BoxLayout({ style_class: 'icon-grid', this.actor = new St.BoxLayout({ style_class: 'icon-grid',
vertical: true }); vertical: true });
// Pulled from CSS, but hardcode some defaults here // Pulled from CSS, but hardcode some defaults here
this._spacing = 0; this._spacing = 0;
this._hItemSize = this._vItemSize = ICON_SIZE; this._hItemSize = this._vItemSize = ICON_SIZE;
@ -199,11 +196,6 @@ const IconGrid = new Lang.Class({
}, },
_getPreferredWidth: function (grid, forHeight, alloc) { _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 children = this._grid.get_children();
let nColumns = this._colLimit ? Math.min(this._colLimit, let nColumns = this._colLimit ? Math.min(this._colLimit,
children.length) children.length)
@ -225,20 +217,12 @@ const IconGrid = new Lang.Class({
}, },
_getPreferredHeight: function (grid, forWidth, alloc) { _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 children = this._getVisibleChildren();
let nColumns, spacing; let nColumns;
if (forWidth < 0) { if (forWidth < 0)
nColumns = children.length; nColumns = children.length;
spacing = this._spacing; else
} else { nColumns = this._computeLayout(forWidth)[0];
[nColumns, , spacing] = this._computeLayout(forWidth);
}
let nRows; let nRows;
if (nColumns > 0) if (nColumns > 0)
nRows = Math.ceil(children.length / nColumns); nRows = Math.ceil(children.length / nColumns);
@ -246,25 +230,18 @@ const IconGrid = new Lang.Class({
nRows = 0; nRows = 0;
if (this._rowLimit) if (this._rowLimit)
nRows = Math.min(nRows, this._rowLimit); nRows = Math.min(nRows, this._rowLimit);
let totalSpacing = Math.max(0, nRows - 1) * spacing; let totalSpacing = Math.max(0, nRows - 1) * this._spacing;
let height = nRows * this._vItemSize + totalSpacing; let height = nRows * this._vItemSize + totalSpacing;
alloc.min_size = height; alloc.min_size = height;
alloc.natural_size = height; alloc.natural_size = height;
}, },
_allocate: function (grid, box, flags) { _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 children = this._getVisibleChildren();
let availWidth = box.x2 - box.x1; let availWidth = box.x2 - box.x1;
let availHeight = box.y2 - box.y1; let availHeight = box.y2 - box.y1;
let [nColumns, usedWidth, spacing] = this._computeLayout(availWidth); let [nColumns, usedWidth] = this._computeLayout(availWidth);
let leftPadding; let leftPadding;
switch(this._xAlign) { switch(this._xAlign) {
@ -303,8 +280,7 @@ const IconGrid = new Lang.Class({
childBox.x2 = childBox.x1 + width; childBox.x2 = childBox.x1 + width;
childBox.y2 = childBox.y1 + height; childBox.y2 = childBox.y1 + height;
if (this._rowLimit && rowIndex >= this._rowLimit || if (this._rowLimit && rowIndex >= this._rowLimit) {
this._fillParent && childBox.y2 > availHeight) {
this._grid.set_skip_paint(children[i], true); this._grid.set_skip_paint(children[i], true);
} else { } else {
children[i].allocate(childBox, flags); children[i].allocate(childBox, flags);
@ -318,10 +294,10 @@ const IconGrid = new Lang.Class({
} }
if (columnIndex == 0) { if (columnIndex == 0) {
y += this._vItemSize + spacing; y += this._vItemSize + this._spacing;
x = box.x1 + leftPadding; x = box.x1 + leftPadding;
} else { } else {
x += this._hItemSize + spacing; x += this._hItemSize + this._spacing;
} }
} }
}, },
@ -337,25 +313,16 @@ const IconGrid = new Lang.Class({
_computeLayout: function (forWidth) { _computeLayout: function (forWidth) {
let nColumns = 0; let nColumns = 0;
let usedWidth = 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) && while ((this._colLimit == null || nColumns < this._colLimit) &&
(usedWidth + this._hItemSize <= forWidth)) { (usedWidth + this._hItemSize <= forWidth)) {
usedWidth += this._hItemSize + spacing; usedWidth += this._hItemSize + this._spacing;
nColumns += 1; nColumns += 1;
} }
if (nColumns > 0) if (nColumns > 0)
usedWidth -= spacing; usedWidth -= this._spacing;
return [nColumns, usedWidth, spacing]; return [nColumns, usedWidth];
}, },
_onStyleChanged: function() { _onStyleChanged: function() {

View File

@ -2,21 +2,18 @@
const Caribou = imports.gi.Caribou; const Caribou = imports.gi.Caribou;
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const DBus = imports.dbus;
const Gdk = imports.gi.Gdk; const Gdk = imports.gi.Gdk;
const Gio = imports.gi.Gio; const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib; const GLib = imports.gi.GLib;
const Lang = imports.lang; const Lang = imports.lang;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const Signals = imports.signals;
const St = imports.gi.St; const St = imports.gi.St;
const BoxPointer = imports.ui.boxpointer; const BoxPointer = imports.ui.boxpointer;
const Layout = imports.ui.layout;
const Main = imports.ui.main; const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray; const MessageTray = imports.ui.messageTray;
const KEYBOARD_REST_TIME = Layout.KEYBOARD_ANIMATION_TIME * 2 * 1000;
const KEYBOARD_SCHEMA = 'org.gnome.shell.keyboard'; const KEYBOARD_SCHEMA = 'org.gnome.shell.keyboard';
const KEYBOARD_TYPE = 'keyboard-type'; 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') if (this._key.name == 'Control_L' || this._key.name == 'Alt_L')
this._key.latch = true; 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) { if (this._extended_keys.length > 0) {
this._grabbed = false;
this._eventCaptureId = 0;
this._key.connect('notify::show-subkeys', Lang.bind(this, this._onShowSubkeysChanged)); this._key.connect('notify::show-subkeys', Lang.bind(this, this._onShowSubkeysChanged));
this._boxPointer = new BoxPointer.BoxPointer(St.Side.BOTTOM, this._boxPointer = new BoxPointer.BoxPointer(St.Side.BOTTOM,
{ x_fill: true, { x_fill: true,
@ -111,23 +115,52 @@ const Key = new Lang.Class({
this._boxPointer.bin.add_actor(this._extended_keyboard); this._boxPointer.bin.add_actor(this._extended_keyboard);
}, },
get subkeys() { _onEventCapture: function (actor, event) {
return this._boxPointer; 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 () { _onShowSubkeysChanged: function () {
if (this._key.show_subkeys) { if (this._key.show_subkeys) {
this.actor.fake_release();
this._boxPointer.actor.raise_top(); this._boxPointer.actor.raise_top();
this._boxPointer.setPosition(this.actor, 0.5); this._boxPointer.setPosition(this.actor, 0.5);
this.emit('show-subkeys'); this._boxPointer.show(BoxPointer.PopupAnimation.FULL);
this.actor.fake_release();
this.actor.set_hover(false); 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 { } 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({ const Keyboard = new Lang.Class({
// HACK: we can't set Name, because it collides with Name dbus property // 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._focusInExtendedKeys = false;
this._timestamp = global.display.get_current_time_roundtrip(); 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 = new Gio.Settings({ schema: KEYBOARD_SCHEMA });
this._keyboardSettings.connect('changed', Lang.bind(this, this._settingsChanged)); this._keyboardSettings.connect('changed', Lang.bind(this, this._settingsChanged));
this._a11yApplicationsSettings = new Gio.Settings({ schema: A11Y_APPLICATIONS_SCHEMA }); this._a11yApplicationsSettings = new Gio.Settings({ schema: A11Y_APPLICATIONS_SCHEMA });
this._a11yApplicationsSettings.connect('changed', Lang.bind(this, this._settingsChanged)); this._a11yApplicationsSettings.connect('changed', Lang.bind(this, this._settingsChanged));
this._settingsChanged(); this._settingsChanged();
},
this._showIdleId = 0; init: function () {
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));
this._redraw(); this._redraw();
}, },
@ -176,19 +199,25 @@ const Keyboard = new Lang.Class({
if (this._keyboard) if (this._keyboard)
this._destroyKeyboard(); this._destroyKeyboard();
if (this._enableKeyboard) if (this._enableKeyboard) {
this._setupKeyboard(); // If we've been called because the setting actually just
else // 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); Main.layoutManager.hideKeyboard(true);
}, },
_destroyKeyboard: function() { _destroyKeyboard: function() {
if (this._keyboardNotifyId) if (this._keyboardNotifyId)
this._keyboard.disconnect(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) if (this._focusNotifyId)
global.stage.disconnect(this._focusNotifyId); global.stage.disconnect(this._focusNotifyId);
this._keyboard = null; this._keyboard = null;
@ -198,7 +227,7 @@ const Keyboard = new Lang.Class({
this._destroySource(); this._destroySource();
}, },
_setupKeyboard: function() { _setupKeyboard: function(show) {
this.actor = new St.BoxLayout({ name: 'keyboard', vertical: true, reactive: true }); this.actor = new St.BoxLayout({ name: 'keyboard', vertical: true, reactive: true });
Main.layoutManager.keyboardBox.add_actor(this.actor); Main.layoutManager.keyboardBox.add_actor(this.actor);
Main.layoutManager.trackChrome(this.actor); Main.layoutManager.trackChrome(this.actor);
@ -219,11 +248,12 @@ const Keyboard = new Lang.Class({
this.actor.text_direction = Clutter.TextDirection.LTR; this.actor.text_direction = Clutter.TextDirection.LTR;
this._keyboardNotifyId = this._keyboard.connect('notify::active-group', Lang.bind(this, this._onGroupChanged)); 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._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 () { _onKeyFocusChanged: function () {
@ -243,102 +273,88 @@ const Keyboard = new Lang.Class({
return; return;
let time = global.get_current_time(); let time = global.get_current_time();
if (!(focus instanceof Clutter.Text)) { if (focus instanceof Clutter.Text)
this.Show(time);
else
this.Hide(time); 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 () { _addKeys: function () {
let groups = this._keyboard.get_groups(); let groups = this._keyboard.get_groups();
for (let i = 0; i < groups.length; ++i) { for (let i = 0; i < groups.length; ++i) {
let gname = groups[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(); this._setActiveLayer();
}, },
_onCapturedEvent: function(actor, event) { _getTrayIcon: function () {
let type = event.type(); let trayButton = new St.Button ({ label: _("tray"),
let press = type == Clutter.EventType.BUTTON_PRESS; style_class: 'keyboard-key' });
let release = type == Clutter.EventType.BUTTON_RELEASE; trayButton.key_width = 1;
trayButton.connect('button-press-event', Lang.bind(this, function () {
Main.messageTray.toggle();
}));
if (press) Main.overview.connect('showing', Lang.bind(this, function () {
this._capturedPress = true; trayButton.reactive = false;
else if (release && this._capturedPress) trayButton.add_style_pseudo_class('grayed');
this._hideSubkeys(); }));
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) { _addRows : function (keys, layout) {
let keyboard_row = new St.BoxLayout(); let keyboard_row = new St.BoxLayout();
for (let i = 0; i < keys.length; ++i) { for (let i = 0; i < keys.length; ++i) {
let children = keys[i].get_children(); 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 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) { for (let j = 0; j < children.length; ++j) {
if (this._numOfHorizKeys == 0) if (this._numOfHorizKeys == 0)
this._numOfHorizKeys = children.length; this._numOfHorizKeys = children.length;
let key = children[j]; let key = children[j];
let button = new Key(key); let button = new Key(key);
switch (key.align) { if (key.align == 'right')
case 'right':
right_box.add(button.actor); right_box.add(button.actor);
break; else
case 'center':
center_box.add(button.actor);
break;
case 'left':
default:
left_box.add(button.actor); left_box.add(button.actor);
break;
}
if (key.name == 'Caribou_Prefs') { if (key.name == 'Caribou_Prefs') {
key.connect('key-released', Lang.bind(this, this.hide)); key.connect('key-released', Lang.bind(this, this.hide));
}
button.connect('show-subkeys', Lang.bind(this, function() { // Add new key for hiding message tray
if (this._subkeysBoxPointer) right_box.add(this._getTrayIcon());
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();
}));
} }
keyboard_row.add(left_box, { expand: true, x_fill: false, x_align: St.Align.START }); 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 }); keyboard_row.add(right_box, { expand: true, x_fill: false, x_align: St.Align.END });
} }
layout.add(keyboard_row); layout.add(keyboard_row);
@ -411,14 +427,6 @@ const Keyboard = new Lang.Class({
this._redraw(); this._redraw();
}, },
_onGroupAdded: function (keyboard, gname) {
this._groups[gname] = this._createLayersForGroup(gname);
},
_onGroupRemoved: function (keyboard, gname) {
delete this._groups[gname];
},
_setActiveLayer: function () { _setActiveLayer: function () {
let active_group_name = this._keyboard.active_group; let active_group_name = this._keyboard.active_group;
let active_group = this._keyboard.get_group(active_group_name); 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; actor._extended_keys || actor.extended_key;
}, },
_clearKeyboardRestTimer: function() {
if (!this._keyboardRestingId)
return;
GLib.source_remove(this._keyboardRestingId);
this._keyboardRestingId = 0;
},
show: function (monitor) { 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; Main.layoutManager.keyboardIndex = monitor;
this._redraw(); this._redraw();
Main.layoutManager.showKeyboard(); Main.layoutManager.showKeyboard();
@ -492,41 +470,10 @@ const Keyboard = new Lang.Class({
}, },
hide: function () { 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(); Main.layoutManager.hideKeyboard();
this._createSource(); 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 () { _moveTemporarily: function () {
let currentWindow = global.screen.get_display().focus_window; let currentWindow = global.screen.get_display().focus_window;
let rect = currentWindow.get_outer_rect(); let rect = currentWindow.get_outer_rect();
@ -555,13 +502,6 @@ const Keyboard = new Lang.Class({
return one - two; return one - two;
}, },
_clearShowIdle: function() {
if (!this._showIdleId)
return;
GLib.source_remove(this._showIdleId);
this._showIdleId = 0;
},
// D-Bus methods // D-Bus methods
Show: function(timestamp) { Show: function(timestamp) {
if (!this._enableKeyboard) if (!this._enableKeyboard)
@ -570,8 +510,6 @@ const Keyboard = new Lang.Class({
if (this._compareTimestamp(timestamp, this._timestamp) < 0) if (this._compareTimestamp(timestamp, this._timestamp) < 0)
return; return;
this._clearShowIdle();
if (timestamp != Clutter.CURRENT_TIME) if (timestamp != Clutter.CURRENT_TIME)
this._timestamp = timestamp; this._timestamp = timestamp;
this.show(Main.layoutManager.focusIndex); this.show(Main.layoutManager.focusIndex);
@ -584,8 +522,6 @@ const Keyboard = new Lang.Class({
if (this._compareTimestamp(timestamp, this._timestamp) < 0) if (this._compareTimestamp(timestamp, this._timestamp) < 0)
return; return;
this._clearShowIdle();
if (timestamp != Clutter.CURRENT_TIME) if (timestamp != Clutter.CURRENT_TIME)
this._timestamp = timestamp; this._timestamp = timestamp;
this.hide(); this.hide();
@ -620,7 +556,11 @@ const KeyboardSource = new Lang.Class({
this.keepTrayOnSummaryClick = true; 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(); this.open();
return true; return true;
}, },

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,6 @@
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const Lang = imports.lang; const Lang = imports.lang;
const Meta = imports.gi.Meta; const Meta = imports.gi.Meta;
const Signals = imports.signals;
const St = imports.gi.St; const St = imports.gi.St;
const Params = imports.misc.params; const Params = imports.misc.params;
@ -102,7 +101,6 @@ const Lightbox = new Lang.Class({
}, },
show: function() { show: function() {
Tweener.removeTweens(this.actor);
if (this._fadeInTime) { if (this._fadeInTime) {
this.shown = false; this.shown = false;
this.actor.opacity = 0; this.actor.opacity = 0;
@ -112,20 +110,17 @@ const Lightbox = new Lang.Class({
transition: 'easeOutQuad', transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() { onComplete: Lang.bind(this, function() {
this.shown = true; this.shown = true;
this.emit('shown');
}) })
}); });
} else { } else {
this.actor.opacity = 255 * this._fadeFactor; this.actor.opacity = 255 * this._fadeFactor;
this.shown = true; this.shown = true;
this.emit('shown');
} }
this.actor.show(); this.actor.show();
}, },
hide: function() { hide: function() {
this.shown = false; this.shown = false;
Tweener.removeTweens(this.actor);
if (this._fadeOutTime) { if (this._fadeOutTime) {
Tweener.addTween(this.actor, Tweener.addTween(this.actor,
{ opacity: 0, { opacity: 0,
@ -202,4 +197,3 @@ const Lightbox = new Lang.Class({
this.highlight(null); this.highlight(null);
} }
}); });
Signals.addSignalMethods(Lightbox.prototype);

View File

@ -39,7 +39,6 @@ var commandHeader = 'const Clutter = imports.gi.Clutter; ' +
* in the shell core code too. */ * in the shell core code too. */
'const stage = global.stage; ' + 'const stage = global.stage; ' +
/* Special lookingGlass functions */ /* Special lookingGlass functions */
'const inspect = Lang.bind(Main.lookingGlass, Main.lookingGlass.inspect); ' +
'const it = Main.lookingGlass.getIt(); ' + 'const it = Main.lookingGlass.getIt(); ' +
'const r = Lang.bind(Main.lookingGlass, Main.lookingGlass.getResult); '; 'const r = Lang.bind(Main.lookingGlass, Main.lookingGlass.getResult); ';
@ -685,8 +684,7 @@ const Memory = new Lang.Class({
const Extensions = new Lang.Class({ const Extensions = new Lang.Class({
Name: 'Extensions', Name: 'Extensions',
_init: function(lookingGlass) { _init: function() {
this._lookingGlass = lookingGlass;
this.actor = new St.BoxLayout({ vertical: true, this.actor = new St.BoxLayout({ vertical: true,
name: 'lookingGlassExtensions' }); name: 'lookingGlassExtensions' });
this._noExtensions = new St.Label({ style_class: 'lg-extensions-none', 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 () { inspectIcon.connect('button-press-event', Lang.bind(this, function () {
let inspector = new Inspector(this); let inspector = new Inspector(this);
inspector.connect('target', Lang.bind(this, function(i, target, stageX, stageY) { 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() { inspector.connect('closed', Lang.bind(this, function() {
this.actor.show(); this.actor.show();
@ -913,7 +912,7 @@ const LookingGlass = new Lang.Class({
this._memory = new Memory(); this._memory = new Memory();
notebook.appendPage('Memory', this._memory.actor); notebook.appendPage('Memory', this._memory.actor);
this._extensions = new Extensions(this); this._extensions = new Extensions();
notebook.appendPage('Extensions', this._extensions.actor); notebook.appendPage('Extensions', this._extensions.actor);
this._entry.clutter_text.connect('activate', Lang.bind(this, function (o, e) { this._entry.clutter_text.connect('activate', Lang.bind(this, function (o, e) {
@ -951,7 +950,9 @@ const LookingGlass = new Lang.Class({
_updateFont: function() { _updateFont: function() {
let fontName = this._interfaceSettings.get_string('monospace-font-name'); 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 // 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. // monospace font to be bold/oblique/etc. Could easily be added here.
this.actor.style = this.actor.style =
@ -1056,10 +1057,6 @@ const LookingGlass = new Lang.Class({
this._entry.text = ''; this._entry.text = '';
}, },
inspect: function(x, y) {
return global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y);
},
getIt: function () { getIt: function () {
return this._it; return this._it;
}, },
@ -1132,7 +1129,7 @@ const LookingGlass = new Lang.Class({
if (this._open) if (this._open)
return; return;
if (!Main.pushModal(this._entry, { keybindingMode: Shell.KeyBindingMode.LOOKING_GLASS })) if (!Main.pushModal(this._entry, { keybindingMode: Main.KeybindingMode.LOOKING_GLASS }))
return; return;
this._notebook.selectIndex(0); this._notebook.selectIndex(0);

View File

@ -18,7 +18,6 @@ const ExtensionSystem = imports.ui.extensionSystem;
const ExtensionDownloader = imports.ui.extensionDownloader; const ExtensionDownloader = imports.ui.extensionDownloader;
const Keyboard = imports.ui.keyboard; const Keyboard = imports.ui.keyboard;
const MessageTray = imports.ui.messageTray; const MessageTray = imports.ui.messageTray;
const OsdWindow = imports.ui.osdWindow;
const Overview = imports.ui.overview; const Overview = imports.ui.overview;
const Panel = imports.ui.panel; const Panel = imports.ui.panel;
const Params = imports.misc.params; const Params = imports.misc.params;
@ -41,6 +40,19 @@ const Util = imports.misc.util;
const OVERRIDES_SCHEMA = 'org.gnome.shell.overrides'; const OVERRIDES_SCHEMA = 'org.gnome.shell.overrides';
const DEFAULT_BACKGROUND_COLOR = Clutter.Color.from_pixel(0x2e3436ff); 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 componentManager = null;
let panel = null; let panel = null;
let overview = null; let overview = null;
@ -52,13 +64,12 @@ let screenShield = null;
let notificationDaemon = null; let notificationDaemon = null;
let windowAttentionHandler = null; let windowAttentionHandler = null;
let ctrlAltTabManager = null; let ctrlAltTabManager = null;
let osdWindow = null;
let sessionMode = null; let sessionMode = null;
let shellDBusService = null; let shellDBusService = null;
let shellMountOpDBusService = null; let shellMountOpDBusService = null;
let screenSaverDBus = null; let screenSaverDBus = null;
let modalCount = 0; let modalCount = 0;
let keybindingMode = Shell.KeyBindingMode.NORMAL; let keybindingMode = KeybindingMode.NORMAL;
let modalActorFocusStack = []; let modalActorFocusStack = [];
let uiGroup = null; let uiGroup = null;
let magnifier = null; let magnifier = null;
@ -70,17 +81,19 @@ let _defaultCssStylesheet = null;
let _cssStylesheet = null; let _cssStylesheet = null;
let _overridesSettings = null; let _overridesSettings = null;
let background = null;
function _sessionUpdated() { function _sessionUpdated() {
wm.setCustomKeybindingHandler('panel-main-menu', wm.setCustomKeybindingHandler('panel-main-menu',
Shell.KeyBindingMode.NORMAL | KeybindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW, KeybindingMode.OVERVIEW,
sessionMode.hasOverview ? Lang.bind(overview, overview.toggle) : null); sessionMode.hasOverview ? Lang.bind(overview, overview.toggle) : null);
wm.allowKeybinding('overlay-key', Shell.KeyBindingMode.NORMAL | wm.allowKeybinding('overlay-key', KeybindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW); KeybindingMode.OVERVIEW);
wm.setCustomKeybindingHandler('panel-run-dialog', wm.setCustomKeybindingHandler('panel-run-dialog',
Shell.KeyBindingMode.NORMAL | KeybindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW, KeybindingMode.OVERVIEW,
sessionMode.hasRunDialog ? openRunDialog : null); sessionMode.hasRunDialog ? openRunDialog : null);
if (sessionMode.isGreeter) if (sessionMode.isGreeter)
screenShield.showDialog(); screenShield.showDialog();
@ -91,26 +104,12 @@ function start() {
global.logError = window.log; global.logError = window.log;
global.log = window.log; global.log = window.log;
// Hide the stage until we're ready for it
global.stage.hide();
// Chain up async errors reported from C // Chain up async errors reported from C
global.connect('notify-error', function (global, msg, detail) { notifyError(msg, detail); }); global.connect('notify-error', function (global, msg, detail) { notifyError(msg, detail); });
Gio.DesktopAppInfo.set_desktop_env('GNOME'); Gio.DesktopAppInfo.set_desktop_env('GNOME');
sessionMode = new SessionMode.SessionMode(); sessionMode = new SessionMode.SessionMode();
// start session after we know what mode we're running in
let signalId = sessionMode.connect('updated', function() {
sessionMode.disconnect(signalId);
startSession();
});
}
function startSession() {
sessionMode.connect('updated', _loadDefaultStylesheet);
shellDBusService = new ShellDBus.GnomeShell(); shellDBusService = new ShellDBus.GnomeShell();
shellMountOpDBusService = new ShellMountOperation.GnomeShellMountOpHandler(); shellMountOpDBusService = new ShellMountOperation.GnomeShellMountOpHandler();
@ -127,19 +126,40 @@ function startSession() {
tracker.connect('startup-sequence-changed', _queueCheckWorkspaces); 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(); 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(); xdndHandler = new XdndHandler.XdndHandler();
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager(); ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
osdWindow = new OsdWindow.OsdWindow();
overview = new Overview.Overview(); overview = new Overview.Overview();
wm = new WindowManager.WindowManager(); wm = new WindowManager.WindowManager();
magnifier = new Magnifier.Magnifier(); magnifier = new Magnifier.Magnifier();
@ -147,9 +167,6 @@ function startSession() {
screenShield = new ScreenShield.ScreenShield(); screenShield = new ScreenShield.ScreenShield();
else else
screenShield = new ScreenShield.ScreenShieldFallback(); screenShield = new ScreenShield.ScreenShieldFallback();
// The message tray relies on being constructed
// after the panel.
panel = new Panel.Panel(); panel = new Panel.Panel();
messageTray = new MessageTray.MessageTray(); messageTray = new MessageTray.MessageTray();
keyboard = new Keyboard.Keyboard(); keyboard = new Keyboard.Keyboard();
@ -158,7 +175,7 @@ function startSession() {
componentManager = new Components.ComponentManager(); componentManager = new Components.ComponentManager();
layoutManager.init(); layoutManager.init();
layoutManager.prepareStartupAnimation(); keyboard.init();
overview.init(); overview.init();
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT, global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT,
@ -171,9 +188,6 @@ function startSession() {
// initiate logouts. // initiate logouts.
EndSessionDialog.init(); EndSessionDialog.init();
// We're ready for the session manager to move to the next phase
Meta.register_with_session();
_startDate = new Date(); _startDate = new Date();
log('GNOME Shell started at ' + _startDate); log('GNOME Shell started at ' + _startDate);
@ -198,10 +212,6 @@ function startSession() {
ExtensionDownloader.init(); ExtensionDownloader.init();
ExtensionSystem.init(); ExtensionSystem.init();
layoutManager.connect('startup-prepared', function() {
layoutManager.startupAnimation();
});
} }
let _workspaces = []; let _workspaces = [];
@ -385,18 +395,6 @@ function _nWorkspacesChanged() {
return false; return false;
} }
function _loadDefaultStylesheet() {
if (!sessionMode.isPrimary)
return;
let stylesheet = global.datadir + '/theme/' + sessionMode.stylesheetName;
if (_defaultCssStylesheet == stylesheet)
return;
_defaultCssStylesheet = stylesheet;
loadTheme();
}
/** /**
* getThemeStylesheet: * getThemeStylesheet:
* *
@ -477,6 +475,17 @@ function notifyError(msg, details) {
notify(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) { function _findModal(actor) {
for (let i = 0; i < modalActorFocusStack.length; i++) { for (let i = 0; i < modalActorFocusStack.length; i++) {
if (modalActorFocusStack[i].actor == actor) if (modalActorFocusStack[i].actor == actor)
@ -485,6 +494,10 @@ function _findModal(actor) {
return -1; return -1;
} }
function isInModalStack(actor) {
return _findModal(actor) != -1;
}
/** /**
* pushModal: * pushModal:
* @actor: #ClutterActor which will be given keyboard focus * @actor: #ClutterActor which will be given keyboard focus
@ -507,7 +520,7 @@ function _findModal(actor) {
* - options: Meta.ModalOptions flags to indicate that the pointer is * - options: Meta.ModalOptions flags to indicate that the pointer is
* already grabbed * 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 * global keybindings; the default of NONE will filter
* out all keybindings * out all keybindings
* *
@ -516,7 +529,7 @@ function _findModal(actor) {
function pushModal(actor, params) { function pushModal(actor, params) {
params = Params.parse(params, { timestamp: global.get_current_time(), params = Params.parse(params, { timestamp: global.get_current_time(),
options: 0, options: 0,
keybindingMode: Shell.KeyBindingMode.NONE }); keybindingMode: KeybindingMode.NONE });
if (modalCount == 0) { if (modalCount == 0) {
if (!global.begin_modal(params.timestamp, params.options)) { if (!global.begin_modal(params.timestamp, params.options)) {
@ -534,20 +547,19 @@ function pushModal(actor, params) {
if (index >= 0) if (index >= 0)
popModal(actor); popModal(actor);
}); });
let curFocus = global.stage.get_key_focus();
let prevFocus = global.stage.get_key_focus(); let curFocusDestroyId;
let prevFocusDestroyId; if (curFocus != null) {
if (prevFocus != null) { curFocusDestroyId = curFocus.connect('destroy', function() {
prevFocusDestroyId = prevFocus.connect('destroy', function() {
let index = _findModal(actor); let index = _findModal(actor);
if (index >= 0) if (index >= 0)
modalActorFocusStack[index].prevFocus = null; modalActorFocusStack[index].actor = null;
}); });
} }
modalActorFocusStack.push({ actor: actor, modalActorFocusStack.push({ actor: actor,
focus: curFocus,
destroyId: actorDestroyId, destroyId: actorDestroyId,
prevFocus: prevFocus, focusDestroyId: curFocusDestroyId,
prevFocusDestroyId: prevFocusDestroyId,
keybindingMode: keybindingMode }); keybindingMode: keybindingMode });
keybindingMode = params.keybindingMode; keybindingMode = params.keybindingMode;
@ -577,7 +589,7 @@ function popModal(actor, timestamp) {
global.stage.set_key_focus(null); global.stage.set_key_focus(null);
global.end_modal(timestamp); global.end_modal(timestamp);
global.set_stage_input_mode(Shell.StageInputMode.NORMAL); global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
keybindingMode = Shell.KeyBindingMode.NORMAL; keybindingMode = KeybindingMode.NORMAL;
throw new Error('incorrect pop'); throw new Error('incorrect pop');
} }
@ -588,33 +600,18 @@ function popModal(actor, timestamp) {
record.actor.disconnect(record.destroyId); record.actor.disconnect(record.destroyId);
if (focusIndex == modalActorFocusStack.length - 1) { if (focusIndex == modalActorFocusStack.length - 1) {
if (record.prevFocus) if (record.focus)
record.prevFocus.disconnect(record.prevFocusDestroyId); record.focus.disconnect(record.focusDestroyId);
keybindingMode = record.keybindingMode; keybindingMode = record.keybindingMode;
global.stage.set_key_focus(record.prevFocus); global.stage.set_key_focus(record.focus);
} else { } 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]; let t = modalActorFocusStack[modalActorFocusStack.length - 1];
if (t.prevFocus) if (t.focus)
t.prevFocus.disconnect(t.prevFocusDestroyId); t.focus.disconnect(t.focusDestroyId);
// Remove from the middle, shift the focus chain up // Remove from the middle, shift the focus chain up
for (let i = modalActorFocusStack.length - 1; i > focusIndex; i--) { for (let i = modalActorFocusStack.length - 1; i > focusIndex; i--) {
modalActorFocusStack[i].prevFocus = modalActorFocusStack[i - 1].prevFocus; modalActorFocusStack[i].focus = modalActorFocusStack[i - 1].focus;
modalActorFocusStack[i].prevFocusDestroyId = modalActorFocusStack[i - 1].prevFocusDestroyId; modalActorFocusStack[i].focusDestroyId = modalActorFocusStack[i - 1].focusDestroyId;
modalActorFocusStack[i].keybindingMode = modalActorFocusStack[i - 1].keybindingMode; modalActorFocusStack[i].keybindingMode = modalActorFocusStack[i - 1].keybindingMode;
} }
} }
@ -626,7 +623,7 @@ function popModal(actor, timestamp) {
global.end_modal(timestamp); global.end_modal(timestamp);
global.set_stage_input_mode(Shell.StageInputMode.NORMAL); global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
Meta.enable_unredirect_for_screen(global.screen); Meta.enable_unredirect_for_screen(global.screen);
keybindingMode = Shell.KeyBindingMode.NORMAL; keybindingMode = KeybindingMode.NORMAL;
} }
function createLookingGlass() { function createLookingGlass() {

File diff suppressed because it is too large Load Diff

View File

@ -37,7 +37,7 @@ const ModalDialog = new Lang.Class({
params = Params.parse(params, { shellReactive: false, params = Params.parse(params, { shellReactive: false,
styleClass: null, styleClass: null,
parentActor: Main.uiGroup, parentActor: Main.uiGroup,
keybindingMode: Shell.KeyBindingMode.SYSTEM_MODAL, keybindingMode: Main.KeybindingMode.SYSTEM_MODAL,
shouldFadeIn: true }); shouldFadeIn: true });
this.state = State.CLOSED; this.state = State.CLOSED;
@ -58,9 +58,7 @@ const ModalDialog = new Lang.Class({
this._group.connect('destroy', Lang.bind(this, this._onGroupDestroy)); this._group.connect('destroy', Lang.bind(this, this._onGroupDestroy));
this._pressedKey = null;
this._buttonKeys = {}; 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._group.connect('key-release-event', Lang.bind(this, this._onKeyReleaseEvent));
this._backgroundBin = new St.Bin(); this._backgroundBin = new St.Bin();
@ -82,7 +80,7 @@ const ModalDialog = new Lang.Class({
let stack = new Shell.Stack(); let stack = new Shell.Stack();
this._backgroundBin.child = 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._eventBlocker);
stack.add_actor(this.dialogLayout); stack.add_actor(this.dialogLayout);
} else { } else {
@ -98,7 +96,8 @@ const ModalDialog = new Lang.Class({
y_align: St.Align.START }); y_align: St.Align.START });
this.buttonLayout = new St.BoxLayout({ style_class: 'modal-dialog-button-box', this.buttonLayout = new St.BoxLayout({ style_class: 'modal-dialog-button-box',
vertical: false }); visible: false,
vertical: false });
this.dialogLayout.add(this.buttonLayout, this.dialogLayout.add(this.buttonLayout,
{ expand: true, { expand: true,
x_align: St.Align.MIDDLE, x_align: St.Align.MIDDLE,
@ -121,6 +120,7 @@ const ModalDialog = new Lang.Class({
setButtons: function(buttons) { setButtons: function(buttons) {
this.clearButtons(); this.clearButtons();
this.buttonLayout.visible = (buttons.length > 0);
for (let i = 0; i < buttons.length; i++) { for (let i = 0; i < buttons.length; i++) {
let buttonInfo = buttons[i]; let buttonInfo = buttons[i];
@ -159,7 +159,6 @@ const ModalDialog = new Lang.Class({
keys = []; keys = [];
let button = new St.Button({ style_class: 'modal-dialog-button', let button = new St.Button({ style_class: 'modal-dialog-button',
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
reactive: true, reactive: true,
can_focus: true, can_focus: true,
label: label }); label: label });
@ -181,19 +180,10 @@ const ModalDialog = new Lang.Class({
return button; return button;
}, },
_onKeyPressEvent: function(object, event) {
this._pressedKey = event.get_key_symbol();
},
_onKeyReleaseEvent: function(object, event) { _onKeyReleaseEvent: function(object, event) {
let pressedKey = this._pressedKey;
this._pressedKey = null;
let symbol = event.get_key_symbol(); let symbol = event.get_key_symbol();
if (symbol != pressedKey)
return false;
let buttonInfo = this._buttonKeys[symbol]; let buttonInfo = this._buttonKeys[symbol];
if (!buttonInfo) if (!buttonInfo)
return false; return false;
@ -276,7 +266,6 @@ const ModalDialog = new Lang.Class({
function() { function() {
this.state = State.CLOSED; this.state = State.CLOSED;
this._group.hide(); this._group.hide();
this.emit('closed');
}) })
}); });
}, },

View File

@ -103,126 +103,6 @@ const STANDARD_TRAY_ICON_IMPLEMENTATIONS = {
'ibus-ui-gtk': 'keyboard' '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({ const NotificationDaemon = new Lang.Class({
Name: 'NotificationDaemon', Name: 'NotificationDaemon',
@ -247,42 +127,42 @@ const NotificationDaemon = new Lang.Class({
this._trayManager.manage_stage(global.stage, Main.messageTray.actor); this._trayManager.manage_stage(global.stage, Main.messageTray.actor);
}, },
_imageForNotificationData: function(hints) { _iconForNotificationData: function(icon, hints) {
if (hints['image-data']) { // 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, let [width, height, rowStride, hasAlpha,
bitsPerSample, nChannels, data] = hints['image-data']; bitsPerSample, nChannels, data] = hints['image-data'];
return Shell.util_create_pixbuf_from_data(data, GdkPixbuf.Colorspace.RGB, hasAlpha, return Shell.util_create_pixbuf_from_data(data, GdkPixbuf.Colorspace.RGB, hasAlpha,
bitsPerSample, width, height, rowStride); bitsPerSample, width, height, rowStride);
} else if (hints['image-path']) { } else if (hints['image-path']) {
return new Gio.FileIcon({ file: Gio.File.new_for_path(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) { _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); source.setTransient(isForTransientNotification);
if (!isForTransientNotification) { if (!isForTransientNotification) {
@ -475,8 +355,12 @@ const NotificationDaemon = new Lang.Class({
[ndata.id, ndata.icon, ndata.summary, ndata.body, [ndata.id, ndata.icon, ndata.summary, ndata.body,
ndata.actions, ndata.hints, ndata.notification]; ndata.actions, ndata.hints, ndata.notification];
let gicon = this._iconForNotificationData(icon, hints);
if (notification == null) { if (notification == null) {
notification = new MessageTray.Notification(source); notification = new MessageTray.Notification(source, summary, body,
{ gicon: gicon,
bannerMarkup: true });
ndata.notification = notification; ndata.notification = notification;
notification.connect('destroy', Lang.bind(this, notification.connect('destroy', Lang.bind(this,
function(n, reason) { function(n, reason) {
@ -499,38 +383,29 @@ const NotificationDaemon = new Lang.Class({
function(n, actionId) { function(n, actionId) {
this._emitActionInvoked(ndata.id, 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 // We only display a large image if an icon is also specified.
notification.isMusic = (ndata.hints['category'] == 'x-gnome.music'); if (icon && (hints['image-data'] || hints['image-path'])) {
let image = null;
let gicon = this._iconForNotificationData(icon, hints); if (hints['image-data']) {
let gimage = this._imageForNotificationData(hints); let [width, height, rowStride, hasAlpha,
bitsPerSample, nChannels, data] = hints['image-data'];
let image = null; image = St.TextureCache.get_default().load_from_raw(data, hasAlpha,
width, height, rowStride, notification.IMAGE_SIZE);
// If an icon is not specified, we use 'image-data' or 'image-path' hint for an icon } else if (hints['image-path']) {
// and don't show a large image. There are currently many applications that use image = St.TextureCache.get_default().load_uri_async(GLib.filename_to_uri(hints['image-path'], null),
// notify_notification_set_icon_from_pixbuf() from libnotify, which in turn sets notification.IMAGE_SIZE,
// the 'image-data' hint. These applications don't typically pass in 'app_icon' notification.IMAGE_SIZE);
// 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 notification.setImage(image);
// one of 'image-data' or 'image-path' are specified, we show both an icon and } else {
// a large image. notification.unsetImage();
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);
if (actions.length) { if (actions.length) {
notification.setUseActionIcons(hints['action-icons'] == true); 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 // of the 'transient' hint with hints['transient'] rather than hints.transient
notification.setTransient(hints['transient'] == true); notification.setTransient(hints['transient'] == true);
let sourceGIcon = source.useNotificationIcon ? gicon : null; let sourceGIcon = source.useNotificationIcon ? this._iconForNotificationData(icon, hints) : null;
source.processNotification(notification, sourceGIcon); source.processNotification(notification, sourceGIcon);
}, },
@ -584,7 +459,7 @@ const NotificationDaemon = new Lang.Class({
// 'icon-multi', // 'icon-multi',
'icon-static', 'icon-static',
'persistence', 'persistence',
'sound', // 'sound',
]; ];
}, },
@ -640,22 +515,12 @@ const Source = new Lang.Class({
Name: 'NotificationDaemonSource', Name: 'NotificationDaemonSource',
Extends: MessageTray.Source, Extends: MessageTray.Source,
_init: function(title, pid, sender, trayIcon, appId) { _init: function(title, pid, sender, trayIcon) {
// 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);
this.parent(title); this.parent(title);
this.initialTitle = title; this.initialTitle = title;
if (this.app) this.pid = pid;
this.title = this.app.get_name();
else
this.useNotificationIcon = true;
if (sender) if (sender)
this._nameWatcherId = Gio.DBus.session.watch_name(sender, this._nameWatcherId = Gio.DBus.session.watch_name(sender,
Gio.BusNameWatcherFlags.NONE, Gio.BusNameWatcherFlags.NONE,
@ -664,19 +529,16 @@ const Source = new Lang.Class({
else else
this._nameWatcherId = 0; this._nameWatcherId = 0;
if (this.trayIcon) { this._setApp();
// Try again finding the app, using the WM_CLASS from the tray icon if (this.app)
this._setSummaryIcon(this.trayIcon); this.title = this.app.get_name();
this.useNotificationIcon = false; else
} this.useNotificationIcon = true;
},
_createPolicy: function() { this.trayIcon = trayIcon;
if (this.app) { if (this.trayIcon) {
let id = this.app.get_id().replace(/\.desktop$/,''); this._setSummaryIcon(this.trayIcon);
return new NotificationApplicationPolicy(id); this.useNotificationIcon = false;
} else {
return new NotificationGenericPolicy();
} }
}, },
@ -703,17 +565,19 @@ const Source = new Lang.Class({
this.notify(notification); this.notify(notification);
}, },
handleSummaryClick: function(button) { handleSummaryClick: function() {
if (!this.trayIcon) if (!this.trayIcon)
return false; return false;
let event = Clutter.get_current_event(); 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 // Left clicks are passed through only where there aren't unacknowledged
// notifications, so it possible to open them in summary mode; right // notifications, so it possible to open them in summary mode; right
// clicks are always forwarded, as the right click menu is not useful for // clicks are always forwarded, as the right click menu is not useful for
// tray icons // tray icons
if (button == 1 && if (event.get_button() == 1 &&
this.notifications.length > 0) this.notifications.length > 0)
return false; return false;
@ -726,7 +590,7 @@ const Source = new Lang.Class({
return true; return true;
}, },
_getApp: function(appId) { _getApp: function() {
let app; let app;
app = Shell.WindowTracker.get_default().get_app_from_pid(this.pid); app = Shell.WindowTracker.get_default().get_app_from_pid(this.pid);
@ -734,13 +598,7 @@ const Source = new Lang.Class({
return app; return app;
if (this.trayIcon) { if (this.trayIcon) {
app = Shell.AppSystem.get_default().lookup_wmclass(this.trayIcon.wm_class); app = Shell.AppSystem.get_default().lookup_wmclass(this.trayIcon.wmclass);
if (app != null)
return app;
}
if (appId) {
app = Shell.AppSystem.get_default().lookup_app(appId + '.desktop');
if (app != null) if (app != null)
return app; return app;
} }
@ -748,11 +606,11 @@ const Source = new Lang.Class({
return null; return null;
}, },
_setApp: function(appId) { _setApp: function() {
if (this.app) if (this.app)
return; return;
this.app = this._getApp(appId); this.app = this._getApp();
if (!this.app) if (!this.app)
return; return;

View File

@ -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 130x130 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 = 130 * Math.max(1, scale);
this._box.set_size(size, size);
this._box.translation_y = monitor.height / 4;
this._icon.icon_size = size / 2;
}
});

View File

@ -10,13 +10,10 @@ const St = imports.gi.St;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const Gdk = imports.gi.Gdk; const Gdk = imports.gi.Gdk;
const Background = imports.ui.background;
const Dash = imports.ui.dash; const Dash = imports.ui.dash;
const DND = imports.ui.dnd; const DND = imports.ui.dnd;
const LayoutManager = imports.ui.layout;
const Main = imports.ui.main; const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray; const MessageTray = imports.ui.messageTray;
const OverviewControls = imports.ui.overviewControls;
const Panel = imports.ui.panel; const Panel = imports.ui.panel;
const Params = imports.misc.params; const Params = imports.misc.params;
const Tweener = imports.ui.tweener; const Tweener = imports.ui.tweener;
@ -26,13 +23,26 @@ const WorkspaceThumbnail = imports.ui.workspaceThumbnail;
// Time for initial animation going into Overview mode // Time for initial animation going into Overview mode
const ANIMATION_TIME = 0.25; const ANIMATION_TIME = 0.25;
// Must be less than ANIMATION_TIME, since we switch to
// or from the overview completely after ANIMATION_TIME,
// and don't want the shading animation to get cut off
const SHADE_ANIMATION_TIME = .20;
const DND_WINDOW_SWITCH_TIMEOUT = 1250; const DND_WINDOW_SWITCH_TIMEOUT = 1250;
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({ const ShellInfo = new Lang.Class({
Name: 'ShellInfo', Name: 'ShellInfo',
@ -93,9 +103,7 @@ const Overview = new Lang.Class({
_init: function() { _init: function() {
this._overviewCreated = false; this._overviewCreated = false;
this._initCalled = false; this._initCalled = false;
this._controlPressed = false;
global.stage.connect('captured-event', Lang.bind(this, this._capturedEvent));
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated)); Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
this._sessionUpdated(); this._sessionUpdated();
}, },
@ -109,49 +117,41 @@ const Overview = new Lang.Class({
this._overviewCreated = true; 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 // hidden when displaying the overview, so we create a new
// one. Instances of this class share a single CoglTexture behind the // one. Instances of this class share a single CoglTexture behind the
// scenes which allows us to show the background with different // scenes which allows us to show the background with different
// rendering options without duplicating the texture data. // 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(); this._desktopFade = new St.Bin();
global.overlay_group.add_actor(this._desktopFade); 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 /* Translators: This is the main view to select
activities. See also note for "Activities" string. */ activities. See also note for "Activities" string. */
this._overview = new St.BoxLayout({ name: 'overview', this._overview = new St.BoxLayout({ name: 'overview',
accessible_name: _("Overview"), accessible_name: _("Overview"),
reactive: true, reactive: true,
vertical: true, vertical: true });
x_expand: true,
y_expand: true });
this._overview._delegate = this; this._overview._delegate = this;
this._groupStack = new St.Widget({ layout_manager: new Clutter.BinLayout(), this._group = new St.BoxLayout({ name: 'overview-group' });
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._backgroundGroup = new Meta.BackgroundGroup(); this._capturedEventId = 0;
global.overlay_group.add_child(this._backgroundGroup); this._buttonPressId = 0;
this._backgroundGroup.hide();
this._bgManagers = [];
this.visible = false; // animating to overview, in overview, animating out this.visible = false; // animating to overview, in overview, animating out
this._shown = false; // show() and not hide() this._shown = false; // show() and not hide()
this._shownTemporarily = false; // showTemporarily() and not hideTemporarily() this._shownTemporarily = false; // showTemporarily() and not hideTemporarily()
this._modal = false; // have a modal grab this._modal = false; // have a modal grab
this.animationInProgress = false; this.animationInProgress = false;
this.visibleTarget = false; this._hideInProgress = false;
// During transitions, we raise this to the top to avoid having the overview // 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 // area be reactive; it causes too many issues such as double clicks on
@ -161,9 +161,8 @@ const Overview = new Lang.Class({
this._overview.add_actor(this._coverPane); this._overview.add_actor(this._coverPane);
this._coverPane.connect('event', Lang.bind(this, function (actor, event) { return true; })); this._coverPane.connect('event', Lang.bind(this, function (actor, event) { return true; }));
this._stack.hide(); this._overview.hide();
this._stack.add_actor(this._overview); global.overlay_group.add_actor(this._overview);
global.overlay_group.add_actor(this._stack);
this._coverPane.hide(); this._coverPane.hide();
@ -176,7 +175,6 @@ const Overview = new Lang.Class({
Main.xdndHandler.connect('drag-end', Lang.bind(this, this._onDragEnd)); Main.xdndHandler.connect('drag-end', Lang.bind(this, this._onDragEnd));
global.screen.connect('restacked', Lang.bind(this, this._onRestacked)); global.screen.connect('restacked', Lang.bind(this, this._onRestacked));
this._group.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
this._windowSwitchTimeoutId = 0; this._windowSwitchTimeoutId = 0;
this._windowSwitchTimestamp = 0; this._windowSwitchTimestamp = 0;
@ -188,70 +186,6 @@ const Overview = new Lang.Class({
this.init(); 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'
});
}
},
_capturedEvent: function(actor, event) {
let type = event.type();
if (type != Clutter.EventType.KEY_PRESS &&
type != Clutter.EventType.KEY_RELEASE)
return false;
let symbol = event.get_key_symbol();
if (symbol == Clutter.KEY_Control_L ||
symbol == Clutter.KEY_Control_R)
this._controlPressed = type == Clutter.EventType.KEY_PRESS;
return false;
},
_sessionUpdated: function() { _sessionUpdated: function() {
this.isDummy = !Main.sessionMode.hasOverview; this.isDummy = !Main.sessionMode.hasOverview;
this._createOverview(); this._createOverview();
@ -281,46 +215,43 @@ const Overview = new Lang.Class({
in the search entry when no search is in the search entry when no search is
active; it should not exceed ~30 active; it should not exceed ~30
characters. */ characters. */
hint_text: _("Type to search"), hint_text: _("Type to search..."),
track_hover: true, track_hover: true,
can_focus: true }); can_focus: true });
this._searchEntryBin = new St.Bin({ child: this._searchEntry, this._searchEntryBin = new St.Bin({ child: this._searchEntry,
x_align: St.Align.MIDDLE }); x_align: St.Align.MIDDLE });
this._overview.add_actor(this._searchEntryBin); 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 // 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.dashIconSize = this._dash.iconSize;
this._dash.connect('icon-size-changed', this._dash.connect('icon-size-changed',
Lang.bind(this, function() { Lang.bind(this, function() {
this.dashIconSize = this._dash.iconSize; 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)); Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._relayout));
this._relayout(); this._relayout();
}, },
@ -413,10 +344,6 @@ const Overview = new Lang.Class({
return DND.DragMotionResult.CONTINUE; return DND.DragMotionResult.CONTINUE;
}, },
_onScrollEvent: function(actor, event) {
this.emit('scroll-event', event);
},
addAction: function(action) { addAction: function(action) {
if (this.isDummy) if (this.isDummy)
return; return;
@ -446,12 +373,16 @@ const Overview = new Lang.Class({
// when it is next shown. // when it is next shown.
this.hide(); this.hide();
let workArea = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex); let primary = Main.layoutManager.primaryMonitor;
this._coverPane.set_position(0, workArea.y); let contentY = Main.panel.actor.height;
this._coverPane.set_size(workArea.width, workArea.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() { _onRestacked: function() {
@ -535,7 +466,6 @@ const Overview = new Lang.Class({
this.visible = true; this.visible = true;
this.animationInProgress = true; this.animationInProgress = true;
this.visibleTarget = true;
// All the the actors in the window group are completely obscured, // All the the actors in the window group are completely obscured,
// hiding the group holding them while the Overview is displayed greatly // hiding the group holding them while the Overview is displayed greatly
@ -548,20 +478,24 @@ const Overview = new Lang.Class({
// Disable unredirection while in the overview // Disable unredirection while in the overview
Meta.disable_unredirect_for_screen(global.screen); Meta.disable_unredirect_for_screen(global.screen);
global.window_group.hide(); global.window_group.hide();
global.top_window_group.hide(); this._overview.show();
this._stack.show(); this._background.show();
this._backgroundGroup.show();
this._viewSelector.show(); this._viewSelector.show();
this._stack.opacity = 0; this._overview.opacity = 0;
Tweener.addTween(this._stack, Tweener.addTween(this._overview,
{ opacity: 255, { opacity: 255,
transition: 'easeOutQuad', transition: 'easeOutQuad',
time: ANIMATION_TIME, time: ANIMATION_TIME,
onComplete: this._showDone, onComplete: this._showDone,
onCompleteScope: this onCompleteScope: this
}); });
this._shadeBackgrounds();
Tweener.addTween(this._background,
{ dim_factor: 0.8,
time: ANIMATION_TIME,
transition: 'easeOutQuad'
});
this._coverPane.raise_top(); this._coverPane.raise_top();
this._coverPane.show(); this._coverPane.show();
@ -596,9 +530,6 @@ const Overview = new Lang.Class({
if (!this._shown) if (!this._shown)
return; return;
if (this._controlPressed)
return;
if (!this._shownTemporarily) if (!this._shownTemporarily)
this._animateNotVisible(); this._animateNotVisible();
@ -645,7 +576,7 @@ const Overview = new Lang.Class({
if (this._shown) { if (this._shown) {
if (!this._modal) { if (!this._modal) {
if (Main.pushModal(this._overview, if (Main.pushModal(this._overview,
{ keybindingMode: Shell.KeyBindingMode.OVERVIEW })) { keybindingMode: Main.KeybindingMode.OVERVIEW }))
this._modal = true; this._modal = true;
else else
this.hide(); this.hide();
@ -671,19 +602,24 @@ const Overview = new Lang.Class({
return; return;
this.animationInProgress = true; this.animationInProgress = true;
this.visibleTarget = false; this._hideInProgress = true;
this._viewSelector.zoomFromOverview(); this._viewSelector.zoomFromOverview();
// Make other elements fade out. // Make other elements fade out.
Tweener.addTween(this._stack, Tweener.addTween(this._overview,
{ opacity: 0, { opacity: 0,
transition: 'easeOutQuad', transition: 'easeOutQuad',
time: ANIMATION_TIME, time: ANIMATION_TIME,
onComplete: this._hideDone, onComplete: this._hideDone,
onCompleteScope: this onCompleteScope: this
}); });
this._unshadeBackgrounds();
Tweener.addTween(this._background,
{ dim_factor: 1.0,
time: ANIMATION_TIME,
transition: 'easeOutQuad'
});
this._coverPane.raise_top(); this._coverPane.raise_top();
this._coverPane.show(); this._coverPane.show();
@ -709,15 +645,15 @@ const Overview = new Lang.Class({
Meta.enable_unredirect_for_screen(global.screen); Meta.enable_unredirect_for_screen(global.screen);
global.window_group.show(); global.window_group.show();
global.top_window_group.show();
this._viewSelector.hide(); this._viewSelector.hide();
this._desktopFade.hide(); this._desktopFade.hide();
this._backgroundGroup.hide(); this._background.hide();
this._stack.hide(); this._overview.hide();
this.visible = false; this.visible = false;
this.animationInProgress = false; this.animationInProgress = false;
this._hideInProgress = false;
this._coverPane.hide(); this._coverPane.hide();

View File

@ -1,551 +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;
this._sources.forEach(Lang.bind(this,
function(source) {
count += source.indicatorCount;
}));
this._count = count;
this._label.text = ngettext("%d new message",
"%d new messages",
count).format(count);
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();
}
});

View File

@ -203,7 +203,7 @@ const TextShadower = new Lang.Class({
let child = children[i]; let child = children[i];
let childBox = new Clutter.ActorBox(); let childBox = new Clutter.ActorBox();
// The order of the labels here is arbitrary, except // 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 // sorts by Z order
switch (i) { switch (i) {
case 0: // top case 0: // top
@ -315,7 +315,7 @@ const AppMenuButton = new Lang.Class({
}, },
show: function() { show: function() {
if (this._visible) if (this._visible || Main.screenShield.locked)
return; return;
this._visible = true; this._visible = true;
@ -598,11 +598,6 @@ const AppMenuButton = new Lang.Class({
return; return;
menu = new PopupMenu.RemoteMenu(this.actor, this._targetApp.menu, this._targetApp.action_group); 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 { } else {
if (this.menu.isDummyQuitMenu) if (this.menu.isDummyQuitMenu)
return; return;
@ -644,7 +639,7 @@ const ActivitiesButton = new Lang.Class({
this.actor.label_actor = this._label; this.actor.label_actor = this._label;
this.hotCorner = new Layout.HotCorner(Main.layoutManager); this.hotCorner = new Layout.HotCorner();
container.add_actor(this.hotCorner.actor); container.add_actor(this.hotCorner.actor);
this.actor.connect('captured-event', Lang.bind(this, this._onCapturedEvent)); this.actor.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
@ -739,6 +734,7 @@ const ActivitiesButton = new Lang.Class({
if (pickedActor == this.actor) { if (pickedActor == this.actor) {
if (!Main.overview.visible && !Main.overview.animationInProgress) { if (!Main.overview.visible && !Main.overview.animationInProgress) {
Main.overview.showTemporarily(); Main.overview.showTemporarily();
Main.overview.beginItemDrag(actor);
} }
} }
@ -750,9 +746,12 @@ const ActivitiesButton = new Lang.Class({
const PanelCorner = new Lang.Class({ const PanelCorner = new Lang.Class({
Name: 'PanelCorner', Name: 'PanelCorner',
_init: function(side) { _init: function(box, side) {
this._side = 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 = new St.DrawingArea({ style_class: 'panel-corner' });
this.actor.connect('style-changed', Lang.bind(this, this._styleChanged)); this.actor.connect('style-changed', Lang.bind(this, this._styleChanged));
this.actor.connect('repaint', Lang.bind(this, this._repaint)); this.actor.connect('repaint', Lang.bind(this, this._repaint));
@ -808,12 +807,12 @@ const PanelCorner = new Lang.Class({
return children[index]; return children[index];
}, },
setStyleParent: function(box) { _boxStyleChanged: function() {
let side = this._side; let side = this._side;
let rtlAwareContainer = box instanceof St.BoxLayout; let rtlAwareContainer = this._box instanceof St.BoxLayout;
if (rtlAwareContainer && if (rtlAwareContainer &&
box.get_text_direction() == Clutter.TextDirection.RTL) { this._box.get_text_direction() == Clutter.TextDirection.RTL) {
if (this._side == St.Side.LEFT) if (this._side == St.Side.LEFT)
side = St.Side.RIGHT; side = St.Side.RIGHT;
else if (this._side == St.Side.RIGHT) else if (this._side == St.Side.RIGHT)
@ -822,9 +821,9 @@ const PanelCorner = new Lang.Class({
let button; let button;
if (side == St.Side.LEFT) if (side == St.Side.LEFT)
button = this._findLeftmostButton(box); button = this._findLeftmostButton(this._box);
else if (side == St.Side.RIGHT) else if (side == St.Side.RIGHT)
button = this._findRightmostButton(box); button = this._findRightmostButton(this._box);
if (button) { if (button) {
if (this._button && this._buttonStyleChangedSignalId) { if (this._button && this._buttonStyleChangedSignalId) {
@ -851,7 +850,7 @@ const PanelCorner = new Lang.Class({
// The corner doesn't support theme transitions, so override // The corner doesn't support theme transitions, so override
// the .panel-button default // the .panel-button default
button.style = 'transition-duration: 0ms'; button.style = 'transition-duration: 0';
} }
}, },
@ -864,8 +863,8 @@ const PanelCorner = new Lang.Class({
let backgroundColor = node.get_color('-panel-corner-background-color'); let backgroundColor = node.get_color('-panel-corner-background-color');
let borderColor = node.get_color('-panel-corner-border-color'); let borderColor = node.get_color('-panel-corner-border-color');
let overlap = borderColor.alpha != 0; let noOverlap = borderColor.alpha == 0;
let offsetY = overlap ? 0 : borderWidth; let offsetY = noOverlap ? borderWidth : 0;
let cr = this.actor.get_context(); let cr = this.actor.get_context();
cr.setOperator(Cairo.Operator.SOURCE); cr.setOperator(Cairo.Operator.SOURCE);
@ -889,18 +888,17 @@ const PanelCorner = new Lang.Class({
Clutter.cairo_set_source_color(cr, over); Clutter.cairo_set_source_color(cr, over);
cr.fill(); cr.fill();
if (overlap) { if (noOverlap)
let offset = borderWidth; return;
Clutter.cairo_set_source_color(cr, backgroundColor);
cr.save(); let offset = borderWidth;
cr.translate(xOffsetDirection * offset, - offset); Clutter.cairo_set_source_color(cr, backgroundColor);
cr.appendPath(savedPath);
cr.fill();
cr.restore();
}
cr.$dispose(); cr.save();
cr.translate(xOffsetDirection * offset, - offset);
cr.appendPath(savedPath);
cr.fill();
cr.restore();
}, },
_styleChanged: function() { _styleChanged: function() {
@ -919,7 +917,6 @@ const PANEL_ITEM_IMPLEMENTATIONS = {
'appMenu': AppMenuButton, 'appMenu': AppMenuButton,
'dateMenu': imports.ui.dateMenu.DateMenuButton, 'dateMenu': imports.ui.dateMenu.DateMenuButton,
'a11y': imports.ui.status.accessibility.ATIndicator, 'a11y': imports.ui.status.accessibility.ATIndicator,
'a11yGreeter': imports.ui.status.accessibility.ATGreeterIndicator,
'volume': imports.ui.status.volume.Indicator, 'volume': imports.ui.status.volume.Indicator,
'battery': imports.ui.status.power.Indicator, 'battery': imports.ui.status.power.Indicator,
'lockScreen': imports.ui.status.lockScreenMenu.Indicator, 'lockScreen': imports.ui.status.lockScreenMenu.Indicator,
@ -961,10 +958,17 @@ const Panel = new Lang.Class({
this._rightBox = new St.BoxLayout({ name: 'panelRight' }); this._rightBox = new St.BoxLayout({ name: 'panelRight' });
this.actor.add_actor(this._rightBox); 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.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.add_actor(this._rightCorner.actor);
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth)); this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
@ -972,13 +976,6 @@ const Panel = new Lang.Class({
this.actor.connect('allocate', Lang.bind(this, this._allocate)); this.actor.connect('allocate', Lang.bind(this, this._allocate));
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress)); 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.layoutManager.panelBox.add(this.actor);
Main.ctrlAltTabManager.addGroup(this.actor, _("Top Bar"), 'emblem-system-symbolic', Main.ctrlAltTabManager.addGroup(this.actor, _("Top Bar"), 'emblem-system-symbolic',
{ sortGroup: CtrlAltTab.SortGroup.TOP }); { sortGroup: CtrlAltTab.SortGroup.TOP });
@ -1065,9 +1062,6 @@ const Panel = new Lang.Class({
}, },
_onButtonPress: function(actor, event) { _onButtonPress: function(actor, event) {
if (Main.modalCount > 0)
return false;
if (event.get_source() != actor) if (event.get_source() != actor)
return false; return false;
@ -1147,13 +1141,17 @@ const Panel = new Lang.Class({
this._sessionStyle = Main.sessionMode.panelStyle; this._sessionStyle = Main.sessionMode.panelStyle;
if (this._sessionStyle) if (this._sessionStyle)
this._addStyleClassName(this._sessionStyle); this._addStyleClassName(this._sessionStyle);
},
if (this.actor.get_text_direction() == Clutter.TextDirection.RTL) { _initBox: function(elements, box) {
this._leftCorner.setStyleParent(this._rightBox); for (let i = 0; i < elements.length; i++) {
this._rightCorner.setStyleParent(this._leftBox); let role = elements[i];
} else { let constructor = PANEL_ITEM_IMPLEMENTATIONS[role];
this._leftCorner.setStyleParent(this._leftBox); if (!constructor) {
this._rightCorner.setStyleParent(this._rightBox); // panel icon is not supported (can happen for
// bluetooth or network)
continue;
}
} }
}, },

View File

@ -205,8 +205,10 @@ const Button = new Lang.Class({
// Setting the max-height won't do any good if the minimum height of the // 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 // 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 // scrollable so the minimum height is smaller than the natural height
let workArea = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex); let monitor = Main.layoutManager.primaryMonitor;
this.menu.actor.style = ('max-height: ' + Math.round(workArea.height) + 'px;'); this.menu.actor.style = ('max-height: ' +
Math.round(monitor.height - Main.panel.actor.height) +
'px;');
}, },
destroy: function() { destroy: function() {
@ -242,21 +244,18 @@ const SystemStatusButton = new Lang.Class({
this.setIcon(iconName); this.setIcon(iconName);
}, },
get icons() {
return this._box.get_children();
},
addIcon: function(gicon) { addIcon: function(gicon) {
let icon = new St.Icon({ gicon: gicon, let icon = new St.Icon({ gicon: gicon,
style_class: 'system-status-icon' }); style_class: 'system-status-icon' });
this._box.add_actor(icon); this._box.add_actor(icon);
this.emit('icons-changed');
return icon; return icon;
}, },
setIcon: function(iconName) { setIcon: function(iconName) {
// Need to first add a NULL GIcon and then set icon_name, to ensure
// compatibility with -symbolic fallbacks
if (!this.mainIcon) if (!this.mainIcon)
this.mainIcon = this.addIcon(null); this.mainIcon = this.addIcon(null);
this.mainIcon.icon_name = iconName; this.mainIcon.icon_name = iconName;

View File

@ -41,9 +41,10 @@ const PointerWatcher = new Lang.Class({
Name: 'PointerWatcher', Name: 'PointerWatcher',
_init: function() { _init: function() {
this._idleMonitor = new GnomeDesktop.IdleMonitor(); let idleMonitor = new GnomeDesktop.IdleMonitor();
this._idleMonitor.add_idle_watch(IDLE_TIME, Lang.bind(this, this._onIdleMonitorBecameIdle)); idleMonitor.connect('became-active', Lang.bind(this, this._onIdleMonitorBecameActive));
this._idle = this._idleMonitor.get_idletime() > IDLE_TIME; idleMonitor.add_watch(IDLE_TIME, Lang.bind(this, this._onIdleMonitorBecameIdle));
this._idle = idleMonitor.get_idletime() > IDLE_TIME;
this._watches = []; this._watches = [];
this.pointerX = null; this.pointerX = null;
this.pointerY = null; this.pointerY = null;
@ -86,7 +87,6 @@ const PointerWatcher = new Lang.Class({
_onIdleMonitorBecameIdle: function(monitor) { _onIdleMonitorBecameIdle: function(monitor) {
this._idle = true; this._idle = true;
this._idleMonitor.add_user_active_watch(Lang.bind(this, this._onIdleMonitorBecameActive));
this._updateTimeout(); this._updateTimeout();
}, },

View File

@ -207,7 +207,6 @@ const PopupBaseMenuItem = new Lang.Class({
color.alpha / 255); color.alpha / 255);
cr.arc(width / 2, height / 2, width / 3, 0, 2 * Math.PI); cr.arc(width / 2, height / 2, width / 3, 0, 2 * Math.PI);
cr.fill(); cr.fill();
cr.$dispose();
}, },
// This returns column widths in logical order (i.e. from the dot // This returns column widths in logical order (i.e. from the dot
@ -605,7 +604,6 @@ const PopupSliderMenuItem = new Lang.Class({
color.alpha / 255); color.alpha / 255);
cr.arc(handleX, handleY, handleRadius, 0, 2 * Math.PI); cr.arc(handleX, handleY, handleRadius, 0, 2 * Math.PI);
cr.fill(); cr.fill();
cr.$dispose();
}, },
_startDragging: function(actor, event) { _startDragging: function(actor, event) {
@ -638,34 +636,20 @@ const PopupSliderMenuItem = new Lang.Class({
return true; return true;
}, },
scroll: function(event) { _onScrollEvent: function (actor, event) {
let direction = event.get_scroll_direction(); let direction = event.get_scroll_direction();
let delta;
if (event.is_pointer_emulated())
return;
if (direction == Clutter.ScrollDirection.DOWN) { if (direction == Clutter.ScrollDirection.DOWN) {
delta = -SLIDER_SCROLL_STEP; this._value = Math.max(0, this._value - SLIDER_SCROLL_STEP);
} else if (direction == Clutter.ScrollDirection.UP) { }
delta = +SLIDER_SCROLL_STEP; else if (direction == Clutter.ScrollDirection.UP) {
} else if (direction == Clutter.ScrollDirection.SMOOTH) { this._value = Math.min(1, this._value + SLIDER_SCROLL_STEP);
let [dx, dy] = event.get_scroll_delta();
// Even though the slider is horizontal, use dy to match
// the UP/DOWN above.
delta = -dy / 10;
} }
this._value = Math.min(Math.max(0, this._value + delta), 1);
this._slider.queue_repaint(); this._slider.queue_repaint();
this.emit('value-changed', this._value); this.emit('value-changed', this._value);
}, },
_onScrollEvent: function(actor, event) {
this.scroll(event);
},
_motionEvent: function(actor, event) { _motionEvent: function(actor, event) {
let absX, absY; let absX, absY;
[absX, absY] = event.get_coords(); [absX, absY] = event.get_coords();
@ -867,6 +851,7 @@ const PopupMenuBase = new Lang.Class({
this.blockSourceEvents = false; this.blockSourceEvents = false;
this._activeMenuItem = null; this._activeMenuItem = null;
this._childMenus = [];
this._settingsActions = { }; this._settingsActions = { };
this._sessionUpdatedId = Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated)); this._sessionUpdatedId = Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
@ -888,7 +873,7 @@ const PopupMenuBase = new Lang.Class({
addSettingsAction: function(title, desktopFile) { addSettingsAction: function(title, desktopFile) {
let menuItem = this.addAction(title, function() { 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) { if (!app) {
log('Settings panel for desktop file ' + desktopFile + ' could not be loaded!'); log('Settings panel for desktop file ' + desktopFile + ' could not be loaded!');
@ -913,15 +898,29 @@ const PopupMenuBase = new Lang.Class({
}, },
isEmpty: function() { isEmpty: function() {
let hasVisibleChildren = this.box.get_children().some(function(child) { return this.box.get_n_children() == 0;
return child.visible;
});
return !hasVisibleChildren;
}, },
isChildMenu: function() { isChildMenu: function(menu) {
return false; 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); global.focus_manager.add_group(this.actor);
this.actor.reactive = true; this.actor.reactive = true;
this._childMenus = [];
}, },
_boxGetPreferredWidth: function (actor, forHeight, alloc) { _boxGetPreferredWidth: function (actor, forHeight, alloc) {
@ -1224,28 +1221,6 @@ const PopupMenu = new Lang.Class({
this._boxPointer.setSourceAlignment(alignment); 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) { open: function(animate) {
if (this.isOpen) if (this.isOpen)
return; return;
@ -1267,10 +1242,6 @@ const PopupMenu = new Lang.Class({
if (this._activeMenuItem) if (this._activeMenuItem)
this._activeMenuItem.setActive(false); this._activeMenuItem.setActive(false);
this._childMenus.forEach(function(childMenu) {
childMenu.close();
});
if (this._boxPointer.actor.visible) if (this._boxPointer.actor.visible)
this._boxPointer.hide(animate); this._boxPointer.hide(animate);
@ -1691,7 +1662,9 @@ const PopupComboBoxMenuItem = new Lang.Class({
_getTopMenu: function() { _getTopMenu: function() {
let actor = this.actor.get_parent(); let actor = this.actor.get_parent();
while (actor) { while (actor) {
if (actor._delegate && actor._delegate instanceof PopupMenu) if (actor._delegate &&
(actor._delegate instanceof PopupMenu ||
actor._delegate instanceof PopupComboMenu))
return actor._delegate; return actor._delegate;
actor = actor.get_parent(); actor = actor.get_parent();

View File

@ -1,11 +1,9 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const GdkPixbuf = imports.gi.GdkPixbuf;
const Gio = imports.gi.Gio; const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib; const GLib = imports.gi.GLib;
const Lang = imports.lang; const Lang = imports.lang;
const St = imports.gi.St; const St = imports.gi.St;
const Shell = imports.gi.Shell;
const FileUtils = imports.misc.fileUtils; const FileUtils = imports.misc.fileUtils;
const Search = imports.ui.search; const Search = imports.ui.search;
@ -123,10 +121,6 @@ function loadRemoteSearchProvider(file, info, data) {
function remoteProvidersLoaded(loadState) { function remoteProvidersLoaded(loadState) {
let searchSettings = new Gio.Settings({ schema: Search.SEARCH_PROVIDERS_SCHEMA }); let searchSettings = new Gio.Settings({ schema: Search.SEARCH_PROVIDERS_SCHEMA });
let sortOrder = searchSettings.get_strv('sort-order'); let sortOrder = searchSettings.get_strv('sort-order');
// Special case gnome-control-center to be always active and always first
sortOrder.unshift('gnome-control-center.desktop');
let numSorted = sortOrder.length; let numSorted = sortOrder.length;
loadState.loadedProviders.sort( loadState.loadedProviders.sort(
@ -141,12 +135,8 @@ function remoteProvidersLoaded(loadState) {
idxB = sortOrder.indexOf(appIdB); idxB = sortOrder.indexOf(appIdB);
// if no provider is found in the order, use alphabetical order // if no provider is found in the order, use alphabetical order
if ((idxA == -1) && (idxB == -1)) { if ((idxA == -1) && (idxB == -1))
let nameA = providerA.appInfo.get_name(); return GLib.utf8_collate(providerA.title, providerB.title);
let nameB = providerB.appInfo.get_name();
return GLib.utf8_collate(nameA, nameB);
}
if (numSorted > 1) { if (numSorted > 1) {
// if providerA is the last, it goes after everything // if providerA is the last, it goes after everything
@ -197,18 +187,18 @@ const RemoteSearchProvider = new Lang.Class({
}, },
createIcon: function(size, meta) { createIcon: function(size, meta) {
let gicon;
if (meta['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']) { } else if (meta['icon-data']) {
let [width, height, rowStride, hasAlpha, let [width, height, rowStride, hasAlpha,
bitsPerSample, nChannels, data] = meta['icon-data']; bitsPerSample, nChannels, data] = meta['icon-data'];
gicon = Shell.util_create_pixbuf_from_data(data, GdkPixbuf.Colorspace.RGB, hasAlpha, let textureCache = St.TextureCache.get_default();
bitsPerSample, width, height, rowStride); return textureCache.load_from_raw(data, hasAlpha,
width, height, rowStride, size);
} }
return new St.Icon({ gicon: gicon, return null;
icon_size: size });
}, },
_getResultsFinished: function(results, error) { _getResultsFinished: function(results, error) {
@ -225,7 +215,7 @@ const RemoteSearchProvider = new Lang.Class({
Lang.bind(this, this._getResultsFinished), Lang.bind(this, this._getResultsFinished),
this._cancellable); this._cancellable);
} catch(e) { } 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, []); this.searchSystem.pushResults(this, []);
} }
}, },
@ -238,7 +228,7 @@ const RemoteSearchProvider = new Lang.Class({
Lang.bind(this, this._getResultsFinished), Lang.bind(this, this._getResultsFinished),
this._cancellable); this._cancellable);
} catch(e) { } 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, []); this.searchSystem.pushResults(this, []);
} }
}, },
@ -255,7 +245,6 @@ const RemoteSearchProvider = new Lang.Class({
metas[i][prop] = metas[i][prop].deep_unpack(); metas[i][prop] = metas[i][prop].deep_unpack();
resultMetas.push({ id: metas[i]['id'], resultMetas.push({ id: metas[i]['id'],
name: metas[i]['name'], name: metas[i]['name'],
description: metas[i]['description'],
createIcon: Lang.bind(this, createIcon: Lang.bind(this,
this.createIcon, metas[i]) }); this.createIcon, metas[i]) });
} }
@ -270,7 +259,7 @@ const RemoteSearchProvider = new Lang.Class({
Lang.bind(this, this._getResultMetasFinished, callback), Lang.bind(this, this._getResultMetasFinished, callback),
this._cancellable); this._cancellable);
} catch(e) { } 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([]); callback([]);
} }
}, },

File diff suppressed because it is too large Load Diff

View File

@ -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();
})
});
}
});

View File

@ -60,18 +60,28 @@ const SearchSystem = new Lang.Class({
this.emit('search-updated', this._previousResults[i]); 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) { updateSearchResults: function(terms) {
if (!terms) if (!terms)
return; return;
let searchString = terms.join(' '); let isSubSearch = terms.length == this._previousTerms.length;
let previousSearchString = this._previousTerms.join(' '); if (isSubSearch) {
if (searchString == previousSearchString) for (let i = 0; i < terms.length; i++) {
return; if (terms[i].indexOf(this._previousTerms[i]) != 0) {
isSubSearch = false;
let isSubSearch = false; break;
if (this._previousTerms.length > 0) }
isSubSearch = searchString.indexOf(previousSearchString) == 0; }
}
let previousResultsArr = this._previousResults; let previousResultsArr = this._previousResults;
@ -100,6 +110,6 @@ const SearchSystem = new Lang.Class({
} }
} }
} }
} },
}); });
Signals.addSignalMethods(SearchSystem.prototype); Signals.addSignalMethods(SearchSystem.prototype);

View File

@ -17,26 +17,6 @@ const Search = imports.ui.search;
const MAX_LIST_SEARCH_RESULTS_ROWS = 3; const MAX_LIST_SEARCH_RESULTS_ROWS = 3;
const MAX_GRID_SEARCH_RESULTS_ROWS = 1; 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({ const SearchResult = new Lang.Class({
Name: 'SearchResult', Name: 'SearchResult',
@ -103,9 +83,10 @@ const ListSearchResult = new Lang.Class({
x_align: St.Align.START, x_align: St.Align.START,
y_align: St.Align.START }); y_align: St.Align.START });
// TODO: should highlight terms in the description here
if (this.metaInfo['description']) { if (this.metaInfo['description']) {
let description = new St.Label({ style_class: 'list-search-result-description' }); let description = new St.Label({ style_class: 'list-search-result-description',
description.clutter_text.set_markup(this.metaInfo['description']); text: '"' + this.metaInfo['description'] + '"' });
details.add(description, { x_fill: false, details.add(description, { x_fill: false,
y_fill: false, y_fill: false,
x_align: St.Align.START, x_align: St.Align.START,
@ -197,7 +178,7 @@ const ListSearchResults = new Lang.Class({
this._content = new St.BoxLayout({ style_class: 'list-search-results', this._content = new St.BoxLayout({ style_class: 'list-search-results',
vertical: true }); vertical: true });
this.actor.add(this._content, { expand: true }); this.actor.add_actor(this._content);
this._notDisplayedResult = []; this._notDisplayedResult = [];
this._terms = []; this._terms = [];
@ -320,20 +301,12 @@ const SearchResults = new Lang.Class({
this._content = new St.BoxLayout({ name: 'searchResultsContent', this._content = new St.BoxLayout({ name: 'searchResultsContent',
vertical: true }); 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, this._scrollView = new St.ScrollView({ x_fill: true,
y_fill: false, y_fill: false,
overlay_scrollbars: true, style_class: 'vfade' });
style_class: 'search-display vfade' });
this._scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC); 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 }); let action = new Clutter.PanAction({ interpolate: true });
action.connect('pan', Lang.bind(this, this._onPan)); action.connect('pan', Lang.bind(this, this._onPan));
this._scrollView.add_action(action); this._scrollView.add_action(action);
@ -423,15 +396,18 @@ const SearchResults = new Lang.Class({
this._searchSystem.reset(); this._searchSystem.reset();
this._statusBin.hide(); this._statusBin.hide();
this._clearDisplay(); this._clearDisplay();
this._defaultResult = null;
}, },
startingSearch: function() { startingSearch: function() {
this.reset(); this.reset();
this._statusText.set_text(_("Searching")); this._statusText.set_text(_("Searching..."));
this._statusBin.show(); this._statusBin.show();
}, },
doSearch: function (searchString) {
this._searchSystem.updateSearch(searchString);
},
_metaForProvider: function(provider) { _metaForProvider: function(provider) {
return this._providerMeta[this._providers.indexOf(provider)]; return this._providerMeta[this._providers.indexOf(provider)];
}, },

View File

@ -30,6 +30,5 @@ const HorizontalSeparator = new Lang.Class({
cr.setSource(pattern); cr.setSource(pattern);
cr.rectangle(margin, gradientOffset, gradientWidth, gradientHeight); cr.rectangle(margin, gradientOffset, gradientWidth, gradientHeight);
cr.fill(); cr.fill();
cr.$dispose();
} }
}); });

View File

@ -15,7 +15,6 @@ const DEFAULT_MODE = 'restrictive';
const _modes = { const _modes = {
'restrictive': { 'restrictive': {
parentMode: null, parentMode: null,
stylesheetName: 'gnome-shell.css',
hasOverview: false, hasOverview: false,
showCalendarEvents: false, showCalendarEvents: false,
allowSettings: false, allowSettings: false,
@ -47,7 +46,7 @@ const _modes = {
panel: { panel: {
left: ['logo'], left: ['logo'],
center: ['dateMenu'], center: ['dateMenu'],
right: ['a11yGreeter', 'display', 'keyboard', right: ['a11y', 'display', 'keyboard',
'volume', 'battery', 'powerMenu'] 'volume', 'battery', 'powerMenu']
}, },
panelStyle: 'login-screen' panelStyle: 'login-screen'
@ -84,7 +83,7 @@ const _modes = {
panel: { panel: {
left: [], left: [],
center: ['dateMenu'], center: ['dateMenu'],
right: ['a11yGreeter', 'keyboard', 'volume'] right: ['a11y', 'keyboard', 'volume']
} }
}, },

View File

@ -1,18 +1,16 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Lang = imports.lang;
const Gio = imports.gi.Gio; const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib; const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const Config = imports.misc.config; const Config = imports.misc.config;
const ExtensionSystem = imports.ui.extensionSystem; const ExtensionSystem = imports.ui.extensionSystem;
const ExtensionDownloader = imports.ui.extensionDownloader; const ExtensionDownloader = imports.ui.extensionDownloader;
const ExtensionUtils = imports.misc.extensionUtils; const ExtensionUtils = imports.misc.extensionUtils;
const Hash = imports.misc.hash; const Flashspot = imports.ui.flashspot;
const Main = imports.ui.main; const Main = imports.ui.main;
const Screenshot = imports.ui.screenshot;
const GnomeShellIface = <interface name="org.gnome.Shell"> const GnomeShellIface = <interface name="org.gnome.Shell">
<method name="Eval"> <method name="Eval">
@ -20,26 +18,34 @@ const GnomeShellIface = <interface name="org.gnome.Shell">
<arg type="b" direction="out" name="success" /> <arg type="b" direction="out" name="success" />
<arg type="s" direction="out" name="result" /> <arg type="s" direction="out" name="result" />
</method> </method>
<method name="ShowOSD"> <method name="ScreenshotArea">
<arg type="a{sv}" direction="in" name="params"/> <arg type="i" direction="in" name="x"/>
</method> <arg type="i" direction="in" name="y"/>
<method name="GrabAccelerator"> <arg type="i" direction="in" name="width"/>
<arg type="s" direction="in" name="accelerator"/> <arg type="i" direction="in" name="height"/>
<arg type="u" direction="in" name="flags"/> <arg type="b" direction="in" name="flash"/>
<arg type="u" direction="out" name="action"/> <arg type="s" direction="in" name="filename"/>
</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"/>
<arg type="b" direction="out" name="success"/> <arg type="b" direction="out" name="success"/>
</method> </method>
<signal name="AcceleratorActivated"> <method name="ScreenshotWindow">
<arg name="action" type="u" /> <arg type="b" direction="in" name="include_frame"/>
<arg name="deviceid" type="u" /> <arg type="b" direction="in" name="include_cursor"/>
</signal> <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="Mode" type="s" access="read" />
<property name="OverviewActive" type="b" access="readwrite" /> <property name="OverviewActive" type="b" access="readwrite" />
<property name="ShellVersion" type="s" access="read" /> <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 = Gio.DBusExportedObject.wrapJSObject(GnomeShellIface, this);
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell'); this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell');
this._extensionsService = new GnomeShellExtensions(); this._extensionsSerivce = 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);
}));
}, },
/** /**
@ -114,101 +111,81 @@ const GnomeShell = new Lang.Class({
return [success, returnValue]; return [success, returnValue];
}, },
ShowOSD: function(params) { _onScreenshotComplete: function(obj, result, area, flash, invocation) {
for (let param in params) if (flash && result) {
params[param] = params[param].deep_unpack(); let flashspot = new Flashspot.Flashspot(area);
flashspot.fire();
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);
} }
return bindingAction; let retval = GLib.Variant.new('(b)', [result]);
invocation.return_value(retval);
}, },
_ungrabAccelerator: function(action) { /**
let ungrabSucceeded = global.display.ungrab_accelerator(action); * ScreenshotArea:
if (ungrabSucceeded) * @x: The X coordinate of the area
this._grabbedAccelerators.delete(action); * @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(); * ScreenshotWindow:
for (let i = 0; i < grabs.length; i++) { * @include_frame: Whether to include the frame or not
let [action, sender] = grabs[i]; * @include_cursor: Whether to include the cursor image or not
if (sender == name) * @flash: Whether to flash the window area or not
this._ungrabAccelerator(action); * @filename: The filename for the screenshot
} *
Gio.bus_unwatch_name(this._grabbers.get(name)); * Takes a screenshot of the focused window (optionally omitting the frame)
this._grabbers.delete(name); * 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, Mode: global.session_mode,
@ -373,8 +350,8 @@ const ScreenSaverDBus = new Lang.Class({
this.parent(); this.parent();
this._screenShield = screenShield; this._screenShield = screenShield;
screenShield.connect('active-changed', Lang.bind(this, function(shield) { screenShield.connect('lock-status-changed', Lang.bind(this, function(shield) {
this._dbusImpl.emit_signal('ActiveChanged', GLib.Variant.new('(b)', [shield.active])); this._dbusImpl.emit_signal('ActiveChanged', GLib.Variant.new('(b)', [shield.locked]));
})); }));
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(ScreenSaverIface, this); this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(ScreenSaverIface, this);
@ -395,13 +372,13 @@ const ScreenSaverDBus = new Lang.Class({
SetActive: function(active) { SetActive: function(active) {
if (active) if (active)
this._screenShield.activate(true); this._screenShield.lock(true);
else else
this._screenShield.unlock(false); this._screenShield.unlock();
}, },
GetActive: function() { GetActive: function() {
return this._screenShield.active; return this._screenShield.locked;
}, },
GetActiveTime: function() { GetActiveTime: function() {

View File

@ -1,11 +1,8 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const Gtk = imports.gi.Gtk; const Gtk = imports.gi.Gtk;
const Lang = imports.lang; const Lang = imports.lang;
const St = imports.gi.St; const St = imports.gi.St;
const BoxPointer = imports.ui.boxpointer;
const Main = imports.ui.main; const Main = imports.ui.main;
const Params = imports.misc.params; const Params = imports.misc.params;
const PopupMenu = imports.ui.popupMenu; const PopupMenu = imports.ui.popupMenu;
@ -68,33 +65,32 @@ const EntryMenu = new Lang.Class({
} }
}, },
open: function(animate) { open: function() {
this._updatePasteItem(); this._updatePasteItem();
this._updateCopyItem(); this._updateCopyItem();
if (this._passwordItem) if (this._passwordItem)
this._updatePasswordItem(); this._updatePasswordItem();
this.parent(animate);
this._entry.add_style_pseudo_class('focus');
let direction = Gtk.DirectionType.TAB_FORWARD; let direction = Gtk.DirectionType.TAB_FORWARD;
if (!this.actor.navigate_focus(null, direction, false)) if (!this.actor.navigate_focus(null, direction, false))
this.actor.grab_key_focus(); 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._entry.grab_key_focus();
this.parent(animate); this.parent();
}, },
_updateCopyItem: function() { _updateCopyItem: function() {
let selection = this._entry.clutter_text.get_selection(); let selection = this._entry.clutter_text.get_selection();
this._copyItem.setSensitive(!this._entry.clutter_text.password_char && this._copyItem.setSensitive(selection && selection != '');
selection && selection != '');
}, },
_updatePasteItem: function() { _updatePasteItem: function() {
this._clipboard.get_text(St.ClipboardType.CLIPBOARD, Lang.bind(this, this._clipboard.get_text(Lang.bind(this,
function(clipboard, text) { function(clipboard, text) {
this._pasteItem.setSensitive(text && text != ''); this._pasteItem.setSensitive(text && text != '');
})); }));
@ -110,11 +106,11 @@ const EntryMenu = new Lang.Class({
_onCopyActivated: function() { _onCopyActivated: function() {
let selection = this._entry.clutter_text.get_selection(); let selection = this._entry.clutter_text.get_selection();
this._clipboard.set_text(St.ClipboardType.CLIPBOARD, selection); this._clipboard.set_text(selection);
}, },
_onPasteActivated: function() { _onPasteActivated: function() {
this._clipboard.get_text(St.ClipboardType.CLIPBOARD, Lang.bind(this, this._clipboard.get_text(Lang.bind(this,
function(clipboard, text) { function(clipboard, text) {
if (!text) if (!text)
return; return;
@ -138,12 +134,12 @@ function _setMenuAlignment(entry, stageX) {
function _onButtonPressEvent(actor, event, entry) { function _onButtonPressEvent(actor, event, entry) {
if (entry.menu.isOpen) { if (entry.menu.isOpen) {
entry.menu.close(BoxPointer.PopupAnimation.FULL); entry.menu.close();
return true; return true;
} else if (event.get_button() == 3) { } else if (event.get_button() == 3) {
let [stageX, stageY] = event.get_coords(); let [stageX, stageY] = event.get_coords();
_setMenuAlignment(entry, stageX); _setMenuAlignment(entry, stageX);
entry.menu.open(BoxPointer.PopupAnimation.FULL); entry.menu.open();
return true; return true;
} }
return false; return false;
@ -153,7 +149,7 @@ function _onPopup(actor, entry) {
let [success, textX, textY, lineHeight] = entry.clutter_text.position_to_coords(-1); let [success, textX, textY, lineHeight] = entry.clutter_text.position_to_coords(-1);
if (success) if (success)
entry.menu.setSourceAlignment(textX / entry.width); entry.menu.setSourceAlignment(textX / entry.width);
entry.menu.open(BoxPointer.PopupAnimation.FULL); entry.menu.open();
}; };
function addContextMenu(entry, params) { function addContextMenu(entry, params) {

View File

@ -2,7 +2,6 @@
const Gio = imports.gi.Gio; const Gio = imports.gi.Gio;
const Lang = imports.lang; const Lang = imports.lang;
const Mainloop = imports.mainloop;
const PanelMenu = imports.ui.panelMenu; const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu; 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 APPLICATIONS_SCHEMA = 'org.gnome.desktop.a11y.applications';
const DPI_LOW_REASONABLE_VALUE = 50;
const DPI_HIGH_REASONABLE_VALUE = 500;
const DPI_FACTOR_LARGE = 1.25; const DPI_FACTOR_LARGE = 1.25;
const DPI_FACTOR_LARGER = 1.5;
const DPI_FACTOR_LARGEST = 2.0;
const WM_SCHEMA = 'org.gnome.desktop.wm.preferences'; const WM_SCHEMA = 'org.gnome.desktop.wm.preferences';
const KEY_VISUAL_BELL = 'visual-bell'; const KEY_VISUAL_BELL = 'visual-bell';
@ -70,25 +74,6 @@ const ATIndicator = new Lang.Class({
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addSettingsAction(_("Universal Access Settings"), 'gnome-universal-access-panel.desktop'); 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) { _buildItemExtended: function(string, initial_value, writable, on_set) {
@ -110,11 +95,9 @@ const ATIndicator = new Lang.Class({
function(enabled) { function(enabled) {
return settings.set_boolean(key, 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)); widget.setToggleState(settings.get_boolean(key));
});
this._queueSyncMenuVisibility();
}));
return widget; return widget;
}, },
@ -146,7 +129,7 @@ const ATIndicator = new Lang.Class({
wmSettings.reset(KEY_WM_THEME); 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); let value = interfaceSettings.get_string(KEY_GTK_THEME);
if (value == HIGH_CONTRAST_THEME) { if (value == HIGH_CONTRAST_THEME) {
highContrast.setToggleState(true); highContrast.setToggleState(true);
@ -154,9 +137,7 @@ const ATIndicator = new Lang.Class({
highContrast.setToggleState(false); highContrast.setToggleState(false);
gtkTheme = value; gtkTheme = value;
} }
});
this._queueSyncMenuVisibility();
}));
interfaceSettings.connect('changed::' + KEY_ICON_THEME, function() { interfaceSettings.connect('changed::' + KEY_ICON_THEME, function() {
let value = interfaceSettings.get_string(KEY_ICON_THEME); let value = interfaceSettings.get_string(KEY_ICON_THEME);
if (value != HIGH_CONTRAST_THEME) if (value != HIGH_CONTRAST_THEME)
@ -185,22 +166,11 @@ const ATIndicator = new Lang.Class({
else else
settings.reset(KEY_TEXT_SCALING_FACTOR); 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 factor = settings.get_double(KEY_TEXT_SCALING_FACTOR);
let active = (factor > 1.0); let active = (factor > 1.0);
widget.setToggleState(active); widget.setToggleState(active);
});
this._queueSyncMenuVisibility();
}));
return widget; return widget;
} }
}); });
const ATGreeterIndicator = new Lang.Class({
Name: 'ATGreeterIndicator',
Extends: ATIndicator,
// Override visibility handling to be always visible
_syncMenuVisibility: function() { },
_queueSyncMenuVisibility: function() { }
});

View File

@ -9,7 +9,6 @@ const St = imports.gi.St;
const Main = imports.ui.main; const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray; const MessageTray = imports.ui.messageTray;
const NotificationDaemon = imports.ui.notificationDaemon;
const PanelMenu = imports.ui.panelMenu; const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu; const PopupMenu = imports.ui.popupMenu;
@ -56,8 +55,8 @@ const Indicator = new Lang.Class({
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._fullMenuItems = [new PopupMenu.PopupSeparatorMenuItem(), this._fullMenuItems = [new PopupMenu.PopupSeparatorMenuItem(),
new PopupMenu.PopupMenuItem(_("Send Files to Device")), new PopupMenu.PopupMenuItem(_("Send Files to Device...")),
new PopupMenu.PopupMenuItem(_("Set Up a New Device")), new PopupMenu.PopupMenuItem(_("Set up a New Device...")),
new PopupMenu.PopupSeparatorMenuItem()]; new PopupMenu.PopupSeparatorMenuItem()];
this._hasDevices = false; this._hasDevices = false;
@ -236,7 +235,7 @@ const Indicator = new Lang.Class({
} }
if (device.capabilities & GnomeBluetoothApplet.Capabilities.OBEX_PUSH) { 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); this._applet.send_to_address(device.bdaddr, device.alias);
})); }));
} }
@ -287,7 +286,6 @@ const Indicator = new Lang.Class({
_ensureSource: function() { _ensureSource: function() {
if (!this._source) { if (!this._source) {
this._source = new MessageTray.Source(_("Bluetooth"), 'bluetooth-active'); this._source = new MessageTray.Source(_("Bluetooth"), 'bluetooth-active');
this._source.policy = new NotificationDaemon.NotificationApplicationPolicy('gnome-bluetooth-panel');
Main.messageTray.add(this._source); Main.messageTray.add(this._source);
} }
}, },
@ -448,5 +446,10 @@ const PinNotification = new Lang.Class({
return this._entry.clutter_text.text.length == 6; return this._entry.clutter_text.text.length == 6;
else else
return true; return true;
},
grabFocus: function(lockTray) {
this.parent(lockTray);
global.stage.set_key_focus(this._entry);
} }
}); });

View File

@ -115,14 +115,9 @@ const IBusManager = new Lang.Class({
this._panelService.connect('update-property', Lang.bind(this, this._updateProperty)); this._panelService.connect('update-property', Lang.bind(this, this._updateProperty));
// If an engine is already active we need to get its properties // 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) { this._ibus.get_global_engine_async(-1, null, Lang.bind(this, function(i, result) {
let engine; let engine = this._ibus.get_global_engine_async_finish(result);
try { if (!engine)
engine = this._ibus.get_global_engine_async_finish(result);
if (!engine)
return;
} catch(e) {
return; return;
}
this._engineChanged(this._ibus, engine.get_name()); this._engineChanged(this._ibus, engine.get_name());
})); }));
this._updateReadiness(); this._updateReadiness();
@ -337,14 +332,14 @@ const InputSourceIndicator = new Lang.Class({
Main.wm.addKeybinding('switch-input-source', Main.wm.addKeybinding('switch-input-source',
new Gio.Settings({ schema: "org.gnome.desktop.wm.keybindings" }), new Gio.Settings({ schema: "org.gnome.desktop.wm.keybindings" }),
Meta.KeyBindingFlags.REVERSES, Meta.KeyBindingFlags.REVERSES,
Shell.KeyBindingMode.ALL, Main.KeybindingMode.ALL,
Lang.bind(this, this._switchInputSource)); Lang.bind(this, this._switchInputSource));
this._keybindingActionBackward = this._keybindingActionBackward =
Main.wm.addKeybinding('switch-input-source-backward', Main.wm.addKeybinding('switch-input-source-backward',
new Gio.Settings({ schema: "org.gnome.desktop.wm.keybindings" }), new Gio.Settings({ schema: "org.gnome.desktop.wm.keybindings" }),
Meta.KeyBindingFlags.REVERSES | Meta.KeyBindingFlags.REVERSES |
Meta.KeyBindingFlags.REVERSED, Meta.KeyBindingFlags.REVERSED,
Shell.KeyBindingMode.ALL, Main.KeybindingMode.ALL,
Lang.bind(this, this._switchInputSource)); Lang.bind(this, this._switchInputSource));
this._settings = new Gio.Settings({ schema: DESKTOP_INPUT_SOURCES_SCHEMA }); this._settings = new Gio.Settings({ schema: DESKTOP_INPUT_SOURCES_SCHEMA });
this._settings.connect('changed::' + KEY_CURRENT_INPUT_SOURCE, Lang.bind(this, this._currentInputSourceChanged)); this._settings.connect('changed::' + KEY_CURRENT_INPUT_SOURCE, Lang.bind(this, this._currentInputSourceChanged));
@ -370,14 +365,7 @@ const InputSourceIndicator = new Lang.Class({
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated)); Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
this._sessionUpdated(); this._sessionUpdated();
this.menu.addSettingsAction(_("Region & Language Settings"), 'gnome-region-panel.desktop'); this.menu.addSettingsAction(_("Region and 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();
}, },
_sessionUpdated: function() { _sessionUpdated: function() {
@ -398,9 +386,6 @@ const InputSourceIndicator = new Lang.Class({
}, },
_switchInputSource: function(display, screen, window, binding) { _switchInputSource: function(display, screen, window, binding) {
if (this._mruSources.length < 2)
return;
let popup = new InputSourcePopup(this._mruSources, this._keybindingAction, this._keybindingActionBackward); let popup = new InputSourcePopup(this._mruSources, this._keybindingAction, this._keybindingActionBackward);
let modifiers = binding.get_modifiers(); let modifiers = binding.get_modifiers();
let backwards = modifiers & Meta.VirtualModifier.SHIFT_MASK; let backwards = modifiers & Meta.VirtualModifier.SHIFT_MASK;
@ -413,9 +398,6 @@ const InputSourceIndicator = new Lang.Class({
let newSourceIndex = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE); let newSourceIndex = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE);
let newSource = this._inputSources[newSourceIndex]; let newSource = this._inputSources[newSourceIndex];
let oldSource;
[oldSource, this._currentSource] = [this._currentSource, newSource];
if (!newSource || (nVisibleSources < 2 && !newSource.properties)) { if (!newSource || (nVisibleSources < 2 && !newSource.properties)) {
// This source index might be invalid if we weren't able // This source index might be invalid if we weren't able
// to build a menu item for it, so we hide ourselves since // to build a menu item for it, so we hide ourselves since
@ -430,13 +412,16 @@ const InputSourceIndicator = new Lang.Class({
this.actor.show(); this.actor.show();
let oldSource;
[oldSource, this._currentSource] = [this._currentSource, newSource];
if (oldSource) { if (oldSource) {
oldSource.menuItem.setShowDot(false); oldSource.menuItem.setShowDot(false);
oldSource.indicatorLabel.hide(); this._container.set_skip_paint(oldSource.indicatorLabel, true);
} }
newSource.menuItem.setShowDot(true); newSource.menuItem.setShowDot(true);
newSource.indicatorLabel.show(); this._container.set_skip_paint(newSource.indicatorLabel, false);
this._buildPropSection(newSource.properties); this._buildPropSection(newSource.properties);
@ -446,8 +431,6 @@ const InputSourceIndicator = new Lang.Class({
this._mruSources = currentSource.concat(this._mruSources); this._mruSources = currentSource.concat(this._mruSources);
break; break;
} }
this._changePerWindowSource();
}, },
_inputSourcesChanged: function() { _inputSourcesChanged: function() {
@ -487,8 +470,6 @@ const InputSourceIndicator = new Lang.Class({
let is = new InputSource(type, id, displayName, shortName, i); let is = new InputSource(type, id, displayName, shortName, i);
is.connect('activate', Lang.bind(this, function() { is.connect('activate', Lang.bind(this, function() {
if (this._currentSource.index == is.index)
return;
this._settings.set_value(KEY_CURRENT_INPUT_SOURCE, this._settings.set_value(KEY_CURRENT_INPUT_SOURCE,
GLib.Variant.new_uint32(is.index)); GLib.Variant.new_uint32(is.index));
})); }));
@ -513,8 +494,8 @@ const InputSourceIndicator = new Lang.Class({
this.menu.addMenuItem(is.menuItem, menuIndex++); this.menu.addMenuItem(is.menuItem, menuIndex++);
is.indicatorLabel.hide();
this._container.add_actor(is.indicatorLabel); this._container.add_actor(is.indicatorLabel);
this._container.set_skip_paint(is.indicatorLabel, true);
} }
let sourcesList = []; let sourcesList = [];
@ -721,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) { _containerGetPreferredWidth: function(container, for_height, alloc) {
// Here, and in _containerGetPreferredHeight, we need to query // Here, and in _containerGetPreferredHeight, we need to query
// for the height of all children, but we ignore the results // for the height of all children, but we ignore the results

View File

@ -10,31 +10,6 @@ const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu; const PopupMenu = imports.ui.popupMenu;
const VolumeMenu = imports.ui.status.volume; 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({ const Indicator = new Lang.Class({
Name: 'LockScreenMenuIndicator', Name: 'LockScreenMenuIndicator',
Extends: PanelMenu.SystemStatusButton, Extends: PanelMenu.SystemStatusButton,
@ -43,20 +18,41 @@ const Indicator = new Lang.Class({
this.parent(null, _("Volume, network, battery")); this.parent(null, _("Volume, network, battery"));
this._box.style_class = 'lock-screen-status-button-box'; this._box.style_class = 'lock-screen-status-button-box';
this._volumeControl = VolumeMenu.getMixerControl(); this._volume = Main.panel.statusArea.volume;
this._volumeMenu = new VolumeMenu.VolumeMenu(this._volumeControl); if (this._volume) {
this.menu.addMenuItem(this._volumeMenu); 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._volumeControl = VolumeMenu.getMixerControl();
this._box.add_child(this._volume.actor); this._volumeMenu = new VolumeMenu.VolumeMenu(this._volumeControl);
this.menu.addMenuItem(this._volumeMenu);
// 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._battery = new FakeStatusIcon(Main.panel.statusArea.battery); this._network = Main.panel.statusArea.network;
this._box.add_child(this._battery.actor); 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);
}
} }
}); });

View File

@ -8,26 +8,16 @@ const NMClient = imports.gi.NMClient;
const Signals = imports.signals; const Signals = imports.signals;
const St = imports.gi.St; 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 Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu; const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu; const PopupMenu = imports.ui.popupMenu;
const MessageTray = imports.ui.messageTray; const MessageTray = imports.ui.messageTray;
const NotificationDaemon = imports.ui.notificationDaemon;
const ModemManager = imports.misc.modemManager; const ModemManager = imports.misc.modemManager;
const Util = imports.misc.util; const Util = imports.misc.util;
const NMConnectionCategory = { const NMConnectionCategory = {
INVALID: 'invalid', INVALID: 'invalid',
WIRED: 'wired', WIRED: 'wired',
VIRTUAL: 'virtual',
WIRELESS: 'wireless', WIRELESS: 'wireless',
WWAN: 'wwan', WWAN: 'wwan',
VPN: 'vpn' VPN: 'vpn'
@ -49,7 +39,7 @@ const NM80211ApFlags = NetworkManager['80211ApFlags'];
const NM80211ApSecurityFlags = NetworkManager['80211ApSecurityFlags']; const NM80211ApSecurityFlags = NetworkManager['80211ApSecurityFlags'];
// number of wireless networks that should be visible // 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; const NUM_VISIBLE_NETWORKS = 5;
function macToArray(string) { 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({ const NMWirelessSectionTitleMenuItem = new Lang.Class({
Name: 'NMWirelessSectionTitleMenuItem', Name: 'NMWirelessSectionTitleMenuItem',
Extends: PopupMenu.PopupSwitchMenuItem, Extends: PopupMenu.PopupSwitchMenuItem,
@ -309,16 +339,19 @@ const NMDevice = new Lang.Class({
Extends: NMConnectionBased, Extends: NMConnectionBased,
_init: function(client, device, connections) { _init: function(client, device, connections) {
this._client = client; this.device = device;
this._setDevice(device); this.device._delegate = this;
this.parent(connections); this._stateChangedId = this.device.connect('state-changed', Lang.bind(this, this._deviceStateChanged));
// protected
this._client = client;
this.parent(connections);
this._activeConnection = null; this._activeConnection = null;
this._activeConnectionItem = null; this._activeConnectionItem = null;
this._autoConnectionItem = null; this._autoConnectionItem = null;
this._overflowItem = 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) { this._statusChanged = this.statusItem.connect('toggled', Lang.bind(this, function(item, state) {
let ok; let ok;
if (state) if (state)
@ -337,12 +370,23 @@ const NMDevice = new Lang.Class({
}, },
destroy: function() { destroy: function() {
this._setDevice(null); if (this.device)
this.device._delegate = null;
if (this._deferredWorkId) { if (this._stateChangedId) {
// Just clear out, the actual removal is handled when the // Need to go through GObject.Object.prototype because
// actor is destroyed // nm_device_disconnect conflicts with g_signal_disconnect
this._deferredWorkId = 0; 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(); this._clearSection();
@ -351,33 +395,6 @@ const NMDevice = new Lang.Class({
this.section.destroy(); 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() { deactivate: function() {
this.device.disconnect(null); this.device.disconnect(null);
return true; return true;
@ -392,7 +409,7 @@ const NMDevice = new Lang.Class({
// Otherwise, if no connection is currently configured, // Otherwise, if no connection is currently configured,
// try automatic configuration (or summon the config dialog) // try automatic configuration (or summon the config dialog)
if (this._connections.length == 1) { 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; return true;
} else if (this._connections.length == 0) { } else if (this._connections.length == 0) {
return this._activateAutomaticConnection(); return this._activateAutomaticConnection();
@ -412,7 +429,7 @@ const NMDevice = new Lang.Class({
}, },
get connected() { get connected() {
return this.device && this.device.state == NetworkManager.DeviceState.ACTIVATED; return this.device.state == NetworkManager.DeviceState.ACTIVATED;
}, },
clearActiveConnection: function(activeConnection) { clearActiveConnection: function(activeConnection) {
@ -432,6 +449,7 @@ const NMDevice = new Lang.Class({
this._activeConnection = activeConnection; this._activeConnection = activeConnection;
this._clearSection();
this._queueCreateSection(); this._queueCreateSection();
}, },
@ -445,9 +463,6 @@ const NMDevice = new Lang.Class({
}, },
getStatusLabel: function() { getStatusLabel: function() {
if (!this.device)
return null;
switch(this.device.state) { switch(this.device.state) {
case NetworkManager.DeviceState.DISCONNECTED: case NetworkManager.DeviceState.DISCONNECTED:
case NetworkManager.DeviceState.ACTIVATED: 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 // protected
_createAutomaticConnection: function() { _createAutomaticConnection: function() {
throw new TypeError('Invoking pure virtual function NMDevice.createAutomaticConnection'); throw new TypeError('Invoking pure virtual function NMDevice.createAutomaticConnection');
}, },
_queueCreateSection: function() { _queueCreateSection: function() {
if (this._deferredWorkId) { this._clearSection();
this._clearSection(); Main.queueDeferredWork(this._deferredWorkId);
Main.queueDeferredWork(this._deferredWorkId);
}
}, },
_clearSection: function() { _clearSection: function() {
@ -549,7 +557,7 @@ const NMDevice = new Lang.Class({
if (j + activeOffset >= NUM_VISIBLE_NETWORKS) { if (j + activeOffset >= NUM_VISIBLE_NETWORKS) {
if (!this._overflowItem) { if (!this._overflowItem) {
this._overflowItem = new PopupMenu.PopupSubMenuMenuItem(_("More")); this._overflowItem = new PopupMenu.PopupSubMenuMenuItem(_("More..."));
this.section.addMenuItem(this._overflowItem); this.section.addMenuItem(this._overflowItem);
} }
this._overflowItem.menu.addMenuItem(obj.item); this._overflowItem.menu.addMenuItem(obj.item);
@ -611,6 +619,7 @@ const NMDevice = new Lang.Class({
this._updateStatusItem(); this._updateStatusItem();
this._clearSection();
this._queueCreateSection(); this._queueCreateSection();
this.emit('state-changed'); this.emit('state-changed');
}, },
@ -634,6 +643,25 @@ const NMDevice = new Lang.Class({
this.statusItem.setStatus(this.getStatusLabel()); this.statusItem.setStatus(this.getStatusLabel());
this.emit('state-changed'); 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, Extends: NMDeviceSimple,
_init: function(client, device, connections) { _init: function(client, device, connections) {
device._description = _("Wired");
this._autoConnectionName = _("Auto Ethernet"); this._autoConnectionName = _("Auto Ethernet");
this.category = NMConnectionCategory.WIRED; this.category = NMConnectionCategory.WIRED;
@ -692,24 +719,12 @@ const NMDeviceModem = new Lang.Class({
_init: function(client, device, connections) { _init: function(client, device, connections) {
let is_wwan = false; let is_wwan = false;
device._description = _("Mobile broadband");
this._enabled = true; this._enabled = true;
this.mobileDevice = null; this.mobileDevice = null;
this._connectionType = 'ppp'; this._connectionType = 'ppp';
this._capabilities = device.current_capabilities; this._capabilities = device.current_capabilities;
// Support new ModemManager1 devices if (this._capabilities & NetworkManager.DeviceModemCapabilities.GSM_UMTS) {
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) {
is_wwan = true; is_wwan = true;
this.mobileDevice = new ModemManager.ModemGsm(device.udi); this.mobileDevice = new ModemManager.ModemGsm(device.udi);
this._connectionType = NetworkManager.SETTING_GSM_SETTING_NAME; this._connectionType = NetworkManager.SETTING_GSM_SETTING_NAME;
@ -719,8 +734,7 @@ const NMDeviceModem = new Lang.Class({
this._connectionType = NetworkManager.SETTING_CDMA_SETTING_NAME; this._connectionType = NetworkManager.SETTING_CDMA_SETTING_NAME;
} else if (this._capabilities & NetworkManager.DeviceModemCapabilities.LTE) { } else if (this._capabilities & NetworkManager.DeviceModemCapabilities.LTE) {
is_wwan = true; is_wwan = true;
this.mobileDevice = new ModemManager.ModemGsm(device.udi); // FIXME: support signal quality
this._connectionType = NetworkManager.SETTING_GSM_SETTING_NAME;
} }
if (is_wwan) { if (is_wwan) {
@ -823,7 +837,6 @@ const NMDeviceBluetooth = new Lang.Class({
Extends: NMDevice, Extends: NMDevice,
_init: function(client, device, connections) { _init: function(client, device, connections) {
device._description = _("Bluetooth");
this._autoConnectionName = this._makeConnectionName(device); this._autoConnectionName = this._makeConnectionName(device);
device.connect('notify::name', Lang.bind(this, this._updateAutoConnectionName)); device.connect('notify::name', Lang.bind(this, this._updateAutoConnectionName));
@ -866,8 +879,13 @@ const NMDeviceBluetooth = new Lang.Class({
_updateAutoConnectionName: function() { _updateAutoConnectionName: function() {
this._autoConnectionName = this._makeConnectionName(this.device); this._autoConnectionName = this._makeConnectionName(this.device);
this._clearSection();
this._queueCreateSection(); this._queueCreateSection();
this._updateStatusItem(); this._updateStatusItem();
},
_getDescription: function() {
return this.device.name || _("Bluetooth");
} }
}); });
@ -1127,8 +1145,10 @@ const NMDeviceWireless = new Lang.Class({
this._networks.splice(res.network, 1); this._networks.splice(res.network, 1);
let newPos = Util.insertSorted(this._networks, network, Lang.bind(this, this._networkSortFunction)); let newPos = Util.insertSorted(this._networks, network, Lang.bind(this, this._networkSortFunction));
if (newPos != res.network) if (newPos != res.network) {
this._clearSection();
this._queueCreateSection(); this._queueCreateSection();
}
}, },
_accessPointAdded: function(device, accessPoint) { _accessPointAdded: function(device, accessPoint) {
@ -1181,8 +1201,10 @@ const NMDeviceWireless = new Lang.Class({
let newPos = Util.insertSorted(this._networks, apObj, this._networkSortFunction); let newPos = Util.insertSorted(this._networks, apObj, this._networkSortFunction);
// Queue an update of the UI if we changed the order // Queue an update of the UI if we changed the order
if (newPos != pos) if (newPos != pos) {
this._clearSection();
this._queueCreateSection(); this._queueCreateSection();
}
}, },
_accessPointRemoved: function(device, accessPoint) { _accessPointRemoved: function(device, accessPoint) {
@ -1242,10 +1264,12 @@ const NMDeviceWireless = new Lang.Class({
if (res.network < this._networks.length-1) if (res.network < this._networks.length-1)
okNext = this._networkSortFunction(this._networks[res.network + 1], apObj) <= 0; okNext = this._networkSortFunction(this._networks[res.network + 1], apObj) <= 0;
if (!okPrev || !okNext) if (!okPrev || !okNext) {
this._clearSection();
this._queueCreateSection(); this._queueCreateSection();
else if (apObj.item) } else if (apObj.item) {
apObj.item.updateBestAP(apObj.accessPoints[0]); apObj.item.updateBestAP(apObj.accessPoints[0]);
}
} }
}, },
@ -1321,6 +1345,7 @@ const NMDeviceWireless = new Lang.Class({
if (forceupdate) { if (forceupdate) {
this._networks.sort(this._networkSortFunction); this._networks.sort(this._networkSortFunction);
this._clearSection();
this._queueCreateSection(); this._queueCreateSection();
} }
}, },
@ -1353,6 +1378,7 @@ const NMDeviceWireless = new Lang.Class({
if (forceupdate) { if (forceupdate) {
this._networks.sort(this._networkSortFunction); this._networks.sort(this._networkSortFunction);
this._clearSection();
this._queueCreateSection(); this._queueCreateSection();
} }
}, },
@ -1432,7 +1458,7 @@ const NMDeviceWireless = new Lang.Class({
this.section.addMenuItem(apObj.item, position); this.section.addMenuItem(apObj.item, position);
} else { } else {
if (!this._overflowItem) { if (!this._overflowItem) {
this._overflowItem = new PopupMenu.PopupSubMenuMenuItem(_("More")); this._overflowItem = new PopupMenu.PopupSubMenuMenuItem(_("More..."));
this.section.addMenuItem(this._overflowItem); this.section.addMenuItem(this._overflowItem);
} }
this._overflowItem.menu.addMenuItem(apObj.item, position - NUM_VISIBLE_NETWORKS); 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({ const NMVPNSection = new Lang.Class({
Name: 'NMVPNSection', Name: 'NMVPNSection',
Extends: NMConnectionBased, Extends: NMConnectionBased,
@ -1624,7 +1600,7 @@ const NMVPNSection = new Lang.Class({
if (j >= NUM_VISIBLE_NETWORKS) { if (j >= NUM_VISIBLE_NETWORKS) {
if (!this._overflowItem) { if (!this._overflowItem) {
this._overflowItem = new PopupMenu.PopupSubMenuMenuItem(_("More")); this._overflowItem = new PopupMenu.PopupSubMenuMenuItem(_("More..."));
this.section.addMenuItem(this._overflowItem); this.section.addMenuItem(this._overflowItem);
} }
this._overflowItem.menu.addMenuItem(obj.item); 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 = this.addIcon(new Gio.ThemedIcon({ name: 'network-vpn-symbolic' }));
this.secondaryIcon.hide(); this.secondaryIcon.hide();
// Device types this._client = NMClient.Client.new();
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._statusSection = new PopupMenu.PopupMenuSection(); this._statusSection = new PopupMenu.PopupMenuSection();
this._statusItem = new PopupMenu.PopupMenuItem('', { reactive: false }); this._statusItem = new PopupMenu.PopupMenuItem('', { reactive: false });
@ -1743,32 +1667,23 @@ const NMApplet = new Lang.Class({
this._mobileUpdateId = 0; this._mobileUpdateId = 0;
this._mobileUpdateDevice = null; this._mobileUpdateDevice = null;
this._nmDevices = [];
this._devices = { }; this._devices = { };
this._virtualDevices = [ ];
this._devices.wired = { this._devices.wired = {
section: new PopupMenu.PopupMenuSection(), section: new PopupMenu.PopupMenuSection(),
devices: [ ], devices: [ ],
item: new NMWiredSectionTitleMenuItem(_("Wired"))
}; };
this._devices.wired.section.addMenuItem(this._devices.wired.item);
this._devices.wired.section.actor.hide(); this._devices.wired.section.actor.hide();
this.menu.addMenuItem(this._devices.wired.section); this.menu.addMenuItem(this._devices.wired.section);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); 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 = { this._devices.wireless = {
section: new PopupMenu.PopupMenuSection(), section: new PopupMenu.PopupMenuSection(),
devices: [ ], 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.addMenuItem(this._devices.wireless.item);
this._devices.wireless.section.actor.hide(); this._devices.wireless.section.actor.hide();
@ -1778,7 +1693,9 @@ const NMApplet = new Lang.Class({
this._devices.wwan = { this._devices.wwan = {
section: new PopupMenu.PopupMenuSection(), section: new PopupMenu.PopupMenuSection(),
devices: [ ], devices: [ ],
item: this._makeToggleItem('wwan', _("Mobile broadband"))
}; };
this._devices.wwan.section.addMenuItem(this._devices.wwan.item);
this._devices.wwan.section.actor.hide(); this._devices.wwan.section.actor.hide();
this.menu.addMenuItem(this._devices.wwan.section); this.menu.addMenuItem(this._devices.wwan.section);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
@ -1789,24 +1706,52 @@ const NMApplet = new Lang.Class({
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addSettingsAction(_("Network Settings"), 'gnome-network-panel.desktop'); this.menu.addSettingsAction(_("Network Settings"), 'gnome-network-panel.desktop');
this._readConnections(); // Device types
this._readDevices(); this._dtypes = { };
this._syncNMState(); 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)); // Connection types
this._client.connect('notify::networking-enabled', Lang.bind(this, this._syncNMState)); this._ctypes = { };
this._client.connect('notify::state', Lang.bind(this, this._syncNMState)); this._ctypes[NetworkManager.SETTING_WIRELESS_SETTING_NAME] = NMConnectionCategory.WIRELESS;
this._client.connect('notify::active-connections', Lang.bind(this, this._updateIcon)); this._ctypes[NetworkManager.SETTING_WIRED_SETTING_NAME] = NMConnectionCategory.WIRED;
this._client.connect('device-added', Lang.bind(this, this._deviceAdded)); this._ctypes[NetworkManager.SETTING_PPPOE_SETTING_NAME] = NMConnectionCategory.WIRED;
this._client.connect('device-removed', Lang.bind(this, this._deviceRemoved)); this._ctypes[NetworkManager.SETTING_PPP_SETTING_NAME] = NMConnectionCategory.WIRED;
this._settings.connect('new-connection', Lang.bind(this, this._newConnection)); 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() { _ensureSource: function() {
if (!this._source) { if (!this._source) {
this._source = new MessageTray.Source(_("Network Manager"), this._source = new MessageTray.Source(_("Network Manager"),
'network-transmit-receive'); 'network-transmit-receive');
this._source.policy = new NotificationDaemon.NotificationApplicationPolicy('gnome-network-panel');
this._source.connect('destroy', Lang.bind(this, function() { this._source.connect('destroy', Lang.bind(this, function() {
this._source = null; this._source = null;
@ -1831,34 +1776,20 @@ const NMApplet = new Lang.Class({
let devices = this._devices[category].devices; let devices = this._devices[category].devices;
let item = this._devices[category].item; let item = this._devices[category].item;
let section = this._devices[category].section; let section = this._devices[category].section;
if (devices.length == 0)
let visible;
if (category == NMConnectionCategory.VIRTUAL)
visible = !section.isEmpty();
else
visible = devices.length > 0;
if (!visible)
section.actor.hide(); section.actor.hide();
else { else {
section.actor.show(); section.actor.show();
if (devices.length == 1) {
// Sync the relation between the section title let dev = devices[0];
// item (the one with the airplane mode switch) dev.statusItem.actor.hide();
// and the individual device switches item.updateForDevice(dev);
if (item) { } else {
if (devices.length == 1) { devices.forEach(function(dev) {
let dev = devices[0]; dev.statusItem.actor.show();
dev.statusItem.actor.hide(); });
item.updateForDevice(dev); // remove status text from the section title item
} else { item.updateForDevice(null);
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() { _readDevices: function() {
let devices = this._client.get_devices() || [ ]; let devices = this._client.get_devices() || [ ];
for (let i = 0; i < devices.length; ++i) { 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) { _notifyForDevice: function(device, iconName, title, text, urgency) {
@ -1899,50 +1829,9 @@ const NMApplet = new Lang.Class({
MessageTray.Urgency.HIGH); MessageTray.Urgency.HIGH);
}, },
_syncDeviceNames: function() { _makeWrapperDevice: function(wrapperClass, device) {
if (NMGtk) { let wrapper = new wrapperClass(this._client, device, this._connections);
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();
}
}
},
_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', wrapper._activationFailedId = wrapper.connect('activation-failed',
Lang.bind(this, this._onActivationFailed)); Lang.bind(this, this._onActivationFailed));
wrapper._deviceStateChangedId = wrapper.connect('state-changed', Lang.bind(this, function(dev) { wrapper._deviceStateChangedId = wrapper.connect('state-changed', Lang.bind(this, function(dev) {
@ -1954,36 +1843,35 @@ const NMApplet = new Lang.Class({
wrapper.disconnect(wrapper._destroyId); wrapper.disconnect(wrapper._destroyId);
}); });
let section = this._devices[wrapper.category].section; return wrapper;
section.addMenuItem(wrapper.statusItem); },
section.addMenuItem(wrapper.section);
let devices = this._devices[wrapper.category].devices; _deviceAdded: function(client, device) {
devices.push(wrapper); 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) { _deviceRemoved: function(client, device) {
let pos = this._nmDevices.indexOf(device); if (!device._delegate) {
if (pos != -1) {
this._nmDevices.splice(pos, 1);
this._syncDeviceNames();
}
let wrapper = device._delegate;
if (!wrapper) {
log('Removing a network device that was not added'); log('Removing a network device that was not added');
return; return;
} }
if (wrapper instanceof NMDeviceVirtual) let wrapper = device._delegate;
wrapper.removeDevice(device);
else
this._removeDeviceWrapper(wrapper);
},
_removeDeviceWrapper: function(wrapper) {
wrapper.destroy(); wrapper.destroy();
let devices = this._devices[wrapper.category].devices; let devices = this._devices[wrapper.category].devices;
@ -2184,13 +2072,6 @@ const NMApplet = new Lang.Class({
devices[i].removeConnection(connection); 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._removedId);
connection.disconnect(connection._updatedId); connection.disconnect(connection._updatedId);
connection._removedId = connection._updatedId = 0; connection._removedId = connection._updatedId = 0;
@ -2204,27 +2085,6 @@ const NMApplet = new Lang.Class({
let section = connection._section; 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) if (section == NMConnectionCategory.INVALID)
return; return;
if (section == NMConnectionCategory.VPN) { 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() { _hideDevices: function() {
this._devicesHidden = true; this._devicesHidden = true;
@ -2261,7 +2112,6 @@ const NMApplet = new Lang.Class({
this._statusSection.actor.hide(); this._statusSection.actor.hide();
this._syncSectionTitle(NMConnectionCategory.WIRED); this._syncSectionTitle(NMConnectionCategory.WIRED);
this._syncSectionTitle(NMConnectionCategory.VIRTUAL);
this._syncSectionTitle(NMConnectionCategory.WIRELESS); this._syncSectionTitle(NMConnectionCategory.WIRELESS);
this._syncSectionTitle(NMConnectionCategory.WWAN); this._syncSectionTitle(NMConnectionCategory.WWAN);
}, },
@ -2299,7 +2149,6 @@ const NMApplet = new Lang.Class({
this.setIcon('network-wireless-acquiring-symbolic'); this.setIcon('network-wireless-acquiring-symbolic');
break; break;
case NMConnectionCategory.WIRED: case NMConnectionCategory.WIRED:
case NMConnectionCategory.VIRTUAL:
this.setIcon('network-wired-acquiring-symbolic'); this.setIcon('network-wired-acquiring-symbolic');
break; break;
default: default:
@ -2339,7 +2188,6 @@ const NMApplet = new Lang.Class({
break; break;
} }
case NMConnectionCategory.WIRED: case NMConnectionCategory.WIRED:
case NMConnectionCategory.VIRTUAL:
this.setIcon('network-wired-symbolic'); this.setIcon('network-wired-symbolic');
break; break;
case NMConnectionCategory.WWAN: case NMConnectionCategory.WWAN:

View File

@ -7,7 +7,7 @@ const St = imports.gi.St;
const PanelMenu = imports.ui.panelMenu; const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu; const PopupMenu = imports.ui.popupMenu;
const BUS_NAME = 'org.gnome.SettingsDaemon.Power'; const BUS_NAME = 'org.gnome.SettingsDaemon';
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Power'; const OBJECT_PATH = '/org/gnome/SettingsDaemon/Power';
const UPDeviceType = { const UPDeviceType = {
@ -54,23 +54,14 @@ const Indicator = new Lang.Class({
_init: function() { _init: function() {
this.parent('battery-missing-symbolic', _("Battery")); this.parent('battery-missing-symbolic', _("Battery"));
this._proxy = new PowerManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH, 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._deviceItems = [ ]; this._deviceItems = [ ];
this._hasPrimary = false; this._hasPrimary = false;
this._primaryDeviceId = null; this._primaryDeviceId = null;
this._batteryItem = new PopupMenu.PopupMenuItem('', { reactive: false }); 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._batteryItem.addActor(this._primaryPercentage, { align: St.Align.END });
this.menu.addMenuItem(this._batteryItem); this.menu.addMenuItem(this._batteryItem);
@ -79,6 +70,10 @@ const Indicator = new Lang.Class({
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addSettingsAction(_("Power Settings"), 'gnome-power-panel.desktop'); this.menu.addSettingsAction(_("Power Settings"), 'gnome-power-panel.desktop');
this._proxy.connect('g-properties-changed',
Lang.bind(this, this._devicesChanged));
this._devicesChanged();
}, },
_readPrimaryDevice: function() { _readPrimaryDevice: function() {
@ -96,7 +91,7 @@ const Indicator = new Lang.Class({
if (time == 0) { if (time == 0) {
// 0 is reported when UPower does not have enough data // 0 is reported when UPower does not have enough data
// to estimate battery life // to estimate battery life
this._batteryItem.label.text = _("Estimating"); this._batteryItem.label.text = _("Estimating...");
} else { } else {
let minutes = time % 60; let minutes = time % 60;
let hours = Math.floor(time / 60); let hours = Math.floor(time / 60);
@ -188,8 +183,7 @@ const DeviceItem = new Lang.Class({
this._box.add_actor(this._label); this._box.add_actor(this._label);
this.addActor(this._box); this.addActor(this._box);
let percentLabel = new St.Label({ text: C_("percent of battery remaining", "%d%%").format(Math.round(percentage)), let percentLabel = new St.Label({ text: C_("percent of battery remaining", "%d%%").format(Math.round(percentage)) });
style_class: 'popup-battery-percentage' });
this.addActor(percentLabel, { align: St.Align.END }); this.addActor(percentLabel, { align: St.Align.END });
//FIXME: ideally we would like to expose this._label and percentLabel //FIXME: ideally we would like to expose this._label and percentLabel
this.actor.label_actor = percentLabel; this.actor.label_actor = percentLabel;
@ -198,9 +192,9 @@ const DeviceItem = new Lang.Class({
_deviceTypeToString: function(type) { _deviceTypeToString: function(type) {
switch (type) { switch (type) {
case UPDeviceType.AC_POWER: case UPDeviceType.AC_POWER:
return _("AC Adapter"); return _("AC adapter");
case UPDeviceType.BATTERY: case UPDeviceType.BATTERY:
return _("Laptop Battery"); return _("Laptop battery");
case UPDeviceType.UPS: case UPDeviceType.UPS:
return _("UPS"); return _("UPS");
case UPDeviceType.MONITOR: case UPDeviceType.MONITOR:
@ -212,9 +206,9 @@ const DeviceItem = new Lang.Class({
case UPDeviceType.PDA: case UPDeviceType.PDA:
return _("PDA"); return _("PDA");
case UPDeviceType.PHONE: case UPDeviceType.PHONE:
return _("Cell Phone"); return _("Cell phone");
case UPDeviceType.MEDIA_PLAYER: case UPDeviceType.MEDIA_PLAYER:
return _("Media Player"); return _("Media player");
case UPDeviceType.TABLET: case UPDeviceType.TABLET:
return _("Tablet"); return _("Tablet");
case UPDeviceType.COMPUTER: case UPDeviceType.COMPUTER:

View File

@ -5,7 +5,6 @@ const Lang = imports.lang;
const Gio = imports.gi.Gio; const Gio = imports.gi.Gio;
const Gvc = imports.gi.Gvc; const Gvc = imports.gi.Gvc;
const St = imports.gi.St; const St = imports.gi.St;
const Signals = imports.signals;
const PanelMenu = imports.ui.panelMenu; const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu; const PopupMenu = imports.ui.popupMenu;
@ -27,210 +26,6 @@ function getMixerControl() {
return _mixerControl; 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({ const VolumeMenu = new Lang.Class({
Name: 'VolumeMenu', Name: 'VolumeMenu',
Extends: PopupMenu.PopupMenuSection, 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('state-changed', Lang.bind(this, this._onControlStateChanged));
this._control.connect('default-sink-changed', Lang.bind(this, this._readOutput)); 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('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 */ /* Translators: This is the label for audio volume */
this._output = new OutputStreamSlider(this._control, _("Volume")); this._outputTitle = new PopupMenu.PopupMenuItem(_("Volume"), { reactive: false });
this._output.connect('stream-updated', Lang.bind(this, function() { this._outputSlider = new PopupMenu.PopupSliderMenuItem(0);
this.emit('icon-changed'); this._outputSlider.connect('value-changed', Lang.bind(this, this._sliderChanged, '_output'));
})); this._outputSlider.connect('drag-end', Lang.bind(this, this._notifyVolumeChange));
this._output.connect('headphones-changed', Lang.bind(this, function(stream, value) { this.addMenuItem(this._outputTitle);
this.emit('headphones-changed', value); this.addMenuItem(this._outputSlider);
}));
this.addMenuItem(this._output.item);
this._input = new InputStreamSlider(this._control, _("Microphone"));
this.addMenuItem(this._input.item);
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); 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(); this._onControlStateChanged();
}, },
scroll: function(event) { scroll: function(direction) {
this._output.scroll(event); 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() { _onControlStateChanged: function() {
if (this._control.get_state() == Gvc.MixerControlState.READY) { if (this._control.get_state() == Gvc.MixerControlState.READY) {
this._readInput();
this._readOutput(); this._readOutput();
this._readInput();
this._maybeShowInput();
} else { } 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() { _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() { _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() { _maybeShowInput: function() {
return this._output.getIcon(); // 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._control = getMixerControl();
this._volumeMenu = new VolumeMenu(this._control); this._volumeMenu = new VolumeMenu(this._control);
this._volumeMenu.connect('icon-changed', Lang.bind(this, function(menu) { this._volumeMenu.connect('icon-changed', Lang.bind(this, function(menu, icon) {
let icon = this._volumeMenu.getIcon(); this._hasPulseAudio = (icon != null);
this.actor.visible = (icon != null);
this.setIcon(icon); this.setIcon(icon);
this._syncVisibility();
})); }));
this._volumeMenu.connect('headphones-changed', Lang.bind(this, function(menu, value) { this._volumeMenu.connect('headphones-changed', Lang.bind(this, function() {
this._headphoneIcon.visible = value; this._syncVisibility();
})); }));
this._headphoneIcon = this.addIcon(new Gio.ThemedIcon({ name: 'headphones-symbolic' })); 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)); 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) { _onScrollEvent: function(actor, event) {
this._volumeMenu.scroll(event); this._volumeMenu.scroll(event.get_scroll_direction());
} }
}); });

View File

@ -46,8 +46,7 @@ const SwitcherPopup = new Lang.Class({
this._selectedIndex = 0; this._selectedIndex = 0;
this.actor = new Shell.GenericContainer({ style_class: 'switcher-popup', this.actor = new Shell.GenericContainer({ style_class: 'switcher-popup',
reactive: true, reactive: true });
visible: false });
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth)); 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('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
this.actor.connect('allocate', Lang.bind(this, this._allocate)); this.actor.connect('allocate', Lang.bind(this, this._allocate));
@ -160,7 +159,6 @@ const SwitcherPopup = new Lang.Class({
// disturbed by the popup briefly flashing. // disturbed by the popup briefly flashing.
this._initialDelayTimeoutId = Mainloop.timeout_add(POPUP_DELAY_TIMEOUT, this._initialDelayTimeoutId = Mainloop.timeout_add(POPUP_DELAY_TIMEOUT,
Lang.bind(this, function () { Lang.bind(this, function () {
Main.osdWindow.cancel();
this.actor.opacity = 255; this.actor.opacity = 255;
this._initialDelayTimeoutId = 0; this._initialDelayTimeoutId = 0;
})); }));
@ -305,7 +303,7 @@ const SwitcherList = new Lang.Class({
this.actor.connect('allocate', Lang.bind(this, this._allocateTop)); this.actor.connect('allocate', Lang.bind(this, this._allocateTop));
// Here we use a GenericContainer so that we can force all the // 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 = new Shell.GenericContainer({ style_class: 'switcher-list-item-container' });
this._list.spacing = 0; this._list.spacing = 0;
this._list.connect('style-changed', Lang.bind(this, function() { this._list.connect('style-changed', Lang.bind(this, function() {
@ -340,6 +338,7 @@ const SwitcherList = new Lang.Class({
this._items = []; this._items = [];
this._highlighted = -1; this._highlighted = -1;
this._separator = null;
this._squareItems = squareItems; this._squareItems = squareItems;
this._minSize = 0; this._minSize = 0;
this._scrollableRight = true; this._scrollableRight = true;
@ -402,6 +401,12 @@ const SwitcherList = new Lang.Class({
this._itemEntered(index); 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) { highlight: function(index, justOutline) {
if (this._highlighted != -1) { if (this._highlighted != -1) {
this._items[this._highlighted].remove_style_pseudo_class('outlined'); this._items[this._highlighted].remove_style_pseudo_class('outlined');
@ -509,8 +514,14 @@ const SwitcherList = new Lang.Class({
_getPreferredWidth: function (actor, forHeight, alloc) { _getPreferredWidth: function (actor, forHeight, alloc) {
let [maxChildMin, maxChildNat] = this._maxChildWidth(forHeight); 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); 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; alloc.natural_size = alloc.min_size;
this._minSize = alloc.min_size; this._minSize = alloc.min_size;
}, },
@ -541,7 +552,14 @@ const SwitcherList = new Lang.Class({
let [maxChildMin, maxChildNat] = this._maxChildWidth(childHeight); let [maxChildMin, maxChildNat] = this._maxChildWidth(childHeight);
let totalSpacing = this._list.spacing * (this._items.length - 1); 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 x = 0;
let children = this._list.get_children(); let children = this._list.get_children();
@ -561,6 +579,14 @@ const SwitcherList = new Lang.Class({
children[i].allocate(childBox, flags); children[i].allocate(childBox, flags);
x += this._list.spacing + childWidth; 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 { } else {
// Something else, eg, AppSwitcher's arrows; // Something else, eg, AppSwitcher's arrows;
// we don't allocate it. // we don't allocate it.
@ -611,6 +637,5 @@ function drawArrow(area, side) {
Clutter.cairo_set_source_color(cr, bodyColor); Clutter.cairo_set_source_color(cr, bodyColor);
cr.fill(); cr.fill();
cr.$dispose();
} }

View File

@ -2,7 +2,6 @@
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib; const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang; const Lang = imports.lang;
const Mainloop = imports.mainloop; const Mainloop = imports.mainloop;
const Shell = imports.gi.Shell; 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, 'onStart', _tweenStarted);
_addHandler(target, tweeningParameters, 'onComplete', _tweenCompleted); _addHandler(target, tweeningParameters, 'onComplete', _tweenCompleted);
} }

View File

@ -18,7 +18,6 @@ const Panel = imports.ui.panel;
const ShellEntry = imports.ui.shellEntry; const ShellEntry = imports.ui.shellEntry;
const Tweener = imports.ui.tweener; const Tweener = imports.ui.tweener;
const UserMenu = imports.ui.userMenu; const UserMenu = imports.ui.userMenu;
const UserWidget = imports.ui.userWidget;
const Batch = imports.gdm.batch; const Batch = imports.gdm.batch;
const GdmUtil = imports.gdm.util; const GdmUtil = imports.gdm.util;
@ -56,6 +55,59 @@ function isSupported() {
} }
} }
// 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({ const UnlockDialog = new Lang.Class({
Name: 'UnlockDialog', Name: 'UnlockDialog',
Extends: ModalDialog.ModalDialog, Extends: ModalDialog.ModalDialog,
@ -63,7 +115,7 @@ const UnlockDialog = new Lang.Class({
_init: function(parentActor) { _init: function(parentActor) {
this.parent({ shellReactive: true, this.parent({ shellReactive: true,
styleClass: 'login-dialog', styleClass: 'login-dialog',
keybindingMode: Shell.KeyBindingMode.UNLOCK_SCREEN, keybindingMode: Main.KeybindingMode.UNLOCK_SCREEN,
parentActor: parentActor parentActor: parentActor
}); });
@ -86,7 +138,7 @@ const UnlockDialog = new Lang.Class({
this._userVerifier.connect('show-login-hint', Lang.bind(this, this._showLoginHint)); this._userVerifier.connect('show-login-hint', Lang.bind(this, this._showLoginHint));
this._userVerifier.connect('hide-login-hint', Lang.bind(this, this._hideLoginHint)); 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.contentLayout.add_actor(this._userWidget.actor);
this._promptLayout = new St.BoxLayout({ style_class: 'login-dialog-prompt-layout', this._promptLayout = new St.BoxLayout({ style_class: 'login-dialog-prompt-layout',
@ -146,23 +198,18 @@ const UnlockDialog = new Lang.Class({
x_align: St.Align.END, x_align: St.Align.END,
y_align: St.Align.MIDDLE }); y_align: St.Align.MIDDLE });
let screenSaverSettings = new Gio.Settings({ schema: 'org.gnome.desktop.screensaver' }); let otherUserLabel = new St.Label({ text: _("Log in as another user"),
if (screenSaverSettings.get_boolean('user-switch-enabled')) { style_class: 'login-dialog-not-listed-label' });
let otherUserLabel = new St.Label({ text: _("Log in as another user"), this._otherUserButton = new St.Button({ style_class: 'login-dialog-not-listed-button',
style_class: 'login-dialog-not-listed-label' }); can_focus: true,
this._otherUserButton = new St.Button({ style_class: 'login-dialog-not-listed-button', child: otherUserLabel,
can_focus: true, reactive: true,
child: otherUserLabel, x_align: St.Align.START,
reactive: true, x_fill: true });
x_align: St.Align.START, this._otherUserButton.connect('clicked', Lang.bind(this, this._otherUserClicked));
x_fill: true }); this.dialogLayout.add(this._otherUserButton,
this._otherUserButton.connect('clicked', Lang.bind(this, this._otherUserClicked)); { x_align: St.Align.START,
this.dialogLayout.add(this._otherUserButton, x_fill: false });
{ x_align: St.Align.START,
x_fill: false });
} else {
this._otherUserButton = null;
}
this._updateSensitivity(true); this._updateSensitivity(true);
@ -177,17 +224,15 @@ const UnlockDialog = new Lang.Class({
Main.ctrlAltTabManager.addGroup(this.dialogLayout, _("Unlock Window"), 'dialog-password-symbolic'); Main.ctrlAltTabManager.addGroup(this.dialogLayout, _("Unlock Window"), 'dialog-password-symbolic');
this._idleMonitor = new GnomeDesktop.IdleMonitor(); 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) { _updateSensitivity: function(sensitive) {
this._promptEntry.reactive = sensitive; this._promptEntry.reactive = sensitive;
this._promptEntry.clutter_text.editable = sensitive; this._promptEntry.clutter_text.editable = sensitive;
this._updateOkButtonSensitivity(sensitive && this._promptEntry.text.length > 0); this._updateOkButtonSensitivity(sensitive && this._promptEntry.text.length > 0);
if (this._otherUserButton) { this._otherUserButton.reactive = sensitive;
this._otherUserButton.reactive = sensitive; this._otherUserButton.can_focus = sensitive;
this._otherUserButton.can_focus = sensitive;
}
}, },
_updateOkButtonSensitivity: function(sensitive) { _updateOkButtonSensitivity: function(sensitive) {

View File

@ -4,20 +4,17 @@ const AccountsService = imports.gi.AccountsService;
const Gdm = imports.gi.Gdm; const Gdm = imports.gi.Gdm;
const Gio = imports.gi.Gio; const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib; const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang; const Lang = imports.lang;
const Pango = imports.gi.Pango; const Pango = imports.gi.Pango;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const St = imports.gi.St; const St = imports.gi.St;
const Tp = imports.gi.TelepathyGLib; const Tp = imports.gi.TelepathyGLib;
const Atk = imports.gi.Atk; const Atk = imports.gi.Atk;
const Clutter = imports.gi.Clutter;
const BoxPointer = imports.ui.boxpointer; const BoxPointer = imports.ui.boxpointer;
const GnomeSession = imports.misc.gnomeSession; const GnomeSession = imports.misc.gnomeSession;
const LoginManager = imports.misc.loginManager; const LoginManager = imports.misc.loginManager;
const Main = imports.ui.main; const Main = imports.ui.main;
const ModalDialog = imports.ui.modalDialog;
const PanelMenu = imports.ui.panelMenu; const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu; const PopupMenu = imports.ui.popupMenu;
const Params = imports.misc.params; const Params = imports.misc.params;
@ -25,17 +22,15 @@ const Util = imports.misc.util;
const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown'; const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown';
const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver'; const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver';
const PRIVACY_SCHEMA = 'org.gnome.desktop.privacy'
const DISABLE_USER_SWITCH_KEY = 'disable-user-switching'; const DISABLE_USER_SWITCH_KEY = 'disable-user-switching';
const DISABLE_LOCK_SCREEN_KEY = 'disable-lock-screen'; const DISABLE_LOCK_SCREEN_KEY = 'disable-lock-screen';
const DISABLE_LOG_OUT_KEY = 'disable-log-out'; 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 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 DIALOG_ICON_SIZE = 64;
const MAX_USERS_IN_SESSION_DIALOG = 5;
const IMStatus = { const IMStatus = {
AVAILABLE: 0, AVAILABLE: 0,
BUSY: 1, BUSY: 1,
@ -46,17 +41,6 @@ const IMStatus = {
LAST: 6 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 // Adapted from gdm/gui/user-switch-applet/applet.c
// //
// Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>. // Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
@ -84,7 +68,7 @@ const UserAvatarWidget = new Lang.Class({
update: function() { update: function() {
let iconFile = this._user.get_icon_file(); 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; iconFile = null;
if (iconFile) { if (iconFile) {
@ -493,7 +477,6 @@ const UserMenuButton = new Lang.Class({
this._screenSaverSettings = new Gio.Settings({ schema: SCREENSAVER_SCHEMA }); this._screenSaverSettings = new Gio.Settings({ schema: SCREENSAVER_SCHEMA });
this._lockdownSettings = new Gio.Settings({ schema: LOCKDOWN_SCHEMA }); this._lockdownSettings = new Gio.Settings({ schema: LOCKDOWN_SCHEMA });
this._privacySettings = new Gio.Settings({ schema: PRIVACY_SCHEMA });
this._userManager = AccountsService.UserManager.get_default(); this._userManager = AccountsService.UserManager.get_default();
@ -570,12 +553,10 @@ const UserMenuButton = new Lang.Class({
Lang.bind(this, this._updateLogout)); Lang.bind(this, this._updateLogout));
this._lockdownSettings.connect('changed::' + DISABLE_LOCK_SCREEN_KEY, this._lockdownSettings.connect('changed::' + DISABLE_LOCK_SCREEN_KEY,
Lang.bind(this, this._updateLockScreen)); Lang.bind(this, this._updateLockScreen));
global.settings.connect('changed::' + ALWAYS_SHOW_LOG_OUT_KEY, this._screenSaverSettings.connect('changed::' + SHOW_FULL_NAME_KEY,
Lang.bind(this, this._updateLogout));
this._screenSaverSettings.connect('changed::' + SHOW_FULL_NAME_IN_TOP_BAR_KEY,
Lang.bind(this, this._updateUserName)); Lang.bind(this, this._updateUserName));
this._privacySettings.connect('changed::' + SHOW_FULL_NAME_IN_TOP_BAR_KEY, global.settings.connect('changed::' + SHOW_FULL_NAME_KEY,
Lang.bind(this, this._updateUserName)); Lang.bind(this, this._updateUserName));
this._updateSwitchUser(); this._updateSwitchUser();
this._updateLogout(); this._updateLogout();
this._updateLockScreen(); this._updateLockScreen();
@ -600,7 +581,6 @@ const UserMenuButton = new Lang.Class({
Lang.bind(this, this._updateHaveShutdown)); Lang.bind(this, this._updateHaveShutdown));
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated)); Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
Main.screenShield.connect('locked-changed', Lang.bind(this, this._updatePresenceIcon));
this._sessionUpdated(); this._sessionUpdated();
}, },
@ -622,10 +602,10 @@ const UserMenuButton = new Lang.Class({
}, },
_updateUserName: function() { _updateUserName: function() {
let settings = this._privacySettings; let settings = global.settings;
if (Main.sessionMode.isLocked) if (Main.sessionMode.isLocked)
settings = this._screenSaverSettings; 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()); this._name.set_text(this._user.get_real_name());
else else
this._name.set_text(""); this._name.set_text("");
@ -720,11 +700,6 @@ const UserMenuButton = new Lang.Class({
this._iconBox.child = this._idleIcon; this._iconBox.child = this._idleIcon;
else else
this._iconBox.child = this._offlineIcon; this._iconBox.child = this._offlineIcon;
if (Main.sessionMode.isLocked)
this._iconBox.visible = Main.screenShield.locked;
else
this._iconBox.visible = true;
}, },
_setupAccounts: function() { _setupAccounts: function() {
@ -845,7 +820,7 @@ const UserMenuButton = new Lang.Class({
_onMyAccountActivate: function() { _onMyAccountActivate: function() {
Main.overview.hide(); 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(); app.activate();
}, },
@ -880,115 +855,25 @@ const UserMenuButton = new Lang.Class({
this._session.RebootRemote(); 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)
userLabelText = _("%s (remote)").format(userName);
else if (session.info.type == "tty")
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() { _onSuspendOrPowerOffActivate: function() {
Main.overview.hide(); Main.overview.hide();
if (this._haveShutdown && if (this._haveShutdown &&
this._suspendOrPowerOffItem.state == PopupMenu.PopupAlternatingMenuItemState.DEFAULT) { this._suspendOrPowerOffItem.state == PopupMenu.PopupAlternatingMenuItemState.DEFAULT) {
this._loginManager.listSessions(Lang.bind(this, this._session.ShutdownRemote();
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();
}));
} else { } else {
this.menu.close(BoxPointer.PopupAnimation.NONE); if (this._screenSaverSettings.get_boolean(LOCK_ENABLED_KEY)) {
this._loginManager.suspend(); 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();
}
} }
} }
}); });

View File

@ -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();
}
});

View File

@ -12,8 +12,6 @@ const St = imports.gi.St;
const AppDisplay = imports.ui.appDisplay; const AppDisplay = imports.ui.appDisplay;
const Main = imports.ui.main; const Main = imports.ui.main;
const OverviewControls = imports.ui.overviewControls;
const Params = imports.misc.params;
const RemoteSearch = imports.ui.remoteSearch; const RemoteSearch = imports.ui.remoteSearch;
const Search = imports.ui.search; const Search = imports.ui.search;
const SearchDisplay = imports.ui.searchDisplay; const SearchDisplay = imports.ui.searchDisplay;
@ -24,12 +22,6 @@ const WorkspacesView = imports.ui.workspacesView;
const SHELL_KEYBINDINGS_SCHEMA = 'org.gnome.shell.keybindings'; const SHELL_KEYBINDINGS_SCHEMA = 'org.gnome.shell.keybindings';
const ViewPage = {
WINDOWS: 1,
APPS: 2,
SEARCH: 3
};
const FocusTrap = new Lang.Class({ const FocusTrap = new Lang.Class({
Name: 'FocusTrap', Name: 'FocusTrap',
Extends: St.Widget, 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({ const ViewSelector = new Lang.Class({
Name: 'ViewSelector', Name: 'ViewSelector',
@ -57,13 +41,13 @@ const ViewSelector = new Lang.Class({
_init : function(searchEntry, showAppsButton) { _init : function(searchEntry, showAppsButton) {
this.actor = new Shell.Stack({ name: 'viewSelector' }); this.actor = new Shell.Stack({ name: 'viewSelector' });
this._showAppsBlocked = false;
this._showAppsButton = showAppsButton; this._showAppsButton = showAppsButton;
this._showAppsButton.connect('notify::checked', Lang.bind(this, this._onShowAppsButtonToggled)); this._showAppsButton.connect('notify::checked', Lang.bind(this, this._onShowAppsButtonToggled));
this._activePage = null; this._activePage = null;
this._searchActive = false; this.active = false;
this._searchPending = false;
this._searchTimeoutId = 0; this._searchTimeoutId = 0;
this._searchSystem = new Search.SearchSystem(); this._searchSystem = new Search.SearchSystem();
@ -83,26 +67,26 @@ const ViewSelector = new Lang.Class({
this._entry.connect('notify::mapped', Lang.bind(this, this._onMapped)); this._entry.connect('notify::mapped', Lang.bind(this, this._onMapped));
global.stage.connect('notify::key-focus', Lang.bind(this, this._onStageKeyFocusChanged)); global.stage.connect('notify::key-focus', Lang.bind(this, this._onStageKeyFocusChanged));
this._entry.set_primary_icon(new St.Icon({ style_class: 'search-entry-icon', this._inactiveIcon = new St.Icon({ style_class: 'search-entry-icon',
icon_name: 'edit-find-symbolic' })); icon_name: 'edit-find-symbolic' });
this._clearIcon = new St.Icon({ style_class: 'search-entry-icon', this._activeIcon = new St.Icon({ style_class: 'search-entry-icon',
icon_name: 'edit-clear-symbolic' }); icon_name: 'edit-clear-symbolic' });
this._entry.set_secondary_icon(this._inactiveIcon);
this._iconClickedId = 0; this._iconClickedId = 0;
this._capturedEventId = 0; this._capturedEventId = 0;
this._workspacesDisplay = new WorkspacesView.WorkspacesDisplay(); this._workspacesDisplay = new WorkspacesView.WorkspacesDisplay();
this._workspacesPage = this._addPage(this._workspacesDisplay.actor, this._workspacesPage = this._addPage(this._workspacesDisplay.actor, null,
_("Windows"), 'emblem-documents-symbolic'); _("Windows"), 'emblem-documents-symbolic');
this._appDisplay = new AppDisplay.AppDisplay(); this._appDisplay = new AppDisplay.AllAppDisplay();
this._appsPage = this._addPage(this._appDisplay.actor, this._appsPage = this._addPage(this._appDisplay.actor, null,
_("Applications"), 'view-grid-symbolic'); _("Applications"), 'view-grid-symbolic');
this._searchResults = new SearchDisplay.SearchResults(this._searchSystem); this._searchResults = new SearchDisplay.SearchResults(this._searchSystem);
this._searchPage = this._addPage(this._searchResults.actor, this._searchPage = this._addPage(this._searchResults.actor, this._entry,
_("Search"), 'edit-find-symbolic', _("Search"), 'edit-find-symbolic');
{ a11yFocus: this._entry });
this._searchSettings = new Gio.Settings({ schema: Search.SEARCH_PROVIDERS_SCHEMA }); this._searchSettings = new Gio.Settings({ schema: Search.SEARCH_PROVIDERS_SCHEMA });
this._searchSettings.connect('changed::disabled', Lang.bind(this, this._reloadRemoteProviders)); this._searchSettings.connect('changed::disabled', Lang.bind(this, this._reloadRemoteProviders));
@ -113,6 +97,7 @@ const ViewSelector = new Lang.Class({
// Wanda comes obviously first // Wanda comes obviously first
this.addSearchProvider(new Wanda.WandaSearchProvider()); this.addSearchProvider(new Wanda.WandaSearchProvider());
this.addSearchProvider(new AppDisplay.AppSearchProvider()); this.addSearchProvider(new AppDisplay.AppSearchProvider());
this.addSearchProvider(new AppDisplay.SettingsSearchProvider());
// Load remote search providers provided by applications // Load remote search providers provided by applications
RemoteSearch.loadRemoteSearchProviders(Lang.bind(this, this.addSearchProvider)); RemoteSearch.loadRemoteSearchProviders(Lang.bind(this, this.addSearchProvider));
@ -128,6 +113,9 @@ const ViewSelector = new Lang.Class({
global.focus_manager.add_group(this._searchResults.actor); global.focus_manager.add_group(this._searchResults.actor);
Main.overview.connect('item-drag-begin',
Lang.bind(this, this._resetShowAppsButton));
this._stageKeyPressId = 0; this._stageKeyPressId = 0;
Main.overview.connect('showing', Lang.bind(this, Main.overview.connect('showing', Lang.bind(this,
function () { function () {
@ -147,8 +135,8 @@ const ViewSelector = new Lang.Class({
Main.wm.addKeybinding('toggle-application-view', Main.wm.addKeybinding('toggle-application-view',
new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }), new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }),
Meta.KeyBindingFlags.NONE, Meta.KeyBindingFlags.NONE,
Shell.KeyBindingMode.NORMAL | Main.KeybindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW, Main.KeybindingMode.OVERVIEW,
Lang.bind(this, this._toggleAppsPage)); Lang.bind(this, this._toggleAppsPage));
}, },
@ -160,7 +148,6 @@ const ViewSelector = new Lang.Class({
show: function() { show: function() {
this._activePage = this._workspacesPage; this._activePage = this._workspacesPage;
this.reset();
this._appsPage.hide(); this._appsPage.hide();
this._searchPage.hide(); this._searchPage.hide();
this._workspacesDisplay.show(); this._workspacesDisplay.show();
@ -168,7 +155,7 @@ const ViewSelector = new Lang.Class({
if (!this._workspacesDisplay.activeWorkspaceHasMaximizedWindows()) if (!this._workspacesDisplay.activeWorkspaceHasMaximizedWindows())
Main.overview.fadeOutDesktop(); Main.overview.fadeOutDesktop();
this._showPage(this._workspacesPage, true); this._showPage(this._workspacesPage);
}, },
zoomFromOverview: function() { zoomFromOverview: function() {
@ -182,16 +169,14 @@ const ViewSelector = new Lang.Class({
this._workspacesDisplay.hide(); this._workspacesDisplay.hide();
}, },
_addPage: function(actor, name, a11yIcon, params) { _addPage: function(actor, a11yFocus, name, a11yIcon) {
params = Params.parse(params, { a11yFocus: null });
let page = new St.Bin({ child: actor, let page = new St.Bin({ child: actor,
x_align: St.Align.START, x_align: St.Align.START,
y_align: St.Align.START, y_align: St.Align.START,
x_fill: true, x_fill: true,
y_fill: true }); y_fill: true });
if (params.a11yFocus) if (a11yFocus)
Main.ctrlAltTabManager.addGroup(params.a11yFocus, name, a11yIcon); Main.ctrlAltTabManager.addGroup(a11yFocus, name, a11yIcon);
else else
Main.ctrlAltTabManager.addGroup(actor, name, a11yIcon, Main.ctrlAltTabManager.addGroup(actor, name, a11yIcon,
{ proxy: this.actor, { proxy: this.actor,
@ -204,40 +189,29 @@ const ViewSelector = new Lang.Class({
return page; return page;
}, },
_fadePageIn: function(oldPage) { _showPage: function(page) {
if (oldPage) if(page == this._activePage)
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)
return; return;
let oldPage = this._activePage; if(this._activePage) {
this._activePage = page; Tweener.addTween(this._activePage,
this.emit('page-changed');
if (oldPage && !noFade)
Tweener.addTween(oldPage,
{ opacity: 0, { opacity: 0,
time: OverviewControls.SIDE_CONTROLS_ANIMATION_TIME, time: 0.1,
transition: 'easeOutQuad', transition: 'easeOutQuad',
onComplete: Lang.bind(this, onComplete: Lang.bind(this,
function() { 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) { _a11yFocusPage: function(page) {
@ -246,19 +220,15 @@ const ViewSelector = new Lang.Class({
}, },
_onShowAppsButtonToggled: function() { _onShowAppsButtonToggled: function() {
if (this._showAppsBlocked) if (this.active)
return; this.reset();
else
this._showPage(this._showAppsButton.checked ? this._showPage(this._showAppsButton.checked ? this._appsPage
this._appsPage : this._workspacesPage); : this._workspacesPage);
}, },
_resetShowAppsButton: function() { _resetShowAppsButton: function() {
this._showAppsBlocked = true;
this._showAppsButton.checked = false; this._showAppsButton.checked = false;
this._showAppsBlocked = false;
this._showPage(this._workspacesPage, true);
}, },
_onStageKeyPress: function(actor, event) { _onStageKeyPress: function(actor, event) {
@ -271,16 +241,17 @@ const ViewSelector = new Lang.Class({
let symbol = event.get_key_symbol(); let symbol = event.get_key_symbol();
if (symbol == Clutter.Escape) { if (symbol == Clutter.Escape) {
if (this._searchActive) if (this.active)
this.reset(); this.reset();
else if (this._showAppsButton.checked) else if (this._showAppsButton.checked)
this._showAppsButton.checked = false; this._resetShowAppsButton();
else else
Main.overview.hide(); Main.overview.hide();
return true; return true;
} else if (this._shouldTriggerSearch(symbol)) { } else if (Clutter.keysym_to_unicode(symbol) ||
(symbol == Clutter.BackSpace && this.active)) {
this.startSearch(event); this.startSearch(event);
} else if (!this._searchActive) { } else if (!this.active) {
if (symbol == Clutter.Tab || symbol == Clutter.Down) { if (symbol == Clutter.Tab || symbol == Clutter.Down) {
this._activePage.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false); this._activePage.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
return true; 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) { startSearch: function(event) {
global.stage.set_key_focus(this._text); global.stage.set_key_focus(this._text);
this._text.event(event, true); this._text.event(event, true);
@ -365,39 +325,39 @@ const ViewSelector = new Lang.Class({
}, },
_onTextChanged: function (se, prop) { _onTextChanged: function (se, prop) {
let terms = getTermsForSearchString(this._entry.get_text()); let searchPreviouslyActive = this.active;
this.active = this._entry.get_text() != '';
let searchPreviouslyActive = this._searchActive; this._searchPending = this.active && !searchPreviouslyActive;
this._searchActive = (terms.length > 0); if (this._searchPending) {
let startSearch = this._searchActive && !searchPreviouslyActive;
if (startSearch)
this._searchResults.startingSearch(); this._searchResults.startingSearch();
}
if (this.active) {
this._entry.set_secondary_icon(this._activeIcon);
if (this._searchActive) { if (this._iconClickedId == 0) {
this._entry.set_secondary_icon(this._clearIcon);
if (this._iconClickedId == 0)
this._iconClickedId = this._entry.connect('secondary-icon-clicked', this._iconClickedId = this._entry.connect('secondary-icon-clicked',
Lang.bind(this, this.reset)); Lang.bind(this, function() {
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;
} }
} 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) { if (this._searchTimeoutId > 0) {
Mainloop.source_remove(this._searchTimeoutId); Mainloop.source_remove(this._searchTimeoutId);
this._searchTimeoutId = 0; this._searchTimeoutId = 0;
} }
return;
this._entry.set_secondary_icon(null);
this._searchCancelled();
} }
if (this._searchTimeoutId > 0)
return;
this._searchTimeoutId = Mainloop.timeout_add(150, Lang.bind(this, this._doSearch));
}, },
_onKeyPress: function(entry, event) { _onKeyPress: function(entry, event) {
@ -407,7 +367,16 @@ const ViewSelector = new Lang.Class({
this.reset(); this.reset();
return true; 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; let arrowNext, nextDirection;
if (entry.get_text_direction() == Clutter.TextDirection.RTL) { if (entry.get_text_direction() == Clutter.TextDirection.RTL) {
arrowNext = Clutter.Left; arrowNext = Clutter.Left;
@ -431,15 +400,6 @@ const ViewSelector = new Lang.Class({
} else if (symbol == arrowNext && this._text.position == -1) { } else if (symbol == arrowNext && this._text.position == -1) {
this._searchResults.navigateFocus(nextDirection); this._searchResults.navigateFocus(nextDirection);
return true; 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; return false;
@ -462,10 +422,9 @@ const ViewSelector = new Lang.Class({
_doSearch: function () { _doSearch: function () {
this._searchTimeoutId = 0; 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); this._showPage(this._searchPage);
}, },
@ -502,31 +461,6 @@ const ViewSelector = new Lang.Class({
removeSearchProvider: function(provider) { removeSearchProvider: function(provider) {
this._searchSystem.unregisterProvider(provider); this._searchSystem.unregisterProvider(provider);
this._searchResults.destroyProviderMeta(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); Signals.addSignalMethods(ViewSelector.prototype);

View File

@ -105,75 +105,84 @@ const WindowManager = new Lang.Class({
this._workspaceSwitcherPopup = null; this._workspaceSwitcherPopup = null;
this.setCustomKeybindingHandler('switch-to-workspace-left', this.setCustomKeybindingHandler('switch-to-workspace-left',
Shell.KeyBindingMode.NORMAL | Main.KeybindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW, Main.KeybindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher)); Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('switch-to-workspace-right', this.setCustomKeybindingHandler('switch-to-workspace-right',
Shell.KeyBindingMode.NORMAL | Main.KeybindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW, Main.KeybindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher)); Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('switch-to-workspace-up', this.setCustomKeybindingHandler('switch-to-workspace-up',
Shell.KeyBindingMode.NORMAL | Main.KeybindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW, Main.KeybindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher)); Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('switch-to-workspace-down', this.setCustomKeybindingHandler('switch-to-workspace-down',
Shell.KeyBindingMode.NORMAL | Main.KeybindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW, Main.KeybindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher)); Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('move-to-workspace-left', this.setCustomKeybindingHandler('move-to-workspace-left',
Shell.KeyBindingMode.NORMAL | Main.KeybindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW, Main.KeybindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher)); Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('move-to-workspace-right', this.setCustomKeybindingHandler('move-to-workspace-right',
Shell.KeyBindingMode.NORMAL | Main.KeybindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW, Main.KeybindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher)); Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('move-to-workspace-up', this.setCustomKeybindingHandler('move-to-workspace-up',
Shell.KeyBindingMode.NORMAL | Main.KeybindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW, Main.KeybindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher)); Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('move-to-workspace-down', this.setCustomKeybindingHandler('move-to-workspace-down',
Shell.KeyBindingMode.NORMAL | Main.KeybindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW, Main.KeybindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher)); Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('switch-applications', this.setCustomKeybindingHandler('switch-applications',
Shell.KeyBindingMode.NORMAL, Main.KeybindingMode.NORMAL,
Lang.bind(this, this._startAppSwitcher)); Lang.bind(this, this._startAppSwitcher));
this.setCustomKeybindingHandler('switch-group', this.setCustomKeybindingHandler('switch-group',
Shell.KeyBindingMode.NORMAL, Main.KeybindingMode.NORMAL,
Lang.bind(this, this._startAppSwitcher)); Lang.bind(this, this._startAppSwitcher));
this.setCustomKeybindingHandler('switch-applications-backward', this.setCustomKeybindingHandler('switch-applications-backward',
Shell.KeyBindingMode.NORMAL, Main.KeybindingMode.NORMAL,
Lang.bind(this, this._startAppSwitcher)); Lang.bind(this, this._startAppSwitcher));
this.setCustomKeybindingHandler('switch-group-backward', this.setCustomKeybindingHandler('switch-group-backward',
Shell.KeyBindingMode.NORMAL, Main.KeybindingMode.NORMAL,
Lang.bind(this, this._startAppSwitcher)); Lang.bind(this, this._startAppSwitcher));
this.setCustomKeybindingHandler('switch-windows', this.setCustomKeybindingHandler('switch-windows',
Shell.KeyBindingMode.NORMAL, Main.KeybindingMode.NORMAL,
Lang.bind(this, this._startWindowSwitcher)); Lang.bind(this, this._startWindowSwitcher));
this.setCustomKeybindingHandler('switch-windows-backward', this.setCustomKeybindingHandler('switch-windows-backward',
Shell.KeyBindingMode.NORMAL, Main.KeybindingMode.NORMAL,
Lang.bind(this, this._startWindowSwitcher)); Lang.bind(this, this._startWindowSwitcher));
this.setCustomKeybindingHandler('switch-panels', this.setCustomKeybindingHandler('switch-panels',
Shell.KeyBindingMode.NORMAL | Main.KeybindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW | Main.KeybindingMode.OVERVIEW |
Shell.KeyBindingMode.LOCK_SCREEN | Main.KeybindingMode.LOCK_SCREEN |
Shell.KeyBindingMode.UNLOCK_SCREEN | Main.KeybindingMode.UNLOCK_SCREEN |
Shell.KeyBindingMode.LOGIN_SCREEN, Main.KeybindingMode.LOGIN_SCREEN,
Lang.bind(this, this._startA11ySwitcher)); Lang.bind(this, this._startA11ySwitcher));
this.setCustomKeybindingHandler('switch-panels-backward', this.setCustomKeybindingHandler('switch-panels-backward',
Shell.KeyBindingMode.NORMAL | Main.KeybindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW | Main.KeybindingMode.OVERVIEW |
Shell.KeyBindingMode.LOCK_SCREEN | Main.KeybindingMode.LOCK_SCREEN |
Shell.KeyBindingMode.UNLOCK_SCREEN | Main.KeybindingMode.UNLOCK_SCREEN |
Shell.KeyBindingMode.LOGIN_SCREEN, Main.KeybindingMode.LOGIN_SCREEN,
Lang.bind(this, this._startA11ySwitcher)); Lang.bind(this, this._startA11ySwitcher));
this.addKeybinding('open-application-menu', this.addKeybinding('open-application-menu',
new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }), new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }),
Meta.KeyBindingFlags.NONE, Meta.KeyBindingFlags.NONE,
Shell.KeyBindingMode.NORMAL, Main.KeybindingMode.NORMAL,
Lang.bind(this, this._openAppMenu)); Lang.bind(this, this._openAppMenu));
Main.overview.connect('showing', Lang.bind(this, function() {
for (let i = 0; i < this._dimmedWindows.length; i++)
this._undimWindow(this._dimmedWindows[i]);
}));
Main.overview.connect('hiding', Lang.bind(this, function() {
for (let i = 0; i < this._dimmedWindows.length; i++)
this._dimWindow(this._dimmedWindows[i]);
}));
}, },
setCustomKeybindingHandler: function(name, modes, handler) { setCustomKeybindingHandler: function(name, modes, handler) {
@ -190,7 +199,7 @@ const WindowManager = new Lang.Class({
removeKeybinding: function(name) { removeKeybinding: function(name) {
if (global.display.remove_keybinding(name)) if (global.display.remove_keybinding(name))
this.allowKeybinding(name, Shell.KeyBindingMode.NONE); this.allowKeybinding(name, Main.KeybindingMode.NONE);
}, },
allowKeybinding: function(name, modes) { allowKeybinding: function(name, modes) {
@ -233,12 +242,24 @@ const WindowManager = new Lang.Class({
} }
actor.set_scale(1.0, 1.0); 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); this._minimizing.push(actor);
if (actor.meta_window.is_monitor_sized()) { let monitor = Main.layoutManager.findMonitorForWindow(actor.meta_window);
Tweener.addTween(actor, let xDest = monitor.x;
{ opacity: 0, 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, time: WINDOW_ANIMATION_TIME,
transition: 'easeOutQuad', transition: 'easeOutQuad',
onComplete: this._minimizeWindowDone, onComplete: this._minimizeWindowDone,
@ -248,46 +269,12 @@ const WindowManager = new Lang.Class({
onOverwriteScope: this, onOverwriteScope: this,
onOverwriteParams: [shellwm, actor] 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) { _minimizeWindowDone : function(shellwm, actor) {
if (this._removeEffect(this._minimizing, actor)) { if (this._removeEffect(this._minimizing, actor)) {
Tweener.removeTweens(actor); Tweener.removeTweens(actor);
actor.set_scale(1.0, 1.0); actor.set_scale(1.0, 1.0);
actor.set_opacity(255);
actor.move_anchor_point_from_gravity(Clutter.Gravity.NORTH_WEST); actor.move_anchor_point_from_gravity(Clutter.Gravity.NORTH_WEST);
shellwm.completed_minimize(actor); shellwm.completed_minimize(actor);
@ -333,13 +320,15 @@ const WindowManager = new Lang.Class({
if (shouldDim && !window._dimmed) { if (shouldDim && !window._dimmed) {
window._dimmed = true; window._dimmed = true;
this._dimmedWindows.push(window); this._dimmedWindows.push(window);
this._dimWindow(window); if (!Main.overview.visible)
this._dimWindow(window);
} else if (!shouldDim && window._dimmed) { } else if (!shouldDim && window._dimmed) {
window._dimmed = false; window._dimmed = false;
this._dimmedWindows = this._dimmedWindows.filter(function(win) { this._dimmedWindows = this._dimmedWindows.filter(function(win) {
return win != window; return win != window;
}); });
this._undimWindow(window); if (!Main.overview.visible)
this._undimWindow(window);
} }
}, },
@ -505,14 +494,14 @@ const WindowManager = new Lang.Class({
}, },
_filterKeybinding: function(shellwm, binding) { _filterKeybinding: function(shellwm, binding) {
if (Main.keybindingMode == Shell.KeyBindingMode.NONE) if (Main.keybindingMode == Main.KeybindingMode.NONE)
return true; return true;
// There's little sense in implementing a keybinding in mutter and // There's little sense in implementing a keybinding in mutter and
// not having it work in NORMAL mode; handle this case generically // not having it work in NORMAL mode; handle this case generically
// so we don't have to explicitly allow all builtin keybindings in // so we don't have to explicitly allow all builtin keybindings in
// NORMAL mode. // NORMAL mode.
if (Main.keybindingMode == Shell.KeyBindingMode.NORMAL && if (Main.keybindingMode == Main.KeybindingMode.NORMAL &&
binding.is_builtin()) binding.is_builtin())
return false; return false;
@ -536,11 +525,11 @@ const WindowManager = new Lang.Class({
if (direction == Meta.MotionDirection.UP || if (direction == Meta.MotionDirection.UP ||
direction == Meta.MotionDirection.UP_LEFT || direction == Meta.MotionDirection.UP_LEFT ||
direction == Meta.MotionDirection.UP_RIGHT) direction == Meta.MotionDirection.UP_RIGHT)
yDest = global.screen_height - Main.panel.actor.height; yDest = global.screen_height;
else if (direction == Meta.MotionDirection.DOWN || else if (direction == Meta.MotionDirection.DOWN ||
direction == Meta.MotionDirection.DOWN_LEFT || direction == Meta.MotionDirection.DOWN_LEFT ||
direction == Meta.MotionDirection.DOWN_RIGHT) direction == Meta.MotionDirection.DOWN_RIGHT)
yDest = -global.screen_height + Main.panel.actor.height; yDest = -global.screen_height;
if (direction == Meta.MotionDirection.LEFT || if (direction == Meta.MotionDirection.LEFT ||
direction == Meta.MotionDirection.UP_LEFT || direction == Meta.MotionDirection.UP_LEFT ||
@ -553,9 +542,9 @@ const WindowManager = new Lang.Class({
let switchData = {}; let switchData = {};
this._switchData = switchData; this._switchData = switchData;
switchData.inGroup = new Clutter.Actor(); switchData.inGroup = new Clutter.Group();
switchData.outGroup = new Clutter.Actor(); switchData.outGroup = new Clutter.Group();
switchData.movingWindowBin = new Clutter.Actor(); switchData.movingWindowBin = new Clutter.Group();
switchData.windows = []; switchData.windows = [];
let wgroup = global.window_group; let wgroup = global.window_group;
@ -582,7 +571,7 @@ const WindowManager = new Lang.Class({
switchData.windows.push({ window: window, switchData.windows.push({ window: window,
parent: window.get_parent() }); parent: window.get_parent() });
window.reparent(switchData.inGroup); window.reparent(switchData.inGroup);
window.show(); window.show_all();
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -55,9 +55,10 @@ const WorkspaceSwitcherPopup = new Lang.Class({
_getPreferredHeight : function (actor, forWidth, alloc) { _getPreferredHeight : function (actor, forWidth, alloc) {
let children = this._list.get_children(); 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.actor.get_theme_node().get_vertical_padding();
availHeight -= this._container.get_theme_node().get_vertical_padding(); availHeight -= this._container.get_theme_node().get_vertical_padding();
availHeight -= this._list.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++) { for (let i = 0; i < children.length; i++) {
let [childMinHeight, childNaturalHeight] = children[i].get_preferred_height(-1); let [childMinHeight, childNaturalHeight] = children[i].get_preferred_height(-1);
let [childMinWidth, childNaturalWidth] = children[i].get_preferred_width(childNaturalHeight); 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); let spacing = this._itemSpacing * (global.screen.n_workspaces - 1);
@ -80,8 +81,8 @@ const WorkspaceSwitcherPopup = new Lang.Class({
}, },
_getPreferredWidth : function (actor, forHeight, alloc) { _getPreferredWidth : function (actor, forHeight, alloc) {
let workArea = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex); let primary = Main.layoutManager.primaryMonitor;
this._childWidth = Math.round(this._childHeight * workArea.width / workArea.height); this._childWidth = Math.round(this._childHeight * primary.width / primary.height);
alloc.min_size = this._childWidth; alloc.min_size = this._childWidth;
alloc.natural_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 [containerMinHeight, containerNatHeight] = this._container.get_preferred_height(global.screen_width);
let [containerMinWidth, containerNatWidth] = this._container.get_preferred_width(containerNatHeight); let [containerMinWidth, containerNatWidth] = this._container.get_preferred_width(containerNatHeight);
this._container.x = workArea.x + Math.floor((workArea.width - containerNatWidth) / 2); this._container.x = primary.x + Math.floor((primary.width - containerNatWidth) / 2);
this._container.y = workArea.y + Math.floor((workArea.height - containerNatHeight) / 2); this._container.y = primary.y + Main.panel.actor.height +
Math.floor(((primary.height - Main.panel.actor.height) - containerNatHeight) / 2);
}, },
_show : function() { _show : function() {

View File

@ -9,11 +9,9 @@ const Shell = imports.gi.Shell;
const Signals = imports.signals; const Signals = imports.signals;
const St = imports.gi.St; const St = imports.gi.St;
const Background = imports.ui.background;
const DND = imports.ui.dnd; const DND = imports.ui.dnd;
const Main = imports.ui.main; const Main = imports.ui.main;
const Tweener = imports.ui.tweener; const Tweener = imports.ui.tweener;
const WindowManager = imports.ui.windowManager;
const Workspace = imports.ui.workspace; const Workspace = imports.ui.workspace;
const WorkspacesView = imports.ui.workspacesView; const WorkspacesView = imports.ui.workspacesView;
@ -32,49 +30,20 @@ const WORKSPACE_KEEP_ALIVE_TIME = 100;
const OVERRIDE_SCHEMA = 'org.gnome.shell.overrides'; const OVERRIDE_SCHEMA = 'org.gnome.shell.overrides';
/* A layout manager that requests size only for primary_actor, but then allocates
all using a fixed layout */
const PrimaryActorLayout = new Lang.Class({
Name: 'PrimaryActorLayout',
Extends: Clutter.FixedLayout,
_init: function(primaryActor) {
this.parent();
this.primaryActor = primaryActor;
},
vfunc_get_preferred_width: function(forHeight) {
return this.primaryActor.get_preferred_width(forHeight);
},
vfunc_get_preferred_height: function(forWidth) {
return this.primaryActor.get_preferred_height(forWidth);
},
});
const WindowClone = new Lang.Class({ const WindowClone = new Lang.Class({
Name: 'WindowClone', Name: 'WindowClone',
_init : function(realWindow) { _init : function(realWindow) {
this.clone = new Clutter.Clone({ source: realWindow }); this.actor = new Clutter.Clone({ source: realWindow.get_texture(),
/* Can't use a Shell.GenericContainer because of DND and reparenting... */
this.actor = new Clutter.Actor({ layout_manager: new PrimaryActorLayout(this.clone),
reactive: true }); reactive: true });
this.actor._delegate = this; this.actor._delegate = this;
this.actor.add_child(this.clone);
this.realWindow = realWindow; this.realWindow = realWindow;
this.metaWindow = realWindow.meta_window; this.metaWindow = realWindow.meta_window;
this.clone._updateId = this.realWindow.connect('position-changed', this._positionChangedId = this.realWindow.connect('position-changed',
Lang.bind(this, this._onPositionChanged)); Lang.bind(this, this._onPositionChanged));
this.clone._destroyId = this.realWindow.connect('destroy', Lang.bind(this, function() { this._realWindowDestroyedId = this.realWindow.connect('destroy',
// First destroy the clone and then destroy everything Lang.bind(this, this._disconnectRealWindowSignals));
// This will ensure that we never see it in the _disconnectSignals loop
this.clone.destroy();
this.destroy();
}));
this._onPositionChanged(); this._onPositionChanged();
this.actor.connect('button-release-event', this.actor.connect('button-release-event',
@ -90,24 +59,6 @@ const WindowClone = new Lang.Class({
this._draggable.connect('drag-cancelled', Lang.bind(this, this._onDragCancelled)); this._draggable.connect('drag-cancelled', Lang.bind(this, this._onDragCancelled));
this._draggable.connect('drag-end', Lang.bind(this, this._onDragEnd)); this._draggable.connect('drag-end', Lang.bind(this, this._onDragEnd));
this.inDrag = false; this.inDrag = false;
let iter = Lang.bind(this, function(win) {
let actor = win.get_compositor_private();
if (!actor)
return false;
if (!win.is_attached_dialog())
return false;
this._doAddAttachedDialog(win, actor);
win.foreach_transient(iter);
return true;
});
this.metaWindow.foreach_transient(iter);
this._dimmer = new WindowManager.WindowDimmer(this.clone);
this._updateDimmer();
}, },
setStackAbove: function (actor) { setStackAbove: function (actor) {
@ -122,57 +73,25 @@ const WindowClone = new Lang.Class({
this.actor.destroy(); this.actor.destroy();
}, },
addAttachedDialog: function(win) {
this._doAddAttachedDialog(win, win.get_compositor_private());
this._updateDimmer();
},
_doAddAttachedDialog: function(metaDialog, realDialog) {
let clone = new Clutter.Clone({ source: realDialog });
this._updateDialogPosition(realDialog, clone);
clone._updateId = realDialog.connect('position-changed',
Lang.bind(this, this._updateDialogPosition, clone));
clone._destroyId = realDialog.connect('destroy', Lang.bind(this, function() {
clone.destroy();
this._updateDimmer();
}));
this.actor.add_child(clone);
},
_updateDimmer: function() {
if (this.actor.get_n_children() > 1) {
this._dimmer.setEnabled(true);
this._dimmer.dimFactor = 1.0;
} else {
this._dimmer.setEnabled(false);
}
},
_updateDialogPosition: function(realDialog, cloneDialog) {
let metaDialog = realDialog.meta_window;
let dialogRect = metaDialog.get_outer_rect();
let rect = this.metaWindow.get_outer_rect();
cloneDialog.set_position(dialogRect.x - rect.x, dialogRect.y - rect.y);
},
_onPositionChanged: function() { _onPositionChanged: function() {
let rect = this.metaWindow.get_outer_rect(); let rect = this.metaWindow.get_outer_rect();
this.actor.set_position(this.realWindow.x, this.realWindow.y); this.actor.set_position(this.realWindow.x, this.realWindow.y);
}, },
_disconnectSignals: function() { _disconnectRealWindowSignals: function() {
this.actor.get_children().forEach(function(child) { if (this._positionChangedId != 0) {
let realWindow = child.source; this.realWindow.disconnect(this._positionChangedId);
this._positionChangedId = 0;
}
realWindow.disconnect(child._updateId); if (this._realWindowDestroyedId != 0) {
realWindow.disconnect(child._destroyId); this.realWindow.disconnect(this._realWindowDestroyedId);
}); this._realWindowDestroyedId = 0;
}
}, },
_onDestroy: function() { _onDestroy: function() {
this._disconnectSignals(); this._disconnectRealWindowSignals();
this.actor._delegate = null; this.actor._delegate = null;
@ -246,20 +165,18 @@ const WorkspaceThumbnail = new Lang.Class({
style_class: 'workspace-thumbnail' }); style_class: 'workspace-thumbnail' });
this.actor._delegate = this; this.actor._delegate = this;
this._contents = new Clutter.Actor(); this._contents = new Clutter.Group();
this.actor.add_child(this._contents); this.actor.add_actor(this._contents);
this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); 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; let monitor = Main.layoutManager.primaryMonitor;
this.setPorthole(monitor.x, monitor.y, monitor.width, monitor.height); this.setPorthole(monitor.x, monitor.y, monitor.width, monitor.height);
let windows = global.get_window_actors().filter(Lang.bind(this, function(actor) { let windows = global.get_window_actors().filter(this._isWorkspaceWindow, this);
let win = actor.meta_window;
return win.located_on_workspace(metaWorkspace);
}));
// Create clones for windows that should be visible in the Overview // Create clones for windows that should be visible in the Overview
this._windows = []; this._windows = [];
@ -293,12 +210,6 @@ const WorkspaceThumbnail = new Lang.Class({
this._collapseFraction = 0; // Not collapsed 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) { setPorthole: function(x, y, width, height) {
this._portholeX = x; this._portholeX = x;
this._portholeY = y; this._portholeY = y;
@ -322,7 +233,7 @@ const WorkspaceThumbnail = new Lang.Class({
let clone = this._windows[i]; let clone = this._windows[i];
let metaWindow = clone.metaWindow; let metaWindow = clone.metaWindow;
if (i == 0) { if (i == 0) {
clone.setStackAbove(this._bgManager.background.actor); clone.setStackAbove(this._background);
} else { } else {
let previousClone = this._windows[i - 1]; let previousClone = this._windows[i - 1];
clone.setStackAbove(previousClone.actor); clone.setStackAbove(previousClone.actor);
@ -400,26 +311,10 @@ const WorkspaceThumbnail = new Lang.Class({
if (this._lookupIndex (metaWin) != -1) if (this._lookupIndex (metaWin) != -1)
return; return;
if (!this._isMyWindow(win)) if (!this._isMyWindow(win) || !this._isOverviewWindow(win))
return; return;
if (this._isOverviewWindow(win)) { let clone = this._addWindowClone(win);
this._addWindowClone(win);
} else if (metaWin.is_attached_dialog()) {
let parent = metaWin.get_transient_for();
while (parent.is_attached_dialog())
parent = metaWin.get_transient_for();
let idx = this._lookupIndex (parent);
if (idx < 0) {
// parent was not created yet, it will take care
// of the dialog when created
return;
}
let clone = this._windows[idx];
clone.addAttachedDialog(metaWin);
}
}, },
_windowAdded : function(metaWorkspace, metaWin) { _windowAdded : function(metaWorkspace, metaWin) {
@ -458,8 +353,6 @@ const WorkspaceThumbnail = new Lang.Class({
destroy : function() { destroy : function() {
this.actor.destroy(); this.actor.destroy();
this._bgManager.destroy();
this._bgManager = null;
}, },
workspaceRemoved : function() { workspaceRemoved : function() {
@ -484,11 +377,15 @@ const WorkspaceThumbnail = new Lang.Class({
this.actor = null; this.actor = null;
}, },
// Tests if @actor belongs to this workspace and monitor // Tests if @win belongs to this workspace
_isMyWindow : function (actor) { _isWorkspaceWindow : function (win) {
let win = actor.meta_window; return Main.isWindowActorDisplayedOnWorkspace(win, this.metaWorkspace.index());
return win.located_on_workspace(this.metaWorkspace) && },
(win.get_monitor() == this.monitorIndex);
// 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 // Tests if @win should be shown in the Overview
@ -521,7 +418,7 @@ const WorkspaceThumbnail = new Lang.Class({
this._contents.add_actor(clone.actor); this._contents.add_actor(clone.actor);
if (this._windows.length == 0) if (this._windows.length == 0)
clone.setStackAbove(this._bgManager.background.actor); clone.setStackAbove(this._background);
else else
clone.setStackAbove(this._windows[this._windows.length - 1].actor); clone.setStackAbove(this._windows[this._windows.length - 1].actor);
@ -647,6 +544,7 @@ const ThumbnailsBox = new Lang.Class({
this.actor.connect('button-press-event', function() { return true; }); this.actor.connect('button-press-event', function() { return true; });
this.actor.connect('button-release-event', Lang.bind(this, this._onButtonRelease)); 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', Main.overview.connect('showing',
Lang.bind(this, this._createThumbnails)); Lang.bind(this, this._createThumbnails));
@ -685,7 +583,7 @@ const ThumbnailsBox = new Lang.Class({
let thumbnail = this._thumbnails[i] let thumbnail = this._thumbnails[i]
let [w, h] = thumbnail.actor.get_transformed_size(); let [w, h] = thumbnail.actor.get_transformed_size();
if (y >= thumbnail.actor.y && y <= thumbnail.actor.y + h) { if (y >= thumbnail.actor.y && y <= thumbnail.actor.y + h) {
thumbnail.activate(event.get_time()); thumbnail.activate(event.time);
break; break;
} }
} }
@ -861,7 +759,14 @@ const ThumbnailsBox = new Lang.Class({
this._stateCounts[ThumbnailState[key]] = 0; this._stateCounts[ThumbnailState[key]] = 0;
// The "porthole" is the portion of the screen that we show in the workspaces // 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); this.addThumbnails(0, global.screen.n_workspaces);
@ -1095,6 +1000,8 @@ const ThumbnailsBox = new Lang.Class({
// See comment about this._background in _init() // See comment about this._background in _init()
let themeNode = this._background.get_theme_node(); 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 // 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 // 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. // that the actors aren't depending on the virtual functions being called.
@ -1310,5 +1217,16 @@ const ThumbnailsBox = new Lang.Class({
}, },
onCompleteScope: this 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;
}
} }
}); });

View File

@ -23,13 +23,14 @@ const MAX_WORKSPACES = 16;
const OVERRIDE_SCHEMA = 'org.gnome.shell.overrides'; const OVERRIDE_SCHEMA = 'org.gnome.shell.overrides';
const CONTROLS_POP_IN_TIME = 0.1;
const WorkspacesView = new Lang.Class({ const WorkspacesView = new Lang.Class({
Name: 'WorkspacesView', Name: 'WorkspacesView',
_init: function(workspaces) { _init: function(workspaces) {
this.actor = new St.Widget({ style_class: 'workspaces-view', this.actor = new St.Widget({ style_class: 'workspaces-view' });
reactive: true });
// The actor itself isn't a drop target, so we don't want to pick on its area // The actor itself isn't a drop target, so we don't want to pick on its area
this.actor.set_size(0, 0); this.actor.set_size(0, 0);
@ -47,6 +48,10 @@ const WorkspacesView = new Lang.Class({
this._height = 0; this._height = 0;
this._x = 0; this._x = 0;
this._y = 0; this._y = 0;
this._clipX = 0;
this._clipY = 0;
this._clipWidth = 0;
this._clipHeight = 0;
this._spacing = 0; this._spacing = 0;
this._animating = false; // tweening this._animating = false; // tweening
this._scrolling = false; // swipe-scrolling this._scrolling = false; // swipe-scrolling
@ -85,8 +90,8 @@ const WorkspacesView = new Lang.Class({
this._overviewShownId = this._overviewShownId =
Main.overview.connect('shown', Main.overview.connect('shown',
Lang.bind(this, function() { Lang.bind(this, function() {
this.actor.set_clip(this._x, this._y, this.actor.set_clip(this._clipX, this._clipY,
this._width, this._height); this._clipWidth, this._clipHeight);
})); }));
this.scrollAdjustment = new St.Adjustment({ value: activeWorkspaceIndex, this.scrollAdjustment = new St.Adjustment({ value: activeWorkspaceIndex,
@ -153,6 +158,13 @@ const WorkspacesView = new Lang.Class({
this._workspaces[i].setGeometry(x, y, width, height); 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) { _lookupWorkspaceForMetaWindow: function (metaWindow) {
for (let i = 0; i < this._workspaces.length; i++) { for (let i = 0; i < this._workspaces.length; i++) {
if (this._workspaces[i].containsMetaWindow(metaWindow)) if (this._workspaces[i].containsMetaWindow(metaWindow))
@ -191,6 +203,11 @@ const WorkspacesView = new Lang.Class({
this._extraWorkspaces[i].syncStacking(stackIndices); 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() { _scrollToActive: function() {
let active = global.screen.get_active_workspace_index(); let active = global.screen.get_active_workspace_index();
@ -420,9 +437,23 @@ const WorkspacesDisplay = new Lang.Class({
Name: 'WorkspacesDisplay', Name: 'WorkspacesDisplay',
_init: function() { _init: function() {
this.actor = new St.Widget({ clip_to_allocation: true }); this.actor = new Shell.GenericContainer({ style_class: 'workspaces-display' });
this.actor.connect('notify::allocation', Lang.bind(this, this._updateWorkspacesGeometry)); 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.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() let clickAction = new Clutter.ClickAction()
clickAction.connect('clicked', Lang.bind(this, function(action) { clickAction.connect('clicked', Lang.bind(this, function(action) {
@ -443,11 +474,6 @@ const WorkspacesDisplay = new Lang.Class({
this._workspacesViews[i].startSwipeScroll(); this._workspacesViews[i].startSwipeScroll();
return true; 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() { panAction.connect('gesture-end', Lang.bind(this, function() {
clickAction.release(); clickAction.release();
for (let i = 0; i < this._workspacesViews.length; i++) for (let i = 0; i < this._workspacesViews.length; i++)
@ -456,8 +482,23 @@ const WorkspacesDisplay = new Lang.Class({
Main.overview.addAction(panAction); Main.overview.addAction(panAction);
this.actor.bind_property('mapped', panAction, 'enabled', GObject.BindingFlags.SYNC_CREATE); 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._primaryIndex = Main.layoutManager.primaryIndex;
this._thumbnailsBox = new WorkspaceThumbnail.ThumbnailsBox();
controls.add_actor(this._thumbnailsBox.actor);
this._workspacesViews = []; this._workspacesViews = [];
this._workspaces = []; this._workspaces = [];
this._primaryScrollAdjustment = null; this._primaryScrollAdjustment = null;
@ -468,13 +509,43 @@ const WorkspacesDisplay = new Lang.Class({
this._workspacesOnlyOnPrimaryChanged)); this._workspacesOnlyOnPrimaryChanged));
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', global.screen.connect('notify::n-workspaces',
Lang.bind(this, this._workspacesChanged)); Lang.bind(this, this._workspacesChanged));
this._switchWorkspaceNotifyId = 0; 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._notifyOpacityId = 0;
this._scrollEventId = 0; this._swipeScrollBeginId = 0;
this._swipeScrollEndId = 0;
}, },
_onPan: function(action) { _onPan: function(action) {
@ -485,13 +556,49 @@ const WorkspacesDisplay = new Lang.Class({
}, },
show: function() { 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._updateWorkspacesViews();
this._restackedNotifyId = this._restackedNotifyId =
Main.overview.connect('windows-restacked', Main.overview.connect('windows-restacked',
Lang.bind(this, this._onRestacked)); 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() { zoomFromOverview: function() {
@ -501,13 +608,38 @@ const WorkspacesDisplay = new Lang.Class({
}, },
hide: function() { hide: function() {
this._controls.hide();
if (!this._alwaysZoomOut)
this.zoomFraction = 0;
if (this._restackedNotifyId > 0){ if (this._restackedNotifyId > 0){
Main.overview.disconnect(this._restackedNotifyId); Main.overview.disconnect(this._restackedNotifyId);
this._restackedNotifyId = 0; this._restackedNotifyId = 0;
} }
if (this._scrollEventId > 0) { if (this._itemDragBeginId > 0) {
Main.overview.disconnect(this._scrollEventId); Main.overview.disconnect(this._itemDragBeginId);
this._scrollEventId = 0; 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++) for (let i = 0; i < this._workspacesViews.length; i++)
@ -554,7 +686,6 @@ const WorkspacesDisplay = new Lang.Class({
this._workspaces.push(monitorWorkspaces); this._workspaces.push(monitorWorkspaces);
let view = new WorkspacesView(monitorWorkspaces); let view = new WorkspacesView(monitorWorkspaces);
view.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
if (this._workspacesOnlyOnPrimary || i == this._primaryIndex) { if (this._workspacesOnlyOnPrimary || i == this._primaryIndex) {
this._scrollAdjustment = view.scrollAdjustment; this._scrollAdjustment = view.scrollAdjustment;
this._scrollAdjustment.connect('notify::value', this._scrollAdjustment.connect('notify::value',
@ -597,6 +728,76 @@ const WorkspacesDisplay = new Lang.Class({
return this._getPrimaryView().getActiveWorkspace().hasMaximizedWindows(); 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) { _parentSet: function(actor, oldParent) {
if (oldParent && this._notifyOpacityId) if (oldParent && this._notifyOpacityId)
oldParent.disconnect(this._notifyOpacityId); oldParent.disconnect(this._notifyOpacityId);
@ -633,17 +834,37 @@ const WorkspacesDisplay = new Lang.Class({
let width = fullWidth; let width = fullWidth;
let height = fullHeight; 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 [x, y] = this.actor.get_transformed_position();
let rtl = (Clutter.get_default_text_direction () == Clutter.TextDirection.RTL); 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 monitors = Main.layoutManager.monitors;
let m = 0; let m = 0;
for (let i = 0; i < monitors.length; i++) { for (let i = 0; i < monitors.length; i++) {
if (i == this._primaryIndex) { if (i == this._primaryIndex) {
this._workspacesViews[m].setClipRect(clipX, clipY,
clipWidth, clipHeight);
this._workspacesViews[m].setGeometry(x, y, width, height); this._workspacesViews[m].setGeometry(x, y, width, height);
m++; m++;
} else if (!this._workspacesOnlyOnPrimary) { } 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, this._workspacesViews[m].setGeometry(monitors[i].x,
monitors[i].y, monitors[i].y,
monitors[i].width, monitors[i].width,
@ -659,6 +880,9 @@ const WorkspacesDisplay = new Lang.Class({
}, },
_workspacesChanged: function() { _workspacesChanged: function() {
this._updateAlwaysZoom();
this._updateZoom();
if (!this._workspacesViews.length) if (!this._workspacesViews.length)
return; return;
@ -712,18 +936,66 @@ const WorkspacesDisplay = new Lang.Class({
newNumWorkspaces); newNumWorkspaces);
}, },
_onScrollEvent: function(actor, event) { _updateZoom : function() {
if (!this.actor.mapped) if (Main.overview.animationInProgress)
return false; return;
switch (event.get_scroll_direction()) {
case Clutter.ScrollDirection.UP: let shouldZoom = this._alwaysZoomOut || this._controls.hover;
Main.wm.actionMoveWorkspace(Meta.MotionDirection.UP); if (shouldZoom != this._zoomOut) {
return true; this._zoomOut = shouldZoom;
case Clutter.ScrollDirection.DOWN: this._updateWorkspacesGeometry();
Main.wm.actionMoveWorkspace(Meta.MotionDirection.DOWN);
return true; 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); Signals.addSignalMethods(WorkspacesDisplay.prototype);

View File

@ -2,7 +2,6 @@
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const Lang = imports.lang; const Lang = imports.lang;
const Main = imports.ui.main;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const Signals = imports.signals; const Signals = imports.signals;
const DND = imports.ui.dnd; 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 // 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 }); 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); Shell.util_set_hidden_from_pick(this._dummy, true);
this._dummy.hide(); 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-enter', Lang.bind(this, this._onEnter));
global.connect('xdnd-position-changed', Lang.bind(this, this._onPositionChanged)); global.connect('xdnd-position-changed', Lang.bind(this, this._onPositionChanged));
@ -68,7 +74,7 @@ const XdndHandler = new Lang.Class({
source: cursorWindow}); source: cursorWindow});
this._cursorWindowClone = new Clutter.Clone({ 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); Shell.util_set_hidden_from_pick(this._cursorWindowClone, true);
// Make sure that the clone has the same position as the source // 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) { _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 // Make sure that the cursor window is on top
if (this._cursorWindowClone) if (this._cursorWindowClone)

View File

@ -21,7 +21,6 @@ eu
fa fa
fi fi
fr fr
fur
ga ga
gl gl
gu gu

View File

@ -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-screenshot.xml.in
data/50-gnome-shell-system.xml.in data/50-gnome-shell-system.xml.in
data/gnome-shell.desktop.in.in data/gnome-shell.desktop.in.in
@ -13,9 +10,7 @@ js/gdm/util.js
js/misc/util.js js/misc/util.js
js/ui/appDisplay.js js/ui/appDisplay.js
js/ui/appFavorites.js js/ui/appFavorites.js
js/ui/backgroundMenu.js
js/ui/calendar.js js/ui/calendar.js
js/ui/components/automountManager.js
js/ui/components/autorunManager.js js/ui/components/autorunManager.js
js/ui/components/keyring.js js/ui/components/keyring.js
js/ui/components/networkAgent.js js/ui/components/networkAgent.js
@ -33,7 +28,6 @@ js/ui/lookingGlass.js
js/ui/main.js js/ui/main.js
js/ui/messageTray.js js/ui/messageTray.js
js/ui/notificationDaemon.js js/ui/notificationDaemon.js
js/ui/overviewControls.js
js/ui/overview.js js/ui/overview.js
js/ui/panel.js js/ui/panel.js
js/ui/popupMenu.js js/ui/popupMenu.js
@ -61,5 +55,6 @@ src/shell-app.c
src/shell-app-system.c src/shell-app-system.c
src/shell-global.c src/shell-global.c
src/shell-keyring-prompt.c src/shell-keyring-prompt.c
src/shell-mobile-providers.c
src/shell-polkit-authentication-agent.c src/shell-polkit-authentication-agent.c
src/shell-util.c src/shell-util.c

237
po/ar.po
View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: HEAD\n" "Project-Id-Version: HEAD\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-12-23 18:34+0200\n" "POT-Creation-Date: 2012-12-09 04:45+0200\n"
"PO-Revision-Date: 2012-12-09 04:52+0200\n" "PO-Revision-Date: 2012-12-09 04:52+0200\n"
"Last-Translator: Khaled Hosny <khaledhosny@eglug.org>\n" "Last-Translator: Khaled Hosny <khaledhosny@eglug.org>\n"
"Language-Team: Arabic <doc@arabeyes.org>\n" "Language-Team: Arabic <doc@arabeyes.org>\n"
@ -365,8 +365,8 @@ msgstr "نافذة الولوج"
msgid "Power" msgid "Power"
msgstr "الطاقة" msgstr "الطاقة"
#: ../js/gdm/powerMenu.js:93 ../js/ui/userMenu.js:675 ../js/ui/userMenu.js:679 #: ../js/gdm/powerMenu.js:93 ../js/ui/userMenu.js:674 ../js/ui/userMenu.js:678
#: ../js/ui/userMenu.js:790 #: ../js/ui/userMenu.js:789
msgid "Suspend" msgid "Suspend"
msgstr "علّق" msgstr "علّق"
@ -374,8 +374,8 @@ msgstr "علّق"
msgid "Restart" msgid "Restart"
msgstr "أعِد التشغيل" msgstr "أعِد التشغيل"
#: ../js/gdm/powerMenu.js:103 ../js/ui/userMenu.js:677 #: ../js/gdm/powerMenu.js:103 ../js/ui/userMenu.js:676
#: ../js/ui/userMenu.js:679 ../js/ui/userMenu.js:789 #: ../js/ui/userMenu.js:678 ../js/ui/userMenu.js:788
msgid "Power Off" msgid "Power Off"
msgstr "أطفئ الحاسوب" msgstr "أطفئ الحاسوب"
@ -410,19 +410,27 @@ msgid "Execution of '%s' failed:"
msgstr "فشل تنفيذ '%s':" msgstr "فشل تنفيذ '%s':"
#. Translators: Filter to display all applications #. Translators: Filter to display all applications
#: ../js/ui/appDisplay.js:258 #: ../js/ui/appDisplay.js:259
msgid "All" msgid "All"
msgstr "الكل" msgstr "الكل"
#: ../js/ui/appDisplay.js:666 #: ../js/ui/appDisplay.js:318
msgid "APPLICATIONS"
msgstr "التطبيقات"
#: ../js/ui/appDisplay.js:375
msgid "SETTINGS"
msgstr "الإعدادات"
#: ../js/ui/appDisplay.js:679
msgid "New Window" msgid "New Window"
msgstr "نافذة جديدة" msgstr "نافذة جديدة"
#: ../js/ui/appDisplay.js:669 ../js/ui/dash.js:289 #: ../js/ui/appDisplay.js:682 ../js/ui/dash.js:289
msgid "Remove from Favorites" msgid "Remove from Favorites"
msgstr "أزِل من المفضّلة" msgstr "أزِل من المفضّلة"
#: ../js/ui/appDisplay.js:670 #: ../js/ui/appDisplay.js:683
msgid "Add to Favorites" msgid "Add to Favorites"
msgstr "أضِف إلى المفضّلة" msgstr "أضِف إلى المفضّلة"
@ -439,19 +447,19 @@ msgstr "أُزيل %s من مفضّلتك."
#. Translators: Shown in calendar event list for all day events #. Translators: Shown in calendar event list for all day events
#. * Keep it short, best if you can use less then 10 characters #. * Keep it short, best if you can use less then 10 characters
#. #.
#: ../js/ui/calendar.js:61 #: ../js/ui/calendar.js:62
msgctxt "event list time" msgctxt "event list time"
msgid "All Day" msgid "All Day"
msgstr "طوال اليوم" msgstr "طوال اليوم"
#. Translators: Shown in calendar event list, if 24h format #. Translators: Shown in calendar event list, if 24h format
#: ../js/ui/calendar.js:66 #: ../js/ui/calendar.js:67
msgctxt "event list time" msgctxt "event list time"
msgid "%H:%M" msgid "%H:%M"
msgstr "%H:%M" msgstr "%H:%M"
#. Transators: Shown in calendar event list, if 12h format #. Transators: Shown in calendar event list, if 12h format
#: ../js/ui/calendar.js:73 #: ../js/ui/calendar.js:74
msgctxt "event list time" msgctxt "event list time"
msgid "%l:%M %p" msgid "%l:%M %p"
msgstr "%l:%M %p" msgstr "%l:%M %p"
@ -461,43 +469,43 @@ msgstr "%l:%M %p"
#. * NOTE: These grid abbreviations are always shown together #. * NOTE: These grid abbreviations are always shown together
#. * and in order, e.g. "S M T W T F S". #. * and in order, e.g. "S M T W T F S".
#. #.
#: ../js/ui/calendar.js:104 #: ../js/ui/calendar.js:114
msgctxt "grid sunday" msgctxt "grid sunday"
msgid "S" msgid "S"
msgstr "ح" msgstr "ح"
#. Translators: Calendar grid abbreviation for Monday #. Translators: Calendar grid abbreviation for Monday
#: ../js/ui/calendar.js:106 #: ../js/ui/calendar.js:116
msgctxt "grid monday" msgctxt "grid monday"
msgid "M" msgid "M"
msgstr "ن" msgstr "ن"
#. Translators: Calendar grid abbreviation for Tuesday #. Translators: Calendar grid abbreviation for Tuesday
#: ../js/ui/calendar.js:108 #: ../js/ui/calendar.js:118
msgctxt "grid tuesday" msgctxt "grid tuesday"
msgid "T" msgid "T"
msgstr "ث" msgstr "ث"
#. Translators: Calendar grid abbreviation for Wednesday #. Translators: Calendar grid abbreviation for Wednesday
#: ../js/ui/calendar.js:110 #: ../js/ui/calendar.js:120
msgctxt "grid wednesday" msgctxt "grid wednesday"
msgid "W" msgid "W"
msgstr "ر" msgstr "ر"
#. Translators: Calendar grid abbreviation for Thursday #. Translators: Calendar grid abbreviation for Thursday
#: ../js/ui/calendar.js:112 #: ../js/ui/calendar.js:122
msgctxt "grid thursday" msgctxt "grid thursday"
msgid "T" msgid "T"
msgstr "خ" msgstr "خ"
#. Translators: Calendar grid abbreviation for Friday #. Translators: Calendar grid abbreviation for Friday
#: ../js/ui/calendar.js:114 #: ../js/ui/calendar.js:124
msgctxt "grid friday" msgctxt "grid friday"
msgid "F" msgid "F"
msgstr "ج" msgstr "ج"
#. Translators: Calendar grid abbreviation for Saturday #. Translators: Calendar grid abbreviation for Saturday
#: ../js/ui/calendar.js:116 #: ../js/ui/calendar.js:126
msgctxt "grid saturday" msgctxt "grid saturday"
msgid "S" msgid "S"
msgstr "س" msgstr "س"
@ -508,77 +516,77 @@ msgstr "س"
#. * so they need to be unique (e.g. Tuesday and Thursday cannot #. * so they need to be unique (e.g. Tuesday and Thursday cannot
#. * both be 'T'). #. * both be 'T').
#. #.
#: ../js/ui/calendar.js:129 #: ../js/ui/calendar.js:139
msgctxt "list sunday" msgctxt "list sunday"
msgid "Su" msgid "Su"
msgstr "الأحد" msgstr "الأحد"
#. Translators: Event list abbreviation for Monday #. Translators: Event list abbreviation for Monday
#: ../js/ui/calendar.js:131 #: ../js/ui/calendar.js:141
msgctxt "list monday" msgctxt "list monday"
msgid "M" msgid "M"
msgstr "الاثنين" msgstr "الاثنين"
#. Translators: Event list abbreviation for Tuesday #. Translators: Event list abbreviation for Tuesday
#: ../js/ui/calendar.js:133 #: ../js/ui/calendar.js:143
msgctxt "list tuesday" msgctxt "list tuesday"
msgid "T" msgid "T"
msgstr "الثلاثاء" msgstr "الثلاثاء"
#. Translators: Event list abbreviation for Wednesday #. Translators: Event list abbreviation for Wednesday
#: ../js/ui/calendar.js:135 #: ../js/ui/calendar.js:145
msgctxt "list wednesday" msgctxt "list wednesday"
msgid "W" msgid "W"
msgstr "الأربعاء" msgstr "الأربعاء"
#. Translators: Event list abbreviation for Thursday #. Translators: Event list abbreviation for Thursday
#: ../js/ui/calendar.js:137 #: ../js/ui/calendar.js:147
msgctxt "list thursday" msgctxt "list thursday"
msgid "Th" msgid "Th"
msgstr "الخميس" msgstr "الخميس"
#. Translators: Event list abbreviation for Friday #. Translators: Event list abbreviation for Friday
#: ../js/ui/calendar.js:139 #: ../js/ui/calendar.js:149
msgctxt "list friday" msgctxt "list friday"
msgid "F" msgid "F"
msgstr "الجمعة" msgstr "الجمعة"
#. Translators: Event list abbreviation for Saturday #. Translators: Event list abbreviation for Saturday
#: ../js/ui/calendar.js:141 #: ../js/ui/calendar.js:151
msgctxt "list saturday" msgctxt "list saturday"
msgid "S" msgid "S"
msgstr "السبت" msgstr "السبت"
#. Translators: Text to show if there are no events #. Translators: Text to show if there are no events
#: ../js/ui/calendar.js:674 #: ../js/ui/calendar.js:700
msgid "Nothing Scheduled" msgid "Nothing Scheduled"
msgstr "الجدول خال" msgstr "الجدول خال"
#. Translators: Shown on calendar heading when selected day occurs on current year #. Translators: Shown on calendar heading when selected day occurs on current year
#: ../js/ui/calendar.js:690 #: ../js/ui/calendar.js:716
msgctxt "calendar heading" msgctxt "calendar heading"
msgid "%A, %B %d" msgid "%A, %B %d"
msgstr "%A %d %B" msgstr "%A %d %B"
#. Translators: Shown on calendar heading when selected day occurs on different year #. Translators: Shown on calendar heading when selected day occurs on different year
#: ../js/ui/calendar.js:693 #: ../js/ui/calendar.js:719
msgctxt "calendar heading" msgctxt "calendar heading"
msgid "%A, %B %d, %Y" msgid "%A, %B %d, %Y"
msgstr "%A %d %B %Y" msgstr "%A %d %B %Y"
#: ../js/ui/calendar.js:703 #: ../js/ui/calendar.js:729
msgid "Today" msgid "Today"
msgstr "اليوم" msgstr "اليوم"
#: ../js/ui/calendar.js:707 #: ../js/ui/calendar.js:733
msgid "Tomorrow" msgid "Tomorrow"
msgstr "غدا" msgstr "غدا"
#: ../js/ui/calendar.js:718 #: ../js/ui/calendar.js:744
msgid "This week" msgid "This week"
msgstr "هذا الأسبوع" msgstr "هذا الأسبوع"
#: ../js/ui/calendar.js:726 #: ../js/ui/calendar.js:752
msgid "Next week" msgid "Next week"
msgstr "الأسبوع القادم" msgstr "الأسبوع القادم"
@ -940,7 +948,7 @@ msgstr "أظهر الحساب"
msgid "Unknown reason" msgid "Unknown reason"
msgstr "السبب غير معروف" msgstr "السبب غير معروف"
#: ../js/ui/ctrlAltTab.js:29 ../js/ui/viewSelector.js:81 #: ../js/ui/ctrlAltTab.js:29 ../js/ui/viewSelector.js:87
msgid "Windows" msgid "Windows"
msgstr "النوافذ" msgstr "النوافذ"
@ -1080,7 +1088,7 @@ msgstr "هل تريد تنزيل وتثبيت '%s' من extensions.gnome.org؟"
msgid "tray" msgid "tray"
msgstr "لوحة النظام" msgstr "لوحة النظام"
#: ../js/ui/keyboard.js:555 ../js/ui/status/keyboard.js:309 #: ../js/ui/keyboard.js:555 ../js/ui/status/keyboard.js:195
#: ../js/ui/status/power.js:205 #: ../js/ui/status/power.js:205
msgid "Keyboard" msgid "Keyboard"
msgstr "لوحة المفاتيح" msgstr "لوحة المفاتيح"
@ -1107,9 +1115,7 @@ msgstr "اظهر الأخطاء"
msgid "Enabled" msgid "Enabled"
msgstr "مفعّل" msgstr "مفعّل"
#. translators: #: ../js/ui/lookingGlass.js:767
#. * The device has been disabled
#: ../js/ui/lookingGlass.js:767 ../src/gvc/gvc-mixer-control.c:1830
msgid "Disabled" msgid "Disabled"
msgstr "معطّل" msgstr "معطّل"
@ -1141,15 +1147,15 @@ msgstr "افتح"
msgid "Remove" msgid "Remove"
msgstr "أزِل" msgstr "أزِل"
#: ../js/ui/messageTray.js:1552 #: ../js/ui/messageTray.js:1551
msgid "No Messages" msgid "No Messages"
msgstr "لا رسائل" msgstr "لا رسائل"
#: ../js/ui/messageTray.js:1570 #: ../js/ui/messageTray.js:1568
msgid "Message Tray" msgid "Message Tray"
msgstr "لوحة الرسائل" msgstr "لوحة الرسائل"
#: ../js/ui/messageTray.js:2639 #: ../js/ui/messageTray.js:2635
msgid "System Information" msgid "System Information"
msgstr "معلومات النظام" msgstr "معلومات النظام"
@ -1158,11 +1164,11 @@ msgctxt "program"
msgid "Unknown" msgid "Unknown"
msgstr "غير معروف" msgstr "غير معروف"
#: ../js/ui/overview.js:92 #: ../js/ui/overview.js:95
msgid "Undo" msgid "Undo"
msgstr "تراجع" msgstr "تراجع"
#: ../js/ui/overview.js:139 #: ../js/ui/overview.js:144
msgid "Overview" msgid "Overview"
msgstr "نظرة عامة" msgstr "نظرة عامة"
@ -1170,13 +1176,13 @@ msgstr "نظرة عامة"
#. in the search entry when no search is #. in the search entry when no search is
#. active; it should not exceed ~30 #. active; it should not exceed ~30
#. characters. #. characters.
#: ../js/ui/overview.js:218 #: ../js/ui/overview.js:221
msgid "Type to search..." msgid "Type to search..."
msgstr "اكتب نصا للبحث عنه..." msgstr "اكتب نصا للبحث عنه..."
#. Translators: this is the name of the dock/favorites area on #. Translators: this is the name of the dock/favorites area on
#. the left of the overview #. the left of the overview
#: ../js/ui/overview.js:236 #: ../js/ui/overview.js:242
msgid "Dash" msgid "Dash"
msgstr "الشريط" msgstr "الشريط"
@ -1199,7 +1205,7 @@ msgstr "الشريط العلوي"
#. "ON" and "OFF") or "toggle-switch-intl" (for toggle #. "ON" and "OFF") or "toggle-switch-intl" (for toggle
#. switches containing "◯" and "|"). Other values will #. switches containing "◯" and "|"). Other values will
#. simply result in invisible toggle switches. #. simply result in invisible toggle switches.
#: ../js/ui/popupMenu.js:725 #: ../js/ui/popupMenu.js:732
msgid "toggle-switch-us" msgid "toggle-switch-us"
msgstr "toggle-switch-intl" msgstr "toggle-switch-intl"
@ -1239,15 +1245,15 @@ msgstr[3] "%d تنبيهات جديدة"
msgstr[4] "%d تنبيها جديدا" msgstr[4] "%d تنبيها جديدا"
msgstr[5] "%d تنبيه جديد" msgstr[5] "%d تنبيه جديد"
#: ../js/ui/screenShield.js:402 ../js/ui/userMenu.js:781 #: ../js/ui/screenShield.js:402 ../js/ui/userMenu.js:780
msgid "Lock" msgid "Lock"
msgstr "أوصِد" msgstr "أوصِد"
#: ../js/ui/searchDisplay.js:403 #: ../js/ui/searchDisplay.js:277
msgid "Searching..." msgid "Searching..."
msgstr "يبحث..." msgstr "يبحث..."
#: ../js/ui/searchDisplay.js:451 #: ../js/ui/searchDisplay.js:325
msgid "No results." msgid "No results."
msgstr "لا نتائج." msgstr "لا نتائج."
@ -1363,7 +1369,7 @@ msgid "disconnecting..."
msgstr "يقطع الاتّصال..." msgstr "يقطع الاتّصال..."
#: ../js/ui/status/bluetooth.js:220 ../js/ui/status/network.js:442 #: ../js/ui/status/bluetooth.js:220 ../js/ui/status/network.js:442
#: ../js/ui/status/network.js:1464 #: ../js/ui/status/network.js:1453
msgid "connecting..." msgid "connecting..."
msgstr "يتّصل..." msgstr "يتّصل..."
@ -1379,7 +1385,7 @@ msgstr "إعدادات لوحة المفاتيح"
msgid "Mouse Settings" msgid "Mouse Settings"
msgstr "إعدادات الفأرة" msgstr "إعدادات الفأرة"
#: ../js/ui/status/bluetooth.js:253 ../js/ui/status/volume.js:314 #: ../js/ui/status/bluetooth.js:253 ../js/ui/status/volume.js:270
msgid "Sound Settings" msgid "Sound Settings"
msgstr "إعدادات الصوت" msgstr "إعدادات الصوت"
@ -1443,15 +1449,15 @@ msgstr "من فضلك أدخل الرقم المذكور على الجهاز."
msgid "OK" msgid "OK"
msgstr "حسنا" msgstr "حسنا"
#: ../js/ui/status/keyboard.js:363 #: ../js/ui/status/keyboard.js:228
msgid "Show Keyboard Layout" msgid "Show Keyboard Layout"
msgstr "أظهر تخطيط لوحة المفاتيح" msgstr "أظهر تخطيط لوحة المفاتيح"
#: ../js/ui/status/keyboard.js:368 #: ../js/ui/status/keyboard.js:233
msgid "Region and Language Settings" msgid "Region and Language Settings"
msgstr "إعدادات الإقليم واللغة" msgstr "إعدادات الإقليم واللغة"
#: ../js/ui/status/lockScreenMenu.js:43 #: ../js/ui/status/lockScreenMenu.js:18
msgid "Volume, network, battery" msgid "Volume, network, battery"
msgstr "الصوت، الشبكة، البطارية" msgstr "الصوت، الشبكة، البطارية"
@ -1471,7 +1477,7 @@ msgid "unmanaged"
msgstr "غير مُدار" msgstr "غير مُدار"
#. Translators: this is for network connections that require some kind of key or password #. Translators: this is for network connections that require some kind of key or password
#: ../js/ui/status/network.js:445 ../js/ui/status/network.js:1467 #: ../js/ui/status/network.js:445 ../js/ui/status/network.js:1456
msgid "authentication required" msgid "authentication required"
msgstr "الاستيثاق مطلوب" msgstr "الاستيثاق مطلوب"
@ -1492,72 +1498,72 @@ msgstr "الكبل مفصول"
msgid "unavailable" msgid "unavailable"
msgstr "غير متاح" msgstr "غير متاح"
#: ../js/ui/status/network.js:469 ../js/ui/status/network.js:1469 #: ../js/ui/status/network.js:469 ../js/ui/status/network.js:1458
msgid "connection failed" msgid "connection failed"
msgstr "فشل الاتصال" msgstr "فشل الاتصال"
#: ../js/ui/status/network.js:525 ../js/ui/status/network.js:1403 #: ../js/ui/status/network.js:525 ../js/ui/status/network.js:1392
#: ../js/ui/status/network.js:1545 #: ../js/ui/status/network.js:1534
msgid "More..." msgid "More..."
msgstr "المزيد..." msgstr "المزيد..."
#. TRANSLATORS: this is the indication that a connection for another logged in user is active, #. TRANSLATORS: this is the indication that a connection for another logged in user is active,
#. and we cannot access its settings (including the name) #. and we cannot access its settings (including the name)
#: ../js/ui/status/network.js:561 ../js/ui/status/network.js:1333 #: ../js/ui/status/network.js:561 ../js/ui/status/network.js:1322
msgid "Connected (private)" msgid "Connected (private)"
msgstr "متّصل (شخصي)" msgstr "متّصل (شخصي)"
#: ../js/ui/status/network.js:641 #: ../js/ui/status/network.js:619
msgid "Auto Ethernet" msgid "Auto Ethernet"
msgstr "إيثرنت تلقائي" msgstr "إيثرنت تلقائي"
#: ../js/ui/status/network.js:688 #: ../js/ui/status/network.js:677
msgid "Auto broadband" msgid "Auto broadband"
msgstr "شبكة هاتف محمول تلقائية" msgstr "شبكة هاتف محمول تلقائية"
#: ../js/ui/status/network.js:691 #: ../js/ui/status/network.js:680
msgid "Auto dial-up" msgid "Auto dial-up"
msgstr "اتصال هاتفي تلقائي" msgstr "اتصال هاتفي تلقائي"
#. TRANSLATORS: this the automatic wireless connection name (including the network name) #. TRANSLATORS: this the automatic wireless connection name (including the network name)
#: ../js/ui/status/network.js:820 ../js/ui/status/network.js:1350 #: ../js/ui/status/network.js:809 ../js/ui/status/network.js:1339
#, c-format #, c-format
msgid "Auto %s" msgid "Auto %s"
msgstr "%s تلقائي" msgstr "%s تلقائي"
#: ../js/ui/status/network.js:822 #: ../js/ui/status/network.js:811
msgid "Auto bluetooth" msgid "Auto bluetooth"
msgstr "بلوتوث تلقائي" msgstr "بلوتوث تلقائي"
#: ../js/ui/status/network.js:1352 #: ../js/ui/status/network.js:1341
msgid "Auto wireless" msgid "Auto wireless"
msgstr "لاسلكي تلقائي" msgstr "لاسلكي تلقائي"
#: ../js/ui/status/network.js:1595 #: ../js/ui/status/network.js:1584
msgid "Enable networking" msgid "Enable networking"
msgstr "فعّل الشبكات" msgstr "فعّل الشبكات"
#: ../js/ui/status/network.js:1627 #: ../js/ui/status/network.js:1616
msgid "Wi-Fi" msgid "Wi-Fi"
msgstr "واي فاي" msgstr "واي فاي"
#: ../js/ui/status/network.js:1646 #: ../js/ui/status/network.js:1635
msgid "Network Settings" msgid "Network Settings"
msgstr "إعدادات الشّبكة" msgstr "إعدادات الشّبكة"
#: ../js/ui/status/network.js:1692 #: ../js/ui/status/network.js:1679
msgid "Network Manager" msgid "Network Manager"
msgstr "مدير الشبكة" msgstr "مدير الشبكة"
#: ../js/ui/status/network.js:1774 #: ../js/ui/status/network.js:1761
msgid "Connection failed" msgid "Connection failed"
msgstr "فشل الاتصال" msgstr "فشل الاتصال"
#: ../js/ui/status/network.js:1775 #: ../js/ui/status/network.js:1762
msgid "Activation of network connection failed" msgid "Activation of network connection failed"
msgstr "فشل تفعيل اتصال الشبكة" msgstr "فشل تفعيل اتصال الشبكة"
#: ../js/ui/status/network.js:2092 #: ../js/ui/status/network.js:2079
msgid "Networking is disabled" msgid "Networking is disabled"
msgstr "عُطّلت الشبكات" msgstr "عُطّلت الشبكات"
@ -1675,11 +1681,11 @@ msgid "Unknown"
msgstr "غير معروف" msgstr "غير معروف"
#. Translators: This is the label for audio volume #. Translators: This is the label for audio volume
#: ../js/ui/status/volume.js:247 ../js/ui/status/volume.js:295 #: ../js/ui/status/volume.js:50 ../js/ui/status/volume.js:251
msgid "Volume" msgid "Volume"
msgstr "شدة الصوت" msgstr "شدة الصوت"
#: ../js/ui/status/volume.js:256 #: ../js/ui/status/volume.js:62
msgid "Microphone" msgid "Microphone"
msgstr "ميكروفون" msgstr "ميكروفون"
@ -1715,31 +1721,31 @@ msgstr "ساكن"
msgid "Offline" msgid "Offline"
msgstr "غير متصل" msgstr "غير متصل"
#: ../js/ui/userMenu.js:755 #: ../js/ui/userMenu.js:754
msgid "Notifications" msgid "Notifications"
msgstr "التنبيهات" msgstr "التنبيهات"
#: ../js/ui/userMenu.js:763 #: ../js/ui/userMenu.js:762
msgid "Settings" msgid "Settings"
msgstr "الإعدادات" msgstr "الإعدادات"
#: ../js/ui/userMenu.js:771 #: ../js/ui/userMenu.js:770
msgid "Switch User" msgid "Switch User"
msgstr "بدّل المستخدم" msgstr "بدّل المستخدم"
#: ../js/ui/userMenu.js:776 #: ../js/ui/userMenu.js:775
msgid "Log Out" msgid "Log Out"
msgstr "اخرج" msgstr "اخرج"
#: ../js/ui/userMenu.js:796 #: ../js/ui/userMenu.js:795
msgid "Install Updates & Restart" msgid "Install Updates & Restart"
msgstr "ثبّت التحديثات وأعد التشغيل" msgstr "ثبّت التحديثات وأعد التشغيل"
#: ../js/ui/userMenu.js:814 #: ../js/ui/userMenu.js:813
msgid "Your chat status will be set to busy" msgid "Your chat status will be set to busy"
msgstr "ستُجعل حالة اتصالك ”مشغول“" msgstr "ستُجعل حالة اتصالك ”مشغول“"
#: ../js/ui/userMenu.js:815 #: ../js/ui/userMenu.js:814
msgid "" msgid ""
"Notifications are now disabled, including chat messages. Your online status " "Notifications are now disabled, including chat messages. Your online status "
"has been adjusted to let others know that you might not see their messages." "has been adjusted to let others know that you might not see their messages."
@ -1747,15 +1753,15 @@ msgstr ""
"التنبيهات معطلة الآن، بما فيها رسائل المحادثة. حالة اتصالك تغيرت حتى يعلم " "التنبيهات معطلة الآن، بما فيها رسائل المحادثة. حالة اتصالك تغيرت حتى يعلم "
"الآخرون أنك قد لا ترى رسائلهم." "الآخرون أنك قد لا ترى رسائلهم."
#: ../js/ui/viewSelector.js:85 #: ../js/ui/viewSelector.js:91
msgid "Applications" msgid "Applications"
msgstr "التطبيقات" msgstr "التطبيقات"
#: ../js/ui/viewSelector.js:89 #: ../js/ui/viewSelector.js:95
msgid "Search" msgid "Search"
msgstr "ابحث" msgstr "ابحث"
#: ../js/ui/wanda.js:92 #: ../js/ui/wanda.js:94
#, c-format #, c-format
msgid "" msgid ""
"Sorry, no wisdom for you today:\n" "Sorry, no wisdom for you today:\n"
@ -1764,11 +1770,15 @@ msgstr ""
"عذرًا, لا حكمة لك اليوم:\n" "عذرًا, لا حكمة لك اليوم:\n"
"%s" "%s"
#: ../js/ui/wanda.js:96 #: ../js/ui/wanda.js:98
#, c-format #, c-format
msgid "%s the Oracle says" msgid "%s the Oracle says"
msgstr "يقول الحكيم %s" msgstr "يقول الحكيم %s"
#: ../js/ui/wanda.js:139
msgid "Your favorite Easter Egg"
msgstr ""
#: ../js/ui/windowAttentionHandler.js:19 #: ../js/ui/windowAttentionHandler.js:19
#, c-format #, c-format
msgid "'%s' is ready" msgid "'%s' is ready"
@ -1778,36 +1788,6 @@ msgstr "'%s' جاهز"
msgid "Evolution Calendar" msgid "Evolution Calendar"
msgstr "تقويم إيفُليوشِن" msgstr "تقويم إيفُليوشِن"
#. translators:
#. * The number of sound outputs on a particular device
#: ../src/gvc/gvc-mixer-control.c:1837
#, c-format
msgid "%u Output"
msgid_plural "%u Outputs"
msgstr[0] "لا مخرَج"
msgstr[1] "مخرَج واحد"
msgstr[2] "مخرَجين"
msgstr[3] "%u مخارج"
msgstr[4] "%u مخرجا"
msgstr[5] "%u مخرج"
#. translators:
#. * The number of sound inputs on a particular device
#: ../src/gvc/gvc-mixer-control.c:1847
#, c-format
msgid "%u Input"
msgid_plural "%u Inputs"
msgstr[0] "لا مدخل"
msgstr[1] "مدخل واحد"
msgstr[2] "مدخلين"
msgstr[3] "%u مداخل"
msgstr[4] "%u مدخلا"
msgstr[5] "%u مدخل"
#: ../src/gvc/gvc-mixer-control.c:2371
msgid "System Sounds"
msgstr "أصوات النظام"
#: ../src/main.c:332 #: ../src/main.c:332
msgid "Print version" msgid "Print version"
msgstr "اطبع الإصدارة" msgstr "اطبع الإصدارة"
@ -1849,12 +1829,6 @@ msgstr "المبدئي"
msgid "Authentication dialog was dismissed by the user" msgid "Authentication dialog was dismissed by the user"
msgstr "أغلق المستخدم مربع الاستيثاق الحِواري" msgstr "أغلق المستخدم مربع الاستيثاق الحِواري"
#~ msgid "APPLICATIONS"
#~ msgstr "التطبيقات"
#~ msgid "SETTINGS"
#~ msgstr "الإعدادات"
#~ msgid "Subscription request" #~ msgid "Subscription request"
#~ msgstr "طلب اشتراك" #~ msgstr "طلب اشتراك"
@ -1903,6 +1877,27 @@ msgstr "أغلق المستخدم مربع الاستيثاق الحِواري"
#~ msgid "System Settings" #~ msgid "System Settings"
#~ msgstr "إعدادات النظام" #~ msgstr "إعدادات النظام"
#~ msgid "%u Output"
#~ msgid_plural "%u Outputs"
#~ msgstr[0] "لا مخرَج"
#~ msgstr[1] "مخرَج واحد"
#~ msgstr[2] "مخرَجين"
#~ msgstr[3] "%u مخارج"
#~ msgstr[4] "%u مخرجا"
#~ msgstr[5] "%u مخرج"
#~ msgid "%u Input"
#~ msgid_plural "%u Inputs"
#~ msgstr[0] "لا مدخل"
#~ msgstr[1] "مدخل واحد"
#~ msgstr[2] "مدخلين"
#~ msgstr[3] "%u مداخل"
#~ msgstr[4] "%u مدخلا"
#~ msgstr[5] "%u مدخل"
#~ msgid "System Sounds"
#~ msgstr "أصوات النظام"
#~ msgid "Failed to unmount '%s'" #~ msgid "Failed to unmount '%s'"
#~ msgstr "فشل فصْل '%s'" #~ msgstr "فشل فصْل '%s'"

475
po/as.po

File diff suppressed because it is too large Load Diff

727
po/be.po

File diff suppressed because it is too large Load Diff

907
po/bg.po

File diff suppressed because it is too large Load Diff

1060
po/ca.po

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

917
po/cs.po

File diff suppressed because it is too large Load Diff

988
po/de.po

File diff suppressed because it is too large Load Diff

1139
po/el.po

File diff suppressed because it is too large Load Diff

994
po/es.po

File diff suppressed because it is too large Load Diff

View File

@ -14,7 +14,7 @@ msgstr ""
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
"shell&keywords=I18N+L10N&component=general\n" "shell&keywords=I18N+L10N&component=general\n"
"POT-Creation-Date: 2012-12-09 13:03+0000\n" "POT-Creation-Date: 2012-12-09 13:03+0000\n"
"PO-Revision-Date: 2012-12-19 16:47+0300\n" "PO-Revision-Date: 2012-12-18 17:13+0300\n"
"Last-Translator: Mattias Põldaru <mahfiaz@gmail.com>\n" "Last-Translator: Mattias Põldaru <mahfiaz@gmail.com>\n"
"Language-Team: Estonian <>\n" "Language-Team: Estonian <>\n"
"Language: et\n" "Language: et\n"
@ -153,8 +153,8 @@ msgid ""
"state of the checkbox." "state of the checkbox."
msgstr "" msgstr ""
"Shell küsib parooli, kui haagitakse krüpteeritud seade või kaugfailisüsteem. " "Shell küsib parooli, kui haagitakse krüpteeritud seade või kaugfailisüsteem. "
"Kui parooli on võimalik salvestada edaspidiseks kasutuseks, näidatakse " "Kui parooli on võimalik salvestada järgmise kasutuse jaoks, näidatakse "
"'Salvesta parool' märkeruutu. See võti määrab selle märkeruudu vaikimisi " "\"Jäta parool meelde\" märkeruutu. See võti määrab märkeruudu vaikimisi "
"oleku." "oleku."
msgid "Show the week date in the calendar" msgid "Show the week date in the calendar"
@ -257,9 +257,9 @@ msgid ""
"are 'thumbnail-only' (shows a thumbnail of the window), 'app-icon-" "are 'thumbnail-only' (shows a thumbnail of the window), 'app-icon-"
"only' (shows only the application icon) or 'both'." "only' (shows only the application icon) or 'both'."
msgstr "" msgstr ""
"Seadistab, kuidas aknaid aknavahetajas kuvatakse. Sobivad väärtused on " "Seadistab, kuidas aknaid akendevahetajas kuvatakse. Sobivad väärtused on "
"'thumbnail-only' (näidatakse ainult pisipilti aknast), 'app-icon-" "'thumbnail-only' (näidatakse ainult pisipilti), 'app-icon-only' (näidatakse "
"only' (näidatakse ainult akna ikooni) või 'both' (mõlemad)." "ainult rakenduse ikooni) või 'both' (näidatakse mõlemaid)."
msgid "Attach modal dialog to the parent window" msgid "Attach modal dialog to the parent window"
msgstr "Modaaldialoog kuulub vanemakna juurde" msgstr "Modaaldialoog kuulub vanemakna juurde"

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