Compare commits

..

15 Commits

Author SHA1 Message Date
45797a977b app-system: Add back StartupWMClass matching
While unfortunate that we still have to scan all apps with get_all(),
support for this feature will be short-lived, so hopefully we can drop
it in the future as new apps adapt to the desktop file / app ID
recommendations.

For now, simply scan all desktop IDs.
2013-10-02 18:22:28 -04:00
884b94233e app-system: Put back support for the installed-changed signal
Use the new GAppInfoMonitor that Ryan added to glib to know when the
set of apps has changed.
2013-10-02 18:22:27 -04:00
74d3e3139f app-system: Lazily create ShellApps for apps we care about
Rather than create all ShellApps up-front, create them lazily. We really
had no reason to do this before as we were scanning GMenu to get all the
apps, but doing this can remove a need for get_all, which is slow and
memory-hungry.
2013-10-02 18:22:27 -04:00
77b5385cc3 appDisplay: Use the desktop file index for app searching
Rather than scanning all apps for searching, use Ryan's new desktop
file index and the glib support APIs for app searching instead of our
own system.
2013-10-02 18:22:27 -04:00
d749d646be appDisplay: Use a proper string key for the app search provider
We were always sort of cheating when we used objects as the search ID.
Since the new desktop file index will return us a list of desktop file
IDs, just use those as IDs instead.
2013-10-02 18:22:27 -04:00
fa8224d7b5 app-system: Remove use of gnome-menus internally
We want to transition to a system in the future where we have a desktop
file cache. As we no longer differentiate categories or similar, it no
longer makes sense to have app visibility based on categories. Thus,
we no longer need to use gnome-menus to list all apps. The potential
issue here is reloading all desktop files when new files are created,
but this can be dealt with individually.

The "All Applications" view still uses gnome-menus.

https://bugzilla.gnome.org/show_bug.cgi?id=698486
2013-10-02 18:22:27 -04:00
f687197ccc appDisplay: Ignore the NoDisplay flag for directories
This makes us match the native app search.

https://bugzilla.gnome.org/show_bug.cgi?id=698486
2013-10-02 18:22:15 -04:00
28a6aefb6c app-system: Remove visible_id_to_app
Since appDisplay.js makes its own GMenu tree, it's not necessary
anymore. This does mean that searches will show apps in NoDisplay
categories, but that's an obscure enough edge case not to matter.

https://bugzilla.gnome.org/show_bug.cgi?id=698486
2013-10-02 18:22:15 -04:00
96c2a90e11 app-system: Remove lookup_app_for_path
It's absurdly silly. Just modify the one place that uses it
to be better.

https://bugzilla.gnome.org/show_bug.cgi?id=698486
2013-10-02 18:22:15 -04:00
63cf46e49b app-system: Remove known_vendor_prefixes
This does remove support for legacy prefixed app infos with
subdirs, but since we want to remove support for the menu spec,
let's not even bother.

https://bugzilla.gnome.org/show_bug.cgi?id=698486
2013-10-02 18:22:15 -04:00
200a9ef1af app-system: Remove get_tree
Make clients construct their own gmenu tree if they need it.

https://bugzilla.gnome.org/show_bug.cgi?id=698486
2013-10-02 18:22:08 -04:00
6050ca6e0c app-system: Map wmclass to ID rather than apps
This makes the refcounting and memory management easier to understand.
2013-10-02 18:22:08 -04:00
8bd7db9227 app-system: Don't use gmenu_tree_entry_get_desktop_app_info
It's a broken method when it comes to giving us a useful GDesktopAppInfo,
and it's hard to fix libgmenu properly, so simply recreate the app info
using the desktop file ID that libgmenu has.
2013-10-02 18:22:07 -04:00
982feb85c1 app: Port to be based on GDesktopAppInfo
We weren't using the GMenuTreeEntry for anything special anymore,
so remove it.

https://bugzilla.gnome.org/show_bug.cgi?id=698486
2013-10-02 18:14:18 -04:00
f165cc23c0 app-system: Remove lookup_app_by_tree_entry
We want to move away from gnome-menus eventually, so the simple
utility method isn't really worth keeping around. Reimplement it
in the one place that uses it.

https://bugzilla.gnome.org/show_bug.cgi?id=698486
2013-10-02 17:39:36 -04:00
154 changed files with 19063 additions and 15483 deletions

2
.gitignore vendored
View File

@ -43,8 +43,6 @@ docs/reference/*/xml/
docs/reference/shell/doc-gen-*
gtk-doc.make
js/misc/config.js
js/js-resources.c
js/js-resources.h
intltool-extract.in
intltool-merge.in
intltool-update.in

View File

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

119
NEWS
View File

@ -1,122 +1,3 @@
3.11.3
======
* Fix fade effect of desktop icons [Florian; #707671]
* Fix issues with background management code [Jasper; #709313]
* Use new Glib facilities for application search [Jasper; #711631]
* Add focus indication to session menu button [Sebastien; #710539]
* Fix hover tracking for StEntries [Jasper; #706749]
* Fix reentrancy issue in message tray [Jasper; #711694]
* Tone down zoom animation on login/unlock [Jasper; #712362]
* Allow specifying monitor for OSD [Carlos; #712664]
* Fix resetting prompt on user switch [Ray; #710456]
* Stop using gnome-bluetooth-applet [Bastien; #719341]
* Add support for EAP-FAST password requests [Dan; #719813]
* Fix entry focus of chat notifications [Jasper; #709853]
* Make window previews keyboard navigatable [Jasper; #644306]
* Fix app switcher order with dialog windows [Florian; #719824]
* Allow remote search providers without icons [Debarshi; #719965]
* Fix various alignment issues in RTL locales [Yosef; #712638, #712596,
#712594, #712600, #712579]
* Misc. bug fixes and cleanups [Jasper, Florian, Giovanni, Dan; #712727,
#712753, #719378, #719730, #719803, #710115, #720017, #719815, #719567,
#720298]
Contributors:
Giovanni Campagna, Carlos Garnacho, Sebastien Lafargue, Tim Lunn,
Florian Müllner, Bastien Nocera, Yosef Or Boczko, Debarshi Ray,
Jasper St. Pierre, Ray Strode, Dan Williams
Translations:
Kjartan Maraas [nb], Reinout van Schouwen [nl], Rafael Ferreira [pt_BR],
Mattias Põldaru [et], Emin Tufan Çetin [tr], Jiri Grönroos [fi],
Khaled Hosny [ar], Fran Diéguez [gl], Victor Ibragimov [tg],
Daniel Mustieles [es]
3.11.2
======
* Cache search result display actors [Jasper; #704912]
* Use username in userWidget if real name doesn't fit [Jasper; #706851]
* Support shell_global_reexec_self() on OpenBSD [Antoine; #709571]
* Support disabling browser plugin [Colin; #711218]
* Restore support for 'disable-restart-buttons' [Florian; #711244]
* Validate parameters of exposed DBus methods [Florian; #699752]
* Connect applications to systemd journal if available [Colin; #711626]
* Misc bug fixes and cleanups [Florian, Jasper; #711205, #698486, #711416,
#644306, #711555, #709806, #711631, #711732]
Contributors:
Cosimo Cecchi, Antoine Jacoutot, Florian Müllner, Jasper St. Pierre,
Rico Tzschichholz, Colin Walters
Translations:
Yuri Myasoedov [ru], Kjartan Maraas [nb], Efstathios Iosifidis [el],
Benjamin Steinwender [de], eternalhui [zh_CN], Shantha kumar [ta]
3.11.1
======
* power: Use UPower directly instead of gnome-settings-daemon [Bastien; #710273]
* Implement support for new GTK+ notification API [Jasper, Giovanni, Florian;
#710137, #710596]
* gdm: Don't allow user-list to fill up the entire screen [Florian; #710555]
* Don't autostart remote search providers at login [Giovanni; #708830]
* Fix spacing in end-session dialog [Sebastien; #710543]
* Prepare for js24 [Tim; #711052]
* Misc bug fixes and cleanups [Jasper, Florian, Adel, Tim, Sebastien; #710347,
#710144, #710541, #691409, #710745, #688331, #704912]
Contributors:
Giovanni Campagna, Adel Gadllah, Sebastien Lafargue, Tim Lunn,
Florian Müllner, Bastien Nocera, Jasper St. Pierre, Rico Tzschichholz
Translations:
Stas Solovey [ru], Yosef Or Boczko [he], Rafael Ferreira [pt_BR]
3.10.1
======
* Make sure lock screen is drawn once before switching user [Giovanni; #708051]
* Fix signal strength indicators in network selector [Jasper; #708442]
* Scroll search results when focusing provider icons [Jasper; #708868]
* Add separate hover/active states to page indicators [Carlos; #708852]
* Tweak appearance of user name and avatar [Yash; #702309]
* Hide "Turn On" in network menu when disabled by hardware [Giovanni; #709635]
* Cancel open keyring prompts when the screen is locked [Florian; #708910]
* Differentiate "Not Connected" and "Off" in network menu [Giovanni; #709043]
* Make network settings items point to the right device [Giovanni; #709246]
* Remove animation of window preview titles [Sebastien; #709392]
* Add 'Notifications' switch to tray menu [Florian; #707073]
* Make dropdown arrows consistent [Carlos; #709564]
* power: Use icon from primary device for status [Jasper; #709925]
* Fix XDND drags to overview [Adel; #708887]
* Fix workspace switcher disappearing with too many workspaces [Jasper; #694881]
* Handle search results with 'special:' prefix specially [Giovanni; #707055]
* gdm: Support pre-authenticated logins from oVirt [Vinzenz; #702162]
* Use ARROW role for labels representing arrows [Alejandro; #710120]
* Make selected view in app picker persistent [Florian; #710042]
* Make network selector navigable by keyboard [Alejandro; #710144]
* Misc bug fixes [Florian, Adel, Jasper, Aleksander, Giovanni, Dan, Michael,
Tim; #709034, #709263, #698486, #709286, #709248, #709543, #696564, #703265,
#709638, #709866, #709998, #710019, #710104, #710115]
Contributors:
Giovanni Campagna, Michael Catanzaro, Vinzenz Feenstra, Adel Gadllah,
Yash Girdhar, Sebastien Lafargue, Tim Lunn, Aleksander Morgado,
Florian Müllner, Alejandro Piñeiro, Carlos Soriano, Jasper St. Pierre,
Dieter Verfaillie, Dan Winship
Translations:
Inaki Larranaga Murgoitio [eu], Christian Kirbach [de], Muhammet Kara [tr],
Aurimas Černius [lt], Ryan Lortie [eo], Rūdolfs Mazurs [lv],
Dušan Kazik [sk], Fran Diéguez [gl], Enrico Nicoletto [pt_BR],
Kjartan Maraas [nb], Victor Ibragimov [tg], Matej Urbančič [sl],
A S Alam [pa], Nilamdyuti Goswami [as], Daniel Mustieles [es],
Cheng-Chia Tseng [zh_HK, zh_TW], Mattias Põldaru [et], Kenneth Nielsen [da],
Milo Casagrande [it], Marek Černocký [cs], Ihar Hrachyshka [be],
Мирослав Николић [sr, sr@latin], Arash Mousavi [fa], Yuri Myasoedov [ru],
Gil Forcada [ca], Carles Ferrando [ca@valencia], Andika Triwidada [id],
Timo Jyrinki [fi], Piotr Drąg [pl], Rafael Ferreira [pt_BR],
Gabor Kelemen [hu], Yosef Or Boczko [he], Daniel Korostil [uk],
Wouter Bolsterlee [nl], António Lima [pt]
3.10.0.1
=========
* Fix login screen [Ray; #708691]

View File

@ -1,5 +1,5 @@
AC_PREREQ(2.63)
AC_INIT([gnome-shell],[3.11.3],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
AC_INIT([gnome-shell],[3.10.0.1],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_SRCDIR([src/shell-global.c])
@ -16,7 +16,6 @@ m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
# Checks for programs.
AC_PROG_CC
AC_PROG_CXX
# Initialize libtool
LT_PREREQ([2.2.6])
@ -58,25 +57,10 @@ fi
AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
AC_ARG_ENABLE([systemd],
AS_HELP_STRING([--enable-systemd], [Use systemd]),
[enable_systemd=$enableval],
[enable_systemd=auto])
AS_IF([test x$enable_systemd != xno], [
AC_MSG_CHECKING([for libsystemd-journal])
PKG_CHECK_EXISTS([libsystemd-journal],
[have_systemd=yes
AC_DEFINE([HAVE_SYSTEMD], [1], [Define if we have systemd])],
[have_systemd=no])
AC_MSG_RESULT($have_systemd)
])
AC_MSG_RESULT($enable_systemd)
CLUTTER_MIN_VERSION=1.13.4
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
GJS_MIN_VERSION=1.39.0
MUTTER_MIN_VERSION=3.11.1
GJS_MIN_VERSION=1.35.4
MUTTER_MIN_VERSION=3.10.0
GTK_MIN_VERSION=3.7.9
GIO_MIN_VERSION=2.37.0
LIBECAL_MIN_VERSION=3.5.3
@ -108,9 +92,6 @@ SHARED_PCS="gio-unix-2.0 >= $GIO_MIN_VERSION
libnm-glib libnm-util >= $NETWORKMANAGER_MIN_VERSION
libnm-gtk >= $NETWORKMANAGER_MIN_VERSION
libsecret-unstable gcr-base-3 >= $GCR_MIN_VERSION"
if test x$have_systemd = xyes; then
SHARED_PCS="${SHARED_PCS} libsystemd-journal"
fi
PKG_CHECK_MODULES(GNOME_SHELL, $SHARED_PCS)
PKG_CHECK_MODULES(MUTTER, libmutter >= $MUTTER_MIN_VERSION)
@ -126,25 +107,25 @@ PKG_CHECK_MODULES(GNOME_SHELL_JS, gio-2.0 gjs-internals-1.0 >= $GJS_MIN_VERSION)
PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 >= 0.6.8 x11)
PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0)
PKG_CHECK_MODULES(SHELL_HOTPLUG_SNIFFER, gio-2.0 gdk-pixbuf-2.0)
PKG_CHECK_MODULES(BROWSER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION json-glib-1.0 >= 0.13.2)
PKG_CHECK_MODULES(TRAY, gtk+-3.0)
PKG_CHECK_MODULES(GVC, libpulse >= $PULSE_MIN_VERS libpulse-mainloop-glib gobject-2.0)
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.7.4)
PKG_CHECK_MODULES(CARIBOU, caribou-1.0 >= 0.4.8)
AC_ARG_ENABLE(browser-plugin,
[AS_HELP_STRING([--enable-browser-plugin],
[Enable browser plugin [default=yes]])],,
enable_browser_plugin=yes)
AS_IF([test x$enable_browser_plugin = xyes], [
PKG_CHECK_MODULES(BROWSER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION json-glib-1.0 >= 0.13.2)
])
AM_CONDITIONAL(BUILD_BROWSER_PLUGIN, test x$enable_browser_plugin = xyes)
PKG_CHECK_MODULES(BLUETOOTH, gnome-bluetooth-1.0 >= 3.9.0,
[AC_DEFINE([HAVE_BLUETOOTH],[1],[Define if you have libgnome-bluetooth-applet])
AC_SUBST([HAVE_BLUETOOTH],[1])],
AC_MSG_CHECKING([for bluetooth support])
PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.9.0],
[BLUETOOTH_DIR=`$PKG_CONFIG --variable=applet_libdir gnome-bluetooth-1.0`
BLUETOOTH_LIBS=`$PKG_CONFIG --variable=applet_libs gnome-bluetooth-1.0`
AC_SUBST([BLUETOOTH_LIBS],["$BLUETOOTH_LIBS"])
AC_SUBST([BLUETOOTH_DIR],["$BLUETOOTH_DIR"])
AC_DEFINE_UNQUOTED([BLUETOOTH_DIR],["$BLUETOOTH_DIR"],[Path to installed GnomeBluetooth typelib and library])
AC_DEFINE([HAVE_BLUETOOTH],[1],[Define if you have libgnome-bluetooth-applet])
AC_SUBST([HAVE_BLUETOOTH],[1])
AC_MSG_RESULT([yes])],
[AC_DEFINE([HAVE_BLUETOOTH],[0])
AC_SUBST([HAVE_BLUETOOTH],[0])])
AC_SUBST([HAVE_BLUETOOTH],[0])
AC_MSG_RESULT([no])])
PKG_CHECK_MODULES(CALENDAR_SERVER, libecal-1.2 >= $LIBECAL_MIN_VERSION libedataserver-1.2 >= $LIBEDATASERVER_MIN_VERSION gio-2.0)
AC_SUBST(CALENDAR_SERVER_CFLAGS)
@ -164,9 +145,6 @@ AC_SUBST(MUTTER_TYPELIB_DIR)
GJS_CONSOLE=`$PKG_CONFIG --variable=gjs_console gjs-1.0`
AC_SUBST(GJS_CONSOLE)
GLIB_COMPILE_RESOURCES=`$PKG_CONFIG --variable glib_compile_resources gio-2.0`
AC_SUBST(GLIB_COMPILE_RESOURCES)
AC_CHECK_FUNCS(fdwalk)
AC_CHECK_FUNCS(mallinfo)
AC_CHECK_HEADERS([sys/resource.h])

View File

@ -1,3 +1,6 @@
wandadir = $(pkgdatadir)
dist_wanda_DATA = wanda.png
desktopdir=$(datadir)/applications
desktop_DATA = gnome-shell.desktop gnome-shell-extension-prefs.desktop
if HAVE_MUTTER_WAYLAND

View File

@ -37,13 +37,6 @@
application view, rather than being displayed inline in the main view.
</_description>
</key>
<key name="app-picker-view" type="u">
<default>0</default>
<summary>App Picker View</summary>
<description>
Index of the currently selected view in the application picker.
</description>
</key>
<key name="command-history" type="as">
<default>[]</default>
<_summary>History for command (Alt-F2) dialog</_summary>

View File

@ -157,7 +157,7 @@ StScrollBar StButton#vhandle:active {
min-width: 200px;
}
.unicode-arrow {
.popup-submenu-menu-item-triangle {
font-size: 120%;
}
@ -306,13 +306,16 @@ StScrollBar StButton#vhandle:active {
font-size: 12pt;
border-bottom: 1px solid #666;
padding: 12px;
spacing: 20px;
}
.nm-dialog-item:selected {
.nm-dialog-item:checked {
background-color: #333;
}
.nm-dialog-item-box {
spacing: 20px;
}
.nm-dialog-icons {
spacing: .5em;
}
@ -690,9 +693,7 @@ StScrollBar StButton#vhandle:active {
padding-bottom: 32px;
}
.workspace-thumbnails {
spacing: 11px;
visible-width: 32px; /* Amount visible before hovering */
.workspace-thumbnails-background {
border: 1px solid rgba(128, 128, 128, 0.4);
border-right: 0px;
border-radius: 9px 0px 0px 9px;
@ -700,13 +701,18 @@ StScrollBar StButton#vhandle:active {
padding: 11px 7px 11px 11px;
}
.workspace-thumbnails:rtl {
.workspace-thumbnails-background:rtl {
border-right: 1px;
border-left: 0px;
border-radius: 0px 9px 9px 0px;
padding: 11px 11px 11px 7px;
}
.workspace-thumbnails {
spacing: 11px;
visible-width: 32px; /* Amount visible before hovering */
}
.workspace-thumbnail-indicator {
border: 4px solid rgba(255,255,255,0.7);
border-radius: 4px;
@ -1944,7 +1950,6 @@ StScrollBar StButton#vhandle:active {
.end-session-dialog-description:rtl {
padding-right: 17px;
text-align: right;
}
.end-session-dialog-logout-icon {
@ -1968,19 +1973,10 @@ StScrollBar StButton#vhandle:active {
padding-left: 50px;
}
.end-session-dialog-session-list,
.end-session-dialog-app-list {
spacing: 1em;
}
.end-session-dialog-list-header {
font-weight: bold;
}
.end-session-dialog-list-header:rtl {
text-align: right;
}
.end-session-dialog-app-list-item,
.end-session-dialog-session-list-item {
spacing: 1em;
@ -2100,10 +2096,6 @@ StScrollBar StButton#vhandle:active {
color: #666666;
}
.prompt-dialog-description:rtl {
text-align: right;
}
.prompt-dialog-password-box {
spacing: 1em;
padding-bottom: 1em;
@ -2284,7 +2276,7 @@ StScrollBar StButton#vhandle:active {
.framed-user-icon {
border: 2px solid #8b8b8b;
border-radius: 3px;
border-radius: 5px;
background-size: contain;
}
@ -2328,10 +2320,8 @@ StScrollBar StButton#vhandle:active {
}
.login-dialog-user-list-item {
border-radius: 5px;
border-radius: 10px;
padding: .2em;
color: #bfbfbf;
text-shadow: black 0px 2px 2px;
}
.login-dialog-user-list-item:ltr {
@ -2342,6 +2332,23 @@ StScrollBar StButton#vhandle:active {
padding-left: 1em;
}
.login-dialog-user-list-item .login-dialog-user-list-item-name {
font-size: 20pt;
padding-left: 9px;
}
.login-dialog-user-list:expanded .login-dialog-user-list-item {
color: #666666;
}
.login-dialog-user-list-item,
.login-dialog-user-list-item:hover .login-dialog-user-list-item-name,
.login-dialog-user-list:expanded .login-dialog-user-list-item:focus .login-dialog-user-list-item-name,
.login-dialog-user-list:expanded .login-dialog-user-list-item:logged-in {
color: white;
text-shadow: black 0px 2px 2px;
}
.login-dialog-user-list-item:hover {
background-color: rgba(255,255,255,0.1);
}
@ -2368,6 +2375,13 @@ StScrollBar StButton#vhandle:active {
background-color: #8b8b8b;
}
.login-dialog-user-list-item-icon {
border: 2px solid #8b8b8b;
border-radius: 8px;
width: 64px;
height: 64px;
}
.login-dialog-not-listed-label {
font-size: 10.5pt;
font-weight: bold;
@ -2375,10 +2389,6 @@ StScrollBar StButton#vhandle:active {
padding-top: 1em;
}
.login-dialog-user-selection-box {
padding: 100px 0;
}
.login-dialog-user-selection-box .login-dialog-not-listed-label {
padding-left: 2px;
}
@ -2418,7 +2428,6 @@ StScrollBar StButton#vhandle:active {
}
.login-dialog-session-list-button:hover,
.login-dialog-session-list-button:focus,
.login-dialog-session-list-button:active {
color: white;
}
@ -2485,21 +2494,13 @@ StScrollBar StButton#vhandle:active {
}
.user-widget-label {
font-size: 20px;
font-size: 16pt;
font-weight: bold;
text-align: left;
color:white;
padding-left: 15px;
text-shadow: black 0px 4px 3px 0px;
}
.user-widget-label:ltr {
padding-left: 18px;
}
.user-widget-label:rtl {
padding-right: 18px;
}
/* Screen shield */
#panel.lock-screen,

BIN
data/wanda.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

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

View File

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

View File

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

View File

@ -59,7 +59,6 @@ const AuthPrompt = new Lang.Class({
this._userVerifier.connect('verification-complete', Lang.bind(this, this._onVerificationComplete));
this._userVerifier.connect('reset', Lang.bind(this, this._onReset));
this._userVerifier.connect('smartcard-status-changed', Lang.bind(this, this._onSmartcardStatusChanged));
this._userVerifier.connect('ovirt-user-authenticated', Lang.bind(this, this._onOVirtUserAuthenticated));
this.smartcardDetected = this._userVerifier.smartcardDetected;
this.connect('next', Lang.bind(this, function() {
@ -80,7 +79,6 @@ const AuthPrompt = new Lang.Class({
if (event.get_key_symbol() == Clutter.KEY_Escape) {
this.cancel();
}
return Clutter.EVENT_PROPAGATE;
}));
this._userWell = new St.Bin({ x_fill: true,
@ -94,7 +92,7 @@ const AuthPrompt = new Lang.Class({
this.actor.add(this._label,
{ expand: true,
x_fill: false,
x_fill: true,
y_fill: true,
x_align: St.Align.START });
this._entry = new St.Entry({ style_class: 'login-dialog-prompt-entry',
@ -112,7 +110,7 @@ const AuthPrompt = new Lang.Class({
this._message = new St.Label({ opacity: 0,
styleClass: 'login-dialog-message' });
this._message.clutter_text.line_wrap = true;
this.actor.add(this._message, { x_fill: false, x_align: St.Align.START, y_align: St.Align.START });
this.actor.add(this._message, { x_fill: true, y_align: St.Align.START });
this._buttonBox = new St.BoxLayout({ style_class: 'login-dialog-button-box',
vertical: false });
@ -221,11 +219,6 @@ const AuthPrompt = new Lang.Class({
this.emit('prompted');
},
_onOVirtUserAuthenticated: function() {
if (this.verificationStatus != AuthPromptStatus.VERIFICATION_SUCCEEDED)
this.reset();
},
_onSmartcardStatusChanged: function() {
this.smartcardDetected = this._userVerifier.smartcardDetected;
@ -264,8 +257,10 @@ const AuthPrompt = new Lang.Class({
},
_onReset: function() {
this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
this.reset();
if (this.verificationStatus != AuthPromptStatus.VERIFICATION_SUCCEEDED) {
this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
this.reset();
}
},
addActorToDefaultButtonWell: function(actor) {
@ -449,11 +444,10 @@ const AuthPrompt = new Lang.Class({
// The user is constant at the unlock screen, so it will immediately
// respond to the request with the username
beginRequestType = BeginRequestType.PROVIDE_USERNAME;
} else if (this._userVerifier.serviceIsForeground(GdmUtil.OVIRT_SERVICE_NAME) ||
(this.smartcardDetected &&
this._userVerifier.serviceIsForeground(GdmUtil.SMARTCARD_SERVICE_NAME))) {
} else if (this.smartcardDetected &&
this._userVerifier.serviceIsForeground(GdmUtil.SMARTCARD_SERVICE_NAME)) {
// We don't need to know the username if the user preempted the login screen
// with a smartcard or with preauthenticated oVirt credentials
// with a smartcard.
beginRequestType = BeginRequestType.DONT_PROVIDE_USERNAME;
} else {
// In all other cases, we should get the username up front.

View File

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

View File

@ -59,7 +59,7 @@ const UserListItem = new Lang.Class({
this._userChangedId = this.user.connect('changed',
Lang.bind(this, this._onUserChanged));
let layout = new St.BoxLayout({ vertical: true });
let layout = new St.BoxLayout({ vertical: false });
this.actor = new St.Button({ style_class: 'login-dialog-user-list-item',
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
can_focus: true,
@ -68,18 +68,39 @@ const UserListItem = new Lang.Class({
x_align: St.Align.START,
x_fill: true });
this._userWidget = new UserWidget.UserWidget(this.user);
layout.add(this._userWidget.actor);
this._userAvatar = new UserWidget.Avatar(this.user,
{ styleClass: 'login-dialog-user-list-item-icon' });
layout.add(this._userAvatar.actor);
let textLayout = new St.BoxLayout({ style_class: 'login-dialog-user-list-item-text-box',
vertical: true });
layout.add(textLayout, { expand: true });
this._nameLabel = new St.Label({ style_class: 'login-dialog-user-list-item-name' });
this.actor.label_actor = this._nameLabel;
textLayout.add(this._nameLabel,
{ y_fill: false,
y_align: St.Align.MIDDLE,
expand: true });
this._timedLoginIndicator = new St.Bin({ style_class: 'login-dialog-timed-login-indicator',
scale_x: 0 });
layout.add(this._timedLoginIndicator);
textLayout.add(this._timedLoginIndicator,
{ x_fill: true,
x_align: St.Align.MIDDLE,
y_fill: false,
y_align: St.Align.END });
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
this._onUserChanged();
},
_onUserChanged: function() {
this._nameLabel.set_text(this.user.get_real_name());
this._userAvatar.update();
this._updateLoggedIn();
},
syncStyleClasses: function() {
this._updateLoggedIn();
},
@ -168,6 +189,7 @@ const UserList = new Lang.Class({
for (let userName in this._items) {
let item = this._items[userName];
item.actor.sync_hover();
item.syncStyleClasses();
}
},
@ -644,7 +666,7 @@ const LoginDialog = new Lang.Class({
onComplete: function() {
Mainloop.idle_add(Lang.bind(this, function() {
this._greeter.call_start_session_when_ready_sync(serviceName, true, null);
return GLib.SOURCE_REMOVE;
return false;
}));
},
onCompleteScope: this });
@ -699,7 +721,6 @@ const LoginDialog = new Lang.Class({
function() {
this._timedLoginAnimationTime -= _TIMED_LOGIN_IDLE_THRESHOLD;
hold.release();
return GLib.SOURCE_REMOVE;
});
return hold;
},
@ -764,7 +785,7 @@ const LoginDialog = new Lang.Class({
global.stage.connect('captured-event',
Lang.bind(this, function(actor, event) {
if (this._timedLoginDelay == undefined)
return Clutter.EVENT_PROPAGATE;
return false;
if (event.type() == Clutter.EventType.KEY_PRESS ||
event.type() == Clutter.EventType.BUTTON_PRESS) {
@ -777,7 +798,7 @@ const LoginDialog = new Lang.Class({
this._resetTimedLogin();
}
return Clutter.EVENT_PROPAGATE;
return false;
}));
},
@ -886,10 +907,6 @@ const LoginDialog = new Lang.Class({
Main.ctrlAltTabManager.removeGroup(this.dialogLayout);
},
cancel: function() {
this._authPrompt.cancel();
},
addCharacter: function(unichar) {
this._authPrompt.addCharacter(unichar);
},

View File

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

View File

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

View File

@ -10,7 +10,6 @@ const St = imports.gi.St;
const Batch = imports.gdm.batch;
const Fprint = imports.gdm.fingerprint;
const OVirt = imports.gdm.oVirt;
const Main = imports.ui.main;
const Params = imports.misc.params;
const ShellEntry = imports.ui.shellEntry;
@ -20,7 +19,6 @@ const Tweener = imports.ui.tweener;
const PASSWORD_SERVICE_NAME = 'gdm-password';
const FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint';
const SMARTCARD_SERVICE_NAME = 'gdm-smartcard';
const OVIRT_SERVICE_NAME = 'gdm-ovirtcred';
const FADE_ANIMATION_TIME = 0.16;
const CLONE_FADE_ANIMATION_TIME = 0.25;
@ -153,14 +151,6 @@ const ShellUserVerifier = new Lang.Class({
this.reauthenticating = false;
this._failCounter = 0;
this._oVirtCredentialsManager = OVirt.getOVirtCredentialsManager();
if (this._oVirtCredentialsManager.hasToken())
this._oVirtUserAuthenticated(this._oVirtCredentialsManager.getToken());
this._oVirtCredentialsManager.connect('user-authenticated',
Lang.bind(this, this._oVirtUserAuthenticated));
},
begin: function(userName, hold) {
@ -250,7 +240,6 @@ const ShellUserVerifier = new Lang.Class({
Lang.bind(this, function() {
this._messageQueueTimeoutId = 0;
this._queueMessageTimeout();
return GLib.SOURCE_REMOVE;
}));
},
@ -288,11 +277,6 @@ const ShellUserVerifier = new Lang.Class({
}));
},
_oVirtUserAuthenticated: function(token) {
this._preemptingService = OVIRT_SERVICE_NAME;
this.emit('ovirt-user-authenticated');
},
_checkForSmartcard: function() {
let smartcardDetected;
@ -471,12 +455,6 @@ const ShellUserVerifier = new Lang.Class({
if (!this.serviceIsForeground(serviceName))
return;
if (serviceName == OVIRT_SERVICE_NAME) {
// The only question asked by this service is "Token?"
this.answerQuery(serviceName, this._oVirtCredentialsManager.getToken());
return;
}
this.emit('ask-question', serviceName, secretQuestion, '\u25cf');
},
@ -537,16 +515,6 @@ const ShellUserVerifier = new Lang.Class({
},
_onConversationStopped: function(client, serviceName) {
// If the login failed with the preauthenticated oVirt credentials
// then discard the credentials and revert to default authentication
// mechanism.
if (this.serviceIsForeground(OVIRT_SERVICE_NAME)) {
this._oVirtCredentialsManager.resetToken();
this._preemptingService = null;
this._verificationFailed(false);
return;
}
// if the password service fails, then cancel everything.
// But if, e.g., fingerprint fails, still give
// password authentication a chance to succeed

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -7,66 +7,58 @@ const Mainloop = imports.mainloop;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const SystemdLoginManagerIface = '<node> \
<interface name="org.freedesktop.login1.Manager"> \
<method name="Suspend"> \
<arg type="b" direction="in"/> \
</method> \
<method name="CanSuspend"> \
<arg type="s" direction="out"/> \
</method> \
<method name="Inhibit"> \
<arg type="s" direction="in"/> \
<arg type="s" direction="in"/> \
<arg type="s" direction="in"/> \
<arg type="s" direction="in"/> \
<arg type="h" direction="out"/> \
</method> \
<method name="GetSession"> \
<arg type="s" direction="in"/> \
<arg type="o" direction="out"/> \
</method> \
<method name="ListSessions"> \
<arg name="sessions" type="a(susso)" direction="out"/> \
</method> \
<signal name="PrepareForSleep"> \
<arg type="b" direction="out"/> \
</signal> \
</interface> \
</node>';
const SystemdLoginManagerIface = <interface name='org.freedesktop.login1.Manager'>
<method name='Suspend'>
<arg type='b' direction='in'/>
</method>
<method name='CanSuspend'>
<arg type='s' direction='out'/>
</method>
<method name='Inhibit'>
<arg type='s' direction='in'/>
<arg type='s' direction='in'/>
<arg type='s' direction='in'/>
<arg type='s' direction='in'/>
<arg type='h' direction='out'/>
</method>
<method name='GetSession'>
<arg type='s' direction='in'/>
<arg type='o' direction='out'/>
</method>
<method name='ListSessions'>
<arg name='sessions' type='a(susso)' direction='out'/>
</method>
<signal name='PrepareForSleep'>
<arg type='b' direction='out'/>
</signal>
</interface>;
const SystemdLoginSessionIface = '<node> \
<interface name="org.freedesktop.login1.Session"> \
<signal name="Lock" /> \
<signal name="Unlock" /> \
</interface> \
</node>';
const SystemdLoginSessionIface = <interface name='org.freedesktop.login1.Session'>
<signal name='Lock' />
<signal name='Unlock' />
</interface>;
const SystemdLoginManager = Gio.DBusProxy.makeProxyWrapper(SystemdLoginManagerIface);
const SystemdLoginSession = Gio.DBusProxy.makeProxyWrapper(SystemdLoginSessionIface);
const ConsoleKitManagerIface = '<node> \
<interface name="org.freedesktop.ConsoleKit.Manager"> \
<method name="CanRestart"> \
<arg type="b" direction="out"/> \
</method> \
<method name="CanStop"> \
<arg type="b" direction="out"/> \
</method> \
<method name="Restart" /> \
<method name="Stop" /> \
<method name="GetCurrentSession"> \
<arg type="o" direction="out" /> \
</method> \
</interface> \
</node>';
const ConsoleKitManagerIface = <interface name='org.freedesktop.ConsoleKit.Manager'>
<method name='CanRestart'>
<arg type='b' direction='out'/>
</method>
<method name='CanStop'>
<arg type='b' direction='out'/>
</method>
<method name='Restart' />
<method name='Stop' />
<method name='GetCurrentSession'>
<arg type='o' direction='out' />
</method>
</interface>;
const ConsoleKitSessionIface = '<node> \
<interface name="org.freedesktop.ConsoleKit.Session"> \
<signal name="Lock" /> \
<signal name="Unlock" /> \
</interface> \
</node>';
const ConsoleKitSessionIface = <interface name='org.freedesktop.ConsoleKit.Session'>
<signal name='Lock' />
<signal name='Unlock' />
</interface>;
const ConsoleKitSession = Gio.DBusProxy.makeProxyWrapper(ConsoleKitSessionIface);
const ConsoleKitManager = Gio.DBusProxy.makeProxyWrapper(ConsoleKitManagerIface);

View File

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

View File

@ -8,21 +8,19 @@ const Signals = imports.signals;
// Specified in the D-Bus specification here:
// http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager
const ObjectManagerIface = '<node> \
<interface name="org.freedesktop.DBus.ObjectManager"> \
<method name="GetManagedObjects"> \
<arg name="objects" type="a{oa{sa{sv}}}" direction="out"/> \
</method> \
<signal name="InterfacesAdded"> \
<arg name="objectPath" type="o"/> \
<arg name="interfaces" type="a{sa{sv}}" /> \
</signal> \
<signal name="InterfacesRemoved"> \
<arg name="objectPath" type="o"/> \
<arg name="interfaces" type="as" /> \
</signal> \
</interface> \
</node>';
const ObjectManagerIface = <interface name="org.freedesktop.DBus.ObjectManager">
<method name="GetManagedObjects">
<arg name="objects" type="a{oa{sa{sv}}}" direction="out"/>
</method>
<signal name="InterfacesAdded">
<arg name="objectPath" type="o"/>
<arg name="interfaces" type="a{sa{sv}}" />
</signal>
<signal name="InterfacesRemoved">
<arg name="objectPath" type="o"/>
<arg name="interfaces" type="as" />
</signal>
</interface>;
const ObjectManagerInfo = Gio.DBusInterfaceInfo.new_for_xml(ObjectManagerIface);

View File

@ -7,14 +7,12 @@ const Signals = imports.signals;
const ObjectManager = imports.misc.objectManager;
const SmartcardTokenIface = '<node> \
<interface name="org.gnome.SettingsDaemon.Smartcard.Token"> \
<property name="Name" type="s" access="read"/> \
<property name="Driver" type="o" access="read"/> \
<property name="IsInserted" type="b" access="read"/> \
<property name="UsedToLogin" type="b" access="read"/> \
</interface> \
</node>';
const SmartcardTokenIface = <interface name="org.gnome.SettingsDaemon.Smartcard.Token">
<property name="Name" type="s" access="read"/>
<property name="Driver" type="o" access="read"/>
<property name="IsInserted" type="b" access="read"/>
<property name="UsedToLogin" type="b" access="read"/>
</interface>;
let _smartcardManager = null;

View File

@ -1,7 +1,6 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const St = imports.gi.St;
@ -80,22 +79,6 @@ function spawnCommandLine(command_line) {
}
}
// spawnApp:
// @argv: an argv array
//
// Runs @argv as if it was an application, handling startup notification
function spawnApp(argv) {
try {
let app = Gio.AppInfo.create_from_commandline(argv.join(' '), null,
Gio.AppInfoCreateFlags.SUPPORTS_STARTUP_NOTIFICATION);
let context = global.create_app_launch_context();
app.launch([], context);
} catch(err) {
_handleSpawnError(argv[0], err);
}
}
// trySpawn:
// @argv: an argv array
//

View File

@ -2,7 +2,6 @@
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
@ -107,8 +106,6 @@ const AppSwitcherPopup = new Lang.Class({
this._switcherList = new AppSwitcher(apps, this);
this._items = this._switcherList.icons;
if (this._items.length == 0)
return false;
return true;
},
@ -313,7 +310,7 @@ const AppSwitcherPopup = new Lang.Class({
this._createThumbnails();
this._thumbnailTimeoutId = 0;
this._thumbnailsFocused = false;
return GLib.SOURCE_REMOVE;
return false;
},
_destroyThumbnails : function() {
@ -378,9 +375,6 @@ const WindowSwitcherPopup = new Lang.Class({
this._switcherList = new WindowList(windows, mode);
this._items = this._switcherList.icons;
if (this._items.length == 0)
return false;
return true;
},
@ -548,7 +542,7 @@ const AppSwitcher = new Lang.Class({
Lang.bind(this, function () {
this._enterItem(index);
this._mouseTimeOutId = 0;
return GLib.SOURCE_REMOVE;
return false;
}));
} else
this._itemEntered(index);

View File

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

View File

@ -92,7 +92,7 @@ const BaseAppView = new Lang.Class({
},
removeAll: function() {
this._grid.destroyAll();
this._grid.removeAll();
this._items = {};
this._allItems = [];
},
@ -415,7 +415,7 @@ const AllView = new Lang.Class({
_onScroll: function(actor, event) {
if (this._displayingPopup)
return Clutter.EVENT_STOP;
return true;
let direction = event.get_scroll_direction();
if (direction == Clutter.ScrollDirection.UP)
@ -423,7 +423,7 @@ const AllView = new Lang.Class({
else if (direction == Clutter.ScrollDirection.DOWN)
this.goToPage(this._currentPage + 1);
return Clutter.EVENT_STOP;
return true;
},
_onPan: function(action) {
@ -454,17 +454,17 @@ const AllView = new Lang.Class({
_onKeyPressEvent: function(actor, event) {
if (this._displayingPopup)
return Clutter.EVENT_STOP;
return true;
if (event.get_key_symbol() == Clutter.Page_Up) {
this.goToPage(this._currentPage - 1);
return Clutter.EVENT_STOP;
return true;
} else if (event.get_key_symbol() == Clutter.Page_Down) {
this.goToPage(this._currentPage + 1);
return Clutter.EVENT_STOP;
return true;
}
return Clutter.EVENT_PROPAGATE;
return false;
},
_getItemId: function(item) {
@ -613,10 +613,6 @@ const FrequentView = new Lang.Class({
return this._usage.get_most_used("").length >= MIN_FREQUENT_APPS_COUNT;
},
removeAll: function() {
this._grid.destroyAll();
},
loadApps: function() {
let mostUsed = this._usage.get_most_used ("");
let hasUsefulData = this.hasUsefulData();
@ -747,15 +743,10 @@ const AppDisplay = new Lang.Class({
this._views[i].control.connect('clicked', Lang.bind(this,
function(actor) {
this._showView(viewIndex);
global.settings.set_uint('app-picker-view', viewIndex);
}));
}
let initialView = Math.min(global.settings.get_uint('app-picker-view'),
this._views.length - 1);
let frequentUseful = this._views[Views.FREQUENT].view.hasUsefulData();
if (initialView == Views.FREQUENT && !frequentUseful)
initialView = Views.ALL;
this._showView(initialView);
this._showView(frequentUseful ? Views.FREQUENT : Views.ALL);
this._updateFrequentVisibility();
// We need a dummy actor to catch the keyboard focus if the
@ -884,29 +875,19 @@ const AppSearchProvider = new Lang.Class({
callback(metas);
},
filterResults: function(results, maxNumber) {
return results.slice(0, maxNumber);
},
getInitialResultSet: function(terms, callback, cancellable) {
let query = terms.join(' ');
let groups = Gio.DesktopAppInfo.search(query);
_compareResults: function(a, b) {
let usage = Shell.AppUsage.get_default();
let results = [];
groups.forEach(function(group) {
group = group.filter(function(appID) {
let app = Gio.DesktopAppInfo.new(appID);
return app && app.should_show();
});
results = results.concat(group.sort(function(a, b) {
return usage.compare('', a, b);
}));
});
callback(results);
return usage.compare('', a, b);
},
getSubsearchResultSet: function(previousResults, terms, callback, cancellable) {
this.getInitialResultSet(terms, callback, cancellable);
getInitialResultSet: function(terms) {
let query = terms.join(' ');
let results = Gio.DesktopAppInfo.search(query, Lang.bind(this, this._compareResults), MAX_COLUMNS);
this.searchSystem.setResults(this, results);
},
getSubsearchResultSet: function(previousResults, terms) {
this.getInitialResultSet(terms);
},
activateResult: function(result) {
@ -929,7 +910,7 @@ const AppSearchProvider = new Lang.Class({
app.open_new_window(workspace);
},
createResultObject: function (resultMeta) {
createResultObject: function (resultMeta, terms) {
let app = this._appSys.lookup_app(resultMeta['id']);
return new AppIcon(app);
}
@ -1223,13 +1204,13 @@ const AppFolderPopup = new Lang.Class({
_onKeyPress: function(actor, event) {
if (!this._isOpen)
return Clutter.EVENT_PROPAGATE;
return false;
if (event.get_key_symbol() != Clutter.KEY_Escape)
return Clutter.EVENT_PROPAGATE;
return false;
this.popdown();
return Clutter.EVENT_STOP;
return true;
},
toggle: function() {
@ -1368,15 +1349,13 @@ const AppIcon = new Lang.Class({
this._removeMenuTimeout();
this._menuTimeoutId = Mainloop.timeout_add(MENU_POPUP_TIMEOUT,
Lang.bind(this, function() {
this._menuTimeoutId = 0;
this.popupMenu();
return GLib.SOURCE_REMOVE;
}));
} else if (button == 3) {
this.popupMenu();
return Clutter.EVENT_STOP;
return true;
}
return Clutter.EVENT_PROPAGATE;
return false;
},
_onClicked: function(actor, button) {
@ -1388,6 +1367,7 @@ const AppIcon = new Lang.Class({
// Last workspace is always empty
let launchWorkspace = global.screen.get_workspace_by_index(global.screen.n_workspaces - 1);
launchWorkspace.activate(global.get_current_time());
this.emit('launching');
this.app.open_new_window(-1);
Main.overview.hide();
}
@ -1446,6 +1426,7 @@ const AppIcon = new Lang.Class({
},
_onActivate: function (event) {
this.emit('launching');
let modifiers = event.get_state();
if (modifiers & Clutter.ModifierType.CONTROL_MASK
@ -1514,9 +1495,7 @@ const AppIconMenu = new Lang.Class({
_redisplay: function() {
this.removeAll();
let windows = this._source.app.get_windows().filter(function(w) {
return Shell.WindowTracker.is_window_interesting(w);
});
let windows = this._source.app.get_windows();
// Display the app windows menu items and the separator between windows
// of the current desktop and other windows.

View File

@ -50,9 +50,11 @@ const BackgroundCache = new Lang.Class({
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;
@ -86,6 +88,7 @@ const BackgroundCache = new Lang.Class({
}
this._patterns.push(content);
return content;
},
@ -113,9 +116,9 @@ const BackgroundCache = new Lang.Class({
_removeContent: function(contentList, content) {
let index = contentList.indexOf(content);
if (index < 0)
throw new Error("Trying to remove invalid content: " + content);
contentList.splice(index, 1);
if (index >= 0)
contentList.splice(index, 1);
},
removePatternContent: function(content) {
@ -125,8 +128,7 @@ const BackgroundCache = new Lang.Class({
removeImageContent: function(content) {
let filename = content.get_filename();
let hasOtherUsers = this._images.some(function(content) { return filename == content.get_filename(); });
if (!hasOtherUsers)
if (filename && this._fileMonitors[filename])
delete this._fileMonitors[filename];
this._removeContent(this._images, content);
@ -184,17 +186,13 @@ const BackgroundCache = new Lang.Class({
for (let j = 0; j < pendingLoad.callers.length; j++) {
if (pendingLoad.callers[j].onFinished) {
let newContent;
if (content && pendingLoad.callers[j].shouldCopy) {
newContent = content.copy(pendingLoad.callers[j].monitorIndex,
pendingLoad.callers[j].effects);
this._images.push(newContent);
} else {
newContent = content;
content = object.copy(pendingLoad.callers[j].monitorIndex,
pendingLoad.callers[j].effects);
}
pendingLoad.callers[j].onFinished(newContent);
pendingLoad.callers[j].onFinished(content);
}
}
@ -212,9 +210,11 @@ const BackgroundCache = new Lang.Class({
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;
@ -222,7 +222,7 @@ const BackgroundCache = new Lang.Class({
continue;
if (params.style == GDesktopEnums.BackgroundStyle.SPANNED &&
this._images[i].monitor != params.monitorIndex)
this._images[i].monitor_index != this._monitorIndex)
continue;
candidateContent = this._images[i];
@ -262,7 +262,6 @@ const BackgroundCache = new Lang.Class({
if (params.onLoaded) {
GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
params.onLoaded(this._animation);
return GLib.SOURCE_REMOVE;
}));
}
}
@ -277,7 +276,6 @@ const BackgroundCache = new Lang.Class({
if (params.onLoaded) {
GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
params.onLoaded(this._animation);
return GLib.SOURCE_REMOVE;
}));
}
}));
@ -317,6 +315,7 @@ const Background = new Lang.Class({
this._brightness = 1.0;
this._vignetteSharpness = 0.2;
this._saturation = 1.0;
this._cancellable = new Gio.Cancellable();
this.isLoaded = false;
@ -377,7 +376,7 @@ const Background = new Lang.Class({
GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
this.emit('loaded');
return GLib.SOURCE_REMOVE;
return false;
}));
},
@ -416,26 +415,29 @@ const Background = new Lang.Class({
this._fileWatches[filename] = signalId;
},
_ensureImage: function(index) {
if (this._images[index])
return;
_addImage: function(content, index, filename) {
content.saturation = this._saturation;
content.brightness = this._brightness;
content.vignette_sharpness = this._vignetteSharpness;
let actor = new Meta.BackgroundActor();
actor.content = content;
// The background pattern is the first actor in
// the group, and all images should be above that.
this.actor.insert_child_at_index(actor, index + 1);
this._images[index] = actor;
this._watchCacheFile(filename);
},
_updateImage: function(index, content, filename) {
_updateImage: function(content, index, filename) {
content.saturation = this._saturation;
content.brightness = this._brightness;
content.vignette_sharpness = this._vignetteSharpness;
let image = this._images[index];
if (image.content)
this._cache.removeImageContent(content);
image.content = content;
this._cache.removeImageContent(this._images[index].content);
this._images[index].content = content;
this._watchCacheFile(filename);
},
@ -483,8 +485,11 @@ const Background = new Lang.Class({
return;
}
this._ensureImage(i);
this._updateImage(i, content, files[i]);
if (!this._images[i]) {
this._addImage(content, i, files[i]);
} else {
this._updateImage(content, i, files[i]);
}
if (numPendingImages == 0) {
this._setLoaded();
@ -519,7 +524,7 @@ const Background = new Lang.Class({
Lang.bind(this, function() {
this._updateAnimationTimeoutId = 0;
this._updateAnimation();
return GLib.SOURCE_REMOVE;
return false;
}));
},
@ -539,27 +544,24 @@ const Background = new Lang.Class({
});
},
_loadImage: function(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) {
this._ensureImage(0);
this._updateImage(0, content, filename);
if (!content) {
if (!this._cancellable.is_cancelled())
this._loadAnimation(filename);
return;
}
this._addImage(content, 0, filename);
this._setLoaded();
})
});
},
_loadFile: function(filename) {
if (filename.endsWith('.xml'))
this._loadAnimation(filename);
else
this._loadImage(filename);
},
_load: function () {
@ -588,6 +590,24 @@ const Background = new Lang.Class({
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;
},
@ -639,13 +659,7 @@ const SystemBackground = new Lang.Class({
this.emit('loaded');
})
});
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
},
_onDestroy: function() {
this._cache.removeImageContent(this.actor.content);
},
}
});
Signals.addSignalMethods(SystemBackground.prototype);
@ -733,10 +747,11 @@ const BackgroundManager = new Lang.Class({
}
},
_updateBackground: function(background) {
let newBackground = this._createBackground();
_updateBackground: function(background, monitorIndex) {
let newBackground = this._createBackground(monitorIndex);
newBackground.vignetteSharpness = background.vignetteSharpness;
newBackground.brightness = background.brightness;
newBackground.saturation = background.saturation;
newBackground.visible = background.visible;
newBackground.loadedSignalId = newBackground.connect('loaded',
@ -783,7 +798,7 @@ const BackgroundManager = new Lang.Class({
background.changeSignalId = background.connect('changed', Lang.bind(this, function() {
background.disconnect(background.changeSignalId);
background.changeSignalId = 0;
this._updateBackground(background);
this._updateBackground(background, this._monitorIndex);
}));
background.actor.connect('destroy', Lang.bind(this, function() {

View File

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

View File

@ -69,7 +69,7 @@ const BoxPointer = new Lang.Class({
_muteInput: function() {
if (this._capturedEventId == 0)
this._capturedEventId = this.actor.connect('captured-event',
function() { return Clutter.EVENT_STOP; });
function() { return true; });
},
_unmuteInput: function() {
@ -121,9 +121,6 @@ const BoxPointer = new Lang.Class({
},
hide: function(animate, onComplete) {
if (!this.actor.visible)
return;
let xOffset = 0;
let yOffset = 0;
let themeNode = this.actor.get_theme_node();
@ -188,9 +185,7 @@ const BoxPointer = new Lang.Class({
},
_getPreferredHeight: function(actor, forWidth, alloc) {
let themeNode = this.actor.get_theme_node();
let borderWidth = themeNode.get_length('-arrow-border-width');
let [minSize, naturalSize] = this.bin.get_preferred_height(forWidth - 2 * borderWidth);
let [minSize, naturalSize] = this.bin.get_preferred_height(forWidth);
alloc.min_size = minSize;
alloc.natural_size = naturalSize;
this._adjustAllocationForArrow(false, alloc);

View File

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

View File

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

View File

@ -75,14 +75,12 @@ function startAppForMount(app, mount) {
/******************************************/
const HotplugSnifferIface = '<node> \
<interface name="org.gnome.Shell.HotplugSniffer"> \
<method name="SniffURI"> \
<arg type="s" direction="in" /> \
<arg type="as" direction="out" /> \
</method> \
</interface> \
</node>';
const HotplugSnifferIface = <interface name="org.gnome.Shell.HotplugSniffer">
<method name="SniffURI">
<arg type="s" direction="in" />
<arg type="as" direction="out" />
</method>
</interface>;
const HotplugSnifferProxy = Gio.DBusProxy.makeProxyWrapper(HotplugSnifferIface);
function HotplugSniffer() {

View File

@ -13,6 +13,8 @@ const ModalDialog = imports.ui.modalDialog;
const ShellEntry = imports.ui.shellEntry;
const CheckBox = imports.ui.checkBox;
let prompter = null;
const KeyringDialog = new Lang.Class({
Name: 'KeyringDialog',
Extends: ModalDialog.ModalDialog,
@ -45,9 +47,7 @@ const KeyringDialog = new Lang.Class({
this.prompt.bind_property('message', subject, 'text', GObject.BindingFlags.SYNC_CREATE);
this._messageBox.add(subject,
{ x_fill: false,
y_fill: false,
x_align: St.Align.START,
{ y_fill: false,
y_align: St.Align.START });
let description = new St.Label({ style_class: 'prompt-dialog-description' });
@ -138,7 +138,6 @@ const KeyringDialog = new Lang.Class({
warning.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
warning.clutter_text.line_wrap = true;
layout.pack(warning, 1, row);
layout.child_set(warning, { x_fill: false, x_align: Clutter.TableAlignment.START });
this.prompt.bind_property('warning-visible', warning, 'visible', GObject.BindingFlags.SYNC_CREATE);
this.prompt.bind_property('warning', warning, 'text', GObject.BindingFlags.SYNC_CREATE);
@ -249,13 +248,11 @@ const KeyringPrompter = new Lang.Class({
function() {
let dialog = this._enabled ? new KeyringDialog()
: new KeyringDummyDialog();
this._currentPrompt = dialog.prompt;
return this._currentPrompt;
return dialog.prompt;
}));
this._dbusId = null;
this._registered = false;
this._enabled = false;
this._currentPrompt = null;
},
enable: function() {
@ -270,10 +267,6 @@ const KeyringPrompter = new Lang.Class({
disable: function() {
this._enabled = false;
if (this._prompter.prompting)
this._currentPrompt.cancel();
this._currentPrompt = null;
}
});

View File

@ -138,8 +138,6 @@ const NetworkSecretDialog = new Lang.Class({
key: Clutter.KEY_Escape,
},
this._okButton]);
this._updateOkButton();
},
_updateOkButton: function() {
@ -255,7 +253,6 @@ const NetworkSecretDialog = new Lang.Class({
case 'leap':
case 'ttls':
case 'peap':
case 'fast':
// TTLS and PEAP are actually much more complicated, but this complication
// is not visible here since we only care about phase2 authentication
// (and don't even care of which one)
@ -435,7 +432,6 @@ const VPNRequestHandler = new Lang.Class({
},
_vpnChildFinished: function(pid, status, requestObj) {
this._childWatch = 0;
if (this._newStylePlugin) {
// For new style plugin, all work is done in the async reading functions
// Just reap the process here

View File

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

View File

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

View File

@ -1,7 +1,6 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const Signals = imports.signals;
const Lang = imports.lang;
const Meta = imports.gi.Meta;
@ -577,8 +576,7 @@ const Dash = new Lang.Class({
Lang.bind(this, function() {
this._labelShowing = true;
item.showLabel();
this._showLabelTimeoutId = 0;
return GLib.SOURCE_REMOVE;
return false;
}));
if (this._resetHoverTimeoutId > 0) {
Mainloop.source_remove(this._resetHoverTimeoutId);
@ -594,8 +592,7 @@ const Dash = new Lang.Class({
this._resetHoverTimeoutId = Mainloop.timeout_add(DASH_ITEM_HOVER_TIMEOUT,
Lang.bind(this, function() {
this._labelShowing = false;
this._resetHoverTimeoutId = 0;
return GLib.SOURCE_REMOVE;
return false;
}));
}
}

View File

@ -113,7 +113,22 @@ const DateMenuButton = new Lang.Class({
this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) {
if (isOpen) {
let now = new Date();
this._calendar.setDate(now);
/* Passing true to setDate() forces events to be reloaded. We
* want this behavior, because
*
* o It will cause activation of the calendar server which is
* useful if it has crashed
*
* o It will cause the calendar server to reload events which
* is useful if dynamic updates are not supported or not
* properly working
*
* Since this only happens when the menu is opened, the cost
* isn't very big.
*/
this._calendar.setDate(now, true);
// No need to update this._eventList as ::selected-date-changed
// signal will fire
}
}));

View File

@ -106,10 +106,10 @@ const _Draggable = new Lang.Class({
_onButtonPress : function (actor, event) {
if (event.get_button() != 1)
return Clutter.EVENT_PROPAGATE;
return false;
if (Tweener.getTweenCount(actor))
return Clutter.EVENT_PROPAGATE;
return false;
this._buttonDown = true;
this._grabActor();
@ -118,7 +118,7 @@ const _Draggable = new Lang.Class({
this._dragStartX = stageX;
this._dragStartY = stageY;
return Clutter.EVENT_PROPAGATE;
return false;
},
_grabActor: function() {
@ -164,11 +164,11 @@ const _Draggable = new Lang.Class({
} else if (this._dragActor != null && !this._animationInProgress) {
// Drag must have been cancelled with Esc.
this._dragComplete();
return Clutter.EVENT_STOP;
return true;
} else {
// Drag has never started.
this._ungrabActor();
return Clutter.EVENT_PROPAGATE;
return false;
}
// We intercept MOTION event to figure out if the drag has started and to draw
// this._dragActor under the pointer when dragging is in progress
@ -184,11 +184,11 @@ const _Draggable = new Lang.Class({
let symbol = event.get_key_symbol();
if (symbol == Clutter.Escape) {
this._cancelDrag(event.get_time());
return Clutter.EVENT_STOP;
return true;
}
}
return Clutter.EVENT_PROPAGATE;
return false;
},
/**
@ -236,7 +236,7 @@ const _Draggable = new Lang.Class({
if (this.actor._delegate && this.actor._delegate.getDragActor) {
this._dragActor = this.actor._delegate.getDragActor();
Main.uiGroup.add_child(this._dragActor);
this._dragActor.reparent(Main.uiGroup);
this._dragActor.raise_top();
Shell.util_set_hidden_from_pick(this._dragActor, true);
@ -285,8 +285,7 @@ const _Draggable = new Lang.Class({
this._dragOffsetX = actorStageX - this._dragStartX;
this._dragOffsetY = actorStageY - this._dragStartY;
this._dragOrigParent.remove_actor(this._dragActor);
Main.uiGroup.add_child(this._dragActor);
this._dragActor.reparent(Main.uiGroup);
this._dragActor.raise_top();
Shell.util_set_hidden_from_pick(this._dragActor, true);
}
@ -346,7 +345,6 @@ const _Draggable = new Lang.Class({
},
_updateDragHover : function () {
this._updateHoverId = 0;
let target = this._dragActor.get_stage().get_actor_at_pos(Clutter.PickMode.ALL,
this._dragX, this._dragY);
let dragEvent = {
@ -362,7 +360,7 @@ const _Draggable = new Lang.Class({
let result = motionFunc(dragEvent);
if (result != DragMotionResult.CONTINUE) {
global.screen.set_cursor(DRAG_CURSOR_MAP[result]);
return GLib.SOURCE_REMOVE;
return false;
}
}
}
@ -380,18 +378,18 @@ const _Draggable = new Lang.Class({
0);
if (result != DragMotionResult.CONTINUE) {
global.screen.set_cursor(DRAG_CURSOR_MAP[result]);
return GLib.SOURCE_REMOVE;
return false;
}
}
target = target.get_parent();
}
global.screen.set_cursor(Meta.Cursor.DND_IN_DRAG);
return GLib.SOURCE_REMOVE;
return false;
},
_queueUpdateDragHover: function() {
if (this._updateHoverId)
return;
GLib.source_remove(this._updateHoverId);
this._updateHoverId = GLib.idle_add(GLib.PRIORITY_DEFAULT,
Lang.bind(this, this._updateDragHover));
@ -557,8 +555,7 @@ const _Draggable = new Lang.Class({
_onAnimationComplete : function (dragActor, eventTime) {
if (this._dragOrigParent) {
Main.uiGroup.remove_child(this._dragActor);
this._dragOrigParent.add_actor(this._dragActor);
dragActor.reparent(this._dragOrigParent);
dragActor.set_scale(this._dragOrigScale, this._dragOrigScale);
dragActor.set_position(this._dragOrigX, this._dragOrigY);
} else {

View File

@ -43,22 +43,20 @@ const _DIALOG_ICON_SIZE = 32;
const GSM_SESSION_MANAGER_LOGOUT_FORCE = 2;
const EndSessionDialogIface = '<node> \
<interface name="org.gnome.SessionManager.EndSessionDialog"> \
<method name="Open"> \
<arg type="u" direction="in" /> \
<arg type="u" direction="in" /> \
<arg type="u" direction="in" /> \
<arg type="ao" direction="in" /> \
</method> \
<method name="Close" /> \
<signal name="ConfirmedLogout" /> \
<signal name="ConfirmedReboot" /> \
<signal name="ConfirmedShutdown" /> \
<signal name="Canceled" /> \
<signal name="Closed" /> \
</interface> \
</node>';
const EndSessionDialogIface = <interface name="org.gnome.SessionManager.EndSessionDialog">
<method name="Open">
<arg type="u" direction="in" />
<arg type="u" direction="in" />
<arg type="u" direction="in" />
<arg type="ao" direction="in" />
</method>
<method name="Close" />
<signal name="ConfirmedLogout" />
<signal name="ConfirmedReboot" />
<signal name="ConfirmedShutdown" />
<signal name="Canceled" />
<signal name="Closed" />
</interface>;
const logoutDialogContent = {
subjectWithUser: C_("title", "Log Out %s"),
@ -133,15 +131,13 @@ const DialogContent = {
const MAX_USERS_IN_SESSION_DIALOG = 5;
const LogindSessionIface = '<node> \
<interface name="org.freedesktop.login1.Session"> \
<property name="Id" type="s" access="read"/> \
<property name="Remote" type="b" access="read"/> \
<property name="Class" type="s" access="read"/> \
<property name="Type" type="s" access="read"/> \
<property name="State" type="s" access="read"/> \
</interface> \
</node>';
const LogindSessionIface = <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 LogindSession = Gio.DBusProxy.makeProxyWrapper(LogindSessionIface);
@ -249,9 +245,7 @@ const EndSessionDialog = new Lang.Class({
this._subjectLabel = new St.Label({ style_class: 'end-session-dialog-subject' });
messageLayout.add(this._subjectLabel,
{ x_fill: false,
y_fill: false,
x_align: St.Align.START,
{ y_fill: false,
y_align: St.Align.START });
this._descriptionLabel = new St.Label({ style_class: 'end-session-dialog-description' });
@ -275,15 +269,13 @@ const EndSessionDialog = new Lang.Class({
this._applicationHeader = new St.Label({ style_class: 'end-session-dialog-list-header',
text: _("Some applications are busy or have unsaved work.") });
this._applicationList = new St.BoxLayout({ style_class: 'end-session-dialog-app-list',
vertical: true });
this._applicationList = new St.BoxLayout({ vertical: true });
this._inhibitorSection.add_actor(this._applicationHeader);
this._inhibitorSection.add_actor(this._applicationList);
this._sessionHeader = new St.Label({ style_class: 'end-session-dialog-list-header',
text: _("Other users are logged in.") });
this._sessionList = new St.BoxLayout({ style_class: 'end-session-dialog-session-list',
vertical: true });
this._sessionList = new St.BoxLayout({ vertical: true });
this._inhibitorSection.add_actor(this._sessionHeader);
this._inhibitorSection.add_actor(this._sessionList);
@ -411,15 +403,14 @@ const EndSessionDialog = new Lang.Class({
this._secondsLeft = this._totalSecondsToStayOpen - secondsElapsed;
if (this._secondsLeft > 0) {
this._sync();
return GLib.SOURCE_CONTINUE;
return true;
}
let dialogContent = DialogContent[this._type];
let button = dialogContent.confirmButtons[dialogContent.confirmButtons.length - 1];
this._confirm(button.signal);
this._timerId = 0;
return GLib.SOURCE_REMOVE;
return false;
}));
},

View File

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

View File

@ -280,7 +280,7 @@ const GrabHelper = new Lang.Class({
if (type == Clutter.EventType.KEY_PRESS &&
event.get_key_symbol() == Clutter.KEY_Escape) {
this.ungrab({ isUser: true });
return Clutter.EVENT_STOP;
return true;
}
let press = type == Clutter.EventType.BUTTON_PRESS;
@ -289,14 +289,14 @@ const GrabHelper = new Lang.Class({
if (release && this._ignoreRelease) {
this._ignoreRelease = false;
return Clutter.EVENT_STOP;
return true;
}
if (this._isWithinGrabbedActor(event.get_source()))
return Clutter.EVENT_PROPAGATE;
return false;
if (Main.keyboard.shouldTakeEvent(event))
return Clutter.EVENT_PROPAGATE;
return false;
if (button) {
// If we have a press event, ignore the next event,
@ -305,9 +305,9 @@ const GrabHelper = new Lang.Class({
this._ignoreRelease = true;
let i = this._actorInGrabStack(event.get_source()) + 1;
this.ungrab({ actor: this._grabStack[i].actor, isUser: true });
return Clutter.EVENT_STOP;
return true;
}
return Clutter.EVENT_STOP;
return true;
},
});

View File

@ -32,7 +32,6 @@ const CandidateArea = new Lang.Class({
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());
return Clutter.EVENT_PROPAGATE;
}));
}

View File

@ -413,18 +413,15 @@ const IconGrid = new Lang.Class({
},
removeAll: function() {
this._items = [];
this._grid.remove_all_children();
},
destroyAll: function() {
this._items = [];
this._grid.destroy_all_children();
},
addItem: function(item, index) {
if (!item.icon instanceof BaseIcon)
throw new Error('Only items with a BaseIcon icon property can be added to IconGrid');
if (!item.icon || !item.icon instanceof BaseIcon) {
log('Only items with a BaseIcon icon property can be added to IconGrid');
return;
}
this._items.push(item);
if (index !== undefined)

View File

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

View File

@ -251,7 +251,7 @@ const LayoutManager = new Lang.Class({
this._inOverview = true;
this._updateVisibility();
this._updateRegions();
this._queueUpdateRegions();
},
hideOverview: function() {
@ -352,26 +352,26 @@ const LayoutManager = new Lang.Class({
this.emit('hot-corners-changed');
},
_addBackgroundMenu: function(bgManager) {
BackgroundMenu.addBackgroundMenu(bgManager.background.actor, this);
},
_createBackgroundManager: function(monitorIndex) {
_createBackground: function(monitorIndex) {
let bgManager = new Background.BackgroundManager({ container: this._backgroundGroup,
layoutManager: this,
monitorIndex: monitorIndex });
BackgroundMenu.addBackgroundMenu(bgManager.background.actor);
bgManager.connect('changed', Lang.bind(this, this._addBackgroundMenu));
this._addBackgroundMenu(bgManager);
bgManager.connect('changed', Lang.bind(this, function() {
BackgroundMenu.addBackgroundMenu(bgManager.background.actor);
}));
return bgManager;
this._bgManagers[monitorIndex] = bgManager;
return bgManager.background;
},
_showSecondaryBackgrounds: function() {
_createSecondaryBackgrounds: function() {
for (let i = 0; i < this.monitors.length; i++) {
if (i != this.primaryIndex) {
let background = this._bgManagers[i].background;
background.actor.show();
let background = this._createBackground(i);
background.actor.opacity = 0;
Tweener.addTween(background.actor,
{ opacity: 255,
@ -381,6 +381,10 @@ const LayoutManager = new Lang.Class({
}
},
_createPrimaryBackground: function() {
this._createBackground(this.primaryIndex);
},
_updateBackgrounds: function() {
let i;
for (i = 0; i < this._bgManagers.length; i++)
@ -391,12 +395,11 @@ const LayoutManager = new Lang.Class({
if (Main.sessionMode.isGreeter)
return;
for (let i = 0; i < this.monitors.length; i++) {
let bgManager = this._createBackgroundManager(i);
this._bgManagers.push(bgManager);
if (this._startingUp)
return;
if (i != this.primaryIndex && this._startingUp)
bgManager.background.actor.hide();
for (let i = 0; i < this.monitors.length; i++) {
this._createBackground(i);
}
},
@ -592,7 +595,7 @@ const LayoutManager = new Lang.Class({
if (Main.sessionMode.isGreeter) {
this.panelBox.translation_y = -this.panelBox.height;
} else {
this._updateBackgrounds();
this._createPrimaryBackground();
// We need to force an update of the regions now before we scale
// the UI group to get the coorect allocation for the struts.
@ -607,7 +610,7 @@ const LayoutManager = new Lang.Class({
this.uiGroup.set_pivot_point(x / global.screen_width,
y / global.screen_height);
this.uiGroup.scale_x = this.uiGroup.scale_y = 0.75;
this.uiGroup.scale_x = this.uiGroup.scale_y = 0.5;
this.uiGroup.opacity = 0;
global.window_group.set_clip(monitor.x, monitor.y, monitor.width, monitor.height);
}
@ -622,7 +625,7 @@ const LayoutManager = new Lang.Class({
// when the system is bogged down
GLib.idle_add(GLib.PRIORITY_LOW, Lang.bind(this, function() {
this._startupAnimation();
return GLib.SOURCE_REMOVE;
return false;
}));
},
@ -670,7 +673,7 @@ const LayoutManager = new Lang.Class({
this.keyboardBox.show();
if (!Main.sessionMode.isGreeter) {
this._showSecondaryBackgrounds();
this._createSecondaryBackgrounds();
global.window_group.remove_clip();
}
@ -1039,7 +1042,7 @@ const LayoutManager = new Lang.Class({
workspace.set_builtin_struts(struts);
}
return GLib.SOURCE_REMOVE;
return false;
}
});
Signals.addSignalMethods(LayoutManager.prototype);
@ -1226,20 +1229,20 @@ const HotCorner = new Lang.Class({
this._entered = true;
this._toggleOverview();
}
return Clutter.EVENT_PROPAGATE;
return false;
},
_onCornerLeft : function(actor, event) {
if (event.get_related() != this.actor)
this._entered = false;
// Consume event, otherwise this will confuse onEnvironsLeft
return Clutter.EVENT_STOP;
return true;
},
_onEnvironsLeft : function(actor, event) {
if (event.get_related() != this._corner)
this._entered = false;
return Clutter.EVENT_PROPAGATE;
return false;
}
});

View File

@ -109,7 +109,6 @@ const AutoComplete = new Lang.Class({
}
this._lastTabTime = currTime;
}
return Clutter.EVENT_PROPAGATE;
},
// Insert characters of text not already included in head at cursor position. i.e., if text="abc" and head="a",
@ -559,7 +558,7 @@ const Inspector = new Lang.Class({
_onKeyPressEvent: function (actor, event) {
if (event.get_key_symbol() == Clutter.Escape)
this._close();
return Clutter.EVENT_STOP;
return true;
},
_onButtonPressEvent: function (actor, event) {
@ -568,7 +567,7 @@ const Inspector = new Lang.Class({
this.emit('target', this._target, stageX, stageY);
}
this._close();
return Clutter.EVENT_STOP;
return true;
},
_onScrollEvent: function (actor, event) {
@ -602,12 +601,12 @@ const Inspector = new Lang.Class({
default:
break;
}
return Clutter.EVENT_STOP;
return true;
},
_onMotionEvent: function (actor, event) {
this._update(event);
return Clutter.EVENT_STOP;
return true;
},
_update: function(event) {
@ -630,6 +629,55 @@ const Inspector = new Lang.Class({
Signals.addSignalMethods(Inspector.prototype);
const Memory = new Lang.Class({
Name: 'Memory',
_init: function() {
this.actor = new St.BoxLayout({ vertical: true });
this._glibc_uordblks = new St.Label();
this.actor.add(this._glibc_uordblks);
this._js_bytes = new St.Label();
this.actor.add(this._js_bytes);
this._gjs_boxed = new St.Label();
this.actor.add(this._gjs_boxed);
this._gjs_gobject = new St.Label();
this.actor.add(this._gjs_gobject);
this._gjs_function = new St.Label();
this.actor.add(this._gjs_function);
this._gjs_closure = new St.Label();
this.actor.add(this._gjs_closure);
this._last_gc_seconds_ago = new St.Label();
this.actor.add(this._last_gc_seconds_ago);
this._gcbutton = new St.Button({ label: 'Full GC',
style_class: 'lg-obj-inspector-button' });
this._gcbutton.connect('clicked', Lang.bind(this, function () { System.gc(); this._renderText(); }));
this.actor.add(this._gcbutton, { x_align: St.Align.START,
x_fill: false });
this.actor.connect('notify::mapped', Lang.bind(this, this._renderText));
},
_renderText: function() {
if (!this.actor.mapped)
return;
let memInfo = global.get_memory_info();
this._glibc_uordblks.text = 'glibc_uordblks: ' + memInfo.glibc_uordblks;
this._js_bytes.text = 'js bytes: ' + memInfo.js_bytes;
this._gjs_boxed.text = 'gjs_boxed: ' + memInfo.gjs_boxed;
this._gjs_gobject.text = 'gjs_gobject: ' + memInfo.gjs_gobject;
this._gjs_function.text = 'gjs_function: ' + memInfo.gjs_function;
this._gjs_closure.text = 'gjs_closure: ' + memInfo.gjs_closure;
this._last_gc_seconds_ago.text = 'last_gc_seconds_ago: ' + memInfo.last_gc_seconds_ago;
}
});
const Extensions = new Lang.Class({
Name: 'Extensions',
@ -829,22 +877,7 @@ const LookingGlass = new Lang.Class({
global.stage.set_key_focus(this._entry);
}));
this.actor.hide();
return Clutter.EVENT_STOP;
}));
let gcIcon = new St.Icon({ icon_name: 'gnome-fs-trash-full',
icon_size: 24 });
toolbar.add_actor(gcIcon);
gcIcon.reactive = true;
gcIcon.connect('button-press-event', Lang.bind(this, function () {
gcIcon.icon_name = 'gnome-fs-trash-empty';
System.gc();
this._timeoutId = Mainloop.timeout_add(500, Lang.bind(this, function () {
gcIcon.icon_name = 'gnome-fs-trash-full';
Mainloop.source_remove(this._timeoutId);
return GLib.SOURCE_REMOVE;
}));
return Clutter.EVENT_PROPAGATE;
return true;
}));
let notebook = new Notebook();
@ -874,6 +907,9 @@ const LookingGlass = new Lang.Class({
this._windowList = new WindowList(this);
notebook.appendPage('Windows', this._windowList.actor);
this._memory = new Memory();
notebook.appendPage('Memory', this._memory.actor);
this._extensions = new Extensions(this);
notebook.appendPage('Extensions', this._extensions.actor);
@ -1066,7 +1102,7 @@ const LookingGlass = new Lang.Class({
} else {
this.close();
}
return Clutter.EVENT_STOP;
return true;
}
// Ctrl+PgUp and Ctrl+PgDown switches tabs in the notebook view
if (modifierState & Clutter.ModifierType.CONTROL_MASK) {
@ -1076,7 +1112,7 @@ const LookingGlass = new Lang.Class({
this._notebook.nextTab();
}
}
return Clutter.EVENT_PROPAGATE;
return false;
},
open : function() {

View File

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

View File

@ -112,6 +112,11 @@ function start() {
Gio.DesktopAppInfo.set_desktop_env('GNOME');
sessionMode = new SessionMode.SessionMode();
sessionMode.connect('sessions-loaded', _sessionsLoaded);
sessionMode.init();
}
function _sessionsLoaded() {
sessionMode.connect('updated', _sessionUpdated);
_initializePrefs();
_initializeUI();
@ -609,7 +614,7 @@ function queueDeferredWork(workId) {
_deferredTimeoutId = Mainloop.timeout_add_seconds(DEFERRED_TIMEOUT_SECONDS, function () {
_runAllDeferredWork();
_deferredTimeoutId = 0;
return GLib.SOURCE_REMOVE;
return false;
});
}
}

View File

@ -76,7 +76,7 @@ const Urgency = {
NORMAL: 1,
HIGH: 2,
CRITICAL: 3
};
}
function _fixMarkup(text, allowMarkup) {
if (allowMarkup) {
@ -187,7 +187,7 @@ const URLHighlighter = new Lang.Class({
// The MessageTray doesn't actually hide us, so
// we need to check for paint opacities as well.
if (!actor.visible || actor.get_paint_opacity() == 0)
return Clutter.EVENT_PROPAGATE;
return false;
// Keep Notification.actor from seeing this and taking
// a pointer grab, which would block our button-release-event
@ -196,7 +196,7 @@ const URLHighlighter = new Lang.Class({
}));
this.actor.connect('button-release-event', Lang.bind(this, function (actor, event) {
if (!actor.visible || actor.get_paint_opacity() == 0)
return Clutter.EVENT_PROPAGATE;
return false;
let urlId = this._findUrlAtPos(event);
if (urlId != -1) {
@ -205,13 +205,13 @@ const URLHighlighter = new Lang.Class({
url = 'http://' + url;
Gio.app_info_launch_default_for_uri(url, global.create_app_launch_context());
return Clutter.EVENT_STOP;
return true;
}
return Clutter.EVENT_PROPAGATE;
return false;
}));
this.actor.connect('motion-event', Lang.bind(this, function(actor, event) {
if (!actor.visible || actor.get_paint_opacity() == 0)
return Clutter.EVENT_PROPAGATE;
return false;
let urlId = this._findUrlAtPos(event);
if (urlId != -1 && !this._cursorChanged) {
@ -221,17 +221,16 @@ const URLHighlighter = new Lang.Class({
global.screen.set_cursor(Meta.Cursor.DEFAULT);
this._cursorChanged = false;
}
return Clutter.EVENT_PROPAGATE;
return false;
}));
this.actor.connect('leave-event', Lang.bind(this, function() {
if (!this.actor.visible || this.actor.get_paint_opacity() == 0)
return Clutter.EVENT_PROPAGATE;
return;
if (this._cursorChanged) {
this._cursorChanged = false;
global.screen.set_cursor(Meta.Cursor.DEFAULT);
}
return Clutter.EVENT_PROPAGATE;
}));
},
@ -281,6 +280,10 @@ const URLHighlighter = new Lang.Class({
}
});
function strHasSuffix(string, suffix) {
return string.substr(-suffix.length) == suffix;
}
// NotificationPolicy:
// An object that holds all bits of configurable policy related to a notification
// source, such as whether to play sound or honour the critical bit.
@ -307,126 +310,6 @@ const NotificationPolicy = new Lang.Class({
});
Signals.addSignalMethods(NotificationPolicy.prototype);
const NotificationGenericPolicy = new Lang.Class({
Name: 'NotificationGenericPolicy',
Extends: 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: 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');
}
});
// Notification:
// @source: the notification's Source
// @title: the title
@ -500,6 +383,7 @@ const Notification = new Lang.Class({
this.focused = false;
this.acknowledged = false;
this._destroyed = false;
this._useActionIcons = false;
this._customContent = false;
this.bannerBodyText = null;
this.bannerBodyMarkup = false;
@ -554,12 +438,7 @@ const Notification = new Lang.Class({
this._bannerLabel = this._bannerUrlHighlighter.actor;
this._bannerBox.add_actor(this._bannerLabel);
// If called with only one argument we assume the caller
// will call .update() later on. This is the case of
// NotificationDaemon, which wants to use the same code
// for new and updated notifications
if (arguments.length != 1)
this.update(title, banner, params);
this.update(title, banner, params);
},
// update:
@ -832,8 +711,19 @@ const Notification = new Lang.Class({
}
},
addButton: function(button, callback) {
// addButton:
// @id: the action ID
// @label: the label for the action's button
//
// Adds a button with the given @label to the notification. All
// action buttons will appear in a single row at the bottom of
// the notification.
//
// If the button is clicked, the notification will emit the
// %action-invoked signal with @id as a parameter
addButton: function(id, label) {
if (!this._buttonBox) {
let box = new St.BoxLayout({ style_class: 'notification-actions' });
this.setActionArea(box, { x_expand: false,
y_expand: false,
@ -841,40 +731,49 @@ const Notification = new Lang.Class({
y_fill: false,
x_align: St.Align.END });
this._buttonBox = box;
global.focus_manager.add_group(this._buttonBox);
}
this._buttonBox.add(button);
button.connect('clicked', Lang.bind(this, function() {
callback();
let button = new St.Button({ can_focus: true });
button._actionId = id;
if (!this.resident) {
// We don't hide a resident notification when the user invokes one of its actions,
// because it is common for such notifications to update themselves with new
// information based on the action. We'd like to display the updated information
// in place, rather than pop-up a new notification.
this.emit('done-displaying');
this.destroy();
}
}));
let iconName = strHasSuffix(id, '-symbolic') ? id : id + '-symbolic';
if (this._useActionIcons && Gtk.IconTheme.get_default().has_icon(iconName)) {
button.add_style_class_name('notification-icon-button');
button.child = new St.Icon({ icon_name: iconName });
} else {
button.add_style_class_name('notification-button');
button.label = label;
}
if (this._buttonBox.get_n_children() > 0)
global.focus_manager.remove_group(this._buttonBox);
this._buttonBox.add(button);
global.focus_manager.add_group(this._buttonBox);
button.connect('clicked', Lang.bind(this, this._onActionInvoked, id));
this.updated();
return button;
},
// addAction:
// @label: the label for the action's button
// @callback: the callback for the action
// setButtonSensitive:
// @id: the action ID
// @sensitive: whether the button should be sensitive
//
// Adds a button with the given @label to the notification. All
// action buttons will appear in a single row at the bottom of
// the notification.
addAction: function(label, callback) {
let button = new St.Button({ style_class: 'notification-button',
label: label,
can_focus: true });
// If the notification contains a button with action ID @id,
// its sensitivity will be set to @sensitive. Insensitive
// buttons cannot be clicked.
setButtonSensitive: function(id, sensitive) {
if (!this._buttonBox)
return;
return this.addButton(button, callback);
let button = this._buttonBox.get_children().filter(function(b) {
return b._actionId == id;
})[0];
if (!button || button.reactive == sensitive)
return;
button.reactive = sensitive;
},
setUrgency: function(urgency) {
@ -893,6 +792,10 @@ const Notification = new Lang.Class({
this.forFeedback = forFeedback;
},
setUseActionIcons: function(useIcons) {
this._useActionIcons = useIcons;
},
_styleChanged: function() {
this._spacing = this._table.get_theme_node().get_length('spacing-columns');
},
@ -1117,6 +1020,18 @@ const Notification = new Lang.Class({
this.actor.add_style_class_name('notification-unexpanded');
},
_onActionInvoked: function(actor, mouseButtonClicked, id) {
this.emit('action-invoked', id);
if (!this.resident) {
// We don't hide a resident notification when the user invokes one of its actions,
// because it is common for such notifications to update themselves with new
// information based on the action. We'd like to display the updated information
// in place, rather than pop-up a new notification.
this.emit('done-displaying');
this.destroy();
}
},
_onClicked: function() {
this.emit('clicked');
// We hide all types of notifications once the user clicks on them because the common
@ -1160,7 +1075,7 @@ const SourceActor = new Lang.Class({
}));
this._actorDestroyed = false;
this._counterLabel = new St.Label({ x_align: Clutter.ActorAlign.CENTER,
this._counterLabel = new St.Label( {x_align: Clutter.ActorAlign.CENTER,
x_expand: true,
y_align: Clutter.ActorAlign.CENTER,
y_expand: true });
@ -1264,6 +1179,7 @@ const Source = new Lang.Class({
this.title = title;
this.iconName = iconName;
this.isTransient = false;
this.isChat = false;
this.isMuted = false;
this.keepTrayOnSummaryClick = false;
@ -1290,10 +1206,6 @@ const Source = new Lang.Class({
return this.count > 1;
},
get isClearable() {
return !this.trayIcon && !this.isChat && !this.resident;
},
countUpdated: function() {
this.emit('count-updated');
},
@ -1323,6 +1235,10 @@ const Source = new Lang.Class({
return rightClickMenu;
},
setTransient: function(isTransient) {
this.isTransient = isTransient;
},
setTitle: function(newTitle) {
this.title = newTitle;
this.emit('title-changed');
@ -1361,25 +1277,25 @@ const Source = new Lang.Class({
return this._mainIcon.actor;
},
_onNotificationDestroy: function(notification) {
let index = this.notifications.indexOf(notification);
if (index < 0)
return;
this.notifications.splice(index, 1);
if (this.notifications.length == 0)
this._lastNotificationRemoved();
this.countUpdated();
},
pushNotification: function(notification) {
if (this.notifications.indexOf(notification) >= 0)
return;
if (this.notifications.indexOf(notification) < 0) {
this.notifications.push(notification);
this.emit('notification-added', notification);
}
notification.connect('destroy', Lang.bind(this, this._onNotificationDestroy));
this.notifications.push(notification);
this.emit('notification-added', notification);
notification.connect('clicked', Lang.bind(this, this.open));
notification.connect('destroy', Lang.bind(this,
function () {
let index = this.notifications.indexOf(notification);
if (index < 0)
return;
this.notifications.splice(index, 1);
if (this.notifications.length == 0)
this._lastNotificationRemoved();
this.countUpdated();
}));
this.countUpdated();
},
@ -1524,9 +1440,9 @@ const SummaryItem = new Lang.Class({
_onKeyPress: function(actor, event) {
if (event.get_key_symbol() == Clutter.KEY_Up) {
actor.emit('clicked', 1);
return Clutter.EVENT_STOP;
return true;
}
return Clutter.EVENT_PROPAGATE;
return false;
},
prepareNotificationStackForShowing: function() {
@ -1591,42 +1507,26 @@ const MessageTrayMenu = new Lang.Class({
this._tray = tray;
this._presence = new GnomeSession.Presence(Lang.bind(this, function(proxy, error) {
if (error) {
logError(error, 'Error while reading gnome-session presence');
return;
}
this._onStatusChanged(proxy.status);
}));
this._presence.connectSignal('StatusChanged', Lang.bind(this, function(proxy, senderName, [status]) {
this._onStatusChanged(status);
}));
this._accountManager = Tp.AccountManager.dup();
this._accountManager.connect('most-available-presence-changed',
Lang.bind(this, this._onIMPresenceChanged));
this._accountManager.prepare_async(null, Lang.bind(this, this._onIMPresenceChanged));
this.actor.hide();
Main.layoutManager.addChrome(this.actor);
this._busyItem = new PopupMenu.PopupSwitchMenuItem(_("Notifications"));
this._busyItem.connect('toggled', Lang.bind(this, this._updatePresence));
this.addMenuItem(this._busyItem);
let separator = new PopupMenu.PopupSeparatorMenuItem();
this.addMenuItem(separator);
this._clearItem = this.addAction(_("Clear Messages"), function() {
let toDestroy = tray.getSources().filter(function(source) {
return source.isClearable;
});
let toDestroy = [];
let sources = tray.getSources();
for (let i = 0; i < sources.length; i++) {
// We exclude trayIcons, chat and resident sources
if (sources[i].trayIcon ||
sources[i].isChat ||
sources[i].resident)
continue;
toDestroy.push(sources[i]);
}
toDestroy.forEach(function(source) {
source.destroy();
});
for (let i = 0; i < toDestroy.length; i++) {
toDestroy[i].destroy();
}
toDestroy = null;
tray.close();
});
@ -1641,43 +1541,9 @@ const MessageTrayMenu = new Lang.Class({
settingsItem.connect('activate', function() { tray.close(); });
},
_onStatusChanged: function(status) {
this._sessionStatus = status;
this._busyItem.setToggleState(status != GnomeSession.PresenceStatus.BUSY);
},
_onIMPresenceChanged: function(am, type) {
if (type == Tp.ConnectionPresenceType.AVAILABLE &&
this._sessionStatus == GnomeSession.PresenceStatus.BUSY)
this._presence.SetStatusRemote(GnomeSession.PresenceStatus.AVAILABLE);
},
_updateClearSensitivity: function() {
this._clearItem.setSensitive(this._tray.clearableCount > 0);
},
_updatePresence: function(item, state) {
let status = state ? GnomeSession.PresenceStatus.AVAILABLE
: GnomeSession.PresenceStatus.BUSY;
this._presence.SetStatusRemote(status);
let [type, s ,msg] = this._accountManager.get_most_available_presence();
let newType = 0;
let newStatus;
if (status == GnomeSession.PresenceStatus.BUSY &&
type == Tp.ConnectionPresenceType.AVAILABLE) {
newType = Tp.ConnectionPresenceType.BUSY;
newStatus = 'busy';
} else if (status == GnomeSession.PresenceStatus.AVAILABLE &&
type == Tp.ConnectionPresenceType.BUSY) {
newType = Tp.ConnectionPresenceType.AVAILABLE;
newStatus = 'available';
}
if (newType > 0)
this._accountManager.set_all_requested_presences(newType,
newStatus, msg);
}
});
const MessageTrayMenuButton = new Lang.Class({
@ -1784,7 +1650,6 @@ const MessageTray = new Lang.Class({
this._setClickedSummaryItem(null);
this._updateState();
actor.grab_key_focus();
return Clutter.EVENT_PROPAGATE;
}));
global.focus_manager.add_group(this.actor);
this._summary = new St.BoxLayout({ style_class: 'message-tray-summary',
@ -1990,32 +1855,32 @@ const MessageTray = new Lang.Class({
this._trayDwellTimeoutId = 0;
if (Main.layoutManager.bottomMonitor.inFullscreen)
return GLib.SOURCE_REMOVE;
return false;
// We don't want to open the tray when a modal dialog
// is up, so we check the modal count for that. When we are in the
// overview we have to take the overview's modal push into account
if (Main.modalCount > (Main.overview.visible ? 1 : 0))
return GLib.SOURCE_REMOVE;
return false;
// If the user interacted with the focus window since we started the tray
// dwell (by clicking or typing), don't activate the message tray
let focusWindow = global.display.focus_window;
let currentUserTime = focusWindow ? focusWindow.user_time : 0;
if (currentUserTime != this._trayDwellUserTime)
return GLib.SOURCE_REMOVE;
return false;
this.openTray();
return GLib.SOURCE_REMOVE;
return false;
},
_onNotificationKeyRelease: function(actor, event) {
if (event.get_key_symbol() == Clutter.KEY_Escape && event.get_state() == 0) {
this._closeNotification();
return Clutter.EVENT_STOP;
return true;
}
return Clutter.EVENT_PROPAGATE;
return false;
},
_closeNotification: function() {
@ -2061,7 +1926,7 @@ const MessageTray = new Lang.Class({
this._summary.insert_child_at_index(summaryItem.actor, this._chatSummaryItemsCount);
}
if (source.isClearable)
if (!source.trayIcon && !source.isChat && !source.resident)
this.clearableCount++;
this._sources.set(source, obj);
@ -2105,7 +1970,7 @@ const MessageTray = new Lang.Class({
if (source.isChat)
this._chatSummaryItemsCount--;
if (source.isClearable)
if (!source.trayIcon && !source.isChat && !source.resident)
this.clearableCount--;
source.disconnect(obj.notifyId);
@ -2170,10 +2035,7 @@ const MessageTray = new Lang.Class({
},
toggleAndNavigate: function() {
if (!this.toggle())
return;
if (this._traySummoned)
if (this.toggle())
this._summary.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
},
@ -2336,7 +2198,7 @@ const MessageTray = new Lang.Class({
this._updateNotificationTimeout(0);
this._updateState();
}
return GLib.SOURCE_REMOVE;
return false;
},
_escapeTray: function() {
@ -2353,13 +2215,6 @@ const MessageTray = new Lang.Class({
// _updateState() figures out what (if anything) needs to be done
// at the present time.
_updateState: function() {
// If our state changes caused _updateState to be called,
// just exit now to prevent reentrancy issues.
if (this._updatingState)
return;
this._updatingState = true;
// Filter out acknowledged notifications.
this._notificationQueue = this._notificationQueue.filter(function(n) {
return !n.acknowledged;
@ -2377,9 +2232,10 @@ const MessageTray = new Lang.Class({
this._showNotification();
}
} else if (this._notificationState == State.SHOWN) {
let pinned = this._pointerInNotification && !this._notificationRemoved;
let expired = (this._userActiveWhileNotificationShown &&
this._notificationTimeoutId == 0 &&
this._notification.urgency != Urgency.CRITICAL &&
!(this._notification.urgency == Urgency.CRITICAL) &&
!this._notification.focused &&
!this._pointerInNotification);
let mustClose = (this._notificationRemoved || !hasNotifications || expired || this._traySummoned);
@ -2387,9 +2243,9 @@ const MessageTray = new Lang.Class({
if (mustClose) {
let animate = hasNotifications && !this._notificationRemoved;
this._hideNotification(animate);
} else if (this._pointerInNotification && !this._notification.expanded) {
} else if (pinned && !this._notification.expanded) {
this._expandNotification(false);
} else if (this._pointerInNotification) {
} else if (pinned) {
this._ensureNotificationFocused();
}
}
@ -2436,12 +2292,11 @@ const MessageTray = new Lang.Class({
this._desktopCloneState == State.SHOWN);
let desktopCloneShouldBeVisible = (trayShouldBeVisible);
if (!desktopCloneIsVisible && desktopCloneShouldBeVisible)
if (!desktopCloneIsVisible && desktopCloneShouldBeVisible) {
this._showDesktopClone();
else if (desktopCloneIsVisible && !desktopCloneShouldBeVisible)
} else if (desktopCloneIsVisible && !desktopCloneShouldBeVisible) {
this._hideDesktopClone();
this._updatingState = false;
}
},
_tween: function(actor, statevar, value, params) {
@ -2669,7 +2524,7 @@ const MessageTray = new Lang.Class({
this._lastSeenMouseX = x;
this._lastSeenMouseY = y;
return GLib.SOURCE_REMOVE;
return false;
},
_hideNotification: function(animate) {
@ -2760,12 +2615,12 @@ const MessageTray = new Lang.Class({
} else if (this._notification.y != expandedY) {
// Tween also opacity here, to override a possible tween that's
// currently hiding the notification.
Tweener.addTween(this._notificationWidget,
{ y: expandedY,
opacity: 255,
time: ANIMATION_TIME,
transition: 'easeOutQuad'
});
this._tween(this._notificationWidget, '_notificationState', State.SHOWN,
{ y: expandedY,
opacity: 255,
time: ANIMATION_TIME,
transition: 'easeOutQuad'
});
}
},
@ -2806,13 +2661,13 @@ const MessageTray = new Lang.Class({
Lang.bind(this, this._onSourceDoneDisplayingContent));
this._summaryBoxPointer.bin.child = child;
this._grabHelper.grab({ actor: this._summaryBoxPointer.bin.child,
onUngrab: Lang.bind(this, this._onSummaryBoxPointerUngrabbed) });
this._summaryBoxPointer.actor.opacity = 0;
this._summaryBoxPointer.actor.show();
this._adjustSummaryBoxPointerPosition();
this._grabHelper.grab({ actor: this._summaryBoxPointer.bin.child,
onUngrab: Lang.bind(this, this._onSummaryBoxPointerUngrabbed) });
this._summaryBoxPointerState = State.SHOWING;
this._summaryBoxPointer.show(BoxPointer.PopupAnimation.FULL, Lang.bind(this, function() {
this._summaryBoxPointerState = State.SHOWN;
@ -2867,13 +2722,13 @@ const MessageTray = new Lang.Class({
case Clutter.KEY_Escape:
this._setClickedSummaryItem(null);
this._updateState();
return Clutter.EVENT_STOP;
return true;
case Clutter.KEY_Delete:
this._clickedSummaryItem.source.destroy();
this._escapeTray();
return Clutter.EVENT_STOP;
return true;
}
return Clutter.EVENT_PROPAGATE;
return false;
},
_onSummaryBoxPointerUngrabbed: function() {
@ -2911,6 +2766,8 @@ const MessageTray = new Lang.Class({
this._summaryBoxPointerItem.doneShowingNotificationStack();
this._summaryBoxPointerItem = null;
if (source.isTransient && !this._reNotifyAfterHideNotification)
source.destroy(NotificationDestroyedReason.EXPIRED);
if (this._reNotifyAfterHideNotification) {
this._onNotify(this._reNotifyAfterHideNotification.source, this._reNotifyAfterHideNotification);
this._reNotifyAfterHideNotification = null;
@ -2929,6 +2786,7 @@ const SystemNotificationSource = new Lang.Class({
_init: function() {
this.parent(_("System Information"), 'dialog-information-symbolic');
this.setTransient(true);
},
open: function() {

View File

@ -229,7 +229,6 @@ const ModalDialog = new Lang.Class({
_onKeyPressEvent: function(object, event) {
this._pressedKey = event.get_key_symbol();
return Clutter.EVENT_PROPAGATE;
},
_onKeyReleaseEvent: function(object, event) {
@ -238,21 +237,21 @@ const ModalDialog = new Lang.Class({
let symbol = event.get_key_symbol();
if (symbol != pressedKey)
return Clutter.EVENT_PROPAGATE;
return false;
let buttonInfo = this._buttonKeys[symbol];
if (!buttonInfo)
return Clutter.EVENT_PROPAGATE;
return false;
let button = buttonInfo['button'];
let action = buttonInfo['action'];
if (action && button.reactive) {
action();
return Clutter.EVENT_STOP;
return true;
}
return Clutter.EVENT_PROPAGATE;
return false;
},
_onGroupDestroy: function() {

View File

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

View File

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

View File

@ -1,7 +1,6 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const Meta = imports.gi.Meta;
const Mainloop = imports.mainloop;
@ -79,8 +78,10 @@ const ShellInfo = new Lang.Class({
}
this._undoCallback = undoCallback;
if (undoCallback)
notification.addAction(_("Undo"), Lang.bind(this, this._onUndoClicked));
if (undoCallback) {
notification.addButton('system-undo', _("Undo"));
notification.connect('action-invoked', Lang.bind(this, this._onUndoClicked));
}
this._source.notify(notification);
}
@ -113,6 +114,9 @@ const Overview = new Lang.Class({
// rendering options without duplicating the texture data.
let monitor = Main.layoutManager.primaryMonitor;
this._desktopFade = new St.Bin();
Main.layoutManager.overviewGroup.add_child(this._desktopFade);
let layout = new Clutter.BinLayout();
this._stack = new Clutter.Actor({ layout_manager: layout });
this._stack.add_constraint(new LayoutManager.MonitorConstraint({ primary: true }));
@ -131,9 +135,6 @@ const Overview = new Lang.Class({
Main.layoutManager.overviewGroup.add_child(this._backgroundGroup);
this._bgManagers = [];
this._desktopFade = new St.Bin();
Main.layoutManager.overviewGroup.add_child(this._desktopFade);
this._activationTime = 0;
this.visible = false; // animating to overview, in overview, animating out
@ -148,7 +149,7 @@ const Overview = new Lang.Class({
this._coverPane = new Clutter.Actor({ opacity: 0,
reactive: true });
Main.layoutManager.overviewGroup.add_child(this._coverPane);
this._coverPane.connect('event', Lang.bind(this, function (actor, event) { return Clutter.EVENT_STOP; }));
this._coverPane.connect('event', Lang.bind(this, function (actor, event) { return true; }));
this._stack.add_actor(this._overview);
Main.layoutManager.overviewGroup.add_child(this._stack);
@ -312,6 +313,9 @@ const Overview = new Lang.Class({
},
_onDragEnd: function(time) {
if (this.animationInProgress)
return;
this._inXdndDrag = false;
// In case the drag was canceled while in the overview
@ -364,13 +368,11 @@ const Overview = new Lang.Class({
this._lastHoveredWindow = dragEvent.targetActor._delegate.metaWindow;
this._windowSwitchTimeoutId = Mainloop.timeout_add(DND_WINDOW_SWITCH_TIMEOUT,
Lang.bind(this, function() {
this._windowSwitchTimeoutId = 0;
this._needsFakePointerEvent = true;
Main.activateWindow(dragEvent.targetActor._delegate.metaWindow,
this._windowSwitchTimestamp);
this.hide();
this._lastHoveredWindow = null;
return GLib.SOURCE_REMOVE;
}));
}
@ -379,7 +381,6 @@ const Overview = new Lang.Class({
_onScrollEvent: function(actor, event) {
this.emit('scroll-event', event);
return Clutter.EVENT_PROPAGATE;
},
addAction: function(action) {
@ -447,17 +448,17 @@ const Overview = new Lang.Class({
this._inDrag = false;
},
beginWindowDrag: function(clone) {
this.emit('window-drag-begin', clone);
beginWindowDrag: function(source) {
this.emit('window-drag-begin');
this._inDrag = true;
},
cancelledWindowDrag: function(clone) {
this.emit('window-drag-cancelled', clone);
cancelledWindowDrag: function(source) {
this.emit('window-drag-cancelled');
},
endWindowDrag: function(clone) {
this.emit('window-drag-end', clone);
endWindowDrag: function(source) {
this.emit('window-drag-end');
this._inDrag = false;
},

View File

@ -36,7 +36,6 @@ const SlideLayout = new Lang.Class({
_init: function(params) {
this._slideX = 1;
this._translationX = 0;
this._direction = SlideDirection.LEFT;
this.parent(params);
@ -56,21 +55,18 @@ const SlideLayout = new Lang.Class({
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 [, natWidth] = child.get_preferred_width(availHeight);
// Align the actor inside the clipped box, as the actor's alignment
// flags only determine what to do if the allocated box is bigger
// than the actor's box.
let realDirection = getRtlSlideDirection(this._direction, child);
let alignX = (realDirection == SlideDirection.LEFT) ? (availWidth - natWidth) : 0;
let translationX = (realDirection == SlideDirection.LEFT) ?
(availWidth - natWidth) : (natWidth - availWidth);
let actorBox = new Clutter.ActorBox();
actorBox.x1 = box.x1 + alignX + this._translationX;
actorBox.x2 = actorBox.x1 + (child.x_expand ? availWidth : natWidth);
actorBox.y1 = box.y1;
actorBox.y2 = actorBox.y1 + availHeight;
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);
},
@ -91,16 +87,7 @@ const SlideLayout = new Lang.Class({
get slideDirection() {
return this._direction;
},
set translationX(value) {
this._translationX = value;
this.layout_changed();
},
get translationX() {
return this._translationX;
},
}
});
const SlidingControl = new Lang.Class({
@ -109,8 +96,8 @@ const SlidingControl = new Lang.Class({
_init: function(params) {
params = Params.parse(params, { slideDirection: SlideDirection.LEFT });
this._visible = true;
this._inDrag = false;
this.visible = true;
this.inDrag = false;
this.layout = new SlideLayout();
this.layout.slideDirection = params.slideDirection;
@ -119,7 +106,6 @@ const SlidingControl = new Lang.Class({
clip_to_allocation: true });
Main.overview.connect('showing', Lang.bind(this, this._onOverviewShowing));
Main.overview.connect('hiding', Lang.bind(this, this._onOverviewHiding));
Main.overview.connect('item-drag-begin', Lang.bind(this, this._onDragBegin));
Main.overview.connect('item-drag-end', Lang.bind(this, this._onDragEnd));
@ -130,12 +116,12 @@ const SlidingControl = new Lang.Class({
Main.overview.connect('window-drag-end', Lang.bind(this, this._onWindowDragEnd));
},
_getSlide: function() {
getSlide: function() {
throw new Error('getSlide() must be overridden');
},
_updateSlide: function() {
Tweener.addTween(this.layout, { slideX: this._getSlide(),
updateSlide: function() {
Tweener.addTween(this.layout, { slideX: this.getSlide(),
time: SIDE_CONTROLS_ANIMATION_TIME,
transition: 'easeOutQuad' });
},
@ -162,30 +148,28 @@ const SlidingControl = new Lang.Class({
let translationEnd = 0;
let translation = this._getTranslation();
if (this._visible) {
if (this.visible) {
translationStart = translation;
} else {
translationEnd = translation;
}
if (this.layout.translationX == translationEnd)
if (this.actor.translation_x == translationEnd)
return;
this.layout.translationX = translationStart;
Tweener.addTween(this.layout, { translationX: translationEnd,
time: SIDE_CONTROLS_ANIMATION_TIME,
transition: 'easeOutQuad' });
this.actor.translation_x = translationStart;
Tweener.addTween(this.actor, { translation_x: translationEnd,
time: SIDE_CONTROLS_ANIMATION_TIME,
transition: 'easeOutQuad'
});
},
_onOverviewShowing: function() {
this._visible = true;
this.layout.slideX = this._getSlide();
this.layout.translationX = this._getTranslation();
this.slideIn();
},
_onOverviewHiding: function() {
this.slideOut();
// 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() {
@ -197,14 +181,14 @@ const SlidingControl = new Lang.Class({
},
_onDragBegin: function() {
this._inDrag = true;
this.layout.translationX = 0;
this._updateSlide();
this.inDrag = true;
this.actor.translation_x = 0;
this.updateSlide();
},
_onDragEnd: function() {
this._inDrag = false;
this._updateSlide();
this.inDrag = false;
this.updateSlide();
},
fadeIn: function() {
@ -222,13 +206,13 @@ const SlidingControl = new Lang.Class({
},
slideIn: function() {
this._visible = true;
this.visible = true;
this._updateTranslation();
// we will update slideX and the translation from pageEmpty
},
slideOut: function() {
this._visible = false;
this.visible = false;
this._updateTranslation();
// we will update slideX from pageEmpty
},
@ -238,7 +222,7 @@ const SlidingControl = new Lang.Class({
// 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.layout.slideX = this.getSlide();
this._updateTranslation();
}
});
@ -252,20 +236,26 @@ const ThumbnailsSlider = new Lang.Class({
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));
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this.updateSlide));
Main.overview.connect('hiding', Lang.bind(this, this.slideOut));
this.actor.connect('notify::hover', Lang.bind(this, this.updateSlide));
this._thumbnailsBox.actor.bind_property('visible', this.actor, 'visible', GObject.BindingFlags.SYNC_CREATE);
},
_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;
let alwaysZoomOut = this.actor.hover || this.inDrag || !Meta.prefs_get_dynamic_workspaces() || global.screen.n_workspaces > 2;
if (!alwaysZoomOut) {
let monitors = Main.layoutManager.monitors;
@ -285,13 +275,20 @@ const ThumbnailsSlider = new Lang.Class({
return alwaysZoomOut;
},
_onOverviewShowing: function() {
this.visible = true;
this.layout.slideX = this.getSlide();
this.actor.translation_x = this._getTranslation();
this.slideIn();
},
getNonExpandedWidth: function() {
let child = this.actor.get_first_child();
return child.get_theme_node().get_length('visible-width');
},
_getSlide: function() {
if (!this._visible)
getSlide: function() {
if (!this.visible)
return 0;
let alwaysZoomOut = this._getAlwaysZoomOut();
@ -327,23 +324,31 @@ const DashSlider = new Lang.Class({
// 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.x_expand = true;
this.actor.x_align = Clutter.ActorAlign.START;
this.actor.y_expand = true;
this.actor.add_actor(this._dash.actor);
this._dash.connect('icon-size-changed', Lang.bind(this, this._updateSlide));
this._dash.connect('icon-size-changed', Lang.bind(this, this.updateSlide));
Main.overview.connect('hiding', Lang.bind(this, this.slideOut));
},
_getSlide: function() {
if (this._visible || this._inDrag)
getSlide: function() {
if (this.visible || this.inDrag)
return 1;
else
return 0;
},
_onOverviewShowing: function() {
this.visible = true;
this.layout.slideX = this.getSlide();
this.actor.translation_x = this._getTranslation();
this.slideIn();
},
_onWindowDragBegin: function() {
this.fadeHalf();
},
@ -460,6 +465,9 @@ const MessagesIndicator = new Lang.Class({
if (source.trayIcon)
return;
if (source.isTransient)
return;
source.connect('count-updated', Lang.bind(this, this._updateCount));
this._sources.push(source);
this._updateCount();

View File

@ -213,7 +213,9 @@ const AppMenuButton = new Lang.Class({
this._label = new TextShadower();
this._label.actor.y_align = Clutter.ActorAlign.CENTER;
this._hbox.add_actor(this._label.actor);
this._arrow = PopupMenu.unicodeArrow(St.Side.BOTTOM);
this._arrow = new St.Label({ text: '\u25BE',
y_expand: true,
y_align: Clutter.ActorAlign.CENTER });
this._hbox.add_actor(this._arrow);
this._iconBottomClip = 0;
@ -602,15 +604,14 @@ const ActivitiesButton = new Lang.Class({
_onCapturedEvent: function(actor, event) {
if (event.type() == Clutter.EventType.BUTTON_PRESS) {
if (!Main.overview.shouldToggleByCornerOrButton())
return Clutter.EVENT_STOP;
return true;
}
return Clutter.EVENT_PROPAGATE;
return false;
},
_onButtonRelease: function() {
Main.overview.toggle();
this.menu.close();
return Clutter.EVENT_PROPAGATE;
},
_onKeyRelease: function(actor, event) {
@ -618,7 +619,6 @@ const ActivitiesButton = new Lang.Class({
if (symbol == Clutter.KEY_Return || symbol == Clutter.KEY_space) {
Main.overview.toggle();
}
return Clutter.EVENT_PROPAGATE;
},
_xdndToggleOverview: function(actor) {
@ -630,7 +630,6 @@ const ActivitiesButton = new Lang.Class({
Mainloop.source_remove(this._xdndTimeOut);
this._xdndTimeOut = 0;
return GLib.SOURCE_REMOVE;
}
});
@ -834,7 +833,9 @@ const AggregateMenu = new Lang.Class({
this._indicators.add_child(this._rfkill.indicators);
this._indicators.add_child(this._volume.indicators);
this._indicators.add_child(this._power.indicators);
this._indicators.add_child(PopupMenu.unicodeArrow(St.Side.BOTTOM));
this._indicators.add_child(new St.Label({ text: '\u25BE',
y_expand: true,
y_align: Clutter.ActorAlign.CENTER }));
this.menu.addMenuItem(this._volume.menu);
this.menu.addMenuItem(this._brightness.menu);
@ -986,23 +987,23 @@ const Panel = new Lang.Class({
_onButtonPress: function(actor, event) {
if (Main.modalCount > 0)
return Clutter.EVENT_PROPAGATE;
return false;
if (event.get_source() != actor)
return Clutter.EVENT_PROPAGATE;
return false;
let button = event.get_button();
if (button != 1)
return Clutter.EVENT_PROPAGATE;
return false;
let focusWindow = global.display.focus_window;
if (!focusWindow)
return Clutter.EVENT_PROPAGATE;
return false;
let dragWindow = focusWindow.is_attached_dialog() ? focusWindow.get_transient_for()
: focusWindow;
if (!dragWindow)
return Clutter.EVENT_PROPAGATE;
return false;
let rect = dragWindow.get_outer_rect();
let [stageX, stageY] = event.get_coords();
@ -1011,7 +1012,7 @@ const Panel = new Lang.Class({
stageX > rect.x && stageX < rect.x + rect.width;
if (!allowDrag)
return Clutter.EVENT_PROPAGATE;
return false;
global.display.begin_grab_op(global.screen,
dragWindow,
@ -1023,7 +1024,7 @@ const Panel = new Lang.Class({
event.get_time(),
stageX, stageY);
return Clutter.EVENT_STOP;
return true;
},
toggleAppMenu: function() {

View File

@ -137,30 +137,29 @@ const Button = new Lang.Class({
_onButtonPress: function(actor, event) {
if (!this.menu)
return Clutter.EVENT_PROPAGATE;
return;
this.menu.toggle();
return Clutter.EVENT_PROPAGATE;
},
_onSourceKeyPress: function(actor, event) {
if (!this.menu)
return Clutter.EVENT_PROPAGATE;
return false;
let symbol = event.get_key_symbol();
if (symbol == Clutter.KEY_space || symbol == Clutter.KEY_Return) {
this.menu.toggle();
return Clutter.EVENT_STOP;
return true;
} else if (symbol == Clutter.KEY_Escape && this.menu.isOpen) {
this.menu.close();
return Clutter.EVENT_STOP;
return true;
} else if (symbol == Clutter.KEY_Down) {
if (!this.menu.isOpen)
this.menu.toggle();
this.menu.actor.navigate_focus(this.actor, Gtk.DirectionType.DOWN, false);
return Clutter.EVENT_STOP;
return true;
} else
return Clutter.EVENT_PROPAGATE;
return false;
},
_onVisibilityChanged: function() {
@ -173,7 +172,7 @@ const Button = new Lang.Class({
_onMenuKeyPress: function(actor, event) {
if (global.focus_manager.navigate_from_event(event))
return Clutter.EVENT_STOP;
return true;
let symbol = event.get_key_symbol();
if (symbol == Clutter.KEY_Left || symbol == Clutter.KEY_Right) {
@ -181,10 +180,10 @@ const Button = new Lang.Class({
if (group) {
let direction = (symbol == Clutter.KEY_Left) ? Gtk.DirectionType.LEFT : Gtk.DirectionType.RIGHT;
group.navigate_focus(this.actor, direction, false);
return Clutter.EVENT_STOP;
return true;
}
}
return Clutter.EVENT_PROPAGATE;
return false;
},
_onOpenStateChanged: function(menu, open) {

View File

@ -1,6 +1,5 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
@ -111,7 +110,7 @@ const PointerWatcher = new Lang.Class({
_onTimeout: function() {
this._updatePointer();
return GLib.SOURCE_CONTINUE;
return true;
},
_updatePointer: function() {

View File

@ -42,33 +42,6 @@ function isPopupMenuItemVisible(child) {
return child.visible;
}
/**
* @side Side to which the arrow points.
*/
function unicodeArrow(side) {
let arrowChar;
switch (side) {
case St.Side.TOP:
arrowChar = '\u25B4';
break;
case St.Side.RIGHT:
arrowChar = '\u25B8';
break;
case St.Side.BOTTOM:
arrowChar = '\u25BE';
break;
case St.Side.LEFT:
arrowChar = '\u25C2';
break;
}
return new St.Label({ text: arrowChar,
style_class: 'unicode-arrow',
accessible_role: Atk.Role.ARROW,
y_expand: true,
y_align: Clutter.ActorAlign.CENTER });
}
const PopupBaseMenuItem = new Lang.Class({
Name: 'PopupBaseMenuItem',
@ -126,7 +99,7 @@ const PopupBaseMenuItem = new Lang.Class({
_onButtonReleaseEvent: function (actor, event) {
this.activate(event);
return Clutter.EVENT_STOP;
return true;
},
_onKeyPressEvent: function (actor, event) {
@ -134,9 +107,9 @@ const PopupBaseMenuItem = new Lang.Class({
if (symbol == Clutter.KEY_space || symbol == Clutter.KEY_Return) {
this.activate(event);
return Clutter.EVENT_STOP;
return true;
}
return Clutter.EVENT_PROPAGATE;
return false;
},
_onKeyFocusIn: function (actor) {
@ -928,10 +901,10 @@ const PopupSubMenu = new Lang.Class({
if (this.isOpen && event.get_key_symbol() == Clutter.KEY_Left) {
this.close(BoxPointer.PopupAnimation.FULL);
this.sourceActor._delegate.setActive(true);
return Clutter.EVENT_STOP;
return true;
}
return Clutter.EVENT_PROPAGATE;
return false;
}
});
@ -989,7 +962,8 @@ const PopupSubMenuMenuItem = new Lang.Class({
y_align: Clutter.ActorAlign.CENTER });
this.actor.add_child(this.status);
this._triangle = unicodeArrow(St.Side.RIGHT);
this._triangle = new St.Label({ text: '\u25B8',
style_class: 'popup-submenu-menu-item-triangle' });
this._triangle.pivot_point = new Clutter.Point({ x: 0.5, y: 0.6 });
this._triangleBin = new St.Widget({ y_expand: true,
@ -1056,10 +1030,10 @@ const PopupSubMenuMenuItem = new Lang.Class({
if (symbol == Clutter.KEY_Right) {
this._setOpenState(true);
this.menu.actor.navigate_focus(null, Gtk.DirectionType.DOWN, false);
return Clutter.EVENT_STOP;
return true;
} else if (symbol == Clutter.KEY_Left && this._getOpenState()) {
this._setOpenState(false);
return Clutter.EVENT_STOP;
return true;
}
return this.parent(actor, event);
@ -1071,7 +1045,6 @@ const PopupSubMenuMenuItem = new Lang.Class({
_onButtonReleaseEvent: function(actor) {
this._setOpenState(!this._getOpenState());
return Clutter.EVENT_PROPAGATE;
}
});
@ -1103,7 +1076,7 @@ const PopupMenuManager = new Lang.Class({
if (source) {
if (!menu.blockSourceEvents)
this._grabHelper.addActor(source);
menudata.enterId = source.connect('enter-event', Lang.bind(this, function() { return this._onMenuSourceEnter(menu); }));
menudata.enterId = source.connect('enter-event', Lang.bind(this, function() { this._onMenuSourceEnter(menu); }));
menudata.focusInId = source.connect('key-focus-in', Lang.bind(this, function() { this._onMenuSourceEnter(menu); }));
}
@ -1115,7 +1088,7 @@ const PopupMenuManager = new Lang.Class({
removeMenu: function(menu) {
if (menu == this.activeMenu)
this._closeMenu(false, menu);
this._closeMenu(menu);
let position = this._findMenu(menu);
if (position == -1) // not a menu we manage
@ -1165,13 +1138,13 @@ const PopupMenuManager = new Lang.Class({
_onMenuSourceEnter: function(menu) {
if (!this._grabHelper.grabbed)
return Clutter.EVENT_PROPAGATE;
return false;
if (this._grabHelper.isActorGrabbed(menu.actor))
return Clutter.EVENT_PROPAGATE;
return false;
this._changeMenu(menu);
return Clutter.EVENT_PROPAGATE;
return false;
},
_onMenuDestroy: function(menu) {

View File

@ -7,63 +7,58 @@ const Lang = imports.lang;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
const FileUtils = imports.misc.fileUtils;
const Search = imports.ui.search;
const KEY_FILE_GROUP = 'Shell Search Provider';
const SearchProviderIface = '<node> \
<interface name="org.gnome.Shell.SearchProvider"> \
<method name="GetInitialResultSet"> \
<arg type="as" direction="in" /> \
<arg type="as" direction="out" /> \
</method> \
<method name="GetSubsearchResultSet"> \
<arg type="as" direction="in" /> \
<arg type="as" direction="in" /> \
<arg type="as" direction="out" /> \
</method> \
<method name="GetResultMetas"> \
<arg type="as" direction="in" /> \
<arg type="aa{sv}" direction="out" /> \
</method> \
<method name="ActivateResult"> \
<arg type="s" direction="in" /> \
</method> \
</interface> \
</node>';
const SearchProviderIface = <interface name="org.gnome.Shell.SearchProvider">
<method name="GetInitialResultSet">
<arg type="as" direction="in" />
<arg type="as" direction="out" />
</method>
<method name="GetSubsearchResultSet">
<arg type="as" direction="in" />
<arg type="as" direction="in" />
<arg type="as" direction="out" />
</method>
<method name="GetResultMetas">
<arg type="as" direction="in" />
<arg type="aa{sv}" direction="out" />
</method>
<method name="ActivateResult">
<arg type="s" direction="in" />
</method>
</interface>;
const SearchProvider2Iface = '<node> \
<interface name="org.gnome.Shell.SearchProvider2"> \
<method name="GetInitialResultSet"> \
<arg type="as" direction="in" /> \
<arg type="as" direction="out" /> \
</method> \
<method name="GetSubsearchResultSet"> \
<arg type="as" direction="in" /> \
<arg type="as" direction="in" /> \
<arg type="as" direction="out" /> \
</method> \
<method name="GetResultMetas"> \
<arg type="as" direction="in" /> \
<arg type="aa{sv}" direction="out" /> \
</method> \
<method name="ActivateResult"> \
<arg type="s" direction="in" /> \
<arg type="as" direction="in" /> \
<arg type="u" direction="in" /> \
</method> \
<method name="LaunchSearch"> \
<arg type="as" direction="in" /> \
<arg type="u" direction="in" /> \
</method> \
</interface> \
</node>';
const SearchProvider2Iface = <interface name="org.gnome.Shell.SearchProvider2">
<method name="GetInitialResultSet">
<arg type="as" direction="in" />
<arg type="as" direction="out" />
</method>
<method name="GetSubsearchResultSet">
<arg type="as" direction="in" />
<arg type="as" direction="in" />
<arg type="as" direction="out" />
</method>
<method name="GetResultMetas">
<arg type="as" direction="in" />
<arg type="aa{sv}" direction="out" />
</method>
<method name="ActivateResult">
<arg type="s" direction="in" />
<arg type="as" direction="in" />
<arg type="u" direction="in" />
</method>
<method name="LaunchSearch">
<arg type="as" direction="in" />
<arg type="u" direction="in" />
</method>
</interface>;
var SearchProviderProxyInfo = Gio.DBusInterfaceInfo.new_for_xml(SearchProviderIface);
var SearchProvider2ProxyInfo = Gio.DBusInterfaceInfo.new_for_xml(SearchProvider2Iface);
var SearchProviderProxy = Gio.DBusProxy.makeProxyWrapper(SearchProviderIface);
var SearchProvider2Proxy = Gio.DBusProxy.makeProxyWrapper(SearchProvider2Iface);
function loadRemoteSearchProviders(callback) {
function loadRemoteSearchProviders(addProviderCallback) {
let objectPaths = {};
let loadedProviders = [];
@ -117,25 +112,30 @@ function loadRemoteSearchProviders(callback) {
}
}
let dataDirs = GLib.get_system_data_dirs();
dataDirs.forEach(function(dataDir) {
let path = GLib.build_filenamev([dataDir, 'gnome-shell', 'search-providers']);
let dir = Gio.File.new_for_path(path);
let fileEnum;
try {
fileEnum = dir.enumerate_children('standard::name,standard::type',
Gio.FileQueryInfoFlags.NONE, null);
} catch (e) {
fileEnum = null;
}
if (fileEnum != null) {
let info;
while ((info = fileEnum.next_file(null)))
loadRemoteSearchProvider(fileEnum.get_child(info));
}
});
let searchSettings = new Gio.Settings({ schema: Search.SEARCH_PROVIDERS_SCHEMA });
if (searchSettings.get_boolean('disable-external')) {
callback([]);
return;
}
FileUtils.collectFromDatadirs('search-providers', false, loadRemoteSearchProvider);
let sortOrder = searchSettings.get_strv('sort-order');
// Special case gnome-control-center to be always active and always first
sortOrder.unshift('gnome-control-center.desktop');
loadedProviders = loadedProviders.filter(function(provider) {
let appId = provider.appInfo.get_id();
let disabled = searchSettings.get_strv('disabled');
return disabled.indexOf(appId) == -1;
});
loadedProviders.sort(function(providerA, providerB) {
let idxA, idxB;
let appIdA, appIdB;
@ -166,34 +166,32 @@ function loadRemoteSearchProviders(callback) {
return (idxA - idxB);
});
callback(loadedProviders);
loadedProviders.forEach(addProviderCallback);
}
const RemoteSearchProvider = new Lang.Class({
Name: 'RemoteSearchProvider',
_init: function(appInfo, dbusName, dbusPath, proxyInfo) {
if (!proxyInfo)
proxyInfo = SearchProviderProxyInfo;
_init: function(appInfo, dbusName, dbusPath, proxyType) {
if (!proxyType)
proxyType = SearchProviderProxy;
this.proxy = new Gio.DBusProxy({ g_bus_type: Gio.BusType.SESSION,
g_name: dbusName,
g_object_path: dbusPath,
g_interface_info: proxyInfo,
g_interface_name: proxyInfo.name,
g_flags: (Gio.DBusProxyFlags.DO_NOT_AUTO_START_AT_CONSTRUCTION |
Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
this.proxy.init_async(GLib.PRIORITY_DEFAULT, null, null);
this.proxy = new proxyType(Gio.DBus.session,
dbusName, dbusPath, Lang.bind(this, this._onProxyConstructed));
this.appInfo = appInfo;
this.id = appInfo.get_id();
this.isRemoteProvider = true;
this._cancellable = new Gio.Cancellable();
},
_onProxyConstructed: function(proxy) {
// Do nothing
},
createIcon: function(size, meta) {
let gicon = null;
let icon = null;
let gicon;
if (meta['icon']) {
gicon = Gio.icon_deserialize(meta['icon']);
} else if (meta['gicon']) {
@ -205,49 +203,44 @@ const RemoteSearchProvider = new Lang.Class({
bitsPerSample, width, height, rowStride);
}
if (gicon)
icon = new St.Icon({ gicon: gicon,
icon_size: size });
return icon;
return new St.Icon({ gicon: gicon,
icon_size: size });
},
filterResults: function(results, maxNumber) {
if (results.length <= maxNumber)
return results;
let regularResults = results.filter(function(r) { return !r.startsWith('special:'); });
let specialResults = results.filter(function(r) { return r.startsWith('special:'); });
return regularResults.slice(0, maxNumber).concat(specialResults.slice(0, maxNumber));
},
_getResultsFinished: function(results, error, callback) {
if (error) {
if (!error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
log('Received error from DBus search provider %s: %s'.format(this.id, String(error)));
callback([]);
_getResultsFinished: function(results, error) {
if (error)
return;
this.searchSystem.setResults(this, results[0]);
},
getInitialResultSet: function(terms) {
this._cancellable.cancel();
this._cancellable.reset();
try {
this.proxy.GetInitialResultSetRemote(terms,
Lang.bind(this, this._getResultsFinished),
this._cancellable);
} catch(e) {
log('Error calling GetInitialResultSet for provider %s: %s'.format(this.id, e.toString()));
this.searchSystem.setResults(this, []);
}
callback(results[0]);
},
getInitialResultSet: function(terms, callback, cancellable) {
this.proxy.GetInitialResultSetRemote(terms,
Lang.bind(this, this._getResultsFinished, callback),
cancellable);
},
getSubsearchResultSet: function(previousResults, newTerms, callback, cancellable) {
this.proxy.GetSubsearchResultSetRemote(previousResults, newTerms,
Lang.bind(this, this._getResultsFinished, callback),
cancellable);
getSubsearchResultSet: function(previousResults, newTerms) {
this._cancellable.cancel();
this._cancellable.reset();
try {
this.proxy.GetSubsearchResultSetRemote(previousResults, newTerms,
Lang.bind(this, this._getResultsFinished),
this._cancellable);
} catch(e) {
log('Error calling GetSubsearchResultSet for provider %s: %s'.format(this.id, e.toString()));
this.searchSystem.setResults(this, []);
}
},
_getResultMetasFinished: function(results, error, callback) {
if (error) {
if (!error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
log('Received error from DBus search provider %s during GetResultMetas: %s'.format(this.id, String(error)));
callback([]);
return;
}
@ -269,10 +262,17 @@ const RemoteSearchProvider = new Lang.Class({
callback(resultMetas);
},
getResultMetas: function(ids, callback, cancellable) {
this.proxy.GetResultMetasRemote(ids,
Lang.bind(this, this._getResultMetasFinished, callback),
cancellable);
getResultMetas: function(ids, callback) {
this._cancellable.cancel();
this._cancellable.reset();
try {
this.proxy.GetResultMetasRemote(ids,
Lang.bind(this, this._getResultMetasFinished, callback),
this._cancellable);
} catch(e) {
log('Error calling GetResultMetas for provider %s: %s'.format(this.id, e.toString()));
callback([]);
}
},
activateResult: function(id) {
@ -292,7 +292,7 @@ const RemoteSearchProvider2 = new Lang.Class({
Extends: RemoteSearchProvider,
_init: function(appInfo, dbusName, dbusPath) {
this.parent(appInfo, dbusName, dbusPath, SearchProvider2ProxyInfo);
this.parent(appInfo, dbusName, dbusPath, SearchProvider2Proxy);
this.canLaunchSearch = true;
},

View File

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

View File

@ -19,7 +19,6 @@ const Background = imports.ui.background;
const GnomeSession = imports.misc.gnomeSession;
const Hash = imports.misc.hash;
const Layout = imports.ui.layout;
const OVirt = imports.gdm.oVirt;
const LoginManager = imports.misc.loginManager;
const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main;
@ -198,8 +197,8 @@ const NotificationsBox = new Lang.Class({
let body = '';
if (n.bannerBodyText) {
body = n.bannerBodyMarkup ? n.bannerBodyText
: GLib.markup_escape_text(n.bannerBodyText, -1);
body = n.bannerBodyMarkup ? n.bannerBodyText :
GLib.markup_escape_text(n.bannerBodyMarkup, -1);
}
let label = new St.Label({ style_class: 'screen-shield-notification-count-text' });
@ -240,6 +239,10 @@ const NotificationsBox = new Lang.Class({
},
_sourceAdded: function(tray, source, initial) {
// Ignore transient sources
if (source.isTransient)
return;
let obj = {
visible: source.policy.showInLockScreen,
detailed: source.policy.detailsInLockScreen,
@ -542,13 +545,6 @@ const ScreenShield = new Lang.Class({
this._liftShield(true, 0);
}));
this._oVirtCredentialsManager = OVirt.getOVirtCredentialsManager();
this._oVirtCredentialsManager.connect('user-authenticated',
Lang.bind(this, function() {
if (this._isLocked)
this._liftShield(true, 0);
}));
this._inhibitor = null;
this._aboutToSuspend = false;
this._loginManager = LoginManager.getLoginManager();
@ -669,11 +665,11 @@ const ScreenShield = new Lang.Class({
// down after cancel.
if (this._lockScreenState != MessageTray.State.SHOWN)
return Clutter.EVENT_PROPAGATE;
return false;
let isEnter = (symbol == Clutter.KEY_Return || symbol == Clutter.KEY_KP_Enter);
if (!isEnter && !(GLib.unichar_isprint(unichar) || symbol == Clutter.KEY_Escape))
return Clutter.EVENT_PROPAGATE;
return false;
if (this._isLocked &&
this._ensureUnlockDialog(true, true) &&
@ -681,12 +677,12 @@ const ScreenShield = new Lang.Class({
this._dialog.addCharacter(unichar);
this._liftShield(true, 0);
return Clutter.EVENT_STOP;
return true;
},
_onLockScreenScroll: function(actor, event) {
if (this._lockScreenState != MessageTray.State.SHOWN)
return Clutter.EVENT_PROPAGATE;
return false;
let delta = 0;
if (event.get_scroll_direction() == Clutter.ScrollDirection.UP)
@ -701,7 +697,7 @@ const ScreenShield = new Lang.Class({
this._liftShield(true, 0);
}
return Clutter.EVENT_STOP;
return true;
},
_inhibitSuspend: function() {
@ -752,7 +748,7 @@ const ScreenShield = new Lang.Class({
});
}
return GLib.SOURCE_CONTINUE;
return true;
},
_onDragBegin: function() {
@ -848,7 +844,7 @@ const ScreenShield = new Lang.Class({
Lang.bind(this, function() {
this._lockTimeoutId = 0;
this.lock(false);
return GLib.SOURCE_REMOVE;
return false;
}));
}
@ -1097,7 +1093,7 @@ const ScreenShield = new Lang.Class({
global.stage.disconnect(motionId);
}
return Clutter.EVENT_PROPAGATE;
return false;
}));
this._cursorTracker.set_pointer_visible(false);
@ -1110,7 +1106,6 @@ const ScreenShield = new Lang.Class({
Mainloop.timeout_add(1000 * MANUAL_FADE_TIME, Lang.bind(this, function() {
this._activateFade(this._shortLightbox, MANUAL_FADE_TIME);
return GLib.SOURCE_REMOVE;
}));
} else {
if (params.fadeToBlack)

View File

@ -9,29 +9,27 @@ const Signals = imports.signals;
const Hash = imports.misc.hash;
const Main = imports.ui.main;
const ScreencastIface = '<node> \
<interface name="org.gnome.Shell.Screencast"> \
<method name="Screencast"> \
<arg type="s" direction="in" name="file_template"/> \
<arg type="a{sv}" direction="in" name="options"/> \
<arg type="b" direction="out" name="success"/> \
<arg type="s" direction="out" name="filename_used"/> \
</method> \
<method name="ScreencastArea"> \
<arg type="i" direction="in" name="x"/> \
<arg type="i" direction="in" name="y"/> \
<arg type="i" direction="in" name="width"/> \
<arg type="i" direction="in" name="height"/> \
<arg type="s" direction="in" name="file_template"/> \
<arg type="a{sv}" direction="in" name="options"/> \
<arg type="b" direction="out" name="success"/> \
<arg type="s" direction="out" name="filename_used"/> \
</method> \
<method name="StopScreencast"> \
<arg type="b" direction="out" name="success"/> \
</method> \
</interface> \
</node>';
const ScreencastIface = <interface name="org.gnome.Shell.Screencast">
<method name="Screencast">
<arg type="s" direction="in" name="file_template"/>
<arg type="a{sv}" direction="in" name="options"/>
<arg type="b" direction="out" name="success"/>
<arg type="s" direction="out" name="filename_used"/>
</method>
<method name="ScreencastArea">
<arg type="i" direction="in" name="x"/>
<arg type="i" direction="in" name="y"/>
<arg type="i" direction="in" name="width"/>
<arg type="i" direction="in" name="height"/>
<arg type="s" direction="in" name="file_template"/>
<arg type="a{sv}" direction="in" name="options"/>
<arg type="b" direction="out" name="success"/>
<arg type="s" direction="out" name="filename_used"/>
</method>
<method name="StopScreencast">
<arg type="b" direction="out" name="success"/>
</method>
</interface>;
const ScreencastService = new Lang.Class({
Name: 'ScreencastService',
@ -105,10 +103,8 @@ const ScreencastService = new Lang.Class({
ScreencastAsync: function(params, invocation) {
let returnValue = [false, ''];
if (!Main.sessionMode.allowScreencast) {
if (!Main.sessionMode.allowScreencast)
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
return;
}
let sender = invocation.get_sender();
let recorder = this._ensureRecorderForSender(sender);
@ -126,10 +122,8 @@ const ScreencastService = new Lang.Class({
ScreencastAreaAsync: function(params, invocation) {
let returnValue = [false, ''];
if (!Main.sessionMode.allowScreencast) {
if (!Main.sessionMode.allowScreencast)
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
return;
}
let sender = invocation.get_sender();
let recorder = this._ensureRecorderForSender(sender);
@ -137,16 +131,6 @@ const ScreencastService = new Lang.Class({
if (!recorder.is_recording()) {
let [x, y, width, height, fileTemplate, options] = params;
if (x < 0 || y < 0 ||
width <= 0 || height <= 0 ||
x + width > global.screen_width ||
y + height > global.screen_height) {
invocation.return_error_literal(Gio.IOErrorEnum,
Gio.IOErrorEnum.CANCELLED,
"Invalid params");
return;
}
recorder.set_file_template(fileTemplate);
recorder.set_area(x, y, width, height);
this._applyOptionalParameters(recorder, options);

View File

@ -15,47 +15,45 @@ const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main;
const Tweener = imports.ui.tweener;
const ScreenshotIface = '<node> \
<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> \
</node>';
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',
@ -79,9 +77,7 @@ const ScreenshotService = new Lang.Class({
ScreenshotAreaAsync : function (params, invocation) {
let [x, y, width, height, flash, filename, callback] = params;
if (x < 0 || y < 0 ||
width <= 0 || height <= 0 ||
x + width > global.screen_width || y + height > global.screen_height) {
if (height <= 0 || width <= 0) {
invocation.return_error_literal(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED,
"Invalid params");
return;
@ -206,12 +202,12 @@ const SelectArea = new Lang.Class({
if (event.get_key_symbol() == Clutter.Escape)
this._destroy(null, false);
return Clutter.EVENT_PROPAGATE;
return;
},
_onMotionEvent: function(actor, event) {
if (this._startX == -1 || this._startY == -1)
return Clutter.EVENT_PROPAGATE;
return false;
[this._lastX, this._lastY] = event.get_coords();
let geometry = this._getGeometry();
@ -219,19 +215,19 @@ const SelectArea = new Lang.Class({
this._rubberband.set_position(geometry.x, geometry.y);
this._rubberband.set_size(geometry.width, geometry.height);
return Clutter.EVENT_PROPAGATE;
return false;
},
_onButtonPress: function(actor, event) {
[this._startX, this._startY] = event.get_coords();
this._rubberband.set_position(this._startX, this._startY);
return Clutter.EVENT_PROPAGATE;
return false;
},
_onButtonRelease: function(actor, event) {
this._destroy(this._getGeometry(), true);
return Clutter.EVENT_PROPAGATE;
return false;
},
_destroy: function(geometry, fade) {

View File

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

View File

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

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

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

View File

@ -102,12 +102,19 @@ const _modes = {
}
};
function _loadMode(file, info) {
function _getModes(modesLoadedCallback) {
FileUtils.collectFromDatadirsAsync('modes',
{ processFile: _loadMode,
loadedCallback: modesLoadedCallback,
data: _modes });
}
function _loadMode(file, info, loadedData) {
let name = info.get_name();
let suffix = name.indexOf('.json');
let modeName = suffix == -1 ? name : name.slice(name, suffix);
if (_modes.hasOwnProperty(modeName))
if (loadedData.hasOwnProperty(modeName))
return;
let fileContent, success, tag, newMode;
@ -118,24 +125,19 @@ function _loadMode(file, info) {
return;
}
_modes[modeName] = {};
loadedData[modeName] = {};
let propBlacklist = ['unlockDialog'];
for (let prop in _modes[DEFAULT_MODE]) {
for (let prop in loadedData[DEFAULT_MODE]) {
if (newMode[prop] !== undefined &&
propBlacklist.indexOf(prop) == -1)
_modes[modeName][prop] = newMode[prop];
loadedData[modeName][prop]= newMode[prop];
}
_modes[modeName]['isPrimary'] = true;
}
function _loadModes() {
FileUtils.collectFromDatadirs('modes', false, _loadMode);
loadedData[modeName]['isPrimary'] = true;
}
function listModes() {
_loadModes();
Mainloop.idle_add(function() {
let names = Object.getOwnPropertyNames(_modes);
_getModes(function(modes) {
let names = Object.getOwnPropertyNames(modes);
for (let i = 0; i < names.length; i++)
if (_modes[names[i]].isPrimary)
print(names[i]);
@ -147,13 +149,17 @@ function listModes() {
const SessionMode = new Lang.Class({
Name: 'SessionMode',
_init: function() {
_loadModes();
let isPrimary = (_modes[global.session_mode] &&
_modes[global.session_mode].isPrimary);
let mode = isPrimary ? global.session_mode : 'user';
this._modeStack = [mode];
this._sync();
init: function() {
_getModes(Lang.bind(this, function(modes) {
this._modes = modes;
let primary = modes[global.session_mode] &&
modes[global.session_mode].isPrimary;
let mode = primary ? global.session_mode : 'user';
this._modeStack = [mode];
this._sync();
this.emit('sessions-loaded');
}));
},
pushMode: function(mode) {
@ -180,13 +186,13 @@ const SessionMode = new Lang.Class({
},
_sync: function() {
let params = _modes[this.currentMode];
let params = this._modes[this.currentMode];
let defaults;
if (params.parentMode)
defaults = Params.parse(_modes[params.parentMode],
_modes[DEFAULT_MODE]);
defaults = Params.parse(this._modes[params.parentMode],
this._modes[DEFAULT_MODE]);
else
defaults = _modes[DEFAULT_MODE];
defaults = this._modes[DEFAULT_MODE];
params = Params.parse(params, defaults);
// A simplified version of Lang.copyProperties, handles

View File

@ -15,63 +15,59 @@ const Main = imports.ui.main;
const Screenshot = imports.ui.screenshot;
const ViewSelector = imports.ui.viewSelector;
const GnomeShellIface = '<node> \
<interface name="org.gnome.Shell"> \
<method name="Eval"> \
<arg type="s" direction="in" name="script" /> \
<arg type="b" direction="out" name="success" /> \
<arg type="s" direction="out" name="result" /> \
</method> \
<method name="FocusSearch"/> \
<method name="ShowOSD"> \
<arg type="a{sv}" direction="in" name="params"/> \
</method> \
<method name="FocusApp"> \
<arg type="s" direction="in" name="id"/> \
</method> \
<method name="ShowApplications" /> \
<method name="GrabAccelerator"> \
<arg type="s" direction="in" name="accelerator"/> \
<arg type="u" direction="in" name="flags"/> \
<arg type="u" direction="out" name="action"/> \
</method> \
<method name="GrabAccelerators"> \
<arg type="a(su)" direction="in" name="accelerators"/> \
<arg type="au" direction="out" name="actions"/> \
</method> \
<method name="UngrabAccelerator"> \
<arg type="u" direction="in" name="action"/> \
<arg type="b" direction="out" name="success"/> \
</method> \
<signal name="AcceleratorActivated"> \
<arg name="action" type="u" /> \
<arg name="deviceid" type="u" /> \
<arg name="timestamp" type="u" /> \
</signal> \
<property name="Mode" type="s" access="read" /> \
<property name="OverviewActive" type="b" access="readwrite" /> \
<property name="ShellVersion" type="s" access="read" /> \
</interface> \
</node>';
const GnomeShellIface = <interface name="org.gnome.Shell">
<method name="Eval">
<arg type="s" direction="in" name="script" />
<arg type="b" direction="out" name="success" />
<arg type="s" direction="out" name="result" />
</method>
<method name="FocusSearch"/>
<method name="ShowOSD">
<arg type="a{sv}" direction="in" name="params"/>
</method>
<method name="FocusApp">
<arg type="s" direction="in" name="id"/>
</method>
<method name="ShowApplications" />
<method name="GrabAccelerator">
<arg type="s" direction="in" name="accelerator"/>
<arg type="u" direction="in" name="flags"/>
<arg type="u" direction="out" name="action"/>
</method>
<method name="GrabAccelerators">
<arg type="a(su)" direction="in" name="accelerators"/>
<arg type="au" direction="out" name="actions"/>
</method>
<method name="UngrabAccelerator">
<arg type="u" direction="in" name="action"/>
<arg type="b" direction="out" name="success"/>
</method>
<signal name="AcceleratorActivated">
<arg name="action" type="u" />
<arg name="deviceid" type="u" />
<arg name="timestamp" type="u" />
</signal>
<property name="Mode" type="s" access="read" />
<property name="OverviewActive" type="b" access="readwrite" />
<property name="ShellVersion" type="s" access="read" />
</interface>;
const ScreenSaverIface = '<node> \
<interface name="org.gnome.ScreenSaver"> \
<method name="Lock"> \
</method> \
<method name="GetActive"> \
<arg name="active" direction="out" type="b" /> \
</method> \
<method name="SetActive"> \
<arg name="value" direction="in" type="b" /> \
</method> \
<method name="GetActiveTime"> \
<arg name="value" direction="out" type="u" /> \
</method> \
<signal name="ActiveChanged"> \
<arg name="new_value" type="b" /> \
</signal> \
</interface> \
</node>';
const ScreenSaverIface = <interface name="org.gnome.ScreenSaver">
<method name="Lock">
</method>
<method name="GetActive">
<arg name="active" direction="out" type="b" />
</method>
<method name="SetActive">
<arg name="value" direction="in" type="b" />
</method>
<method name="GetActiveTime">
<arg name="value" direction="out" type="u" />
</method>
<signal name="ActiveChanged">
<arg name="new_value" type="b" />
</signal>
</interface>;
const GnomeShell = new Lang.Class({
Name: 'GnomeShellDBus',
@ -119,7 +115,7 @@ const GnomeShell = new Lang.Class({
returnValue = '';
success = true;
} catch (e) {
returnValue = '' + e;
returnValue = JSON.stringify(e);
success = false;
}
return [success, returnValue];
@ -133,16 +129,11 @@ const GnomeShell = new Lang.Class({
for (let param in params)
params[param] = params[param].deep_unpack();
let monitorIndex = -1;
if (params['monitor'])
monitorIndex = params['monitor'];
let icon = null;
if (params['icon'])
icon = Gio.Icon.new_for_string(params['icon']);
Main.osdWindow.setIcon(icon);
Main.osdWindow.setMonitor (monitorIndex);
Main.osdWindow.setLabel(params['label']);
Main.osdWindow.setLevel(params['level']);
@ -255,43 +246,41 @@ const GnomeShell = new Lang.Class({
ShellVersion: Config.PACKAGE_VERSION
});
const GnomeShellExtensionsIface = '<node> \
<interface name="org.gnome.Shell.Extensions"> \
<method name="ListExtensions"> \
<arg type="a{sa{sv}}" direction="out" name="extensions" /> \
</method> \
<method name="GetExtensionInfo"> \
<arg type="s" direction="in" name="extension" /> \
<arg type="a{sv}" direction="out" name="info" /> \
</method> \
<method name="GetExtensionErrors"> \
<arg type="s" direction="in" name="extension" /> \
<arg type="as" direction="out" name="errors" /> \
</method> \
<signal name="ExtensionStatusChanged"> \
<arg type="s" name="uuid"/> \
<arg type="i" name="state"/> \
<arg type="s" name="error"/> \
</signal> \
<method name="InstallRemoteExtension"> \
<arg type="s" direction="in" name="uuid"/> \
<arg type="s" direction="out" name="result"/> \
</method> \
<method name="UninstallExtension"> \
<arg type="s" direction="in" name="uuid"/> \
<arg type="b" direction="out" name="success"/> \
</method> \
<method name="LaunchExtensionPrefs"> \
<arg type="s" direction="in" name="uuid"/> \
</method> \
<method name="ReloadExtension"> \
<arg type="s" direction="in" name="uuid"/> \
</method> \
<method name="CheckForUpdates"> \
</method> \
<property name="ShellVersion" type="s" access="read" /> \
</interface> \
</node>';
const GnomeShellExtensionsIface = <interface name="org.gnome.Shell.Extensions">
<method name="ListExtensions">
<arg type="a{sa{sv}}" direction="out" name="extensions" />
</method>
<method name="GetExtensionInfo">
<arg type="s" direction="in" name="extension" />
<arg type="a{sv}" direction="out" name="info" />
</method>
<method name="GetExtensionErrors">
<arg type="s" direction="in" name="extension" />
<arg type="as" direction="out" name="errors" />
</method>
<signal name="ExtensionStatusChanged">
<arg type="s" name="uuid"/>
<arg type="i" name="state"/>
<arg type="s" name="error"/>
</signal>
<method name="InstallRemoteExtension">
<arg type="s" direction="in" name="uuid"/>
<arg type="s" direction="out" name="result"/>
</method>
<method name="UninstallExtension">
<arg type="s" direction="in" name="uuid"/>
<arg type="b" direction="out" name="success"/>
</method>
<method name="LaunchExtensionPrefs">
<arg type="s" direction="in" name="uuid"/>
</method>
<method name="ReloadExtension">
<arg type="s" direction="in" name="uuid"/>
</method>
<method name="CheckForUpdates">
</method>
<property name="ShellVersion" type="s" access="read" />
</interface>;
const GnomeShellExtensions = new Lang.Class({
Name: 'GnomeShellExtensionsDBus',

View File

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

View File

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

View File

@ -111,12 +111,12 @@ const Slider = new Lang.Class({
},
_startDragging: function(actor, event) {
return this.startDragging(event);
this.startDragging(event);
},
startDragging: function(event) {
if (this._dragging)
return Clutter.EVENT_PROPAGATE;
return false;
this._dragging = true;
@ -129,7 +129,7 @@ const Slider = new Lang.Class({
let absX, absY;
[absX, absY] = event.get_coords();
this._moveHandle(absX, absY);
return Clutter.EVENT_STOP;
return true;
},
_endDragging: function() {
@ -143,7 +143,7 @@ const Slider = new Lang.Class({
this.emit('drag-end');
}
return Clutter.EVENT_STOP;
return true;
},
scroll: function(event) {
@ -151,7 +151,7 @@ const Slider = new Lang.Class({
let delta;
if (event.is_pointer_emulated())
return Clutter.EVENT_PROPAGATE;
return;
if (direction == Clutter.ScrollDirection.DOWN) {
delta = -SLIDER_SCROLL_STEP;
@ -168,18 +168,17 @@ const Slider = new Lang.Class({
this.actor.queue_repaint();
this.emit('value-changed', this._value);
return Clutter.EVENT_STOP;
},
_onScrollEvent: function(actor, event) {
return this.scroll(event);
this.scroll(event);
},
_motionEvent: function(actor, event) {
let absX, absY;
[absX, absY] = event.get_coords();
this._moveHandle(absX, absY);
return Clutter.EVENT_STOP;
return true;
},
onKeyPressEvent: function (actor, event) {
@ -190,9 +189,9 @@ const Slider = new Lang.Class({
this.actor.queue_repaint();
this.emit('value-changed', this._value);
this.emit('drag-end');
return Clutter.EVENT_STOP;
return true;
}
return Clutter.EVENT_PROPAGATE;
return false;
},
_moveHandle: function(absX, absY) {

View File

@ -2,7 +2,6 @@
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const St = imports.gi.St;
@ -44,7 +43,9 @@ const ATIndicator = new Lang.Class({
this._hbox = new St.BoxLayout({ style_class: 'panel-status-menu-box' });
this._hbox.add_child(new St.Icon({ style_class: 'system-status-icon',
icon_name: 'preferences-desktop-accessibility-symbolic' }));
this._hbox.add_child(PopupMenu.unicodeArrow(St.Side.BOTTOM));
this._hbox.add_child(new St.Label({ text: '\u25BE',
y_expand: true,
y_align: Clutter.ActorAlign.CENTER }));
this.actor.add_child(this._hbox);
@ -95,7 +96,7 @@ const ATIndicator = new Lang.Class({
this.actor.visible = alwaysShow || items.some(function(f) { return !!f.state; });
return GLib.SOURCE_REMOVE;
return false;
},
_queueSyncMenuVisibility: function() {

View File

@ -2,26 +2,17 @@
const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const GnomeBluetoothApplet = imports.gi.GnomeBluetoothApplet;
const GnomeBluetooth = imports.gi.GnomeBluetooth;
const Lang = imports.lang;
const St = imports.gi.St;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const NotificationDaemon = imports.ui.notificationDaemon;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const BUS_NAME = 'org.gnome.SettingsDaemon.Rfkill';
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Rfkill';
const RfkillManagerInterface = '<node> \
<interface name="org.gnome.SettingsDaemon.Rfkill"> \
<property name="BluetoothAirplaneMode" type="b" access="readwrite" /> \
</interface> \
</node>';
const RfkillManagerProxy = Gio.DBusProxy.makeProxyWrapper(RfkillManagerInterface);
const Indicator = new Lang.Class({
Name: 'BTIndicator',
Extends: PanelMenu.SystemIndicator,
@ -32,69 +23,237 @@ const Indicator = new Lang.Class({
this._indicator = this._addIndicator();
this._indicator.icon_name = 'bluetooth-active-symbolic';
this._proxy = new RfkillManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH,
Lang.bind(this, function(proxy, error) {
if (error) {
log(error.message);
return;
}
}));
// The Bluetooth menu only appears when Bluetooth is in use,
// so just statically build it with a "Turn Off" menu item.
this._item = new PopupMenu.PopupSubMenuMenuItem(_("Bluetooth"), true);
this._item.icon.icon_name = 'bluetooth-active-symbolic';
this._item.menu.addAction(_("Turn Off"), Lang.bind(this, function() {
this._proxy.BluetoothAirplaneMode = true;
this._applet.killswitch_state = GnomeBluetooth.KillswitchState.SOFT_BLOCKED;
}));
this._item.menu.addSettingsAction(_("Bluetooth Settings"), 'gnome-bluetooth-panel.desktop');
this.menu.addMenuItem(this._item);
this._client = new GnomeBluetooth.Client();
this._model = this._client.get_model();
this._model.connect('row-changed', Lang.bind(this, this._sync));
this._model.connect('row-deleted', Lang.bind(this, this._sync));
this._model.connect('row-inserted', Lang.bind(this, this._sync));
this._applet = new GnomeBluetoothApplet.Applet();
this._applet.connect('devices-changed', Lang.bind(this, this._sync));
this._sync();
},
_getDefaultAdapter: function() {
let [ret, iter] = this._model.get_iter_first();
while (ret) {
let isDefault = this._model.get_value(iter,
GnomeBluetooth.Column.DEFAULT);
if (isDefault)
return iter;
ret = this._model.iter_next(iter);
}
return null;
},
_getNConnectedDevices: function() {
let adapter = this._getDefaultAdapter();
if (!adapter)
return 0;
let nDevices = 0;
let [ret, iter] = this._model.iter_children(adapter);
while (ret) {
let isConnected = this._model.get_value(iter,
GnomeBluetooth.Column.CONNECTED);
if (isConnected)
nDevices++;
ret = this._model.iter_next(iter);
}
return nDevices;
this._applet.connect('pincode-request', Lang.bind(this, this._pinRequest));
this._applet.connect('confirm-request', Lang.bind(this, this._confirmRequest));
this._applet.connect('auth-request', Lang.bind(this, this._authRequest));
this._applet.connect('auth-service-request', Lang.bind(this, this._authServiceRequest));
this._applet.connect('cancel-request', Lang.bind(this, this._cancelRequest));
},
_sync: function() {
let nDevices = this._getNConnectedDevices();
let connectedDevices = this._applet.get_devices().filter(function(device) {
return device.connected;
});
let nDevices = connectedDevices.length;
let on = nDevices > 0;
this._indicator.visible = on;
this._item.actor.visible = on;
if (on)
this._item.status.text = ngettext("%d Connected Device", "%d Connected Devices", nDevices).format(nDevices);
this._item.status.text = ngettext("%d Connected Device", "%d Connected Devices").format(nDevices);
},
_ensureSource: function() {
if (!this._source) {
this._source = new MessageTray.Source(_("Bluetooth"), 'bluetooth-active');
this._source.policy = new NotificationDaemon.NotificationApplicationPolicy('gnome-bluetooth-panel');
Main.messageTray.add(this._source);
}
},
_authRequest: function(applet, device_path, name, long_name) {
this._ensureSource();
this._source.notify(new AuthNotification(this._source, this._applet, device_path, name, long_name));
},
_authServiceRequest: function(applet, device_path, name, long_name, uuid) {
this._ensureSource();
this._source.notify(new AuthServiceNotification(this._source, this._applet, device_path, name, long_name, uuid));
},
_confirmRequest: function(applet, device_path, name, long_name, pin) {
this._ensureSource();
this._source.notify(new ConfirmNotification(this._source, this._applet, device_path, name, long_name, pin));
},
_pinRequest: function(applet, device_path, name, long_name, numeric) {
this._ensureSource();
this._source.notify(new PinNotification(this._source, this._applet, device_path, name, long_name, numeric));
},
_cancelRequest: function() {
this._source.destroy();
}
});
const AuthNotification = new Lang.Class({
Name: 'AuthNotification',
Extends: MessageTray.Notification,
_init: function(source, applet, device_path, name, long_name) {
this.parent(source,
_("Bluetooth"),
_("Authorization request from %s").format(name),
{ customContent: true });
this.setResident(true);
this._applet = applet;
this._devicePath = device_path;
this.addBody(_("Device %s wants to pair with this computer").format(long_name));
this.addButton('allow', _("Allow"));
this.addButton('deny', _("Deny"));
this.connect('action-invoked', Lang.bind(this, function(self, action) {
if (action == 'allow')
this._applet.agent_reply_confirm(this._devicePath, true);
else
this._applet.agent_reply_confirm(this._devicePath, false);
this.destroy();
}));
}
});
const AuthServiceNotification = new Lang.Class({
Name: 'AuthServiceNotification',
Extends: MessageTray.Notification,
_init: function(source, applet, device_path, name, long_name, uuid) {
this.parent(source,
_("Bluetooth"),
_("Authorization request from %s").format(name),
{ customContent: true });
this.setResident(true);
this._applet = applet;
this._devicePath = device_path;
this.addBody(_("Device %s wants access to the service '%s'").format(long_name, uuid));
this.addButton('always-grant', _("Always grant access"));
this.addButton('grant', _("Grant this time only"));
this.addButton('reject', _("Reject"));
this.connect('action-invoked', Lang.bind(this, function(self, action) {
switch (action) {
case 'always-grant':
this._applet.agent_reply_auth_service(this._devicePath, true, true);
break;
case 'grant':
this._applet.agent_reply_auth_service(this._devicePath, true, false);
break;
case 'reject':
default:
this._applet.agent_reply_auth_service(this._devicePath, false, false);
}
this.destroy();
}));
}
});
const ConfirmNotification = new Lang.Class({
Name: 'ConfirmNotification',
Extends: MessageTray.Notification,
_init: function(source, applet, device_path, name, long_name, pin) {
this.parent(source,
_("Bluetooth"),
/* Translators: argument is the device short name */
_("Pairing confirmation for %s").format(name),
{ customContent: true });
this.setResident(true);
this._applet = applet;
this._devicePath = device_path;
this.addBody(_("Device %s wants to pair with this computer").format(long_name));
this.addBody(_("Please confirm whether the Passkey '%06d' matches the one on the device.").format(pin));
/* Translators: this is the verb, not the noun */
this.addButton('matches', _("Matches"));
this.addButton('does-not-match', _("Does not match"));
this.connect('action-invoked', Lang.bind(this, function(self, action) {
if (action == 'matches')
this._applet.agent_reply_confirm(this._devicePath, true);
else
this._applet.agent_reply_confirm(this._devicePath, false);
this.destroy();
}));
}
});
const PinNotification = new Lang.Class({
Name: 'PinNotification',
Extends: MessageTray.Notification,
_init: function(source, applet, device_path, name, long_name, numeric) {
this.parent(source,
_("Bluetooth"),
_("Pairing request for %s").format(name),
{ customContent: true });
this.setResident(true);
this._applet = applet;
this._devicePath = device_path;
this._numeric = numeric;
this.addBody(_("Device %s wants to pair with this computer").format(long_name));
this.addBody(_("Please enter the PIN mentioned on the device."));
this._entry = new St.Entry();
this._entry.connect('key-release-event', Lang.bind(this, function(entry, event) {
let key = event.get_key_symbol();
if (key == Clutter.KEY_Return) {
if (this._canActivateOkButton())
this.emit('action-invoked', 'ok');
return true;
} else if (key == Clutter.KEY_Escape) {
this.emit('action-invoked', 'cancel');
return true;
}
return false;
}));
this.addActor(this._entry);
this.addButton('ok', _("OK"));
this.addButton('cancel', _("Cancel"));
this.setButtonSensitive('ok', this._canActivateOkButton());
this._entry.clutter_text.connect('text-changed', Lang.bind(this,
function() {
this.setButtonSensitive('ok', this._canActivateOkButton());
}));
this.connect('action-invoked', Lang.bind(this, function(self, action) {
if (action == 'ok') {
if (this._numeric) {
let num = parseInt(this._entry.text);
if (isNaN(num)) {
// user reply was empty, or was invalid
// cancel the operation
num = -1;
}
this._applet.agent_reply_passkey(this._devicePath, num);
} else
this._applet.agent_reply_pincode(this._devicePath, this._entry.text);
} else {
if (this._numeric)
this._applet.agent_reply_passkey(this._devicePath, -1);
else
this._applet.agent_reply_pincode(this._devicePath, null);
}
this.destroy();
}));
},
_canActivateOkButton: function() {
// PINs have a fixed length of 6
if (this._numeric)
return this._entry.clutter_text.text.length == 6;
else
return true;
}
});

View File

@ -11,11 +11,9 @@ const Slider = imports.ui.slider;
const BUS_NAME = 'org.gnome.SettingsDaemon.Power';
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Power';
const BrightnessInterface = '<node> \
<interface name="org.gnome.SettingsDaemon.Power.Screen"> \
<property name="Brightness" type="i" access="readwrite"/> \
</interface> \
</node>';
const BrightnessInterface = <interface name="org.gnome.SettingsDaemon.Power.Screen">
<property name='Brightness' type='i' access='readwrite'/>
</interface>;
const BrightnessProxy = Gio.DBusProxy.makeProxyWrapper(BrightnessInterface);
@ -48,7 +46,7 @@ const Indicator = new Lang.Class({
this._item.actor.add(icon);
this._item.actor.add(this._slider.actor, { expand: true });
this._item.actor.connect('button-press-event', Lang.bind(this, function(actor, event) {
return this._slider.startDragging(event);
this._slider.startDragging(event);
}));
this._item.actor.connect('key-press-event', Lang.bind(this, function(actor, event) {
return this._slider.onKeyPressEvent(actor, event);

View File

@ -41,13 +41,11 @@ const MAX_INPUT_SOURCE_ACTIVATION_TIME = 4000; // ms
const BUS_NAME = 'org.gnome.SettingsDaemon.Keyboard';
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Keyboard';
const KeyboardManagerInterface = '<node> \
<interface name="org.gnome.SettingsDaemon.Keyboard"> \
<method name="SetInputSource"> \
<arg type="u" direction="in" /> \
</method> \
</interface> \
</node>';
const KeyboardManagerInterface = <interface name="org.gnome.SettingsDaemon.Keyboard">
<method name="SetInputSource">
<arg type="u" direction="in" />
</method>
</interface>;
const KeyboardManagerProxy = Gio.DBusProxy.makeProxyWrapper(KeyboardManagerInterface);
@ -341,7 +339,9 @@ const InputSourceIndicator = new Lang.Class({
this._hbox = new St.BoxLayout({ style_class: 'panel-status-menu-box' });
this._hbox.add_child(this._container);
this._hbox.add_child(PopupMenu.unicodeArrow(St.Side.BOTTOM));
this._hbox.add_child(new St.Label({ text: '\u25BE',
y_expand: true,
y_align: Clutter.ActorAlign.CENTER }));
this.actor.add_child(this._hbox);
this.actor.add_style_class_name('panel-status-button');

View File

@ -16,6 +16,7 @@ const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const MessageTray = imports.ui.messageTray;
const NotificationDaemon = imports.ui.notificationDaemon;
const ModalDialog = imports.ui.modalDialog;
const ModemManager = imports.misc.modemManager;
const Util = imports.misc.util;
@ -72,27 +73,6 @@ function ssidToLabel(ssid) {
return label;
}
function ensureActiveConnectionProps(active, settings) {
if (!active._connection) {
active._connection = settings.get_connection_by_path(active.connection);
// This list is guaranteed to have only one device in it.
let device = active.get_devices()[0]._delegate;
active._primaryDevice = device;
}
}
function createSettingsAction(label, device) {
let item = new PopupMenu.PopupMenuItem(label);
item.connect('activate', function() {
Util.spawnApp(['gnome-control-center', 'network', 'show-device',
device.get_path()]);
});
return item;
}
const NMConnectionItem = new Lang.Class({
Name: 'NMConnectionItem',
@ -190,7 +170,8 @@ const NMConnectionSection = new Lang.Class({
},
destroy: function() {
this.item.destroy();
this.statusItem.destroy();
this.section.destroy();
},
_sync: function() {
@ -287,12 +268,12 @@ const NMConnectionDevice = new Lang.Class({
Extends: NMConnectionSection,
Abstract: true,
_init: function(client, device, settings) {
_init: function(client, device) {
this.parent(client);
this._device = device;
this._settings = settings;
this._autoConnectItem = this.item.menu.addAction(_("Connect"), Lang.bind(this, this._autoConnect));
this.item.menu.addSettingsAction(_("Network Settings"), 'gnome-network-panel.desktop');
this._stateChangedId = this._device.connect('state-changed', Lang.bind(this, this._deviceStateChanged));
this._activeConnectionChangedId = this._device.connect('notify::active-connection', Lang.bind(this, this._activeConnectionChanged));
@ -320,7 +301,6 @@ const NMConnectionDevice = new Lang.Class({
this._activeConnection = this._device.active_connection;
if (this._activeConnection) {
ensureActiveConnectionProps(this._activeConnection, this._settings);
let item = this._connectionItems.get(this._activeConnection._connection.get_uuid());
item.setActiveConnection(this._activeConnection);
}
@ -376,9 +356,8 @@ const NMConnectionDevice = new Lang.Class({
switch(this._device.state) {
case NetworkManager.DeviceState.DISCONNECTED:
return _("Off");
case NetworkManager.DeviceState.ACTIVATED:
return this.parent();
return '';
case NetworkManager.DeviceState.UNMANAGED:
/* Translators: this is for network devices that are physically present but are not
under NetworkManager's control (and thus cannot be used in the menu) */
@ -420,11 +399,8 @@ const NMDeviceModem = new Lang.Class({
Extends: NMConnectionDevice,
category: NMConnectionCategory.WWAN,
_init: function(client, device, settings) {
this.parent(client, device, settings);
this.item.menu.addMenuItem(createSettingsAction(_("Mobile Broadband Settings"), device));
_init: function(client, device) {
this.parent(client, device);
this._mobileDevice = null;
let capabilities = device.current_capabilities;
@ -438,7 +414,16 @@ const NMDeviceModem = new Lang.Class({
this._mobileDevice = new ModemManager.ModemGsm(device.udi);
if (this._mobileDevice) {
this._operatorNameId = this._mobileDevice.connect('notify::operator-name', Lang.bind(this, this._sync));
this._operatorNameId = this._mobileDevice.connect('notify::operator-name', Lang.bind(this, function() {
if (this._operatorItem) {
let name = this._mobileDevice.operator_name;
if (name) {
this._operatorItem.label.text = name;
this._operatorItem.actor.show();
} else
this._operatorItem.actor.hide();
}
}));
this._signalQualityId = this._mobileDevice.connect('notify::signal-quality', Lang.bind(this, function() {
this.emit('icon-changed');
}));
@ -463,20 +448,6 @@ const NMDeviceModem = new Lang.Class({
this.parent();
},
_getStatus: function() {
if (!this._client.wwan_hardware_enabled)
return _("Hardware Disabled");
else if (!this._client.wwan_enabled)
/* Translators: this is for a network device that cannot be activated
because it's disabled by rfkill (airplane mode) */
return _("Disabled");
else if (this._device.state == NetworkManager.DeviceState.ACTIVATED &&
this._mobileDevice && this._mobileDevice.operator_name)
return this._mobileDevice.operator_name;
else
return this.parent();
},
_getMenuIcon: function() {
if (this._device.active_connection)
return this.getIndicatorIcon();
@ -506,12 +477,6 @@ const NMDeviceBluetooth = new Lang.Class({
Extends: NMConnectionDevice,
category: NMConnectionCategory.WWAN,
_init: function(client, device, settings) {
this.parent(client, device, settings);
this.item.menu.addMenuItem(createSettingsAction(_("Mobile Broadband Settings"), device));
},
_autoConnect: function() {
// FIXME: DUN devices are configured like modems, so
// We need to spawn the mobile wizard
@ -549,30 +514,31 @@ const NMWirelessDialogItem = new Lang.Class({
this._network = network;
this._ap = network.accessPoints[0];
this.actor = new St.BoxLayout({ style_class: 'nm-dialog-item',
can_focus: true,
reactive: true });
this.actor = new St.Button({ style_class: 'nm-dialog-item',
can_focus: true,
x_fill: true });
this.actor.connect('key-focus-in', Lang.bind(this, function() {
this.emit('selected');
}));
let action = new Clutter.ClickAction();
action.connect('clicked', Lang.bind(this, function() {
this.actor.connect('clicked', Lang.bind(this, function() {
this.actor.grab_key_focus();
}));
this.actor.add_action(action);
this._content = new St.BoxLayout({ style_class: 'nm-dialog-item-box' });
this.actor.set_child(this._content);
let title = ssidToLabel(this._ap.get_ssid());
this._label = new St.Label({ text: title });
this.actor.label_actor = this._label;
this.actor.add(this._label, { x_align: St.Align.START });
this._content.add(this._label, { x_align: St.Align.START });
this._selectedIcon = new St.Icon({ style_class: 'nm-dialog-icon',
icon_name: 'object-select-symbolic' });
this.actor.add(this._selectedIcon);
this._content.add(this._selectedIcon);
this._icons = new St.BoxLayout({ style_class: 'nm-dialog-icons' });
this.actor.add(this._icons, { expand: true, x_fill: false, x_align: St.Align.END });
this._content.add(this._icons, { expand: true, x_fill: false, x_align: St.Align.END });
this._secureIcon = new St.Icon({ style_class: 'nm-dialog-icon' });
if (this._ap._secType != NMAccessPointSecurity.NONE)
@ -955,13 +921,13 @@ const NMWirelessDialog = new Lang.Class({
_selectNetwork: function(network) {
if (this._selectedNetwork)
this._selectedNetwork.item.actor.remove_style_pseudo_class('selected');
this._selectedNetwork.item.actor.checked = false;
this._selectedNetwork = network;
this._updateSensitivity();
if (this._selectedNetwork)
this._selectedNetwork.item.actor.add_style_pseudo_class('selected');
this._selectedNetwork.item.actor.checked = true;
},
_createNetworkItem: function(network) {
@ -992,10 +958,9 @@ const NMDeviceWireless = new Lang.Class({
this._toggleItem.connect('activate', Lang.bind(this, this._toggleWifi));
this.item.menu.addMenuItem(this._toggleItem);
this.item.menu.addMenuItem(createSettingsAction(_("Wi-Fi Settings"), device));
this.item.menu.addSettingsAction(_("Network Settings"), 'gnome-network-panel.desktop');
this._wirelessEnabledChangedId = this._client.connect('notify::wireless-enabled', Lang.bind(this, this._sync));
this._wirelessHwEnabledChangedId = this._client.connect('notify::wireless-hardware-enabled', Lang.bind(this, this._sync));
this._wirelessEnabledChangedId = this._device.connect('notify::wireless-enabled', Lang.bind(this, this._sync));
this._activeApChangedId = this._device.connect('notify::active-access-point', Lang.bind(this, this._activeApChanged));
this._stateChangedId = this._device.connect('state-changed', Lang.bind(this, this._deviceStateChanged));
@ -1015,14 +980,6 @@ const NMDeviceWireless = new Lang.Class({
this._activeAccessPoint.disconnect(this._strengthChangedId);
this._strengthChangedId = 0;
}
if (this._wirelessEnabledChangedId) {
this._client.disconnect(this._wirelessEnabledChangedId);
this._wirelessEnabledChangedId = 0;
}
if (this._wirelessHwEnabledChangedId) {
this._client.disconnect(this._wirelessHwEnabledChangedId);
this._wirelessHwEnabledChangedId = 0;
}
this.item.destroy();
},
@ -1081,7 +1038,6 @@ const NMDeviceWireless = new Lang.Class({
_sync: function() {
this._toggleItem.label.text = this._client.wireless_enabled ? _("Turn Off") : _("Turn On");
this._toggleItem.actor.visible = this._client.wireless_hardware_enabled;
this.item.status.text = this._getStatus();
this.item.icon.icon_name = this._getMenuIcon();
@ -1095,17 +1051,10 @@ const NMDeviceWireless = new Lang.Class({
_getStatus: function() {
let ap = this._device.active_access_point;
if (!ap)
return _("Off"); // XXX -- interpret actual status
if (ap)
return ssidToLabel(ap.get_ssid());
else if (!this._client.wireless_hardware_enabled)
return _("Hardware Disabled");
else if (!this._client.wireless_enabled)
return _("Off");
else if (this._device.state == NetworkManager.DeviceState.DISCONNECTED)
return _("Not Connected");
else
return '';
return ssidToLabel(ap.get_ssid());
},
_getMenuIcon: function() {
@ -1116,8 +1065,7 @@ const NMDeviceWireless = new Lang.Class({
},
getIndicatorIcon: function() {
if (this._device.state >= NetworkManager.DeviceState.PREPARE &&
this._device.state < NetworkManager.DeviceState.ACTIVATED)
if (this._device.active_connection.state == NetworkManager.ActiveConnectionState.ACTIVATING)
return 'network-wireless-acquiring-symbolic';
let ap = this._device.active_access_point;
@ -1365,7 +1313,7 @@ const NMApplet = new Lang.Class({
if (!this._source) {
this._source = new MessageTray.Source(_("Network Manager"),
'network-transmit-receive');
this._source.policy = new MessageTray.NotificationApplicationPolicy('gnome-network-panel');
this._source.policy = new NotificationDaemon.NotificationApplicationPolicy('gnome-network-panel');
this._source.connect('destroy', Lang.bind(this, function() {
this._source = null;
@ -1477,18 +1425,28 @@ const NMApplet = new Lang.Class({
devices.splice(pos, 1);
},
_ensureActiveConnectionProps: function(a) {
if (!a._connection) {
a._connection = this._settings.get_connection_by_path(a.connection);
// This list is guaranteed to have only one device in it.
let device = a.get_devices()[0]._delegate;
a._primaryDevice = device;
}
},
_getMainConnection: function() {
let connection;
connection = this._client.get_primary_connection();
if (connection) {
ensureActiveConnectionProps(connection, this._settings);
this._ensureActiveConnectionProps(connection);
return connection;
}
connection = this._client.get_activating_connection();
if (connection) {
ensureActiveConnectionProps(connection, this._settings);
this._ensureActiveConnectionProps(connection);
return connection;
}
@ -1524,7 +1482,7 @@ const NMApplet = new Lang.Class({
return (a instanceof NMClient.VPNConnection);
});
vpnConnections.forEach(Lang.bind(this, function(a) {
ensureActiveConnectionProps(a, this._settings);
this._ensureActiveConnectionProps(a);
}));
this._vpnSection.setActiveConnections(vpnConnections);
@ -1627,7 +1585,6 @@ const NMApplet = new Lang.Class({
_updateIcon: function() {
if (!this._client.networking_enabled || !this._mainConnection) {
this._primaryIndicator.icon_name = 'network-offline-symbolic';
this._primaryIndicator.visible = true;
} else {
let dev = this._mainConnection._primaryDevice;
this._primaryIndicator.visible = (dev != null);

View File

@ -8,22 +8,20 @@ const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const BUS_NAME = 'org.freedesktop.UPower';
const OBJECT_PATH = '/org/freedesktop/UPower/devices/DisplayDevice';
const BUS_NAME = 'org.gnome.SettingsDaemon.Power';
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Power';
const DisplayDeviceInterface = '<node> \
<interface name="org.freedesktop.UPower.Device"> \
<property name="Type" type="u" access="read"/> \
<property name="State" type="u" access="read"/> \
<property name="Percentage" type="d" access="read"/> \
<property name="TimeToEmpty" type="x" access="read"/> \
<property name="TimeToFull" type="x" access="read"/> \
<property name="IsPresent" type="b" access="read"/> \
<property name="IconName" type="s" access="read"/> \
</interface> \
</node>';
const PowerManagerInterface = <interface name="org.gnome.SettingsDaemon.Power">
<method name="GetDevices">
<arg type="a(susdut)" direction="out" />
</method>
<method name="GetPrimaryDevice">
<arg type="(susdut)" direction="out" />
</method>
<property name="Icon" type="s" access="read" />
</interface>;
const PowerManagerProxy = Gio.DBusProxy.makeProxyWrapper(DisplayDeviceInterface);
const PowerManagerProxy = Gio.DBusProxy.makeProxyWrapper(PowerManagerInterface);
const Indicator = new Lang.Class({
Name: 'PowerIndicator',
@ -34,7 +32,7 @@ const Indicator = new Lang.Class({
this._indicator = this._addIndicator();
this._proxy = new PowerManagerProxy(Gio.DBus.system, 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);
@ -45,7 +43,7 @@ const Indicator = new Lang.Class({
this._sync();
}));
this._item = new PopupMenu.PopupSubMenuMenuItem("", true);
this._item = new PopupMenu.PopupSubMenuMenuItem(_("Battery"), true);
this._item.menu.addSettingsAction(_("Power Settings"), 'gnome-power-panel.desktop');
this.menu.addMenuItem(this._item);
@ -58,18 +56,11 @@ const Indicator = new Lang.Class({
this.menu.setSensitive(sensitive);
},
_getStatus: function() {
let seconds = 0;
_statusForDevice: function(device) {
let [device_id, device_type, icon, percentage, state, seconds] = device;
if (this._proxy.State == UPower.DeviceState.FULLY_CHARGED)
if (state == UPower.DeviceState.FULLY_CHARGED)
return _("Fully Charged");
else if (this._proxy.State == UPower.DeviceState.CHARGING)
seconds = this._proxy.TimeToFull;
else if (this._proxy.State == UPower.DeviceState.DISCHARGING)
seconds = this._proxy.TimeToEmpty;
// state is one of PENDING_CHARGING, PENDING_DISCHARGING
else
return _("Estimating…");
let time = Math.round(seconds / 60);
if (time == 0) {
@ -81,43 +72,52 @@ const Indicator = new Lang.Class({
let minutes = time % 60;
let hours = Math.floor(time / 60);
if (this._proxy.State == UPower.DeviceState.DISCHARGING) {
if (state == UPower.DeviceState.DISCHARGING) {
// Translators: this is <hours>:<minutes> Remaining (<percentage>)
return _("%d\u2236%02d Remaining (%d%%)").format(hours, minutes, this._proxy.Percentage);
return _("%d\u2236%02d Remaining (%d%%)").format(hours, minutes, percentage);
}
if (this._proxy.State == UPower.DeviceState.CHARGING) {
if (state == UPower.DeviceState.CHARGING) {
// Translators: this is <hours>:<minutes> Until Full (<percentage>)
return _("%d\u2236%02d Until Full (%d%%)").format(hours, minutes, this._proxy.Percentage);
return _("%d\u2236%02d Until Full (%d%%)").format(hours, minutes, percentage);
}
return null;
// state is one of PENDING_CHARGING, PENDING_DISCHARGING
return _("Estimating…");
},
_syncStatusLabel: function() {
this._proxy.GetPrimaryDeviceRemote(Lang.bind(this, function(result, error) {
if (error) {
this._item.actor.hide();
return;
}
let [device] = result;
let [device_id, device_type] = device;
if (device_type == UPower.DeviceKind.BATTERY) {
this._item.status.text = this._statusForDevice(device);
this._item.actor.show();
} else {
this._item.actor.hide();
}
}));
},
_syncIcon: function() {
let icon = this._proxy.Icon;
if (icon) {
let gicon = Gio.icon_new_for_string(icon);
this._indicator.gicon = gicon;
this._item.icon.gicon = gicon;
} else {
// If there's no battery, then we use the power icon.
this._indicator.icon_name = 'system-shutdown-symbolic';
}
},
_sync: function() {
// Do we have batteries or a UPS?
let visible = this._proxy.IsPresent;
if (visible) {
this._item.actor.show();
} else {
// If there's no battery, then we use the power icon.
this._item.actor.hide();
this._indicator.icon_name = 'system-shutdown-symbolic';
return;
}
// The icons
let icon = this._proxy.IconName;
this._indicator.icon_name = icon;
this._item.icon.icon_name = icon;
// The status label
this._item.status.text = this._getStatus();
// The sub-menu heading
if (this._proxy.Type == UPower.DeviceKind.UPS)
this._item.label.text = _("UPS");
else
this._item.label.text = _("Battery");
},
this._syncIcon();
this._syncStatusLabel();
}
});

View File

@ -9,11 +9,9 @@ const PopupMenu = imports.ui.popupMenu;
const BUS_NAME = 'org.gnome.SettingsDaemon.Rfkill';
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Rfkill';
const RfkillManagerInterface = '<node> \
<interface name="org.gnome.SettingsDaemon.Rfkill"> \
<property name="AirplaneMode" type="b" access="readwrite" /> \
</interface> \
</node>';
const RfkillManagerInterface = <interface name="org.gnome.SettingsDaemon.Rfkill">
<property name="AirplaneMode" type="b" access="readwrite" />
</interface>;
const RfkillManagerProxy = Gio.DBusProxy.makeProxyWrapper(RfkillManagerInterface);

View File

@ -18,12 +18,10 @@ const PopupMenu = imports.ui.popupMenu;
const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown';
const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver';
const LOGIN_SCREEN_SCHEMA = 'org.gnome.login-screen';
const PRIVACY_SCHEMA = 'org.gnome.desktop.privacy'
const DISABLE_USER_SWITCH_KEY = 'disable-user-switching';
const DISABLE_LOCK_SCREEN_KEY = 'disable-lock-screen';
const DISABLE_LOG_OUT_KEY = 'disable-log-out';
const DISABLE_RESTART_KEY = 'disable-restart-buttons';
const ALWAYS_SHOW_LOG_OUT_KEY = 'always-show-log-out';
const AltSwitcher = new Lang.Class({
@ -81,7 +79,7 @@ const AltSwitcher = new Lang.Class({
this._sync();
}
return Clutter.EVENT_PROPAGATE;
return false;
},
});
@ -93,7 +91,6 @@ const Indicator = new Lang.Class({
this.parent();
this._screenSaverSettings = new Gio.Settings({ schema: SCREENSAVER_SCHEMA });
this._loginScreenSettings = new Gio.Settings({ schema: LOGIN_SCREEN_SCHEMA });
this._lockdownSettings = new Gio.Settings({ schema: LOCKDOWN_SCHEMA });
this._privacySettings = new Gio.Settings({ schema: PRIVACY_SCHEMA });
this._orientationSettings = new Gio.Settings({ schema: 'org.gnome.settings-daemon.peripherals.touchscreen' });
@ -264,10 +261,7 @@ const Indicator = new Lang.Class({
},
_updatePowerOff: function() {
let disabled = Main.sessionMode.isLocked ||
(Main.sessionMode.isGreeter &&
this._loginScreenSettings.get_boolean(DISABLE_RESTART_KEY));
this._powerOffAction.visible = this._haveShutdown && !disabled;
this._powerOffAction.visible = this._haveShutdown && !Main.sessionMode.isLocked;
this._updateActionsVisibility();
},
@ -279,10 +273,7 @@ const Indicator = new Lang.Class({
},
_updateSuspend: function() {
let disabled = Main.sessionMode.isLocked ||
(Main.sessionMode.isGreeter &&
this._loginScreenSettings.get_boolean(DISABLE_RESTART_KEY));
this._suspendAction.visible = this._haveShutdown && !disabled;
this._suspendAction.visible = this._haveSuspend && !Main.sessionMode.isLocked;
this._updateActionsVisibility();
},

View File

@ -42,7 +42,7 @@ const StreamSlider = new Lang.Class({
this.item.actor.add(this._icon);
this.item.actor.add(this._slider.actor, { expand: true });
this.item.actor.connect('button-press-event', Lang.bind(this, function(actor, event) {
return this._slider.startDragging(event);
this._slider.startDragging(event);
}));
this.item.actor.connect('key-press-event', Lang.bind(this, function(actor, event) {
return this._slider.onKeyPressEvent(actor, event);
@ -94,7 +94,7 @@ const StreamSlider = new Lang.Class({
},
scroll: function(event) {
return this._slider.scroll(event);
this._slider.scroll(event);
},
setValue: function(value) {
@ -276,7 +276,7 @@ const VolumeMenu = new Lang.Class({
},
scroll: function(event) {
return this._output.scroll(event);
this._output.scroll(event);
},
_onControlStateChanged: function() {
@ -329,6 +329,6 @@ const Indicator = new Lang.Class({
},
_onScrollEvent: function(actor, event) {
return this._volumeMenu.scroll(event);
this._volumeMenu.scroll(event);
}
});

View File

@ -1,7 +1,6 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
@ -164,7 +163,6 @@ const SwitcherPopup = new Lang.Class({
Main.osdWindow.cancel();
this.actor.opacity = 255;
this._initialDelayTimeoutId = 0;
return GLib.SOURCE_REMOVE;
}));
return true;
},
@ -194,7 +192,7 @@ const SwitcherPopup = new Lang.Class({
else
this._keyPressHandler(keysym, backwards, action);
return Clutter.EVENT_STOP;
return true;
},
_keyReleaseEvent: function(actor, event) {
@ -204,12 +202,11 @@ const SwitcherPopup = new Lang.Class({
if (state == 0)
this._finish(event.get_time());
return Clutter.EVENT_STOP;
return true;
},
_clickedOutside: function(actor, event) {
this.destroy();
return Clutter.EVENT_PROPAGATE;
},
_scrollHandler: function(direction) {
@ -221,7 +218,6 @@ const SwitcherPopup = new Lang.Class({
_scrollEvent: function(actor, event) {
this._scrollHandler(event.get_scroll_direction());
return Clutter.EVENT_PROPAGATE;
},
_itemActivatedHandler: function(n) {
@ -255,7 +251,6 @@ const SwitcherPopup = new Lang.Class({
_mouseTimedOut: function() {
this._motionTimeoutId = 0;
this.mouseActive = true;
return GLib.SOURCE_REMOVE;
},
_popModal: function() {
@ -405,7 +400,6 @@ const SwitcherList = new Lang.Class({
_onItemEnter: function (index) {
this._itemEntered(index);
return Clutter.EVENT_PROPAGATE;
},
highlight: function(index, justOutline) {

View File

@ -11,8 +11,29 @@ const Signals = imports.signals;
const Tweener = imports.tweener.tweener;
// This is a wrapper around imports.tweener.tweener that adds a bit of
// Clutter integration. If the tweening target is a Clutter.Actor, then
// the tweenings will automatically be removed if the actor is destroyed.
// Clutter integration and some additional callbacks:
//
// 1. If the tweening target is a Clutter.Actor, then the tweenings
// will automatically be removed if the actor is destroyed
//
// 2. If target._delegate.onAnimationStart() exists, it will be
// called when the target starts being animated.
//
// 3. If target._delegate.onAnimationComplete() exists, it will be
// called once the target is no longer being animated.
//
// The onAnimationStart() and onAnimationComplete() callbacks differ
// from the tweener onStart and onComplete parameters, in that (1)
// they track whether or not the target has *any* tweens attached to
// it, as opposed to be called for *each* tween, and (2)
// onAnimationComplete() is always called when the object stops being
// animated, regardless of whether it stopped normally or abnormally.
//
// onAnimationComplete() is called at idle time, which means that if a
// tween completes and then another is added before returning to the
// main loop, the complete callback will not be called (until the new
// tween finishes).
// ActionScript Tweener methods that imports.tweener.tweener doesn't
// currently implement: getTweens, getVersion, registerTransition,
@ -56,6 +77,7 @@ function _wrapTweening(target, tweeningParameters) {
if (!Gtk.Settings.get_default().gtk_enable_animations)
tweeningParameters['time'] = 0.000001;
_addHandler(target, tweeningParameters, 'onStart', _tweenStarted);
_addHandler(target, tweeningParameters, 'onComplete', _tweenCompleted);
}
@ -63,7 +85,7 @@ function _getTweenState(target) {
// If we were paranoid, we could keep a plist mapping targets to
// states... but we're not that paranoid.
if (!target.__ShellTweenerState)
target.__ShellTweenerState = {};
_resetTweenState(target);
return target.__ShellTweenerState;
}
@ -73,6 +95,8 @@ function _resetTweenState(target) {
if (state) {
if (state.destroyedId)
state.actor.disconnect(state.destroyedId);
if (state.idleCompletedId)
Mainloop.source_remove(state.idleCompletedId);
}
target.__ShellTweenerState = {};
@ -98,9 +122,32 @@ function _actorDestroyed(target) {
Tweener.removeTweens(target);
}
function _tweenStarted(target) {
let state = _getTweenState(target);
let delegate = target._delegate;
if (!state.running && delegate && delegate.onAnimationStart)
delegate.onAnimationStart();
state.running = true;
}
function _tweenCompleted(target) {
if (!isTweening(target))
let state = _getTweenState(target);
if (!state.idleCompletedId)
state.idleCompletedId = Mainloop.idle_add(Lang.bind(null, _idleCompleted, target));
}
function _idleCompleted(target) {
let state = _getTweenState(target);
let delegate = target._delegate;
if (!isTweening(target)) {
_resetTweenState(target);
if (delegate && delegate.onAnimationComplete)
delegate.onAnimationComplete();
}
return false;
}
function getTweenCount(scope) {

View File

@ -71,7 +71,7 @@ const UnlockDialog = new Lang.Class({
child: otherUserLabel,
reactive: true,
x_align: St.Align.START,
x_fill: false });
x_fill: true });
this._otherUserButton.connect('clicked', Lang.bind(this, this._otherUserClicked));
this._promptBox.add_child(this._otherUserButton);
} else {

View File

@ -1,8 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
//
// A widget showing the user avatar and name
const Clutter = imports.gi.Clutter;
const AccountsService = imports.gi.AccountsService;
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
@ -57,90 +56,6 @@ const Avatar = new Lang.Class({
}
});
const UserWidgetLabel = new Lang.Class({
Name: 'UserWidgetLabel',
Extends: St.Widget,
_init: function(user) {
this.parent({ layout_manager: new Clutter.BinLayout() });
this._user = user;
this._realNameLabel = new St.Label({ style_class: 'user-widget-label',
y_align: Clutter.ActorAlign.CENTER });
this.add_child(this._realNameLabel);
this._userNameLabel = new St.Label({ style_class: 'user-widget-label',
y_align: Clutter.ActorAlign.CENTER });
this.add_child(this._userNameLabel);
this._currentLabel = null;
this._userLoadedId = this._user.connect('notify::is-loaded', Lang.bind(this, this._updateUser));
this._userChangedId = this._user.connect('changed', Lang.bind(this, this._updateUser));
this._updateUser();
// We can't override the destroy vfunc because that might be called during
// object finalization, and we can't call any JS inside a GC finalize callback,
// so we use a signal, that will be disconnected by GObject the first time
// the actor is destroyed (which is guaranteed to be as part of a normal
// destroy() call from JS, possibly from some ancestor)
this.connect('destroy', Lang.bind(this, this._onDestroy));
},
_onDestroy: function() {
if (this._userLoadedId != 0) {
this._user.disconnect(this._userLoadedId);
this._userLoadedId = 0;
}
if (this._userChangedId != 0) {
this._user.disconnect(this._userChangedId);
this._userChangedId = 0;
}
},
vfunc_allocate: function(box, flags) {
this.set_allocation(box, flags);
let availWidth = box.x2 - box.x1;
let availHeight = box.y2 - box.y1;
let [minRealNameWidth, minRealNameHeight,
natRealNameWidth, natRealNameHeight] = this._realNameLabel.get_preferred_size();
let [minUserNameWidth, minUserNameHeight,
natUserNameWidth, natUserNameHeight] = this._userNameLabel.get_preferred_size();
if (natRealNameWidth <= availWidth)
this._currentLabel = this._realNameLabel;
else
this._currentLabel = this._userNameLabel;
let childBox = new Clutter.ActorBox();
childBox.x1 = 0;
childBox.y1 = 0;
childBox.x2 = availWidth;
childBox.y2 = availHeight;
this._currentLabel.allocate(childBox, flags);
},
vfunc_paint: function() {
this._currentLabel.paint();
},
_updateUser: function() {
if (this._user.is_loaded) {
this._realNameLabel.text = this._user.get_real_name();
this._userNameLabel.text = this._user.get_user_name();
} else {
this._realNameLabel.text = '';
this._userNameLabel.text = '';
}
},
});
const UserWidget = new Lang.Class({
Name: 'UserWidget',
@ -152,14 +67,22 @@ const UserWidget = new Lang.Class({
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
this._avatar = new Avatar(user);
this.actor.add_child(this._avatar.actor);
this.actor.add(this._avatar.actor,
{ x_fill: true, y_fill: true });
this._label = new UserWidgetLabel(user);
this.actor.add_child(this._label);
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));
this._updateUser();
this._userLoadedId = this._user.connect('notify::is-loaded',
Lang.bind(this, this._updateUser));
this._userChangedId = this._user.connect('changed',
Lang.bind(this, this._updateUser));
if (this._user.is_loaded)
this._updateUser();
},
_onDestroy: function() {
@ -175,6 +98,11 @@ const UserWidget = new Lang.Class({
},
_updateUser: function() {
if (this._user.is_loaded)
this._label.text = this._user.get_real_name();
else
this._label.text = '';
this._avatar.update();
}
});

View File

@ -2,7 +2,6 @@
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
@ -15,9 +14,12 @@ const AppDisplay = imports.ui.appDisplay;
const Main = imports.ui.main;
const OverviewControls = imports.ui.overviewControls;
const Params = imports.misc.params;
const RemoteSearch = imports.ui.remoteSearch;
const Search = imports.ui.search;
const SearchDisplay = imports.ui.searchDisplay;
const ShellEntry = imports.ui.shellEntry;
const Tweener = imports.ui.tweener;
const Wanda = imports.ui.wanda;
const WorkspacesView = imports.ui.workspacesView;
const SHELL_KEYBINDINGS_SCHEMA = 'org.gnome.shell.keybindings';
@ -64,6 +66,8 @@ const ViewSelector = new Lang.Class({
this._searchActive = false;
this._searchTimeoutId = 0;
this._searchSystem = new Search.SearchSystem();
this._entry = searchEntry;
ShellEntry.addContextMenu(this._entry);
@ -99,11 +103,24 @@ const ViewSelector = new Lang.Class({
this._appsPage = this._addPage(this.appDisplay.actor,
_("Applications"), 'view-grid-symbolic');
this._searchResults = new Search.SearchResults();
this._searchResults = new SearchDisplay.SearchResults(this._searchSystem);
this._searchPage = this._addPage(this._searchResults.actor,
_("Search"), 'edit-find-symbolic',
{ a11yFocus: this._entry });
this._searchSettings = new Gio.Settings({ schema: Search.SEARCH_PROVIDERS_SCHEMA });
this._searchSettings.connect('changed::disabled', Lang.bind(this, this._reloadRemoteProviders));
this._searchSettings.connect('changed::disable-external', Lang.bind(this, this._reloadRemoteProviders));
this._searchSettings.connect('changed::sort-order', Lang.bind(this, this._reloadRemoteProviders));
// Default search providers
// Wanda comes obviously first
this.addSearchProvider(new Wanda.WandaSearchProvider());
this.addSearchProvider(new AppDisplay.AppSearchProvider());
// Load remote search providers provided by applications
RemoteSearch.loadRemoteSearchProviders(Lang.bind(this, this.addSearchProvider));
// Since the entry isn't inside the results container we install this
// dummy widget as the last results container child so that we can
// include the entry in the keynav tab path
@ -158,14 +175,17 @@ const ViewSelector = new Lang.Class({
},
show: function() {
this.reset();
this._activePage = this._workspacesPage;
this.reset();
this._appsPage.hide();
this._searchPage.hide();
this._workspacesDisplay.show();
this._activePage = null;
this._showPage(this._workspacesPage);
if (!this._workspacesDisplay.activeWorkspaceHasMaximizedWindows())
Main.overview.fadeOutDesktop();
this._showPage(this._workspacesPage, true);
},
zoomFromOverview: function() {
@ -187,7 +207,6 @@ const ViewSelector = new Lang.Class({
params = Params.parse(params, { a11yFocus: null });
let page = new St.Bin({ child: actor,
visible: false,
x_align: St.Align.START,
y_align: St.Align.START,
x_fill: true,
@ -202,7 +221,6 @@ const ViewSelector = new Lang.Class({
this._a11yFocusPage(page);
})
});;
page.hide();
this.actor.add_actor(page);
return page;
},
@ -212,7 +230,7 @@ const ViewSelector = new Lang.Class({
oldPage.hide();
this.emit('page-empty');
this._activePage.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
this._activePage.show();
Tweener.addTween(this._activePage,
{ opacity: 255,
@ -268,7 +286,7 @@ const ViewSelector = new Lang.Class({
// Ignore events while anything but the overview has
// pushed a modal (system modals, looking glass, ...)
if (Main.modalCount > 1)
return Clutter.EVENT_PROPAGATE;
return false;
let modifiers = event.get_state();
let symbol = event.get_key_symbol();
@ -280,11 +298,19 @@ const ViewSelector = new Lang.Class({
this._showAppsButton.checked = false;
else
Main.overview.hide();
return Clutter.EVENT_STOP;
return true;
} else if (this._shouldTriggerSearch(symbol)) {
this.startSearch(event);
} else if (!this._searchActive) {
if (symbol == Clutter.Tab || symbol == Clutter.Down) {
this._activePage.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
return true;
} else if (symbol == Clutter.ISO_Left_Tab) {
this._activePage.navigate_focus(null, Gtk.DirectionType.TAB_BACKWARD, false);
return true;
}
}
return Clutter.EVENT_PROPAGATE;
return false;
},
_searchCancelled: function() {
@ -400,7 +426,7 @@ const ViewSelector = new Lang.Class({
if (symbol == Clutter.Escape) {
if (this._isActivated()) {
this.reset();
return Clutter.EVENT_STOP;
return true;
}
} else if (this._searchActive) {
let arrowNext, nextDirection;
@ -414,18 +440,18 @@ const ViewSelector = new Lang.Class({
if (symbol == Clutter.Tab) {
this._searchResults.navigateFocus(Gtk.DirectionType.TAB_FORWARD);
return Clutter.EVENT_STOP;
return true;
} else if (symbol == Clutter.ISO_Left_Tab) {
this._focusTrap.can_focus = false;
this._searchResults.navigateFocus(Gtk.DirectionType.TAB_BACKWARD);
this._focusTrap.can_focus = true;
return Clutter.EVENT_STOP;
return true;
} else if (symbol == Clutter.Down) {
this._searchResults.navigateFocus(Gtk.DirectionType.DOWN);
return Clutter.EVENT_STOP;
return true;
} else if (symbol == arrowNext && this._text.position == -1) {
this._searchResults.navigateFocus(nextDirection);
return Clutter.EVENT_STOP;
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.
@ -434,10 +460,10 @@ const ViewSelector = new Lang.Class({
this._doSearch();
}
this._searchResults.activateDefault();
return Clutter.EVENT_STOP;
return true;
}
}
return Clutter.EVENT_PROPAGATE;
return false;
},
_onCapturedEvent: function(actor, event) {
@ -452,7 +478,7 @@ const ViewSelector = new Lang.Class({
}
}
return Clutter.EVENT_PROPAGATE;
return false;
},
_doSearch: function () {
@ -460,10 +486,43 @@ const ViewSelector = new Lang.Class({
let terms = getTermsForSearchString(this._entry.get_text());
this._searchResults.setTerms(terms);
this._searchSystem.updateSearchResults(terms);
this._showPage(this._searchPage);
},
return GLib.SOURCE_REMOVE;
_shouldUseSearchProvider: function(provider) {
// the disable-external GSetting only affects remote providers
if (!provider.isRemoteProvider)
return true;
if (this._searchSettings.get_boolean('disable-external'))
return false;
let appId = provider.appInfo.get_id();
let disable = this._searchSettings.get_strv('disabled');
return disable.indexOf(appId) == -1;
},
_reloadRemoteProviders: function() {
// removeSearchProvider() modifies the provider list we iterate on,
// so make a copy first
let remoteProviders = this._searchSystem.getRemoteProviders().slice(0);
remoteProviders.forEach(Lang.bind(this, this.removeSearchProvider));
RemoteSearch.loadRemoteSearchProviders(Lang.bind(this, this.addSearchProvider));
},
addSearchProvider: function(provider) {
if (!this._shouldUseSearchProvider(provider))
return;
this._searchSystem.registerProvider(provider);
this._searchResults.createProviderDisplay(provider);
},
removeSearchProvider: function(provider) {
this._searchSystem.unregisterProvider(provider);
this._searchResults.destroyProviderDisplay(provider);
},
getActivePage: function() {

156
js/ui/wanda.js Normal file
View File

@ -0,0 +1,156 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const GdkPixbuf = imports.gi.GdkPixbuf;
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const St = imports.gi.St;
const IconGrid = imports.ui.iconGrid;
const Layout = imports.ui.layout;
const Main = imports.ui.main;
const Panel = imports.ui.panel;
const FISH_NAME = 'wanda';
const FISH_FILENAME = 'wanda.png';
const FISH_SPEED = 300;
const FISH_COMMAND = 'fortune';
// The size of an individual frame in the animation
const FISH_HEIGHT = 22;
const FISH_WIDTH = 36;
const FISH_GROUP = 'Fish Animation';
const MAGIC_FISH_KEY = 'free the fish';
const WandaIcon = new Lang.Class({
Name: 'WandaIcon',
Extends: IconGrid.BaseIcon,
_init : function(fish, label, params) {
this.parent(label, params);
this._fish = fish;
this._imageFile = GLib.build_filenamev([global.datadir, fish + '.png']);
this._imgHeight = FISH_HEIGHT;
this._imgWidth = FISH_WIDTH;
},
createIcon: function(iconSize) {
this._animations = new Panel.Animation(this._imageFile, this._imgWidth, this._imgHeight, FISH_SPEED);
this._animations.play();
return this._animations.actor;
},
_createIconTexture: function(size) {
if (size == this.iconSize)
return;
this.parent(size);
}
});
const WandaIconBin = new Lang.Class({
Name: 'WandaIconBin',
_init: function(fish, label, params) {
this.actor = new St.Bin({ reactive: true,
track_hover: true });
this.icon = new WandaIcon(fish, label, params);
this.actor.child = this.icon.actor;
this.actor.label_actor = this.icon.label;
},
});
const FortuneDialog = new Lang.Class({
Name: 'FortuneDialog',
_init: function(name, command) {
let text;
try {
let [res, stdout, stderr, status] = GLib.spawn_command_line_sync(command);
text = String.fromCharCode.apply(null, stdout);
} catch(e) {
text = _("Sorry, no wisdom for you today:\n%s").format(e.message);
}
this._title = new St.Label({ style_class: 'prompt-dialog-headline',
text: _("%s the Oracle says").format(name) });
this._label = new St.Label({ style_class: 'prompt-dialog-description',
text: text });
this._label.clutter_text.line_wrap = true;
this._box = new St.BoxLayout({ vertical: true,
style_class: 'prompt-dialog' // this is just to force a reasonable width
});
this._box.add(this._title, { align: St.Align.MIDDLE });
this._box.add(this._label, { expand: true });
this._button = new St.Button({ button_mask: St.ButtonMask.ONE,
style_class: 'modal-dialog',
reactive: true });
this._button.connect('clicked', Lang.bind(this, this.destroy));
this._button.child = this._box;
this._bin = new St.Bin({ x_align: St.Align.MIDDLE,
y_align: St.Align.MIDDLE });
this._bin.add_constraint(new Layout.MonitorConstraint({ primary: true }));
this._bin.add_actor(this._button);
Main.layoutManager.addChrome(this._bin);
GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 10, Lang.bind(this, this.destroy));
},
destroy: function() {
this._bin.destroy();
}
});
function capitalize(str) {
return str[0].toUpperCase() + str.substring(1, str.length);
}
const WandaSearchProvider = new Lang.Class({
Name: 'WandaSearchProvider',
_init: function() {
this.id = 'wanda';
},
getResultMetas: function(fish, callback) {
callback([{ 'id': fish[0], // there may be many fish in the sea, but
// only one which speaks the truth!
'name': capitalize(fish[0]),
'createIcon': function(iconSize) {
return new St.Icon({ gicon: Gio.icon_new_for_string('face-smile'),
icon_size: iconSize });
}
}]);
},
getInitialResultSet: function(terms) {
if (terms.join(' ') == MAGIC_FISH_KEY) {
this.searchSystem.setResults(this, [ FISH_NAME ]);
} else {
this.searchSystem.setResults(this, []);
}
},
getSubsearchResultSet: function(previousResults, terms) {
this.getInitialResultSet(terms);
},
activateResult: function(fish) {
if (this._dialog)
this._dialog.destroy();
this._dialog = new FortuneDialog(capitalize(fish), FISH_COMMAND);
},
createResultObject: function (resultMeta, terms) {
return new WandaIconBin(resultMeta.id, resultMeta.name);
}
});

View File

@ -39,9 +39,6 @@ const WindowAttentionHandler = new Lang.Class({
let [title, banner] = this._getTitleAndBanner(app, window);
let notification = new MessageTray.Notification(source, title, banner);
notification.connect('clicked', function() {
source.open();
});
notification.setForFeedback(true);
source.notify(notification);
@ -82,7 +79,7 @@ const Source = new Lang.Class({
return this._app.create_icon_texture(size);
},
open: function() {
open : function(notification) {
Main.activateWindow(this._window);
this.destroy();
}

View File

@ -106,11 +106,11 @@ const DisplayChangeDialog = new Lang.Class({
/* mutter already takes care of failing at timeout */
this._timeoutId = 0;
this.close();
return GLib.SOURCE_REMOVE;
return false;
}
this._descriptionLabel.text = this._formatCountDown();
return GLib.SOURCE_CONTINUE;
return true;
},
_onFailure: function() {
@ -232,13 +232,12 @@ const WorkspaceTracker = new Lang.Class({
let windows = global.get_window_actors();
for (i = 0; i < windows.length; i++) {
let actor = windows[i];
let win = actor.get_meta_window();
let win = windows[i];
if (win.is_on_all_workspaces())
if (win.get_meta_window().is_on_all_workspaces())
continue;
let workspaceIndex = win.get_workspace().index();
let workspaceIndex = win.get_workspace();
emptyWorkspaces[workspaceIndex] = false;
}
@ -279,7 +278,7 @@ const WorkspaceTracker = new Lang.Class({
workspace._keepAliveId = Mainloop.timeout_add(duration, Lang.bind(this, function() {
workspace._keepAliveId = 0;
this._queueCheckWorkspaces();
return GLib.SOURCE_REMOVE;
return false;
}));
},
@ -291,7 +290,7 @@ const WorkspaceTracker = new Lang.Class({
workspace._lastRemovedWindow = null;
this._queueCheckWorkspaces();
}
return GLib.SOURCE_REMOVE;
return false;
}));
},
@ -976,29 +975,25 @@ const WindowManager = new Lang.Class({
wgroup.add_actor(switchData.movingWindowBin);
for (let i = 0; i < windows.length; i++) {
let actor = windows[i];
let window = actor.get_meta_window();
let window = windows[i];
if (!window.showing_on_its_workspace())
if (!window.meta_window.showing_on_its_workspace())
continue;
if (window.is_on_all_workspaces())
continue;
let record = { window: actor,
parent: actor.get_parent() };
if (this._movingWindow && window == this._movingWindow) {
switchData.movingWindow = record;
if (this._movingWindow && window.meta_window == this._movingWindow) {
switchData.movingWindow = { window: window,
parent: window.get_parent() };
switchData.windows.push(switchData.movingWindow);
actor.reparent(switchData.movingWindowBin);
} else if (window.get_workspace().index() == from) {
switchData.windows.push(record);
actor.reparent(switchData.outGroup);
} else if (window.get_workspace().index() == to) {
switchData.windows.push(record);
actor.reparent(switchData.inGroup);
actor.show();
window.reparent(switchData.movingWindowBin);
} else if (window.get_workspace() == from) {
switchData.windows.push({ window: window,
parent: window.get_parent() });
window.reparent(switchData.outGroup);
} else if (window.get_workspace() == to) {
switchData.windows.push({ window: window,
parent: window.get_parent() });
window.reparent(switchData.inGroup);
window.show();
}
}

View File

@ -1,7 +1,6 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
@ -11,14 +10,19 @@ const St = imports.gi.St;
const Signals = imports.signals;
const DND = imports.ui.dnd;
const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main;
const Overview = imports.ui.overview;
const Panel = imports.ui.panel;
const Tweener = imports.ui.tweener;
const FOCUS_ANIMATION_TIME = 0.15;
const WINDOW_DND_SIZE = 256;
const WINDOW_CLONE_MAXIMUM_SCALE = 0.7;
const LIGHTBOX_FADE_TIME = 0.1;
const CLOSE_BUTTON_FADE_TIME = 0.1;
const DRAGGING_WINDOW_OPACITY = 100;
@ -64,12 +68,11 @@ const WindowClone = new Lang.Class({
// the invisible border; this is inconvenient; rather than trying
// to compensate all over the place we insert a ClutterActor into
// the hierarchy that is sized to only the visible portion.
this.actor = new St.Widget({ reactive: true,
can_focus: true,
x: this.origX,
y: this.origY,
width: outerRect.width,
height: outerRect.height });
this.actor = new Clutter.Actor({ reactive: true,
x: this.origX,
y: this.origY,
width: outerRect.width,
height: outerRect.height });
this.actor.add_child(this._windowClone);
@ -87,10 +90,10 @@ const WindowClone = new Lang.Class({
let clickAction = new Clutter.ClickAction();
clickAction.connect('clicked', Lang.bind(this, this._onClicked));
clickAction.connect('long-press', Lang.bind(this, this._onLongPress));
this.actor.add_action(clickAction);
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
this.actor.connect('key-press-event', Lang.bind(this, this._onKeyPress));
this.actor.connect('enter-event', Lang.bind(this, this._onEnter));
this._draggable = DND.makeDraggable(this.actor,
{ restoreOnSuccess: true,
@ -199,30 +202,11 @@ const WindowClone = new Lang.Class({
this.disconnectAll();
},
_activate: function() {
_onClicked: function(action, actor) {
this._selected = true;
this.emit('selected', global.get_current_time());
},
_onKeyPress: function(actor, event) {
let symbol = event.get_key_symbol();
let isEnter = (symbol == Clutter.KEY_Return || symbol == Clutter.KEY_KP_Enter);
if (isEnter) {
this._activate();
return true;
}
return false;
},
_onEnter: function() {
this.actor.grab_key_focus();
},
_onClicked: function(action, actor) {
this._activate();
},
_onLongPress: function(action, actor, state) {
// Take advantage of the Clutter policy to consider
// a long-press canceled when the pointer movement
@ -322,10 +306,6 @@ const WindowOverlay = new Lang.Class({
Lang.bind(this, this._onEnter));
windowClone.actor.connect('leave-event',
Lang.bind(this, this._onLeave));
windowClone.actor.connect('key-focus-in',
Lang.bind(this, this._onEnter));
windowClone.actor.connect('key-focus-out',
Lang.bind(this, this._onLeave));
this._windowAddedId = 0;
@ -363,6 +343,19 @@ const WindowOverlay = new Lang.Class({
this._animateVisible();
},
fadeIn: function() {
if (!this._hidden)
return;
this.show();
this.title.opacity = 0;
this._parentActor.raise_top();
Tweener.addTween(this.title,
{ opacity: 255,
time: CLOSE_BUTTON_FADE_TIME,
transition: 'easeOutQuad' });
},
chromeHeights: function () {
return [Math.max(this.borderSize, this.closeButton.height - this.closeButton._overlap),
this.title.height + this.title._spacing];
@ -379,6 +372,7 @@ const WindowOverlay = new Lang.Class({
let border = this.border;
Tweener.removeTweens(button);
Tweener.removeTweens(title);
Tweener.removeTweens(border);
let [cloneX, cloneY, cloneWidth, cloneHeight] = this._windowClone.slot;
@ -473,7 +467,7 @@ const WindowOverlay = new Lang.Class({
Mainloop.idle_add(Lang.bind(this,
function() {
this._windowClone.emit('selected');
return GLib.SOURCE_REMOVE;
return false;
}));
}
},
@ -537,17 +531,15 @@ const WindowOverlay = new Lang.Class({
// as the close button will be shown as needed when the overlays
// are shown again
if (this._hidden)
return Clutter.EVENT_PROPAGATE;
return;
this._animateVisible();
this.emit('show-close-button');
return Clutter.EVENT_PROPAGATE;
},
_onLeave: function() {
if (this._idleToggleCloseId == 0)
this._idleToggleCloseId = Mainloop.timeout_add(750, Lang.bind(this, this._idleToggleCloseButton));
return Clutter.EVENT_PROPAGATE;
},
_idleToggleCloseButton: function() {
@ -557,7 +549,7 @@ const WindowOverlay = new Lang.Class({
!this.closeButton.has_pointer)
this._animateInvisible();
return GLib.SOURCE_REMOVE;
return false;
},
hideCloseButton: function() {
@ -914,19 +906,6 @@ function padArea(area, padding) {
};
}
function rectEqual(one, two) {
if (one == two)
return true;
if (!one || !two)
return false;
return (one.x == two.x &&
one.y == two.y &&
one.width == two.width &&
one.height == two.height);
}
/**
* @metaWorkspace: a #Meta.Workspace, or null
*/
@ -1002,17 +981,11 @@ const Workspace = new Lang.Class({
},
setFullGeometry: function(geom) {
if (rectEqual(this._fullGeometry, geom))
return;
this._fullGeometry = geom;
this._recalculateWindowPositions(WindowPositionFlags.NONE);
},
setActualGeometry: function(geom) {
if (rectEqual(this._actualGeometry, geom))
return;
this._actualGeometry = geom;
if (this._actualGeometryLater)
@ -1169,7 +1142,7 @@ const Workspace = new Lang.Class({
clone.actor.set_scale(scale, scale);
clone.actor.set_opacity(255);
clone.overlay.relayout(false);
this._showWindowOverlay(clone, overlay);
this._showWindowOverlay(clone, overlay, isOnCurrentWorkspace);
}
}
},
@ -1199,19 +1172,23 @@ const Workspace = new Lang.Class({
time: Overview.ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() {
this._showWindowOverlay(clone, overlay);
this._showWindowOverlay(clone, overlay, true);
})
});
clone.overlay.relayout(true);
},
_showWindowOverlay: function(clone, overlay) {
_showWindowOverlay: function(clone, overlay, fade) {
if (clone.inDrag)
return;
if (overlay && overlay._hidden)
if (overlay) {
if (fade)
overlay.fadeIn();
else
overlay.show();
}
},
_delayedWindowRepositioning: function() {
@ -1225,18 +1202,17 @@ const Workspace = new Lang.Class({
// store current cursor position
this._cursorX = x;
this._cursorY = y;
return GLib.SOURCE_CONTINUE;
return true;
}
let actorUnderPointer = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y);
for (let i = 0; i < this._windows.length; i++) {
if (this._windows[i].actor == actorUnderPointer)
return GLib.SOURCE_CONTINUE;
return true;
}
this._recalculateWindowPositions(WindowPositionFlags.ANIMATE);
this._repositionWindowsId = 0;
return GLib.SOURCE_REMOVE;
return false;
},
_doRemoveWindow : function(metaWin) {
@ -1311,7 +1287,7 @@ const Workspace = new Lang.Class({
metaWin.get_compositor_private() &&
metaWin.get_workspace() == this.metaWorkspace)
this._doAddWindow(metaWin);
return GLib.SOURCE_REMOVE;
return false;
}));
return;
}
@ -1491,17 +1467,17 @@ const Workspace = new Lang.Class({
clone.connect('selected',
Lang.bind(this, this._onCloneSelected));
clone.connect('drag-begin',
Lang.bind(this, function() {
Main.overview.beginWindowDrag(clone);
Lang.bind(this, function(clone) {
Main.overview.beginWindowDrag();
overlay.hide();
}));
clone.connect('drag-cancelled',
Lang.bind(this, function() {
Main.overview.cancelledWindowDrag(clone);
Lang.bind(this, function(clone) {
Main.overview.cancelledWindowDrag();
}));
clone.connect('drag-end',
Lang.bind(this, function() {
Main.overview.endWindowDrag(clone);
Lang.bind(this, function(clone) {
Main.overview.endWindowDrag();
overlay.show();
}));
clone.connect('size-changed',
@ -1663,7 +1639,9 @@ const Workspace = new Lang.Class({
metaWindow.move_to_monitor(this.monitorIndex);
let index = this.metaWorkspace ? this.metaWorkspace.index() : global.screen.get_active_workspace_index();
metaWindow.change_workspace_by_index(index, false);
metaWindow.change_workspace_by_index(index,
false, // don't create workspace
time);
return true;
} else if (source.shellWorkspaceLaunch) {
source.shellWorkspaceLaunch({ workspace: this.metaWorkspace ? this.metaWorkspace.index() : -1,

View File

@ -1,7 +1,6 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
@ -157,7 +156,6 @@ const WorkspaceSwitcherPopup = new Lang.Class({
onComplete: function() { this.destroy(); },
onCompleteScope: this
});
return GLib.SOURCE_REMOVE;
},
destroy: function() {

View File

@ -2,7 +2,6 @@
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
@ -131,7 +130,7 @@ const WindowClone = new Lang.Class({
_onButtonRelease : function (actor, event) {
this.emit('selected', event.get_time());
return Clutter.EVENT_STOP;
return true;
},
_onDragBegin : function (draggable, time) {
@ -326,7 +325,7 @@ const WorkspaceThumbnail = new Lang.Class({
metaWin.get_compositor_private() &&
metaWin.get_workspace() == this.metaWorkspace)
this._doAddWindow(metaWin);
return GLib.SOURCE_REMOVE;
return false;
}));
return;
}
@ -439,16 +438,16 @@ const WorkspaceThumbnail = new Lang.Class({
this.activate(time);
}));
clone.connect('drag-begin',
Lang.bind(this, function() {
Main.overview.beginWindowDrag(clone);
Lang.bind(this, function(clone) {
Main.overview.beginWindowDrag();
}));
clone.connect('drag-cancelled',
Lang.bind(this, function() {
Main.overview.cancelledWindowDrag(clone);
Lang.bind(this, function(clone) {
Main.overview.cancelledWindowDrag();
}));
clone.connect('drag-end',
Lang.bind(this, function() {
Main.overview.endWindowDrag(clone);
Lang.bind(this, function(clone) {
Main.overview.endWindowDrag();
}));
this._contents.add_actor(clone.actor);
@ -508,7 +507,9 @@ const WorkspaceThumbnail = new Lang.Class({
if (metaWindow.get_monitor() != this.monitorIndex)
metaWindow.move_to_monitor(this.monitorIndex);
metaWindow.change_workspace_by_index(this.metaWorkspace.index(), false);
metaWindow.change_workspace_by_index(this.metaWorkspace.index(),
false, // don't create workspace
time);
return true;
} else if (source.shellWorkspaceLaunch) {
source.shellWorkspaceLaunch({ workspace: this.metaWorkspace ? this.metaWorkspace.index() : -1,
@ -535,6 +536,20 @@ const ThumbnailsBox = new Lang.Class({
this.actor.connect('allocate', Lang.bind(this, this._allocate));
this.actor._delegate = this;
// When we animate the scale, we don't animate the requested size of the thumbnails, rather
// we ask for our final size and then animate within that size. This slightly simplifies the
// interaction with the main workspace windows (instead of constantly reallocating them
// to a new size, they get a new size once, then use the standard window animation code
// allocate the windows to their new positions), however it causes problems for drawing
// the background and border wrapped around the thumbnail as we animate - we can't just pack
// the container into a box and set style properties on the box since that box would wrap
// around the final size not the animating size. So instead we fake the background with
// an actor underneath the content and adjust the allocation of our children to leave space
// for the border and padding of the background actor.
this._background = new St.Bin({ style_class: 'workspace-thumbnails-background' });
this.actor.add_actor(this._background);
let indicator = new St.Bin({ style_class: 'workspace-thumbnail-indicator' });
// We don't want the indicator to affect drag-and-drop
@ -562,7 +577,7 @@ const ThumbnailsBox = new Lang.Class({
this._thumbnails = [];
this.actor.connect('button-press-event', function() { return Clutter.EVENT_STOP; });
this.actor.connect('button-press-event', function() { return true; });
this.actor.connect('button-release-event', Lang.bind(this, this._onButtonRelease));
Main.overview.connect('showing',
@ -607,7 +622,7 @@ const ThumbnailsBox = new Lang.Class({
}
}
return Clutter.EVENT_STOP;
return true;
},
_onDragBegin: function() {
@ -738,12 +753,14 @@ const ThumbnailsBox = new Lang.Class({
// ... move them down one.
windows.forEach(function(win) {
win.meta_window.change_workspace_by_index(win.get_workspace() + 1, true);
win.meta_window.change_workspace_by_index(win.get_workspace() + 1,
true, time);
});
if (isWindow)
// ... and bam, a workspace, good as new.
source.metaWindow.change_workspace_by_index(newWorkspaceIndex, true);
source.metaWindow.change_workspace_by_index(newWorkspaceIndex,
true, time);
else if (source.shellWorkspaceLaunch) {
source.shellWorkspaceLaunch({ workspace: newWorkspaceIndex,
timestamp: time });
@ -1025,6 +1042,9 @@ const ThumbnailsBox = new Lang.Class({
},
_getPreferredHeight: function(actor, forWidth, alloc) {
// See comment about this._background in _init()
let themeNode = this._background.get_theme_node();
// Note that for getPreferredWidth/Height we cheat a bit and skip propagating
// the size request to our children because we know how big they are and know
// that the actors aren't depending on the virtual functions being called.
@ -1032,21 +1052,24 @@ const ThumbnailsBox = new Lang.Class({
if (this._thumbnails.length == 0)
return;
let themeNode = this.actor.get_theme_node();
let spacing = themeNode.get_length('spacing');
let spacing = this.actor.get_theme_node().get_length('spacing');
let nWorkspaces = global.screen.n_workspaces;
let totalSpacing = (nWorkspaces - 1) * spacing;
alloc.min_size = totalSpacing;
alloc.natural_size = totalSpacing + nWorkspaces * this._porthole.height * MAX_THUMBNAIL_SCALE;
[alloc.min_size, alloc.natural_size] =
themeNode.adjust_preferred_height(totalSpacing,
totalSpacing + nWorkspaces * this._porthole.height * MAX_THUMBNAIL_SCALE);
},
_getPreferredWidth: function(actor, forHeight, alloc) {
// See comment about this._background in _init()
let themeNode = this._background.get_theme_node();
if (this._thumbnails.length == 0)
return;
let themeNode = this.actor.get_theme_node();
// We don't animate our preferred width, which is always reported according
// to the actual number of current workspaces, we just animate within that
let spacing = this.actor.get_theme_node().get_length('spacing');
let nWorkspaces = global.screen.n_workspaces;
@ -1058,26 +1081,28 @@ const ThumbnailsBox = new Lang.Class({
scale = Math.min(scale, MAX_THUMBNAIL_SCALE);
let width = Math.round(this._porthole.width * scale);
alloc.min_size = width;
alloc.natural_size = width;
[alloc.min_size, alloc.natural_size] =
themeNode.adjust_preferred_width(width, width);
},
_allocate: function(actor, box, flags) {
let rtl = (Clutter.get_default_text_direction () == Clutter.TextDirection.RTL);
// See comment about this._background in _init()
let themeNode = this._background.get_theme_node();
let contentBox = themeNode.get_content_box(box);
if (this._thumbnails.length == 0) // not visible
return;
let themeNode = this.actor.get_theme_node();
let portholeWidth = this._porthole.width;
let portholeHeight = this._porthole.height;
let spacing = themeNode.get_length('spacing');
let spacing = this.actor.get_theme_node().get_length('spacing');
// Compute the scale we'll need once everything is updated
let nWorkspaces = global.screen.n_workspaces;
let totalSpacing = (nWorkspaces - 1) * spacing;
let avail = (box.y2 - box.y1) - totalSpacing;
let avail = (contentBox.y2 - contentBox.y1) - totalSpacing;
let newScale = (avail / nWorkspaces) / portholeHeight;
newScale = Math.min(newScale, MAX_THUMBNAIL_SCALE);
@ -1106,6 +1131,21 @@ const ThumbnailsBox = new Lang.Class({
else
slideOffset = thumbnailWidth + themeNode.get_padding(St.Side.RIGHT);
let childBox = new Clutter.ActorBox();
// The background is horizontally restricted to correspond to the current thumbnail size
// but otherwise covers the entire allocation
if (rtl) {
childBox.x1 = box.x1;
childBox.x2 = box.x2 - ((contentBox.x2 - contentBox.x1) - thumbnailWidth);
} else {
childBox.x1 = box.x1 + ((contentBox.x2 - contentBox.x1) - thumbnailWidth);
childBox.x2 = box.x2;
}
childBox.y1 = box.y1;
childBox.y2 = box.y2;
this._background.allocate(childBox, flags);
let indicatorY1 = this._indicatorY;
let indicatorY2;
// when not animating, the workspace position overrides this._indicatorY
@ -1117,7 +1157,7 @@ const ThumbnailsBox = new Lang.Class({
let indicatorLeftFullBorder = indicatorThemeNode.get_padding(St.Side.LEFT) + indicatorThemeNode.get_border_width(St.Side.LEFT);
let indicatorRightFullBorder = indicatorThemeNode.get_padding(St.Side.RIGHT) + indicatorThemeNode.get_border_width(St.Side.RIGHT);
let y = box.y1;
let y = contentBox.y1;
if (this._dropPlaceholderPos == -1) {
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
@ -1125,8 +1165,6 @@ const ThumbnailsBox = new Lang.Class({
}));
}
let childBox = new Clutter.ActorBox();
for (let i = 0; i < this._thumbnails.length; i++) {
let thumbnail = this._thumbnails[i];
@ -1135,10 +1173,10 @@ const ThumbnailsBox = new Lang.Class({
let x1, x2;
if (rtl) {
x1 = box.x1 + slideOffset * thumbnail.slidePosition;
x1 = contentBox.x1 + slideOffset * thumbnail.slidePosition;
x2 = x1 + thumbnailWidth;
} else {
x1 = box.x2 - thumbnailWidth + slideOffset * thumbnail.slidePosition;
x1 = contentBox.x2 - thumbnailWidth + slideOffset * thumbnail.slidePosition;
x2 = x1 + thumbnailWidth;
}
@ -1185,11 +1223,11 @@ const ThumbnailsBox = new Lang.Class({
}
if (rtl) {
childBox.x1 = box.x1;
childBox.x2 = box.x1 + thumbnailWidth;
childBox.x1 = contentBox.x1;
childBox.x2 = contentBox.x1 + thumbnailWidth;
} else {
childBox.x1 = box.x2 - thumbnailWidth;
childBox.x2 = box.x2;
childBox.x1 = contentBox.x2 - thumbnailWidth;
childBox.x2 = contentBox.x2;
}
childBox.x1 -= indicatorLeftFullBorder;
childBox.x2 += indicatorRightFullBorder;

View File

@ -23,93 +23,75 @@ const MAX_WORKSPACES = 16;
const OVERRIDE_SCHEMA = 'org.gnome.shell.overrides';
const WorkspacesViewBase = new Lang.Class({
Name: 'WorkspacesViewBase',
function rectEqual(one, two) {
if (one == two)
return true;
_init: function(monitorIndex) {
if (!one || !two)
return false;
return (one.x == two.x &&
one.y == two.y &&
one.width == two.width &&
one.height == two.height);
}
const WorkspacesView = new Lang.Class({
Name: 'WorkspacesView',
_init: function(workspaces) {
this.actor = new St.Widget({ style_class: 'workspaces-view',
reactive: true });
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
global.focus_manager.add_group(this.actor);
// 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._monitorIndex = monitorIndex;
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
this.actor.connect('style-changed', Lang.bind(this,
function() {
let node = this.actor.get_theme_node();
this._spacing = node.get_length('spacing');
this._updateWorkspaceActors(false);
}));
this._fullGeometry = null;
this._actualGeometry = null;
this._inDrag = false;
this._windowDragBeginId = Main.overview.connect('window-drag-begin', Lang.bind(this, this._dragBegin));
this._windowDragEndId = Main.overview.connect('window-drag-end', Lang.bind(this, this._dragEnd));
},
_onDestroy: function() {
this._dragEnd();
if (this._windowDragBeginId > 0) {
Main.overview.disconnect(this._windowDragBeginId);
this._windowDragBeginId = 0;
}
if (this._windowDragEndId > 0) {
Main.overview.disconnect(this._windowDragEndId);
this._windowDragEndId = 0;
}
},
_dragBegin: function(overview, clone) {
this._inDrag = true;
this._setReservedSlot(clone);
},
_dragEnd: function() {
this._inDrag = false;
this._setReservedSlot(null);
},
destroy: function() {
this.actor.destroy();
},
setFullGeometry: function(geom) {
this._fullGeometry = geom;
this._syncGeometry();
},
setActualGeometry: function(geom) {
this._actualGeometry = geom;
this._syncGeometry();
},
});
const WorkspacesView = new Lang.Class({
Name: 'WorkspacesView',
Extends: WorkspacesViewBase,
_init: function(monitorIndex) {
this.parent(monitorIndex);
this._spacing = 0;
this._animating = false; // tweening
this._scrolling = false; // swipe-scrolling
this._animatingScroll = false; // programatically updating the adjustment
this._inDrag = false; // dragging a window
this._settings = new Gio.Settings({ schema: OVERRIDE_SCHEMA });
this._updateExtraWorkspacesId =
this._settings.connect('changed::workspaces-only-on-primary',
Lang.bind(this, this._updateExtraWorkspaces));
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
this.scrollAdjustment = new St.Adjustment({ value: activeWorkspaceIndex,
lower: 0,
page_increment: 1,
page_size: 1,
step_increment: 0,
upper: 0 });
this.scrollAdjustment.connect('notify::value',
Lang.bind(this, this._onScroll));
this._workspaces = workspaces;
this._workspaces = [];
this._updateWorkspaces();
this._updateWorkspacesId = global.screen.connect('notify::n-workspaces', Lang.bind(this, this._updateWorkspaces));
// Add workspace actors
for (let w = 0; w < global.screen.n_workspaces; w++)
this.actor.add_actor(this._workspaces[w].actor);
this._workspaces[activeWorkspaceIndex].actor.raise_top();
this._extraWorkspaces = [];
this._updateExtraWorkspaces();
// Position/scale the desktop windows and their children after the
// workspaces have been created. This cannot be done first because
// window movement depends on the Workspaces object being accessible
// as an Overview member.
this._overviewShowingId =
Main.overview.connect('showing',
Lang.bind(this, function() {
for (let w = 0; w < this._workspaces.length; w++)
this._workspaces[w].zoomToOverview();
for (let w = 0; w < this._extraWorkspaces.length; w++)
this._extraWorkspaces[w].zoomToOverview();
}));
this._overviewShownId =
Main.overview.connect('shown',
Lang.bind(this, function() {
@ -117,21 +99,72 @@ const WorkspacesView = new Lang.Class({
this._fullGeometry.width, this._fullGeometry.height);
}));
this.scrollAdjustment = new St.Adjustment({ value: activeWorkspaceIndex,
lower: 0,
page_increment: 1,
page_size: 1,
step_increment: 0,
upper: this._workspaces.length });
this.scrollAdjustment.connect('notify::value',
Lang.bind(this, this._onScroll));
this._switchWorkspaceNotifyId =
global.window_manager.connect('switch-workspace',
Lang.bind(this, this._activeWorkspaceChanged));
this._itemDragBeginId = Main.overview.connect('item-drag-begin',
Lang.bind(this, this._dragBegin));
this._itemDragEndId = Main.overview.connect('item-drag-end',
Lang.bind(this, this._dragEnd));
this._windowDragBeginId = Main.overview.connect('window-drag-begin',
Lang.bind(this, this._dragBegin));
this._windowDragEndId = Main.overview.connect('window-drag-end',
Lang.bind(this, this._dragEnd));
},
_setReservedSlot: function(clone) {
for (let i = 0; i < this._workspaces.length; i++)
this._workspaces[i].setReservedSlot(clone);
_updateExtraWorkspaces: function() {
this._destroyExtraWorkspaces();
if (!this._settings.get_boolean('workspaces-only-on-primary'))
return;
let monitors = Main.layoutManager.monitors;
for (let i = 0; i < monitors.length; i++) {
if (i == Main.layoutManager.primaryIndex)
continue;
let ws = new Workspace.Workspace(null, i);
ws.setFullGeometry(monitors[i]);
ws.setActualGeometry(monitors[i]);
Main.layoutManager.overviewGroup.add_actor(ws.actor);
this._extraWorkspaces.push(ws);
}
},
_syncGeometry: function() {
_destroyExtraWorkspaces: function() {
for (let m = 0; m < this._extraWorkspaces.length; m++)
this._extraWorkspaces[m].destroy();
this._extraWorkspaces = [];
},
setFullGeometry: function(geom) {
if (rectEqual(this._fullGeometry, geom))
return;
this._fullGeometry = geom;
for (let i = 0; i < this._workspaces.length; i++)
this._workspaces[i].setFullGeometry(this._fullGeometry);
this._workspaces[i].setFullGeometry(geom);
},
setActualGeometry: function(geom) {
if (rectEqual(this._actualGeometry, geom))
return;
this._actualGeometry = geom;
for (let i = 0; i < this._workspaces.length; i++)
this._workspaces[i].setActualGeometry(this._actualGeometry);
this._workspaces[i].setActualGeometry(geom);
},
getActiveWorkspace: function() {
@ -139,22 +172,29 @@ const WorkspacesView = new Lang.Class({
return this._workspaces[active];
},
zoomToOverview: function() {
for (let w = 0; w < this._workspaces.length; w++)
this._workspaces[w].zoomToOverview();
this._updateWorkspaceActors(false);
},
hide: function() {
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
let activeWorkspace = this._workspaces[activeWorkspaceIndex];
activeWorkspace.actor.raise_top();
zoomFromOverview: function() {
this.actor.remove_clip();
for (let w = 0; w < this._workspaces.length; w++)
this._workspaces[w].zoomFromOverview();
for (let w = 0; w < this._extraWorkspaces.length; w++)
this._extraWorkspaces[w].zoomFromOverview();
},
destroy: function() {
this.actor.destroy();
},
syncStacking: function(stackIndices) {
for (let i = 0; i < this._workspaces.length; i++)
this._workspaces[i].syncStacking(stackIndices);
for (let i = 0; i < this._extraWorkspaces.length; i++)
this._extraWorkspaces[i].syncStacking(stackIndices);
},
_scrollToActive: function() {
@ -176,7 +216,7 @@ const WorkspacesView = new Lang.Class({
Tweener.removeTweens(workspace.actor);
let y = (w - active) * this._fullGeometry.height;
let y = (w - active) * (this._fullGeometry.height + this._spacing);
if (showAnimation) {
let params = { y: y,
@ -236,31 +276,25 @@ const WorkspacesView = new Lang.Class({
});
},
_updateWorkspaces: function() {
let oldNumWorkspaces = this._workspaces.length;
let newNumWorkspaces = global.screen.n_workspaces;
updateWorkspaces: function(oldNumWorkspaces, newNumWorkspaces) {
let active = global.screen.get_active_workspace_index();
this.scrollAdjustment.upper = newNumWorkspaces;
Tweener.addTween(this.scrollAdjustment,
{ upper: newNumWorkspaces,
time: WORKSPACE_SWITCH_TIME,
transition: 'easeOutQuad'
});
if (newNumWorkspaces > oldNumWorkspaces) {
for (let w = oldNumWorkspaces; w < newNumWorkspaces; w++) {
let metaWorkspace = global.screen.get_workspace_by_index(w);
let workspace = new Workspace.Workspace(metaWorkspace, this._monitorIndex);
this._workspaces.push(workspace);
this.actor.add_actor(workspace.actor);
this._workspaces[w].setFullGeometry(this._fullGeometry);
if (this._actualGeometry)
this._workspaces[w].setActualGeometry(this._actualGeometry);
this.actor.add_actor(this._workspaces[w].actor);
}
if (this._fullGeometry)
this._updateWorkspaceActors(false);
} else if (newNumWorkspaces < oldNumWorkspaces) {
let nRemoved = (newNumWorkspaces - oldNumWorkspaces);
let removed = this._workspaces.splice(oldNumWorkspaces, nRemoved);
removed.forEach(function(workspace) {
workspace.destroy();
});
this._updateWorkspaceActors(false);
}
this._syncGeometry();
},
_activeWorkspaceChanged: function(wm, from, to, direction) {
@ -271,12 +305,70 @@ const WorkspacesView = new Lang.Class({
},
_onDestroy: function() {
this.parent();
this._destroyExtraWorkspaces();
this.scrollAdjustment.run_dispose();
Main.overview.disconnect(this._overviewShowingId);
Main.overview.disconnect(this._overviewShownId);
global.window_manager.disconnect(this._switchWorkspaceNotifyId);
global.screen.disconnect(this._updateWorkspacesId);
this._settings.disconnect(this._updateExtraWorkspacesId);
if (this._inDrag)
this._dragEnd();
if (this._itemDragBeginId > 0) {
Main.overview.disconnect(this._itemDragBeginId);
this._itemDragBeginId = 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._windowDragEndId > 0) {
Main.overview.disconnect(this._windowDragEndId);
this._windowDragEndId = 0;
}
},
_dragBegin: function() {
if (this._scrolling)
return;
this._inDrag = true;
this._firstDragMotion = true;
this._dragMonitor = {
dragMotion: Lang.bind(this, this._onDragMotion)
};
DND.addDragMonitor(this._dragMonitor);
},
_onDragMotion: function(dragEvent) {
if (Main.overview.animationInProgress)
return DND.DragMotionResult.CONTINUE;
if (this._firstDragMotion) {
this._firstDragMotion = false;
for (let i = 0; i < this._workspaces.length; i++)
this._workspaces[i].setReservedSlot(dragEvent.dragActor._delegate);
for (let i = 0; i < this._extraWorkspaces.length; i++)
this._extraWorkspaces[i].setReservedSlot(dragEvent.dragActor._delegate);
}
return DND.DragMotionResult.CONTINUE;
},
_dragEnd: function() {
DND.removeDragMonitor(this._dragMonitor);
this._inDrag = false;
for (let i = 0; i < this._workspaces.length; i++)
this._workspaces[i].setReservedSlot(null);
for (let i = 0; i < this._extraWorkspaces.length; i++)
this._extraWorkspaces[i].setReservedSlot(null);
},
startSwipeScroll: function() {
@ -335,58 +427,12 @@ const WorkspacesView = new Lang.Class({
});
Signals.addSignalMethods(WorkspacesView.prototype);
const ExtraWorkspaceView = new Lang.Class({
Name: 'ExtraWorkspaceView',
Extends: WorkspacesViewBase,
_init: function(monitorIndex) {
this.parent(monitorIndex);
this._workspace = new Workspace.Workspace(null, monitorIndex);
this.actor.add_actor(this._workspace.actor);
},
_setReservedSlot: function(clone) {
this._workspace.setReservedSlot(clone);
},
_syncGeometry: function() {
this._workspace.setFullGeometry(this._fullGeometry);
this._workspace.setActualGeometry(this._actualGeometry);
},
zoomToOverview: function() {
this._workspace.zoomToOverview();
},
zoomFromOverview: function() {
this._workspace.zoomFromOverview();
},
syncStacking: function(stackIndices) {
this._workspace.syncStacking(stackIndices);
},
startSwipeScroll: function() {
},
endSwipeScroll: function() {
},
});
const DelegateFocusNavigator = new Lang.Class({
Name: 'DelegateFocusNavigator',
Extends: St.Widget,
vfunc_navigate_focus: function(from, direction) {
return this._delegate.navigateFocus(from, direction);
},
});
const WorkspacesDisplay = new Lang.Class({
Name: 'WorkspacesDisplay',
_init: function() {
this.actor = new DelegateFocusNavigator({ clip_to_allocation: true });
this.actor._delegate = this;
this.actor = new St.Widget({ clip_to_allocation: true });
this.actor.connect('notify::allocation', Lang.bind(this, this._updateWorkspacesActualGeometry));
this.actor.connect('parent-set', Lang.bind(this, this._parentSet));
@ -425,6 +471,7 @@ const WorkspacesDisplay = new Lang.Class({
this._primaryIndex = Main.layoutManager.primaryIndex;
this._workspacesViews = [];
this._workspaces = [];
this._primaryScrollAdjustment = null;
this._settings = new Gio.Settings({ schema: OVERRIDE_SCHEMA });
@ -433,6 +480,9 @@ const WorkspacesDisplay = new Lang.Class({
this._workspacesOnlyOnPrimaryChanged));
this._workspacesOnlyOnPrimaryChanged();
global.screen.connect('notify::n-workspaces',
Lang.bind(this, this._workspacesChanged));
this._switchWorkspaceNotifyId = 0;
this._notifyOpacityId = 0;
@ -448,14 +498,8 @@ const WorkspacesDisplay = new Lang.Class({
return false;
},
navigateFocus: function(from, direction) {
return this._getPrimaryView().actor.navigate_focus(from, direction, false);
},
show: function() {
this._updateWorkspacesViews();
for (let i = 0; i < this._workspacesViews.length; i++)
this._workspacesViews[i].zoomToOverview();
this._restackedNotifyId =
Main.overview.connect('windows-restacked',
@ -465,8 +509,9 @@ const WorkspacesDisplay = new Lang.Class({
},
zoomFromOverview: function() {
for (let i = 0; i < this._workspacesViews.length; i++)
this._workspacesViews[i].zoomFromOverview();
for (let i = 0; i < this._workspacesViews.length; i++) {
this._workspacesViews[i].hide();
}
},
hide: function() {
@ -498,38 +543,46 @@ const WorkspacesDisplay = new Lang.Class({
this._workspacesViews[i].destroy();
this._workspacesViews = [];
this._workspaces = [];
let monitors = Main.layoutManager.monitors;
for (let i = 0; i < monitors.length; i++) {
let view;
if (this._workspacesOnlyOnPrimary && i != this._primaryIndex)
view = new ExtraWorkspaceView(i);
else
view = new WorkspacesView(i);
continue; // we are only interested in the primary monitor
let monitorWorkspaces = [];
for (let w = 0; w < global.screen.n_workspaces; w++) {
let metaWorkspace = global.screen.get_workspace_by_index(w);
monitorWorkspaces.push(new Workspace.Workspace(metaWorkspace, i));
}
this._workspaces.push(monitorWorkspaces);
let view = new WorkspacesView(monitorWorkspaces);
view.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
if (i == this._primaryIndex) {
if (this._workspacesOnlyOnPrimary || i == this._primaryIndex) {
this._scrollAdjustment = view.scrollAdjustment;
this._scrollAdjustment.connect('notify::value',
Lang.bind(this, this._scrollValueChanged));
}
this._workspacesViews.push(view);
Main.layoutManager.overviewGroup.add_actor(view.actor);
}
this._updateWorkspacesFullGeometry();
this._updateWorkspacesActualGeometry();
for (let i = 0; i < this._workspacesViews.length; i++)
Main.layoutManager.overviewGroup.add_actor(this._workspacesViews[i].actor);
},
_scrollValueChanged: function() {
if (this._workspacesOnlyOnPrimary)
return;
for (let i = 0; i < this._workspacesViews.length; i++) {
if (i == this._primaryIndex)
continue;
let adjustment = this._workspacesViews[i].scrollAdjustment;
if (!adjustment)
continue;
// the adjustments work in terms of workspaces, so the
// values map directly
adjustment.value = this._scrollAdjustment.value;
@ -539,7 +592,10 @@ const WorkspacesDisplay = new Lang.Class({
_getPrimaryView: function() {
if (!this._workspacesViews.length)
return null;
return this._workspacesViews[this._primaryIndex];
if (this._workspacesOnlyOnPrimary)
return this._workspacesViews[0];
else
return this._workspacesViews[this._primaryIndex];
},
activeWorkspaceHasMaximizedWindows: function() {
@ -585,9 +641,15 @@ const WorkspacesDisplay = new Lang.Class({
return;
let monitors = Main.layoutManager.monitors;
let m = 0;
for (let i = 0; i < monitors.length; i++) {
let geometry = (i == this._primaryIndex) ? this._fullGeometry : monitors[i];
this._workspacesViews[i].setFullGeometry(geometry);
if (i == this._primaryIndex) {
this._workspacesViews[m].setFullGeometry(this._fullGeometry);
m++;
} else if (!this._workspacesOnlyOnPrimary) {
this._workspacesViews[m].setFullGeometry(monitors[i]);
m++;
}
}
},
@ -598,12 +660,18 @@ const WorkspacesDisplay = new Lang.Class({
let [x, y] = this.actor.get_transformed_position();
let width = this.actor.allocation.x2 - this.actor.allocation.x1;
let height = this.actor.allocation.y2 - this.actor.allocation.y1;
let primaryGeometry = { x: x, y: y, width: width, height: height };
let geometry = { x: x, y: y, width: width, height: height };
let monitors = Main.layoutManager.monitors;
let m = 0;
for (let i = 0; i < monitors.length; i++) {
let geometry = (i == this._primaryIndex) ? primaryGeometry : monitors[i];
this._workspacesViews[i].setActualGeometry(geometry);
if (i == this._primaryIndex) {
this._workspacesViews[m].setActualGeometry(geometry);
m++;
} else if (!this._workspacesOnlyOnPrimary) {
this._workspacesViews[m].setActualGeometry(monitors[i]);
m++;
}
}
},
@ -612,9 +680,63 @@ const WorkspacesDisplay = new Lang.Class({
this._workspacesViews[i].syncStacking(stackIndices);
},
_workspacesChanged: function() {
if (!this._workspacesViews.length)
return;
let oldNumWorkspaces = this._workspaces[0].length;
let newNumWorkspaces = global.screen.n_workspaces;
let active = global.screen.get_active_workspace_index();
let lostWorkspaces = [];
if (newNumWorkspaces > oldNumWorkspaces) {
let monitors = Main.layoutManager.monitors;
let m = 0;
for (let i = 0; i < monitors.length; i++) {
if (this._workspacesOnlyOnPrimary &&
i != this._primaryIndex)
continue;
// Assume workspaces are only added at the end
for (let w = oldNumWorkspaces; w < newNumWorkspaces; w++) {
let metaWorkspace = global.screen.get_workspace_by_index(w);
this._workspaces[m][w] =
new Workspace.Workspace(metaWorkspace, i);
}
m++;
}
} else {
// Assume workspaces are only removed sequentially
// (e.g. 2,3,4 - not 2,4,7)
let removedIndex;
let removedNum = oldNumWorkspaces - newNumWorkspaces;
for (let w = 0; w < oldNumWorkspaces; w++) {
let metaWorkspace = global.screen.get_workspace_by_index(w);
if (this._workspaces[0][w].metaWorkspace != metaWorkspace) {
removedIndex = w;
break;
}
}
for (let i = 0; i < this._workspaces.length; i++) {
lostWorkspaces = this._workspaces[i].splice(removedIndex,
removedNum);
for (let l = 0; l < lostWorkspaces.length; l++) {
lostWorkspaces[l].disconnectAll();
lostWorkspaces[l].destroy();
}
}
}
for (let i = 0; i < this._workspacesViews.length; i++)
this._workspacesViews[i].updateWorkspaces(oldNumWorkspaces,
newNumWorkspaces);
},
_onScrollEvent: function(actor, event) {
if (!this.actor.mapped)
return Clutter.EVENT_PROPAGATE;
return false;
let activeWs = global.screen.get_active_workspace();
let ws;
switch (event.get_scroll_direction()) {
@ -625,10 +747,10 @@ const WorkspacesDisplay = new Lang.Class({
ws = activeWs.get_neighbor(Meta.MotionDirection.DOWN);
break;
default:
return Clutter.EVENT_PROPAGATE;
return false;
}
Main.wm.actionMoveWorkspace(ws);
return Clutter.EVENT_STOP;
return true;
}
});
Signals.addSignalMethods(WorkspacesDisplay.prototype);

View File

@ -62,7 +62,7 @@ const XdndHandler = new Lang.Class({
let cursorWindow = windows[windows.length - 1];
// FIXME: more reliable way?
if (!cursorWindow.get_meta_window().is_override_redirect())
if (!cursorWindow.is_override_redirect())
return;
let constraint_position = new Clutter.BindConstraint({ coordinate : Clutter.BindCoordinate.POSITION,

View File

@ -38,7 +38,7 @@ js/ui/panel.js
js/ui/popupMenu.js
js/ui/runDialog.js
js/ui/screenShield.js
js/ui/search.js
js/ui/searchDisplay.js
js/ui/shellEntry.js
js/ui/shellMountOperation.js
js/ui/status/accessibility.js
@ -52,6 +52,7 @@ js/ui/status/system.js
js/ui/status/volume.js
js/ui/unlockDialog.js
js/ui/viewSelector.js
js/ui/wanda.js
js/ui/windowAttentionHandler.js
js/ui/windowManager.js
src/calendar-server/evolution-calendar.desktop.in.in

907
po/ar.po

File diff suppressed because it is too large Load Diff

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