Compare commits

..

2 Commits

Author SHA1 Message Date
David Zeuthen
a69e45b5eb goa: implement marking a message as Starred or Junk
This works with the latest GOA tree

Signed-off-by: David Zeuthen <davidz@redhat.com>
2011-05-19 14:52:52 -04:00
David Zeuthen
a355398b0f Initial goa client work
Signed-off-by: David Zeuthen <davidz@redhat.com>
2011-05-16 17:10:43 -04:00
176 changed files with 17039 additions and 31803 deletions

8
.gitignore vendored
View File

@@ -3,7 +3,6 @@
*.o
.deps
.libs
ABOUT-NLS
ChangeLog
INSTALL
Makefile
@@ -30,13 +29,8 @@ m4/
omf.make
po/*.gmo
po/gnome-shell.pot
po/*.header
po/*.sed
po/*.sin
po/Makefile.in.in
po/Makevars.template
po/POTFILES
po/Rules-quot
po/stamp-it
scripts/launcher.pyc
src/*.gir
@@ -49,11 +43,9 @@ src/calendar-server/org.gnome.Shell.CalendarServer.service
src/gnome-shell
src/gnome-shell-calendar-server
src/gnome-shell-extension-tool
src/gnome-shell-hotplug-sniffer
src/gnome-shell-jhbuild
src/gnome-shell-perf-helper
src/gnome-shell-real
src/hotplug-sniffer/org.gnome.Shell.HotplugSniffer.service
src/run-js-test
src/test-recorder
src/test-recorder.ogg

View File

@@ -3,5 +3,5 @@ E-mail: otaylor@redhat.com
Userid: otaylor
Colin Walters
E-mail: walters@verbum.org
E-mail: walters@redhat.com
Userid: walters

252
NEWS
View File

@@ -1,255 +1,3 @@
3.1.90.1
========
* Fix typo that was breaking the "Login Screen" mode [Marc-Antoine]
* Fix build with new gobject-introspection [Dan]
* Use a better icon for removable devices [Cosimo; #657757]
* Add support for asynchronous search provides [Philippe, Jasper, Seif; #655220]
* Misc bug fixes [Alex, Guillaume, Jasper; #657657, #657696]
* Misc build fixes [Adel; #657697]
Contributors:
Cosimo Cecchi, Guillaume Desmottes, Adel Gadllah, Alexander Larsson, Seif Lotfy,
Philippe Normand, Marc-Antoine Perennou, Jasper St. Pierre, Dan Winship
Translations:
Jorge González, Daniel Mustieles [es], Stas Solovey [ru]
3.1.90
======
* Add an on-screen keyboard that uses Caribou as a backend
[Nohemi, Dan; #612662]
* Allow searching for people in the overview using libfolks
as the backend [Morten; #643018]
* Add a "Login Screen" mode to be used when GDM is running; this
mode has a stripped down user interface, and also contains the
code to display the user list and authentication. [Ray; #657082]
* Rework user menu to separate out "Do Not Disturb" from the IM
status and to visually match GNOME Contacts. [Florian; #652837]
* Implement displaying images such as cover-art in notifications
[Neha, Marina; #621009]
* Support default actions for notifications [Florian; #655818]
* Networking
- Stop using nm-applet for dialogs; do them as proper system modal
dialogs in the shell code. [Giovanni; #650244]
- Fix handling of hidden access points [Giovanni; #646454]
* Telepathy integration
- Support subscription requests [Guillaume, Xavier; #653941]
- Notify on account connection errors [Alban, Jasper, Xavier; #654159]
- Allow approving file transfers [Guillaume; #653940]
- Improve styling of messages [Jasper; #640271]
* Extension system [Jasper; #654770]
- Support live enabling and disabling of extensions
- Add the ability to install extensions from HTTP
- Enhance D-Bus interface for controlling extensions
- Collect errors separately for each extension
* Add Main.panel.addToStatusArea for extension convenience
[Giovanni, Jasper, Marc-Antoine; #653205]
* Port to the new gnome-menus API. Clean up and speed up
application information loading [Colin; #648149, #656546]
* Use the accountsservice library rather than cut-and-pasted GDM code
[Florian; #650893]
* Add a D-Bus interface to take a screenshot; this will avoid various race
conditions with the current gnome-screenshot approach [Adel; #652952]
* Show numeric indicators to distinguish duplicate keyboard names
[Giovanni; #650128]
* Add GNOME Documents to the favorites list [Adel; #657520]
* Update the clock immediately on resume from suspend [Colin; #656403]
* Remove animation support from StAdjustment [Ray; #657082]
* Support configuration of calendar applications via gsettings
[Tassilo; #651190]
* Don't fade in alt-Tab - wait a bit and show it instantly [Rui; #652346]
* Darken workspace background on all workspaces [Rui; #656433]
* Improve detection of the starting day of the week [Florian; #649078]
* Add StButtonAccessible [Alejandro]
* Visual tweaks to match mockups
[Allan, Dan, Jasper, Marina; #640271, #655627, #655428, #656732]
* Misc bug fixes [Dan, Florian, Giovanni, Guillaume, Jasper, Jeremy, Rui;
#645708, #646761, #653119, #654398, #656125, #654707, #654898, #654638,
#656335, #657111]
* Code cleanups [Colin, Dan, Guillaume, Ray;
#652718, #654639, #648651, #655813, #657082]
* String tweaks [Jasper, Jeremy; #652984, #640271]
* Build fixes [Jasper, Nohemi; #644275, #655812]
Contributors:
Jeremy Bicha, Giovanni Campagna, Xavier Claessens, Alban Crequy,
Guillaume Desmottes, Allan Day, Neha Doijode, Nohemi Fernandez,
Tassilo Horn, Rui Matos, Morten Mjelva, Florian Müllner, Alejandro Piñeiro,
Jasper St. Pierre, Ray Strode, Colin Walters, Dan Winship,
Marina Zhurakhinskaya
Translations:
Ivaylo Valkov [bg], Mario Blättermann [de], Diego Escalante Urrelo,
Jorge González, Daniel Mustieles [es], Arash Mousavi [fa], Fran Dieguez [gl],
Yaron Shahrabani [he], Andika Triwidada, Wibiharto [id],
Aurimas Černius [lt], Umarzuki Bin Mochlis Moktar [ml], Kjartan Maraas [nb],
A S Alam [pa], Daniel Nylander [se], Ngô Chin, Nguyễn Thái Ngọc Duy [vi],
Aron Xu [zh_CN], Chao-Hsiung Liao [zh_HK, zh_TW]
3.1.4
=====
* Take over inserted media handling and autorun from gnome-session [Cosimo]
* Message Tray
- Display a count of unread notifications on icons
[Jasper, Guillaume; #649356, #654139]
- Only remove icons when the sender quits from D-Bus, not when it
closes its last window [Neha, Marina; #645764]
- Solve problems switching chats between shell and Empathy
[Guillaume; #654237]
- Fix handling of bad GMarkup in messages [Dan; #650298]
- Never show notifications when the screensaver is active [Dan; #654550]
* Telepathy integrationpp
- Implement Telepathy Debug interface to enable empathy-debugger
[Guillaume; #652816]
- Allow approving room invitations, and audio/video calls
[Guillaume; #653740 #653939]
- Send typing notifications [Jonny; #650196]
* Fix selection highlighting for light-on-dark entries [Jasper; #643768]
* Make control-Return in the overview open a new window [Maxim]
* Delay showing the alt-Tab switcher to reduce visual noise when
flipping betweeen windows [Dan; #652346]
* When we have vertically stacked monitors, put the message tray
on the bottom one [Dan; #636963]
* Fix various problems with keynav and the Activities button
[Dan; #641253 #645759]
* Ensure screensaver is locked when switching users [Colin; #654565]
* Improve extension creation tool [Jasper; #653206]
* Fix compatibility with latest GJS [Giovanni; #654349]
* Code cleanups [Adel, Dan, Jasper; #645759 #654577 #654791 #654987]
* Misc bug fixes [Richard, Dan, Florian, Giovanni, Jasper, Marc-Antoine, Rui;
#647175 #649513 #650452 #651082 #653700 #653989 #654105 #654791 #654267
#654269 #654527 #655446]
* Build fixes [Florian, Siegfried; #654300]
Contributors:
Giovanni Campagna, Cosimo Cecchi, Guillaume Desmottes, Neha Doijode,
Maxim Ermilov, Adel Gadllah, Siegfried-Angel Gevatter Pujals, Richard Hughes,
Jonny Lamb, Rui Matos, Florian Müllner, Marc-Antoine Perennou, Colin Walters,
Dan Winship, Marina Zhurakhinskaya
Translations:
Mario Blättermann, Paul Seyfert [de], Jorge González, Daniel Mustieles [es],
Fran Dieguez [gl], Yaron Shahrabani [he], Luca Ferretti [it],
Rudolfs Mazurs [lv], Kjartan Maraas [nb], A S Alam [pa], Yuri Kozlov [ru],
Michal Štrba, Matej Urbančič [sl]
3.1.3
=====
* Fix problem with "user theme extension" breaking the CSS for other
extensions [Giovanni; #650971]
* Telepathy IM framework integration
- Switch to using telepathy-glib rather than talking to
Telepathy via D-Bus [Guillaume, Jasper; #645585, #649633, #651138, #651227]
- Acknowledge messages when the user clicks on them [Guillaume, #647893]
- Fix problem with telepathy icon blinking for incoming messages
even though the user has been notified of them [Guillaume; #643594]
* Networking
- keep wirelesss networks in predictable order [Giovanni; #646580, #652313]
- Show unmanaged devices in the menu [Giovanni; #646946]
- Fix overflow when too many VPN connections [Giovanni; #651602]
* Bluetooth
- Show "hardware disabled" when disabled by rfkill [Giovanni; #648048]
- Fix bug updating status of devices [Giovanni; #647565]
* LookingGlass console:
- Add a "Memory" tab [Colin; #650692]
- Make escape work from any page [Dan Winship; #647303]
- Provide a way to refer to panel items as, e.g.,
Main.panel._activities [Dan Winship; #646915]
* User menu
- Fix problem with suspend menu option locking the screen even when the user
disabled that. [Florian; #652327]
- Hide "power off..." option if shutdown is disabled via PolicyKit
[Florian; #652038]
* Track changes to WM_CLASS (fixes problems with LibreOffice tracking)
[Colin; #649315]
* Remove app tracking workarounds for Firefox and LibreOffice [Colin; #651015]
* Use upstream gettext autoconfigury rather than glib version [Javier; #631576]
* Show messages in the message tray when an application is fullscreen
[Dan Winship; #608667]
* Don't autohide the workspace pager if there is more than one workspace
[Florian; #652714, #653078, #653142]
* Don't always slide out the workspace pager at drag begin [Florian; #652730]
* Only offer to remove a favorite app when dragging it's icon [Owen; #642895]
* Allow dropping an icon anywhere on a workspace [Adel; #652079]
* st-scroll-view: Make the fade effect and offset themable [Jasper; #651813]
* Obey the user's preference when running an application in a terminal
from the run dialog [Florian; #648422]
* Consistently exit overview when launching external applications
[Colin; #653095]
* Adapt to changes in GJS for how GObject APIs are bound
[Alex, Colin, Florian, Jasper, Marc-Antoine; #649981, #652597]
* Fix problems with scrolling in overflow for alt-Tab switcher
[Dan Winship, Adel; #647807]
* Mark relationships between labels and actors for accessibility [Alejandro]
* Add org.gnome.shell.enabled-extensions complementing disabled-extensions
GSetting [Tassilo; #651088]
* Visual tweaks [Jakub, Jasper; #646261, #652715]
* Switch to building against clutter-1.7 with independent Cogl [Adel; #653397]
* Code cleanups [Colin, Dan Winship, Florian; #633620, #645031, #648755, #648758,
#648760, #649203, #649517, #650317, #652730]
* Memory leak fixes [Colin, Maxim; #649508, #650934]
* Build Fixes [Colin, Dan Winship, Florian, Ionut, Morten, Owen, Sean; #647395,
#648006, #650869, #653199, #653275
* Miscellaneous bug fixes [Adam, Adel, Dan Williams, Dan Winship, Florian,
Ionut, Jasper, Maxim, Ray; #620105, #639459, #641570, #642793, #643513,
#645848, #646919, #647186, #648305, #648410, #648562, #648894, #649001,
#645990, #647893, #647907, #651012, #651086, #651606, #651569, #651866,
#652388, #653511]
Contributors:
Ionut Biru, Giovanni Campagna, Guillaume Desmottes, Adam Dingle,
Maxim Ermilov, Adel Gadllah, Tassilo Horn, Javier Jardón, Jonny Lamb,
Alexander Larsson, Rui Matos, Morten Mjelva, Florian Müllner,
Marc-Antoine Perennou, Alejandro Piñeiro, Jasper St. Pierre, Jakub Steiner,
Ray Strode, Owen Taylor, Colin Walters, Dan Williams, Sean Wilson, Dan Winship
Translations:
Daniel Martinez Cucalon [ar], Ihar Hrachyshka [be], Carles Ferrando,
Gil Forcada, Sílvia Miranda [ca], Kristjan Schmidt [eo], Jorge González,
Daniel Mustieles [es], Seán de Búrca [ga], Fran Diéguez [gl],
Yaron Shahrabani [he], Kjartan Maraas [nb], Misha Shnurapet,
Yuri Myasoedov [ru], Daniel Nylander [se], Peter Mráz [sk],
Matej Urbančič [sl], Krishnababu Krothapalli [te], Daniel Korostil [uk],
Aron Xu [zh_CN]
3.0.2
=====
* Network Menu [Dan Williams]
- Fix connecting to WPA2 Enterprise access points
Fixes https://bugzilla.gnome.org/show_bug.cgi?id=648171
- Show the mobile broadband wizard when selecting 3G network
Fixes https://bugzilla.gnome.org/show_bug.cgi?id=649318
- Miscellaneous bug fixes
648648, 650124
* Fix duplicate icons in the application browser [Owen]
https://bugzilla.gnome.org/show_bug.cgi?id=648739
* Make clicking anywhere on the volume icon slider work [Giovanni]
https://bugzilla.gnome.org/show_bug.cgi?id=646660
* Fix a case where activating and clicking the hot corner
at the same time could result in immediately leaving the
overview [Rui]
https://bugzilla.gnome.org/show_bug.cgi?id=649427
* Fix a case where applications became misordered in Alt-Tab [Jasper]
https://bugzilla.gnome.org/show_bug.cgi?id=643302
* Fix a bug where messages you send could show up in
notifications as if someone else sent them [Jonny]
https://bugzilla.gnome.org/show_bug.cgi?id=650219
* Memory leak fixes [Colin, Maxim]
642652, 649508, 649497
* Miscellaneous minor bug fixes [Adel, Christopher, Jasper]
649596, 648765, 648983, 649632
Contributors:
Christopher Aillon, Giovanni Campagna, Maxim Ermilov,
Adel Gadllah, Jonny Lamb, Rui Matos, Jasper St. Pierre,
Owen Taylor, Colin Walters, Dan Williams
Translations:
Arash Mousavi [fa], Seán de Búrca [ga], Timo Jyrinki [fi],
Sigurd Gartmann [nb], Daniel Nylander [se], Peter Mráz [sl],
Abduxukur Abdurixit [ug], Nguyễn Thái Ngọc Duy [vi]
3.0.1
=====

View File

@@ -1,5 +1,5 @@
AC_PREREQ(2.63)
AC_INIT([gnome-shell],[3.1.90.1],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
AC_INIT([gnome-shell],[3.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])
@@ -23,16 +23,12 @@ AM_PROG_CC_C_O
LT_PREREQ([2.2.6])
LT_INIT([disable-static])
# i18n
IT_PROG_INTLTOOL([0.40])
AM_GNU_GETTEXT([external])
AM_GNU_GETTEXT_VERSION([0.17])
GETTEXT_PACKAGE=gnome-shell
AC_SUBST(GETTEXT_PACKAGE)
AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE",
[The prefix for our gettext translation domains.])
IT_PROG_INTLTOOL(0.26)
AM_GLIB_GNU_GETTEXT
PKG_PROG_PKG_CONFIG([0.22])
@@ -64,57 +60,59 @@ fi
AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
CLUTTER_MIN_VERSION=1.7.5
CLUTTER_MIN_VERSION=1.5.15
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
GJS_MIN_VERSION=1.29.15
GJS_MIN_VERSION=0.7.11
MUTTER_MIN_VERSION=3.0.0
FOLKS_MIN_VERSION=0.5.2
GTK_MIN_VERSION=3.0.0
GIO_MIN_VERSION=2.29.10
GIO_MIN_VERSION=2.25.9
LIBECAL_MIN_VERSION=2.32.0
LIBEDATASERVER_MIN_VERSION=1.2.0
LIBEDATASERVERUI_MIN_VERSION=2.91.6
TELEPATHY_GLIB_MIN_VERSION=0.15.5
LIBEDATASERVERUI2_MIN_VERSION=1.2.0
LIBEDATASERVERUI3_MIN_VERSION=2.91.6
TELEPATHY_GLIB_MIN_VERSION=0.13.12
TELEPATHY_LOGGER_MIN_VERSION=0.2.4
POLKIT_MIN_VERSION=0.100
STARTUP_NOTIFICATION_MIN_VERSION=0.11
# Collect more than 20 libraries for a prize!
PKG_CHECK_MODULES(GNOME_SHELL, gio-2.0 >= $GIO_MIN_VERSION
gio-unix-2.0 dbus-glib-1 libxml-2.0
gtk+-3.0 >= $GTK_MIN_VERSION
folks >= $FOLKS_MIN_VERSION
libmutter >= $MUTTER_MIN_VERSION
gjs-internals-1.0 >= $GJS_MIN_VERSION
libgnome-menu-3.0 $recorder_modules gconf-2.0
gdk-x11-3.0 libsoup-2.4
libgnome-menu $recorder_modules gconf-2.0
gdk-x11-3.0
clutter-x11-1.0 >= $CLUTTER_MIN_VERSION
clutter-glx-1.0 >= $CLUTTER_MIN_VERSION
libstartup-notification-1.0 >= $STARTUP_NOTIFICATION_MIN_VERSION
libstartup-notification-1.0
gobject-introspection-1.0 >= $GOBJECT_INTROSPECTION_MIN_VERSION
libcanberra
telepathy-glib >= $TELEPATHY_GLIB_MIN_VERSION
telepathy-logger-0.2 >= $TELEPATHY_LOGGER_MIN_VERSION
polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes
libnm-glib libnm-util gnome-keyring-1)
polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes)
PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0)
PKG_CHECK_MODULES(SHELL_HOTPLUG_SNIFFER, gio-2.0 gdk-pixbuf-2.0)
GJS_VERSION=`$PKG_CONFIG --modversion gjs-internals-1.0`
AC_DEFINE_UNQUOTED([GJS_VERSION], ["$GJS_VERSION"], [The version of GJS we're linking to])
AC_SUBST([GJS_VERSION], ["$GJS_VERSION"])
GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION])
JHBUILD_TYPELIBDIR="$INTROSPECTION_TYPELIBDIR"
# NM is the only typelib we use that we don't jhbuild
PKG_CHECK_EXISTS([libnm-glib >= 0.8.999],
[NM_TYPELIBDIR=`$PKG_CONFIG --variable=libdir libnm-glib`/girepository-1.0
if test "$INTROSPECTION_TYPELIBDIR" != "$NM_TYPELIBDIR"; then
JHBUILD_TYPELIBDIR="$JHBUILD_TYPELIBDIR:$NM_TYPELIBDIR"
fi])
AC_SUBST(JHBUILD_TYPELIBDIR)
saved_CFLAGS=$CFLAGS
saved_LIBS=$LIBS
CFLAGS=$GNOME_SHELL_CFLAGS
LIBS=$GNOME_SHELL_LIBS
AC_CHECK_FUNCS(JS_NewGlobalObject XFixesCreatePointerBarrier)
# sn_startup_sequence_get_application_id, we can replace with a version check later
AC_CHECK_FUNCS(JS_NewGlobalObject sn_startup_sequence_get_application_id XFixesCreatePointerBarrier)
CFLAGS=$saved_CFLAGS
LIBS=$saved_LIBS
@@ -125,9 +123,9 @@ PKG_CHECK_MODULES(GVC, libpulse libpulse-mainloop-glib gobject-2.0)
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 0.1.7)
AC_MSG_CHECKING([for bluetooth support])
PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.1.0],
[BLUETOOTH_DIR=`$PKG_CONFIG --variable=applet_libdir gnome-bluetooth-1.0`
BLUETOOTH_LIBS=`$PKG_CONFIG --variable=applet_libs gnome-bluetooth-1.0`
PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 2.90.0],
[BLUETOOTH_DIR=`$PKG_CONFIG --variable=libdir gnome-bluetooth-1.0`/gnome-bluetooth
BLUETOOTH_LIBS="-L'$BLUETOOTH_DIR' -lgnome-bluetooth-applet"
AC_SUBST([BLUETOOTH_LIBS],["$BLUETOOTH_LIBS"])
AC_DEFINE_UNQUOTED([BLUETOOTH_DIR],["$BLUETOOTH_DIR"],[Path to installed GnomeBluetooth typelib and library])
AC_DEFINE([HAVE_BLUETOOTH],[1],[Define if you have libgnome-bluetooth-applet])
@@ -137,7 +135,13 @@ PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.1.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 libedataserverui-3.0 >= $LIBEDATASERVERUI_MIN_VERSION gio-2.0)
# Default to libedataserverui-3.0, but allow falling back to 1.2
PKG_CHECK_EXISTS(libedataserverui-3.0,
[EDS_API=3.0
LIBEDATASERVERUI_MIN_VERSION=$LIBEDATASERVERUI3_MIN_VERSION],
[EDS_API=1.2
LIBEDATASERVERUI_MIN_VERSION=$LIBEDATASERVERUI2_MIN_VERSION])
PKG_CHECK_MODULES(CALENDAR_SERVER, libecal-1.2 >= $LIBECAL_MIN_VERSION libedataserver-1.2 >= $LIBEDATASERVER_MIN_VERSION libedataserverui-$EDS_API >= $LIBEDATASERVERUI_MIN_VERSION gio-2.0)
AC_SUBST(CALENDAR_SERVER_CFLAGS)
AC_SUBST(CALENDAR_SERVER_LIBS)
@@ -153,17 +157,6 @@ AC_CHECK_FUNCS(fdwalk)
AC_CHECK_FUNCS(mallinfo)
AC_CHECK_HEADERS([sys/resource.h])
# _NL_TIME_FIRST_WEEKDAY is an enum and not a define
AC_MSG_CHECKING([for _NL_TIME_FIRST_WEEKDAY])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <langinfo.h>]],
[[nl_langinfo(_NL_TIME_FIRST_WEEKDAY);]])],
[langinfo_ok=yes], [langinfo_ok=no])
AC_MSG_RESULT($langinfo_ok)
if test "$langinfo_ok" = "yes"; then
AC_DEFINE([HAVE__NL_TIME_FIRST_WEEKDAY], [1],
[Define if _NL_TIME_FIRST_WEEKDAY is available])
fi
# Sets GLIB_GENMARSHAL and GLIB_MKENUMS
AM_PATH_GLIB_2_0()
G_IR_SCANNER=`$PKG_CONFIG --variable=g_ir_scanner gobject-introspection-1.0`

View File

@@ -29,7 +29,6 @@ dist_theme_DATA = \
theme/dash-placeholder.svg \
theme/filter-selected-ltr.svg \
theme/filter-selected-rtl.svg \
theme/gdm.css \
theme/gnome-shell.css \
theme/panel-border.svg \
theme/panel-button-border.svg \

View File

@@ -11,13 +11,12 @@
using the Alt-F2 dialog.
</_description>
</key>
<key name="enabled-extensions" type="as">
<key name="disabled-extensions" type="as">
<default>[]</default>
<_summary>Uuids of extensions to enable</_summary>
<_summary>Uuids of extensions to disable</_summary>
<_description>
GNOME Shell extensions have a uuid property; this key lists extensions
which should be loaded. disabled-extensions overrides this setting for
extensions that appear in both lists.
GNOME Shell extensions have a uuid property;
this key lists extensions which should not be loaded.
</_description>
</key>
<key name="enable-app-monitoring" type="b">
@@ -31,7 +30,7 @@
</_description>
</key>
<key name="favorite-apps" type="as">
<default>[ 'firefox.desktop', 'evolution.desktop', 'empathy.desktop', 'rhythmbox.desktop', 'shotwell.desktop', 'libreoffice-writer.desktop', 'nautilus.desktop', 'gnome-documents.desktop' ]</default>
<default>[ 'mozilla-firefox.desktop', 'evolution.desktop', 'empathy.desktop', 'rhythmbox.desktop', 'shotwell.desktop', 'openoffice.org-writer.desktop', 'nautilus.desktop' ]</default>
<_summary>List of desktop file IDs for favorite applications</_summary>
<_description>
The applications corresponding to these identifiers
@@ -53,7 +52,6 @@
<child name="clock" schema="org.gnome.shell.clock"/>
<child name="calendar" schema="org.gnome.shell.calendar"/>
<child name="recorder" schema="org.gnome.shell.recorder"/>
<child name="keyboard" schema="org.gnome.shell.keyboard"/>
</schema>
<schema id="org.gnome.shell.calendar" path="/org/gnome/shell/calendar/"
@@ -67,24 +65,6 @@
</key>
</schema>
<schema id="org.gnome.shell.keyboard" path="/org/gnome/shell/keyboard/"
gettext-domain="@GETTEXT_PACKAGE@">
<key name="show-keyboard" type="b">
<default>false</default>
<_summary>Show the onscreen keyboard</_summary>
<_description>
If true, display onscreen keyboard.
</_description>
</key>
<key name="keyboard-type" type="s">
<default>'touch'</default>
<_summary>Which keyboard to use</_summary>
<_description>
The type of keyboard to use.
</_description>
</key>
</schema>
<schema id="org.gnome.shell.clock" path="/org/gnome/shell/clock/"
gettext-domain="@GETTEXT_PACKAGE@">
<key name="show-seconds" type="b">

View File

@@ -1,161 +0,0 @@
/* Copyright 2011, Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
* more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*/
/* Login Dialog */
.login-dialog-title {
font-size: 14pt;
font-weight: bold;
color: #666666;
padding-bottom: 2em;
}
.login-dialog {
border-radius: 16px;
min-height: 150px;
max-height: 700px;
min-width: 350px;
}
.login-dialog-user-list-view {
-st-vfade-offset: 1em;
}
.login-dialog-user-list {
spacing: 12px;
}
.login-dialog-user-list-item {
color: #666666;
}
.login-dialog-user-list-item:ltr {
padding-right: 1em;
}
.login-dialog-user-list-item:rtl {
padding-left: 1em;
}
.login-dialog-user-list-item .login-dialog-user-list-item-name {
font-size: 20pt;
padding-left: 1em;
color: #666666;
}
.login-dialog-user-list-item:hover .login-dialog-user-list-item-name {
color: white;
}
.login-dialog-user-list-item:focus .login-dialog-user-list-item-name {
color: white;
text-shadow: black 0px 2px 2px;
}
.login-dialog-user-list-item-vertical-layout {
spacing: 2px;
}
.login-dialog-user-list-item .login-dialog-user-list-item-focus-bin {
background-color: rgba(0,0,0,0.0);
height: 2px;
}
.login-dialog-user-list-item:focus .login-dialog-user-list-item-focus-bin {
background-color: #666666;
}
.login-dialog-user-list-item-icon {
border: 2px solid #8b8b8b;
border-radius: 8px;
width: 64px;
height: 64px;
}
.login-dialog-not-listed-button {
padding-top: 2em;
}
.login-dialog-not-listed-label {
font-size: 14pt;
font-weight: bold;
color: #666666;
}
.login-dialog-prompt-layout {
padding-bottom: 64px;
}
.login-dialog-prompt-label {
color: white;
font-size: 20pt;
}
.login-dialog-prompt-entry {
padding: 4px;
border-radius: 4px;
border: 2px solid #5656cc;
color: black;
background-color: white;
caret-color: black;
caret-size: 1px;
}
.login-dialog-session-list {
color: #ffffff;
font-size: 10.5pt;
}
.login-dialog-session-list-button {
padding: 4px;
}
.login-dialog-session-list-button:focus {
background-color: #4c4c4c;
}
.login-dialog-session-list-button:active {
background-color: #4c4c4c;
}
.login-dialog-session-list-button:hover {
font-weight: bold;
}
.login-dialog-session-list-scroll-view {
background-gradient-start: rgba(80,80,80,0.3);
background-gradient-end: rgba(80,80,80,0.7);
background-gradient-direction: vertical;
box-shadow: inset 0px 2px 4px rgba(0,0,0,0.9);
border-radius: 8px;
border: 1px solid rgba(80,80,80,1.0);
padding: .5em;
}
.login-dialog-session-list-item:focus {
background-color: #666666;
}
.login-dialog-session-list-triangle {
padding-right: .5em;
}
.login-dialog-session-list-item-box {
spacing: .25em;
}
.login-dialog-session-list-item-dot {
width: .75em;
height: .75em;
}

View File

@@ -39,11 +39,6 @@ StScrollBar
padding: 0px;
}
StScrollView.vfade
{
-st-vfade-offset: 68px;
}
StScrollView StScrollBar
{
min-width: 16px;
@@ -93,12 +88,12 @@ StTooltip StLabel {
/* PopupMenu */
.popup-menu-boxpointer {
-arrow-border-radius: 8px;
-arrow-border-radius: 9px;
-arrow-background-color: rgba(0,0,0,0.9);
-arrow-border-width: 2px;
-arrow-border-color: #a5a5a5;
-arrow-base: 24px;
-arrow-rise: 11px;
-arrow-border-color: #5f5f5f;
-arrow-base: 30px;
-arrow-rise: 15px;
}
.popup-menu {
@@ -139,15 +134,6 @@ StTooltip StLabel {
border-width: 0px;
}
.popup-combo-menu {
background-color: rgba(0,0,0,0.9);
padding: 1em 0em;
color: #ffffff;
font-size: 10.5pt;
border: 1px solid #5f5f5f;
border-radius: 9px;
}
/* The remaining popup-menu sizing is all done in ems, so that if you
* override .popup-menu.font-size, everything else will scale with it.
*/
@@ -167,10 +153,6 @@ StTooltip StLabel {
.popup-image-menu-item {
}
.popup-combobox-item {
spacing: 1em;
}
.popup-separator-menu-item {
-gradient-height: 2px;
-gradient-start: rgba(8,8,8,0);
@@ -200,7 +182,6 @@ StTooltip StLabel {
}
.popup-inactive-menu-item {
font-weight: normal;
color: #999;
}
@@ -236,39 +217,6 @@ StTooltip StLabel {
spacing: .5em;
}
/* Shared button properties */
.dash-search-button, .notification-button, .notification-icon-button,
.hotplug-notification-item, .hotplug-resident-eject-button,
.modal-dialog-button {
color: white;
border: 1px solid #8b8b8b;
background-gradient-direction: vertical;
background-gradient-start: rgba(255, 255, 255, 0.2);
background-gradient-end: rgba(255, 255, 255, 0);
}
.dash-search-button:hover, .notification-button:hover,
.notification-icon-button:hover, .hotplug-notification-item:hover,
.hotplug-resident-eject-button:hover, .modal-dialog-button:hover {
background-gradient-start: rgba(255, 255, 255, 0.3);
background-gradient-end: rgba(255, 255, 255, 0.1);
}
.dash-search-button:selected, .notification-button:focus,
.notification-icon-button:focus, .hotplug-notification-item:focus,
.modal-dialog-button:focus {
border: 2px solid #8b8b8b;
}
.dash-search-button:active, .dash-search-button:pressed,
.notification-button:active, .notification-icon-button:active,
.hotplug-notification-item:active, .hotplug-resident-eject-button:active,
.modal-dialog-button:active, .modal-dialog-button:pressed {
background-gradient-start: rgba(255, 255, 255, 0);
background-gradient-end: rgba(255, 255, 255, 0.2);
}
/* Panel */
#panel {
@@ -310,7 +258,7 @@ StTooltip StLabel {
}
.panel-corner:active,
.panel-corner:overview,
.panel-corner:checked,
.panel-corner:focus {
-panel-corner-inner-border-color: rgba(255,255,255,0.8);
}
@@ -347,7 +295,7 @@ StTooltip StLabel {
}
.panel-button:active,
.panel-button:overview,
.panel-button:checked,
.panel-button:focus {
border-image: url("panel-button-border.svg") 10 10 0 2;
background-image: url("panel-button-highlight-wide.svg");
@@ -367,10 +315,6 @@ StTooltip StLabel {
icon-shadow: black 0px 2px 2px;
}
.panel-menu {
-boxpointer-gap: 4px
}
/* The rounded panel corners we draw don't
* support transitions, so disable transitions
* for the buttons at the left/right edges
@@ -387,49 +331,6 @@ StTooltip StLabel {
spacing: 4px;
}
.status-chooser {
spacing: .4em;
}
.status-chooser .popup-menu-item,
.status-chooser-combo .popup-menu-item {
padding: .4em;
}
.status-chooser-user-icon {
border: 2px solid #8b8b8b;
border-radius: 5px;
width: 48pt;
height: 48pt;
}
.status-chooser-user-icon:hover {
border: 2px solid #bbbbbb;
}
.status-chooser-user-name {
font-weight: bold;
font-size: 1.3em;
}
.status-chooser-combo {
border: 1px solid transparent;
}
.status-chooser-combo.popup-combo-menu {
background-color: rgba(0,0,0,0.7);
padding: .4em 0em;
border-radius: 4px;
border: 1px solid #5f5f5f;
color: #ffffff;
font-size: 10.5pt;
}
.status-chooser-status-item,
.status-chooser-combo > .popup-combobox-item {
spacing: .4em;
}
#legacyTray {
spacing: 14px;
padding-left: 14px;
@@ -452,6 +353,7 @@ StTooltip StLabel {
#overview {
spacing: 12px;
background-color: rgba(0,0,0,0.6);
}
.window-caption {
@@ -556,7 +458,6 @@ StTooltip StLabel {
background-gradient-start: rgba(5,5,6,0.1);
background-gradient-end: rgba(254,254,254,0.1);
background-gradient-direction: vertical;
selected-color: black;
caret-color: rgb(128, 128, 128);
caret-size: 1px;
width: 250px;
@@ -644,26 +545,27 @@ StTooltip StLabel {
spacing: 12px;
}
/* Text labels are an odd number of pixels tall. The uneven top and bottom
* padding compensates for this and ensures that the label is vertically
* centered */
.dash-search-button {
background-gradient-direction: vertical;
background-gradient-start: rgba(255, 255, 255, 0.2);
background-gradient-end: rgba(255, 255, 255, 0);
border: 1px solid #808080;
border-radius: 16px;
padding-top: 4px;
padding-bottom: 5px;
height: 32px;
width: 300px;
font-weight: bold;
}
.dash-search-button:selected {
padding-top: 3px;
padding-bottom: 4px;
width: 298px;
.dash-search-button:selected,
.dash-search-button:hover {
background-gradient-direction: vertical;
background-gradient-start: rgba(255, 255, 255, 0.4);
background-gradient-end: rgba(255, 255, 255, 0.2);
}
.dash-search-button-label {
color: white;
font-size: 11pt;
color: #cccccc;
font-size: 12pt;
}
/* Apps */
@@ -673,11 +575,6 @@ StTooltip StLabel {
-shell-grid-item-size: 118px;
}
.contact-grid {
spacing: 36px;
-shell-grid-item-size: 272px; /* 2 * -shell-grid-item-size + spacing */
}
.icon-grid .overview-icon {
icon-size: 96px;
}
@@ -744,57 +641,11 @@ StTooltip StLabel {
text-align: center;
}
.contact {
width: 272px; /* Same width as two normal results + spacing */
height: 118px; /* Aspect ratio = 1.75. Normal US business card ratio */
border-radius: 4px;
padding: 3px;
border: 1px rgba(0,0,0,0);
transition-duration: 100;
}
.contact-content {
border-radius: 2px;
padding: 8px;
width: 232px;
height: 84px;
background-color: white;
color: black;
text-align: center;
}
.contact-icon {
border-radius: 4px;
}
.contact-details {
padding: 6px 8px 11px 8px;
}
.contact-details-alias {
font-size: 16px;
padding-bottom: 11px;
}
.contact-details-status {
font-size: 11pt;
}
.contact-details-status-icon {
padding-right: 2px;
}
.contact:hover {
background-color: rgba(255,255,255,0.1);
transition-duration: 100;
}
.app-well-app.running > .overview-icon {
text-shadow: black 0px 2px 2px;
background-image: url("running-indicator.svg");
}
.contact:selected,
.app-well-app:selected > .overview-icon,
.search-result-content:selected > .overview-icon {
background-color: rgba(255,255,255,0.33);
@@ -808,7 +659,6 @@ StTooltip StLabel {
transition-duration: 100;
}
.contact:focus,
.app-well-app:focus > .overview-icon,
.search-result-content:focus > .overview-icon {
border: 1px solid #cccccc;
@@ -868,8 +718,6 @@ StTooltip StLabel {
.lg-dialog StEntry
{
color: #88ff66;
selection-background-color: #88ff66;
selected-color: black;
}
.lg-obj-inspector-title
@@ -943,9 +791,9 @@ StTooltip StLabel {
/* Calendar popup */
#calendarEventsArea {
/* this is the width of the second column of the popup */
min-width: 400px;
#calendarArea {
/* this is the width of the entire popup */
min-width: 600px;
}
.calendar-vertical-separator {
@@ -1164,35 +1012,26 @@ StTooltip StLabel {
}
#notification {
font-size: 11pt;
border-radius: 10px 10px 0px 0px;
background: rgba(0,0,0,0.8);
font-size: 12pt;
border-radius: 5px 5px 0px 0px;
background: rgba(0,0,0,0.9);
padding: 8px 8px 4px 8px;
spacing-rows: 10px;
spacing-columns: 10px;
width: 34em;
}
#notification.multi-line-notification {
.multi-line-notification {
padding-bottom: 8px;
}
/* We use row-span = 2 for the image cell, which prevents its height preferences to be
taken into account during allocation, so its height ends up being limited by the height
of the content in the other rows. To avoid showing a stretched image, we set the minimum
height of the table to be ICON_SIZE + IMAGE_SIZE + spacing-rows = 24 + 125 + 10 = 159 */
.notification-with-image {
min-height: 159px;
}
.summary-boxpointer {
-arrow-border-radius: 8px;
-arrow-border-radius: 9px;
-arrow-background-color: rgba(0,0,0,0.9);
-arrow-border-width: 2px;
-arrow-border-color: #a5a5a5;
-arrow-base: 24px;
-arrow-rise: 11px;
color: white;
-arrow-border-color: #5f5f5f;
-arrow-base: 30px;
-arrow-rise: 15px;
}
.summary-boxpointer #notification {
@@ -1246,100 +1085,60 @@ StTooltip StLabel {
}
#notification-actions {
spacing: 10px;
spacing: 5px;
}
.notification-button {
border-radius: 18px;
font-size: 11pt;
padding: 4px 42px 5px;
background-color: #3c3c3c;
padding: 2px 14px;
border-radius: 12px;
border: 1px solid #181818;
}
.notification-button:hover {
border: 1px solid #a1a1a1;
}
.notification-button:focus {
padding: 3px 41px 4px;
background-color: #666666;
}
.notification-button:active {
border: 1px solid #a1a1a1;
background-color: #2b2b2b;
}
.notification-icon-button {
border: 2px rgba(0,0,0,0.0);
border-radius: 5px;
padding: 5px;
}
.notification-icon-button:hover {
border: 2px rgba(161,161,161,0.7);
}
.notification-icon-button:focus {
padding: 4px;
background: rgba(192,192,192,0.7);
}
.notification-icon-button:active {
background: rgba(128,128,128,0.7);
}
.notification-icon-button > StIcon {
icon-size: 36px;
}
.hotplug-transient-box {
spacing: 6px;
padding: 2px 72px 2px 12px;
}
.hotplug-notification-item {
padding: 2px 10px;
border-radius: 18px;
font-size: 10.5pt;
}
.hotplug-notification-item:focus {
padding: 1px 71px 1px 11px;
}
.hotplug-notification-item-icon {
icon-size: 24px;
padding: 2px 5px;
}
.hotplug-resident-box {
spacing: 8px;
}
.hotplug-resident-mount {
spacing: 8px;
border-radius: 4px;
color: #ccc;
}
.hotplug-resident-mount:hover {
background-gradient-direction: horizontal;
background-gradient-start: rgba(255, 255, 255, 0.1);
background-gradient-end: rgba(255, 255, 255, 0);
color: #fff;
}
.hotplug-resident-mount-label {
color: inherit;
padding-left: 6px;
}
.hotplug-resident-mount-icon {
icon-size: 24px;
padding-left: 6px;
}
.hotplug-resident-eject-icon {
icon-size: 16px;
}
.hotplug-resident-eject-button {
padding: 7px;
border-radius: 5px;
color: #ccc;
}
.chat-log-message {
color: #888888;
}
.chat-group-sent, .chat-group-meta {
padding: 8px 0;
}
.chat-received {
background-gradient-direction: horizontal;
background-gradient-start: rgba(255, 255, 255, 0.2);
background-gradient-end: rgba(255, 255, 255, 0);
.chat-sent {
padding-left: 4px;
border-radius: 4px;
}
@@ -1350,20 +1149,23 @@ StTooltip StLabel {
}
.chat-sent {
padding-left: 18pt;
background-gradient-direction: horizontal;
background-gradient-start: rgba(255, 255, 255, 0);
background-gradient-end: rgba(255, 255, 255, 0.2);
padding-left: 4px;
border-radius: 4px;
color: #7E7E7E;
}
.chat-sent:rtl {
padding-left: 0px;
padding-right: 18pt;
padding-right: 4px;
}
.chat-meta-message {
padding-left: 4px;
border-radius: 4px;
font-size: 9pt;
font-size: 10.5pt;
color: #bbbbbb;
}
@@ -1372,35 +1174,22 @@ StTooltip StLabel {
padding-right: 4px;
}
.subscription-message {
font-style: italic;
}
#notification StEntry {
padding: 4px;
border-radius: 4px;
border: 1px solid #565656;
color: #a8a8a8;
selected-color: black;
border: 1px solid rgba(245,245,245,0.2);
background-gradient-direction: vertical;
background-gradient-start: rgb(200,200,200);
background-gradient-end: white;
transition-duration: 300;
box-shadow: inset 0px 2px 4px rgba(0,0,0,0.6);
caret-color: #a8a8a8;
background-color: #404040;
caret-color: #ffffff;
caret-size: 1px;
}
#notification StEntry:focus {
border: 1px solid #8b8b8b;
color: #333333;
background-gradient-direction: vertical;
background-gradient-start: rgb(200,200,200);
background-gradient-end: white;
border: 1px solid #3a3a3a;
color: #545454;
background-color: #e8e8e8;
caret-color: #545454;
selection-background-color: #808080;
box-shadow: 0px 0px 6px 2px rgba(255,255,255,0.9);
}
/* The spacing and padding on the summary is tricky; we want to keep
@@ -1466,16 +1255,6 @@ StTooltip StLabel {
padding-left: 4px;
}
.summary-source-counter {
color: white;
background-color: #3465A4;
text-shadow: black 1px 1px 0;
font-size: 9pt;
border-radius: 1em;
min-height: 1em;
min-width: 1em;
}
.source-title {
font-size: 9pt;
font-weight: bold;
@@ -1624,7 +1403,7 @@ StTooltip StLabel {
border-radius: 24px;
background-color: rgba(0.0, 0.0, 0.0, 0.9);
border: 2px solid #868686;
color: #babdb6;
color: #ffffff;
padding-right: 42px;
padding-left: 42px;
@@ -1637,21 +1416,37 @@ StTooltip StLabel {
}
.modal-dialog-button {
border: 1px solid #8b8b8b;
border-radius: 18px;
font-size: 11pt;
color: white;
font-size: 10.5pt;
margin-left: 10px;
margin-right: 10px;
padding: 4px 32px 5px;
padding-left: 32px;
padding-right: 32px;
padding-top: 8px;
padding-bottom: 8px;
background-gradient-direction: vertical;
background-gradient-start: #29323b;
background-gradient-end: #121a24;
}
.modal-dialog-button:disabled {
color: rgb(60, 60, 60);
.modal-dialog-button:active,
.modal-dialog-button:pressed {
border-color: #a5a5a5;
background-gradient-start: #121a24;
background-gradient-end: #29323b;
}
.modal-dialog-button:focus {
padding: 3px 31px 4px;
border: 2px solid #a5a5a5;
padding-left: 31px;
padding-right: 31px;
padding-top: 7px;
padding-bottom: 7px;
}
/* Run Dialog */
@@ -1675,8 +1470,6 @@ StTooltip StLabel {
font-weight: bold;
width: 23em;
color: white;
selection-background-color: white;
selected-color: black;
}
.run-dialog {
@@ -1780,90 +1573,6 @@ StTooltip StLabel {
color: #444444;
}
/* ShellMountOperation Dialogs */
.shell-mount-operation-icon {
icon-size: 48px;
}
.mount-password-reask {
color: red;
}
.show-processes-dialog,
.mount-question-dialog {
spacing: 24px;
}
.show-processes-dialog-subject,
.mount-question-dialog-subject {
font-size: 12pt;
font-weight: bold;
color: #666666;
padding-top: 10px;
padding-left: 17px;
padding-bottom: 6px;
}
.show-processes-dialog-subject:rtl,
.mount-question-dialog-subject:rtl {
padding-left: 0px;
padding-right: 17px;
}
.show-processes-dialog-description,
.mount-question-dialog-description {
font-size: 10pt;
color: white;
padding-left: 17px;
width: 28em;
}
.show-processes-dialog-description:rtl,
.mount-question-dialog-description:rtl {
padding-right: 17px;
}
.show-processes-dialog-app-list {
font-size: 10pt;
max-height: 200px;
padding-top: 24px;
padding-left: 49px;
padding-right: 32px;
}
.show-processes-dialog-app-list:rtl {
padding-right: 49px;
padding-left: 32px;
}
.show-processes-dialog-app-list-item {
color: #ccc;
}
.show-processes-dialog-app-list-item:hover {
color: white;
}
.show-processes-dialog-app-list-item:ltr {
padding-right: 1em;
}
.show-processes-dialog-app-list-item:rtl {
padding-left: 1em;
}
.show-processes-dialog-app-list-item-icon:ltr {
padding-right: 17px;
}
.show-processes-dialog-app-list-item-icon:rtl {
padding-left: 17px;
}
.show-processes-dialog-app-list-item-name {
font-size: 10pt;
}
/* PolicyKit Authentication Dialog */
.polkit-dialog {
/* this is the width of the entire modal popup */
@@ -1913,16 +1622,9 @@ StTooltip StLabel {
}
.polkit-dialog-password-entry {
background-gradient-start: rgb(236,236,236);
background-gradient-end: white;
background-gradient-direction: vertical;
background-color: white;
color: black;
border-radius: 5px;
border: 2px solid #555753;
}
.polkit-dialog-password-entry:focus {
border: 2px solid #3465a4;
}
.polkit-dialog-error-label {
@@ -1943,17 +1645,6 @@ StTooltip StLabel {
padding-bottom: 8px;
}
.network-dialog-show-password-checkbox {
padding-top: 5px;
padding-bottom: 5px;
font-size: 10pt;
color: white;
spacing: 10px;
}
.network-dialog-secret-table {
spacing-rows: 15px;
}
/* Magnifier */
@@ -1965,57 +1656,48 @@ StTooltip StLabel {
border-width: 0px;
}
/* On-screen Keyboard */
#keyboard {
background: rgba(0,0,0,0.8);
/* goa message popup */
.goa-message-table {
}
.keyboard-layout {
spacing: 10px;
padding: 10px;
.goa-message-base {
font-size: 9pt;
}
.keyboard-row {
spacing: 15px;
}
.keyboard-key {
min-height: 30px;
min-width: 30px;
background-gradient-start: rgba(255,245,245,0.4);
background-gradient-end: rgba(105,105,105,0.1);
background-gradient-direction: vertical;
font-size: 14pt;
.goa-message-from-header {
color: #666666;
font-weight: bold;
border-radius: 10px;
border: 2px solid #a0a0a0;
color: white;
}
.keyboard-key:grayed {
color: #808080;
border-color: #808080;
.goa-message-subject-header {
color: #666666;
font-weight: bold;
}
.keyboard-key:checked,
.keyboard-key:hover {
background: #303030;
border: 3px solid white;
.goa-message-date-header {
color: #666666;
font-weight: bold;
}
.keyboard-key:active {
background: #808080;
.goa-message-from {
font-weight: bold;
min-width: 125px;
}
.keyboard-subkeys {
color: white;
padding: 5px;
-arrow-border-radius: 10px;
-arrow-background-color: #090909;
-arrow-border-width: 2px;
-arrow-border-color: white;
-arrow-base: 20px;
-arrow-rise: 10px;
-boxpointer-gap: 5px;
.goa-message-hbox {
spacing: 0.25em;
min-width: 300px;
}
.goa-message-subject {
}
.goa-message-excerpt {
color: rgba(153, 153, 153, 1.0);
}
.goa-message-date {
min-width: 80px;
}

View File

@@ -2,62 +2,24 @@
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="119.97824"
height="119.97824"
id="svg7355"
version="1.1"
inkscape:version="0.48.1 r9760"
sodipodi:docname="running-indicator.svg">
<metadata
id="metadata4175">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#2c1cff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="1"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1141"
id="namedview4173"
showgrid="false"
inkscape:zoom="8.1348081"
inkscape:cx="81.120662"
inkscape:cy="58.117986"
inkscape:window-x="0"
inkscape:window-y="26"
inkscape:window-maximized="1"
inkscape:current-layer="g30864" />
version="1.1">
<defs
id="defs7357">
<radialGradient
xlink:href="#linearGradient36429"
id="radialGradient7461"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.011539,0,0,0.57582113,-0.39262194,71.83807)"
cx="47.428951"
cy="167.16817"
fx="47.428951"
fy="167.16817"
gradientTransform="matrix(1.0525552,0,0,1.0525552,-2.5162753,-9.0000838)"
cx="47.878681"
cy="171.25"
fx="47.878681"
fy="171.25"
r="37" />
<linearGradient
id="linearGradient36429">
@@ -97,7 +59,7 @@
fx="49.067139"
cy="242.50381"
cx="49.067139"
gradientTransform="matrix(1.1891549,0,0,0.15252127,-9.281289,132.52772)"
gradientTransform="matrix(1.1891549,0,0,0.55513246,-9.281289,36.12653)"
gradientUnits="userSpaceOnUse"
id="radialGradient7488"
xlink:href="#linearGradient36471" />
@@ -110,21 +72,19 @@
id="g30864"
transform="translate(255.223,70.118091)">
<rect
ry="3.4593496"
rx="3.4593496"
y="99.596962"
x="12.596948"
height="71.116341"
width="71.116341"
ry="3.5996203"
rx="3.5996203"
y="98"
x="11"
height="74"
width="74"
id="rect14000"
style="opacity:0.37187500000000001;fill:url(#radialGradient7461);fill-opacity:1;stroke:none" />
style="opacity:0.371875;fill:url(#radialGradient7461);fill-opacity:1;stroke:none" />
<path
id="rect34520"
d="m 83.273151,166.72152 c 0,1.96759 -1.584022,3.55163 -3.551629,3.55163 l -63.443032,0 c -1.967608,0 -3.551648,-1.58402 -3.551643,-3.55164 0,-5.85318 0,-5.85318 0,0"
style="opacity:0.35;fill:none;stroke:url(#radialGradient7488);stroke-width:1;stroke-opacity:1"
connector-curvature="0"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccscc" />
d="m 84.506708,167.95508 c 6e-6,1.96759 -1.584022,3.55162 -3.551629,3.55163 l -65.910146,0 c -1.967608,-1e-5 -3.551648,-1.58402 -3.551643,-3.55164"
style="opacity:0.2;fill:none;stroke:url(#radialGradient7488);stroke-width:1;stroke-opacity:1"
inkscape:connector-curvature="0" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -13,8 +13,8 @@
height="22"
id="svg3199"
version="1.1"
inkscape:version="0.48.1 r9760"
sodipodi:docname="toggle-on-intl.svg">
inkscape:version="0.47 r22583"
sodipodi:docname="New document 11">
<defs
id="defs3201">
<inkscape:perspective
@@ -39,14 +39,14 @@
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1"
inkscape:cx="49.147112"
inkscape:cy="17.532036"
inkscape:zoom="0.35"
inkscape:cx="32.500004"
inkscape:cy="10.999997"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1412"
inkscape:window-height="1067"
inkscape:window-width="609"
inkscape:window-height="501"
inkscape:window-x="0"
inkscape:window-y="26"
inkscape:window-maximized="0" />
@@ -58,7 +58,7 @@
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
@@ -72,7 +72,7 @@
transform="translate(-453.5,448.36218)"
id="g16453">
<rect
style="color:#000000;fill:#4a90d9;fill-opacity:1;fill-rule:nonzero;stroke:#3465a4;stroke-width:0.99999994000000003;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
style="color:#000000;fill:#204a87;fill-opacity:1;fill-rule:nonzero;stroke:#3465a4;stroke-width:0.99999994;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect16256-9-4"
width="63.000004"
height="19"

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@@ -13,8 +13,8 @@
height="22"
id="svg2857"
version="1.1"
inkscape:version="0.48.1 r9760"
sodipodi:docname="toggle-on-us.svg">
inkscape:version="0.47 r22583"
sodipodi:docname="New document 2">
<defs
id="defs2859">
<inkscape:perspective
@@ -40,18 +40,16 @@
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1"
inkscape:cx="19.689855"
inkscape:cy="2.0517979"
inkscape:cx="-69.642856"
inkscape:cy="42.428569"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="941"
inkscape:window-height="751"
inkscape:window-x="2577"
inkscape:window-y="206"
inkscape:window-maximized="0"
borderlayer="true"
inkscape:showpageshadow="false" />
inkscape:window-width="609"
inkscape:window-height="501"
inkscape:window-x="0"
inkscape:window-y="26"
inkscape:window-maximized="0" />
<metadata
id="metadata2862">
<rdf:RDF>
@@ -60,7 +58,7 @@
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
@@ -74,7 +72,7 @@
transform="translate(-351.35714,708.36218)"
id="g16453">
<rect
style="color:#000000;fill:#4a90d9;fill-opacity:1;fill-rule:nonzero;stroke:#3465a4;stroke-width:0.99999994000000003;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
style="color:#000000;fill:#204a87;fill-opacity:1;fill-rule:nonzero;stroke:#3465a4;stroke-width:0.99999994;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect16256-9-4"
width="63.000004"
height="19"

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@@ -2,8 +2,6 @@
jsdir = $(pkgdatadir)/js
nobase_dist_js_DATA = \
gdm/batch.js \
gdm/loginDialog.js \
misc/config.js \
misc/docInfo.js \
misc/fileUtils.js \
@@ -12,17 +10,14 @@ nobase_dist_js_DATA = \
misc/history.js \
misc/modemManager.js \
misc/params.js \
misc/screenSaver.js \
misc/util.js \
perf/core.js \
ui/altTab.js \
ui/appDisplay.js \
ui/appFavorites.js \
ui/automountManager.js \
ui/autorunManager.js \
ui/boxpointer.js \
ui/calendar.js \
ui/contactDisplay.js \
ui/chrome.js \
ui/ctrlAltTab.js \
ui/dash.js \
ui/dateMenu.js \
@@ -31,9 +26,8 @@ nobase_dist_js_DATA = \
ui/endSessionDialog.js \
ui/environment.js \
ui/extensionSystem.js \
ui/goaClient.js \
ui/iconGrid.js \
ui/keyboard.js \
ui/layout.js \
ui/lightbox.js \
ui/link.js \
ui/lookingGlass.js \
@@ -42,8 +36,6 @@ nobase_dist_js_DATA = \
ui/main.js \
ui/messageTray.js \
ui/modalDialog.js \
ui/networkAgent.js \
ui/shellMountOperation.js \
ui/notificationDaemon.js \
ui/overview.js \
ui/panel.js \
@@ -57,6 +49,7 @@ nobase_dist_js_DATA = \
ui/searchDisplay.js \
ui/shellDBus.js \
ui/statusIconDispatcher.js \
ui/statusMenu.js \
ui/status/accessibility.js \
ui/status/keyboard.js \
ui/status/network.js \
@@ -65,7 +58,6 @@ nobase_dist_js_DATA = \
ui/status/bluetooth.js \
ui/telepathyClient.js \
ui/tweener.js \
ui/userMenu.js \
ui/viewSelector.js \
ui/windowAttentionHandler.js \
ui/windowManager.js \

View File

@@ -1,228 +0,0 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*-
*
* Copyright 2011 Red Hat, Inc
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
const Lang = imports.lang;
const Signals = imports.signals;
function Task() {
this._init.apply(this, arguments);
}
Task.prototype = {
_init: function(scope, handler) {
if (scope)
this.scope = scope;
else
this.scope = this;
this.handler = handler;
},
run: function() {
if (this.handler)
return this.handler.call(this.scope);
return null;
},
};
Signals.addSignalMethods(Task.prototype);
function Hold() {
this._init.apply(this, arguments);
}
Hold.prototype = {
__proto__: Task.prototype,
_init: function() {
Task.prototype._init.call(this,
this,
function () {
return this;
});
this._acquisitions = 1;
},
acquire: function() {
if (this._acquisitions <= 0)
throw new Error("Cannot acquire hold after it's been released");
this._acquisitions++;
},
acquireUntilAfter: function(hold) {
if (!hold.isAcquired())
return;
this.acquire();
let signalId = hold.connect('release', Lang.bind(this, function() {
hold.disconnect(signalId);
this.release();
}));
},
release: function() {
this._acquisitions--;
if (this._acquisitions == 0)
this.emit('release');
},
isAcquired: function() {
return this._acquisitions > 0;
}
}
Signals.addSignalMethods(Hold.prototype);
function Batch() {
this._init.apply(this, arguments);
}
Batch.prototype = {
__proto__: Task.prototype,
_init: function(scope, tasks) {
Task.prototype._init.call(this);
this.tasks = [];
for (let i = 0; i < tasks.length; i++) {
let task;
if (tasks[i] instanceof Task) {
task = tasks[i];
} else if (typeof tasks[i] == 'function') {
task = new Task(scope, tasks[i]);
} else {
throw new Error('Batch tasks must be functions or Task, Hold or Batch objects');
}
this.tasks.push(task);
}
},
process: function() {
throw new Error('Not implemented');
},
runTask: function() {
if (!(this._currentTaskIndex in this.tasks)) {
return null;
}
return this.tasks[this._currentTaskIndex].run();
},
_finish: function() {
this.hold.release();
},
nextTask: function() {
this._currentTaskIndex++;
// if the entire batch of tasks is finished, release
// the hold and notify anyone waiting on the batch
if (this._currentTaskIndex >= this.tasks.length) {
this._finish();
return;
}
this.process();
},
_start: function() {
// acquire a hold to get released when the entire
// batch of tasks is finished
this.hold = new Hold();
this._currentTaskIndex = 0;
this.process();
},
run: function() {
this._start();
// hold may be destroyed at this point
// if we're already done running
return this.hold;
},
cancel: function() {
this.tasks = this.tasks.splice(0, this._currentTaskIndex + 1);
}
};
Signals.addSignalMethods(Batch.prototype);
function ConcurrentBatch() {
this._init.apply(this, arguments);
}
ConcurrentBatch.prototype = {
__proto__: Batch.prototype,
_init: function(scope, tasks) {
Batch.prototype._init.call(this, scope, tasks);
},
process: function() {
let hold = this.runTask();
if (hold) {
this.hold.acquireUntilAfter(hold);
}
// Regardless of the state of the just run task,
// fire off the next one, so all the tasks can run
// concurrently.
this.nextTask();
}
};
Signals.addSignalMethods(ConcurrentBatch.prototype);
function ConsecutiveBatch() {
this._init.apply(this, arguments);
}
ConsecutiveBatch.prototype = {
__proto__: Batch.prototype,
_init: function(scope, tasks) {
Batch.prototype._init.call(this, scope, tasks);
},
process: function() {
let hold = this.runTask();
if (hold && hold.isAcquired()) {
// This task is inhibiting the batch. Wait on it
// before processing the next one.
let signalId = hold.connect('release',
Lang.bind(this, function() {
hold.disconnect(signalId);
this.nextTask();
}));
return;
} else {
// This task finished, process the next one
this.nextTask();
}
}
};
Signals.addSignalMethods(ConsecutiveBatch.prototype);

File diff suppressed because it is too large Load Diff

View File

@@ -109,8 +109,7 @@ const SessionManagerIface = {
name: 'org.gnome.SessionManager',
methods: [
{ name: 'Logout', inSignature: 'u', outSignature: '' },
{ name: 'Shutdown', inSignature: '', outSignature: '' },
{ name: 'CanShutdown', inSignature: '', outSignature: 'b' }
{ name: 'Shutdown', inSignature: '', outSignature: '' }
]
};

View File

@@ -1,53 +0,0 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const DBus = imports.dbus;
const Lang = imports.lang;
const ScreenSaverIface = {
name: 'org.gnome.ScreenSaver',
methods: [{ name: 'GetActive',
inSignature: '',
outSignature: 'b' },
{ name: 'Lock',
inSignature: '' },
{ name: 'SetActive',
inSignature: 'b' }],
signals: [{ name: 'ActiveChanged',
inSignature: 'b' }]
};
function ScreenSaverProxy() {
this._init();
}
ScreenSaverProxy.prototype = {
_init: function() {
DBus.session.proxifyObject(this,
'org.gnome.ScreenSaver',
'/org/gnome/ScreenSaver');
DBus.session.watch_name('org.gnome.ScreenSaver',
false, // do not launch a name-owner if none exists
Lang.bind(this, this._onSSAppeared),
Lang.bind(this, this._onSSVanished));
this.screenSaverActive = false;
this.connect('ActiveChanged',
Lang.bind(this, this._onActiveChanged));
},
_onSSAppeared: function(owner) {
this.GetActiveRemote(Lang.bind(this, function(isActive) {
this.screenSaverActive = isActive;
}))
},
_onSSVanished: function(oldOwner) {
this.screenSaverActive = false;
},
_onActiveChanged: function(object, isActive) {
this.screenSaverActive = isActive;
}
};
DBus.proxifyPrototype(ScreenSaverProxy.prototype, ScreenSaverIface);

View File

@@ -45,7 +45,7 @@ function spawn(argv) {
// occur when trying to parse or start the program.
function spawnCommandLine(command_line) {
try {
let [success, argv] = GLib.shell_parse_argv(command_line);
let [success, argc, argv] = GLib.shell_parse_argv(command_line);
trySpawn(argv);
} catch (err) {
_handleSpawnError(command_line, err);
@@ -85,10 +85,10 @@ function trySpawn(argv)
// Runs @command_line in the background. If launching @command_line
// fails, this will throw an error.
function trySpawnCommandLine(command_line) {
let success, argv;
let success, argc, argv;
try {
[success, argv] = GLib.shell_parse_argv(command_line);
[success, argc, argv] = GLib.shell_parse_argv(command_line);
} catch (err) {
// Replace "Error invoking GLib.shell_parse_argv: " with
// something nicer

View File

@@ -113,10 +113,10 @@ function run() {
for (let i = 0; i < 2; i++) {
Scripting.scriptEvent('applicationsShowStart');
Main.overview._viewSelector.switchTab('applications');
Main.overview.viewSelector.switchTab('applications');
yield Scripting.waitLeisure();
Scripting.scriptEvent('applicationsShowDone');
Main.overview._viewSelector.switchTab('windows');
Main.overview.viewSelector.switchTab('windows');
yield Scripting.waitLeisure();
}
}

View File

@@ -14,8 +14,7 @@ const Tweener = imports.ui.tweener;
const POPUP_APPICON_SIZE = 96;
const POPUP_SCROLL_TIME = 0.10; // seconds
const POPUP_DELAY_TIMEOUT = 150; // milliseconds
const POPUP_FADE_OUT_TIME = 0.1; // seconds
const POPUP_FADE_TIME = 0.1; // seconds
const APP_ICON_HOVER_TIMEOUT = 200; // milliseconds
@@ -53,7 +52,6 @@ AltTabPopup.prototype = {
this._currentWindow = -1;
this._thumbnailTimeoutId = 0;
this._motionTimeoutId = 0;
this._initialDelayTimeoutId = 0;
this.thumbnailsVisible = false;
@@ -76,7 +74,7 @@ AltTabPopup.prototype = {
_allocate: function (actor, box, flags) {
let childBox = new Clutter.ActorBox();
let primary = Main.layoutManager.primaryMonitor;
let primary = global.get_primary_monitor();
let leftPadding = this.actor.get_theme_node().get_padding(St.Side.LEFT);
let rightPadding = this.actor.get_theme_node().get_padding(St.Side.RIGHT);
@@ -99,6 +97,8 @@ AltTabPopup.prototype = {
// those calculations
if (this._thumbnails) {
let icon = this._appIcons[this._currentApp].actor;
// Force a stage relayout to make sure we get the correct position
global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, 0, 0);
let [posX, posY] = icon.get_transformed_position();
let thumbnailCenter = posX + icon.width / 2;
let [childMinWidth, childNaturalWidth] = this._thumbnails.actor.get_preferred_width(-1);
@@ -121,7 +121,7 @@ AltTabPopup.prototype = {
}
},
show : function(backward, binding) {
show : function(backward, switch_group) {
let tracker = Shell.WindowTracker.get_default();
let apps = tracker.get_running_apps ('');
@@ -147,10 +147,12 @@ AltTabPopup.prototype = {
// Need to force an allocation so we can figure out whether we
// need to scroll when selecting
this.actor.opacity = 0;
this.actor.show();
this.actor.get_allocation_box();
// Make the initial selection
if (binding == 'switch_group') {
if (switch_group) {
if (backward) {
this._select(0, this._appIcons[0].cachedWindows.length - 1);
} else {
@@ -159,10 +161,6 @@ AltTabPopup.prototype = {
else
this._select(0, 0);
}
} else if (binding == 'switch_group_backward') {
this._select(0, this._appIcons[0].cachedWindows.length - 1);
} else if (binding == 'switch_windows_backward') {
this._select(this._appIcons.length - 1);
} else if (this._appIcons.length == 1) {
this._select(0);
} else if (backward) {
@@ -182,13 +180,11 @@ AltTabPopup.prototype = {
return false;
}
// We delay showing the popup so that fast Alt+Tab users aren't
// disturbed by the popup briefly flashing.
this._initialDelayTimeoutId = Mainloop.timeout_add(POPUP_DELAY_TIMEOUT,
Lang.bind(this, function () {
this.actor.show();
this._initialDelayTimeoutId = 0;
}));
Tweener.addTween(this.actor,
{ opacity: 255,
time: POPUP_FADE_TIME,
transition: 'easeOutQuad'
});
return true;
},
@@ -219,29 +215,37 @@ AltTabPopup.prototype = {
let keysym = event.get_key_symbol();
let event_state = Shell.get_event_state(event);
let backwards = event_state & Clutter.ModifierType.SHIFT_MASK;
let action = global.display.get_keybinding_action(event.get_key_code(), event_state);
let action = global.screen.get_display().get_keybinding_action(event.get_key_code(), event_state);
this._disableHover();
if (keysym == Clutter.Escape) {
this.destroy();
} else if (action == Meta.KeyBindingAction.SWITCH_GROUP) {
if (action == Meta.KeyBindingAction.SWITCH_GROUP)
this._select(this._currentApp, backwards ? this._previousWindow() : this._nextWindow());
} else if (action == Meta.KeyBindingAction.SWITCH_GROUP_BACKWARD) {
this._select(this._currentApp, this._previousWindow());
} else if (action == Meta.KeyBindingAction.SWITCH_WINDOWS) {
this._select(backwards ? this._previousApp() : this._nextApp());
} else if (action == Meta.KeyBindingAction.SWITCH_WINDOWS_BACKWARD) {
this._select(this._previousApp());
} else if (this._thumbnailsFocused) {
if (keysym == Clutter.Left)
else if (keysym == Clutter.Escape)
this.destroy();
else if (this._thumbnailsFocused) {
if (action == Meta.KeyBindingAction.SWITCH_WINDOWS)
if (backwards) {
if (this._currentWindow == 0 || this._currentWindow == -1)
this._select(this._previousApp());
else
this._select(this._currentApp, this._previousWindow());
} else {
if (this._currentWindow == this._appIcons[this._currentApp].cachedWindows.length - 1)
this._select(this._nextApp());
else
this._select(this._currentApp, this._nextWindow());
}
else if (keysym == Clutter.Left)
this._select(this._currentApp, this._previousWindow());
else if (keysym == Clutter.Right)
this._select(this._currentApp, this._nextWindow());
else if (keysym == Clutter.Up)
this._select(this._currentApp, null, true);
} else {
if (keysym == Clutter.Left)
if (action == Meta.KeyBindingAction.SWITCH_WINDOWS)
this._select(backwards ? this._previousApp() : this._nextApp());
else if (keysym == Clutter.Left)
this._select(this._previousApp());
else if (keysym == Clutter.Right)
this._select(this._nextApp());
@@ -370,7 +374,7 @@ AltTabPopup.prototype = {
if (this.actor.visible) {
Tweener.addTween(this.actor,
{ opacity: 0,
time: POPUP_FADE_OUT_TIME,
time: POPUP_FADE_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this,
function() {
@@ -391,8 +395,6 @@ AltTabPopup.prototype = {
Mainloop.source_remove(this._motionTimeoutId);
if (this._thumbnailTimeoutId != 0)
Mainloop.source_remove(this._thumbnailTimeoutId);
if (this._initialDelayTimeoutId != 0)
Mainloop.source_remove(this._initialDelayTimeoutId);
},
/**
@@ -630,7 +632,7 @@ SwitcherList.prototype = {
this._items[this._highlighted].add_style_pseudo_class('selected');
}
let monitor = Main.layoutManager.primaryMonitor;
let monitor = global.get_primary_monitor();
let itemSize = this._items[index].allocation.x2 - this._items[index].allocation.x1;
let [posX, posY] = this._items[index].get_transformed_position();
posX += this.actor.x;
@@ -658,7 +660,7 @@ SwitcherList.prototype = {
_scrollToRight : function() {
this._scrollableLeft = true;
let monitor = Main.layoutManager.primaryMonitor;
let monitor = global.get_primary_monitor();
let padding = this.actor.get_theme_node().get_horizontal_padding();
let parentPadding = this.actor.get_parent().get_theme_node().get_horizontal_padding();
let x = this._items[this._highlighted].allocation.x2 - monitor.width + padding + parentPadding;
@@ -755,7 +757,7 @@ SwitcherList.prototype = {
let children = this._list.get_children();
let childBox = new Clutter.ActorBox();
let primary = Main.layoutManager.primaryMonitor;
let primary = global.get_primary_monitor();
let parentRightPadding = this.actor.get_parent().get_theme_node().get_padding(St.Side.RIGHT);
if (this.actor.allocation.x2 == primary.x + primary.width - parentRightPadding) {
if (this._squareItems)
@@ -885,7 +887,7 @@ AppSwitcher.prototype = {
totalSpacing += this._separator.width + this._list.spacing;
// We just assume the whole screen here due to weirdness happing with the passed width
let primary = Main.layoutManager.primaryMonitor;
let primary = global.get_primary_monitor();
let parentPadding = this.actor.get_parent().get_theme_node().get_horizontal_padding();
let availWidth = primary.width - parentPadding - this.actor.get_theme_node().get_horizontal_padding();
let height = 0;

View File

@@ -3,7 +3,6 @@
const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const GMenu = imports.gi.GMenu;
const Shell = imports.gi.Shell;
const Lang = imports.lang;
const Signals = imports.signals;
@@ -36,7 +35,8 @@ AlphabeticalView.prototype = {
this._appSystem = Shell.AppSystem.get_default();
this._pendingAppLaterId = 0;
this._appIcons = {}; // desktop file id
this._apps = [];
this._filterApp = null;
let box = new St.BoxLayout({ vertical: true });
box.add(this._grid.actor, { y_align: St.Align.START, expand: true });
@@ -44,7 +44,7 @@ AlphabeticalView.prototype = {
this.actor = new St.ScrollView({ x_fill: true,
y_fill: false,
y_align: St.Align.START,
style_class: 'vfade' });
vfade: true });
this.actor.add_actor(box);
this.actor.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
this.actor.connect('notify::mapped', Lang.bind(this,
@@ -63,17 +63,20 @@ AlphabeticalView.prototype = {
_removeAll: function() {
this._grid.removeAll();
this._appIcons = {};
this._apps = [];
},
_addApp: function(app) {
var id = app.get_id();
let appIcon = new AppWellIcon(app);
_addApp: function(appInfo) {
let appIcon = new AppWellIcon(this._appSystem.get_app(appInfo.get_id()));
this._grid.addItem(appIcon.actor);
appIcon.actor.connect('key-focus-in', Lang.bind(this, this._ensureIconVisible));
this._appIcons[id] = appIcon;
appIcon._appInfo = appInfo;
if (this._filterApp && !this._filterApp(appInfo))
appIcon.actor.hide();
this._apps.push(appIcon);
},
_ensureIconVisible: function(icon) {
@@ -102,33 +105,52 @@ AlphabeticalView.prototype = {
transition: 'easeOutQuad' });
},
setVisibleApps: function(apps) {
if (apps == null) { // null implies "all"
for (var id in this._appIcons) {
var icon = this._appIcons[id];
icon.actor.visible = true;
}
setFilter: function(filter) {
this._filterApp = filter;
for (let i = 0; i < this._apps.length; i++)
this._apps[i].actor.visible = filter(this._apps[i]._appInfo);
},
// Create actors for the applications in an idle to avoid blocking
// for too long; see bug 647778
_addPendingApps: function() {
let i;
let startTimeMillis = new Date().getTime();
for (i = 0; i < this._pendingAppIds.length; i++) {
let id = this._pendingAppIds[i];
this._addApp(this._pendingApps[id]);
let currentTimeMillis = new Date().getTime();
if (currentTimeMillis - startTimeMillis > MAX_APPLICATION_WORK_MILLIS)
break;
}
this._pendingAppIds.splice(0, i + 1);
if (this._pendingAppIds.length > 0) {
return true;
} else {
// Set everything to not-visible, then set to visible what we should see
for (var id in this._appIcons) {
var icon = this._appIcons[id];
icon.actor.visible = false;
}
for (var i = 0; i < apps.length; i++) {
var app = apps[i];
var id = app.get_id();
var icon = this._appIcons[id];
icon.actor.visible = true;
}
this._pendingAppLaterId = 0;
this._pendingAppIds = null;
this._pendingApps = null;
return false;
}
},
setAppList: function(apps) {
refresh: function(apps) {
let ids = [];
for (let i in apps)
ids.push(i);
ids.sort(function(a, b) {
return apps[a].get_name().localeCompare(apps[b].get_name());
});
this._removeAll();
for (var i = 0; i < apps.length; i++) {
var app = apps[i];
this._addApp(app);
}
this._pendingAppIds = ids;
this._pendingApps = apps;
if (this._pendingAppLaterId)
Meta.later_remove(this._pendingAppLaterId);
this._pendingAppLaterId = Meta.later_add(Meta.LaterType.BEFORE_REDRAW,
Lang.bind(this, this._addPendingApps));
}
};
@@ -149,24 +171,21 @@ ViewByCategories.prototype = {
// -2 is a flag to indicate that nothing is selected
// (used only before the actor is mapped the first time)
this._currentCategory = -2;
this._categories = [];
this._apps = null;
this._filters = new St.BoxLayout({ vertical: true, reactive: true });
this._filters.connect('scroll-event', Lang.bind(this, this._scrollFilter));
this._categoryBox = new St.BoxLayout({ vertical: true, reactive: true });
this._categoryScroll = new St.ScrollView({ x_fill: false,
y_fill: false,
style_class: 'vfade' });
this._categoryScroll.add_actor(this._categoryBox);
this.actor.add(this._view.actor, { expand: true, x_fill: true, y_fill: true });
this.actor.add(this._categoryScroll, { expand: false, y_fill: false, y_align: St.Align.START });
this.actor.add(this._filters, { expand: false, y_fill: false, y_align: St.Align.START });
// Always select the "All" filter when switching to the app view
this.actor.connect('notify::mapped', Lang.bind(this,
function() {
if (this.actor.mapped && this._allCategoryButton)
if (this.actor.mapped && this._allFilter)
this._selectCategory(-1);
}));
this._sections = [];
// We need a dummy actor to catch the keyboard focus if the
// user Ctrl-Alt-Tabs here before the deferred work creates
// our real contents
@@ -174,101 +193,78 @@ ViewByCategories.prototype = {
this.actor.add(this._focusDummy);
},
_scrollFilter: function(actor, event) {
let direction = event.get_scroll_direction();
if (direction == Clutter.ScrollDirection.UP)
this._selectCategory(Math.max(this._currentCategory - 1, -1))
else if (direction == Clutter.ScrollDirection.DOWN)
this._selectCategory(Math.min(this._currentCategory + 1, this._sections.length - 1));
},
_selectCategory: function(num) {
if (this._currentCategory == num) // nothing to do
return;
this._currentCategory = num;
if (num != -1) {
var category = this._categories[num];
this._allCategoryButton.remove_style_pseudo_class('selected');
this._view.setVisibleApps(category.apps);
} else {
this._allCategoryButton.add_style_pseudo_class('selected');
this._view.setVisibleApps(null);
}
if (num != -1)
this._allFilter.remove_style_pseudo_class('selected');
else
this._allFilter.add_style_pseudo_class('selected');
for (var i = 0; i < this._categories.length; i++) {
this._view.setFilter(Lang.bind(this, function(app) {
if (num == -1)
return true;
return this._sections[num].name == app.get_section();
}));
for (let i = 0; i < this._sections.length; i++) {
if (i == num)
this._categories[i].button.add_style_pseudo_class('selected');
this._sections[i].filterActor.add_style_pseudo_class('selected');
else
this._categories[i].button.remove_style_pseudo_class('selected');
this._sections[i].filterActor.remove_style_pseudo_class('selected');
}
},
// Recursively load a GMenuTreeDirectory; we could put this in ShellAppSystem too
_loadCategory: function(dir, appList) {
var iter = dir.iter();
var nextType;
while ((nextType = iter.next()) != GMenu.TreeItemType.INVALID) {
if (nextType == GMenu.TreeItemType.ENTRY) {
var entry = iter.get_entry();
var app = this._appSystem.lookup_app_by_tree_entry(entry);
if (!entry.get_app_info().get_nodisplay())
appList.push(app);
} else if (nextType == GMenu.TreeItemType.DIRECTORY) {
this._loadCategory(iter.get_directory(), appList);
}
}
},
_addCategory: function(name, index, dir, allApps) {
_addFilter: function(name, num) {
let button = new St.Button({ label: GLib.markup_escape_text (name, -1),
style_class: 'app-filter',
x_align: St.Align.START,
can_focus: true });
this._filters.add(button, { expand: true, x_fill: true, y_fill: false });
button.connect('clicked', Lang.bind(this, function() {
this._selectCategory(index);
this._selectCategory(num);
}));
var apps;
if (dir == null) {
apps = allApps;
this._allCategoryButton = button;
} else {
apps = [];
this._loadCategory(dir, apps);
this._categories.push({ apps: apps,
name: name,
button: button });
}
this._categoryBox.add(button, { expand: true, x_fill: true, y_fill: false });
if (num != -1)
this._sections[num] = { filterActor: button,
name: name };
else
this._allFilter = button;
},
_removeAll: function() {
this._categories = [];
this._categoryBox.destroy_children();
this._sections = [];
this._filters.destroy_children();
},
refresh: function() {
refresh: function(apps) {
this._removeAll();
var allApps = Shell.AppSystem.get_default().get_all();
allApps.sort(function(a, b) {
return a.compare_by_name(b);
});
let sections = this._appSystem.get_sections();
this._apps = apps;
/* Translators: Filter to display all applications */
this._addCategory(_("All"), -1, null, allApps);
this._addFilter(_("All"), -1);
var tree = this._appSystem.get_tree();
var root = tree.get_root_directory();
if (!sections)
return;
var iter = root.iter();
var nextType;
var i = 0;
while ((nextType = iter.next()) != GMenu.TreeItemType.INVALID) {
if (nextType == GMenu.TreeItemType.DIRECTORY) {
var dir = iter.get_directory();
this._addCategory(dir.get_name(), i, dir);
i++;
}
}
for (let i = 0; i < sections.length; i++)
this._addFilter(sections[i], i);
this._view.setAppList(allApps);
this._selectCategory(-1);
this._view.refresh(apps);
if (this._focusDummy) {
let focused = this._focusDummy.has_key_focus();
@@ -301,7 +297,52 @@ AllAppDisplay.prototype = {
},
_redisplay: function() {
this._appView.refresh();
let apps = this._appSystem.get_flattened_apps().filter(function(app) {
return !app.get_is_nodisplay();
});
this._appView.refresh(apps);
}
};
function BaseAppSearchProvider() {
this._init();
}
BaseAppSearchProvider.prototype = {
__proto__: Search.SearchProvider.prototype,
_init: function(name) {
Search.SearchProvider.prototype._init.call(this, name);
this._appSys = Shell.AppSystem.get_default();
},
getResultMeta: function(resultId) {
let app = this._appSys.get_app(resultId);
if (!app)
return null;
return { 'id': resultId,
'name': app.get_name(),
'createIcon': function(size) {
return app.create_icon_texture(size);
}
};
},
activateResult: function(id, params) {
params = Params.parse(params, { workspace: null,
timestamp: null });
let app = this._appSys.get_app(id);
app.activate(params.workspace ? params.workspace.index() : -1);
},
dragActivateResult: function(id, params) {
params = Params.parse(params, { workspace: null,
timestamp: null });
let app = this._appSys.get_app(id);
app.open_new_window(params.workspace ? params.workspace.index() : -1);
}
};
@@ -310,104 +351,44 @@ function AppSearchProvider() {
}
AppSearchProvider.prototype = {
__proto__: Search.SearchProvider.prototype,
__proto__: BaseAppSearchProvider.prototype,
_init: function() {
Search.SearchProvider.prototype._init.call(this, _("APPLICATIONS"));
this._appSys = Shell.AppSystem.get_default();
},
getResultMeta: function(app) {
return { 'id': app,
'name': app.get_name(),
'createIcon': function(size) {
return app.create_icon_texture(size);
}
};
BaseAppSearchProvider.prototype._init.call(this, _("APPLICATIONS"));
},
getInitialResultSet: function(terms) {
return this._appSys.initial_search(terms);
return this._appSys.initial_search(false, terms);
},
getSubsearchResultSet: function(previousResults, terms) {
return this._appSys.subsearch(previousResults, terms);
},
activateResult: function(app, params) {
params = Params.parse(params, { workspace: -1,
timestamp: 0 });
let event = Clutter.get_current_event();
let modifiers = event ? Shell.get_event_state(event) : 0;
let openNewWindow = modifiers & Clutter.ModifierType.CONTROL_MASK;
if (openNewWindow)
app.open_new_window(params.workspace);
else
app.activate_full(params.workspace, params.timestamp);
},
dragActivateResult: function(id, params) {
params = Params.parse(params, { workspace: -1,
timestamp: 0 });
let app = this._appSys.lookup_app(id);
app.open_new_window(workspace);
return this._appSys.subsearch(false, previousResults, terms);
},
createResultActor: function (resultMeta, terms) {
let app = resultMeta['id'];
let app = this._appSys.get_app(resultMeta['id']);
let icon = new AppWellIcon(app);
return icon.actor;
}
};
function SettingsSearchProvider() {
function PrefsSearchProvider() {
this._init();
}
SettingsSearchProvider.prototype = {
__proto__: Search.SearchProvider.prototype,
PrefsSearchProvider.prototype = {
__proto__: BaseAppSearchProvider.prototype,
_init: function() {
Search.SearchProvider.prototype._init.call(this, _("SETTINGS"));
this._appSys = Shell.AppSystem.get_default();
this._gnomecc = this._appSys.lookup_app('gnome-control-center.desktop');
},
getResultMeta: function(pref) {
return { 'id': pref,
'name': pref.get_name(),
'createIcon': function(size) {
return pref.create_icon_texture(size);
}
};
BaseAppSearchProvider.prototype._init.call(this, _("SETTINGS"));
},
getInitialResultSet: function(terms) {
return this._appSys.search_settings(terms);
return this._appSys.initial_search(true, terms);
},
getSubsearchResultSet: function(previousResults, terms) {
return this._appSys.search_settings(terms);
},
activateResult: function(pref, params) {
params = Params.parse(params, { workspace: -1,
timestamp: 0 });
pref.activate_full(params.workspace, params.timestamp);
},
dragActivateResult: function(pref, params) {
this.activateResult(pref, params);
},
createResultActor: function (resultMeta, terms) {
let app = resultMeta['id'];
let icon = new AppWellIcon(app);
return icon.actor;
return this._appSys.subsearch(true, previousResults, terms);
}
};
@@ -433,12 +414,12 @@ AppIcon.prototype = {
}
};
function AppWellIcon(app, iconParams, onActivateOverride) {
this._init(app, iconParams, onActivateOverride);
function AppWellIcon(app, iconParams) {
this._init(app, iconParams);
}
AppWellIcon.prototype = {
_init : function(app, iconParams, onActivateOverride) {
_init : function(app, iconParams) {
this.app = app;
this.actor = new St.Button({ style_class: 'app-well-app',
reactive: true,
@@ -453,8 +434,6 @@ AppWellIcon.prototype = {
this.actor.label_actor = this.icon.label;
// A function callback to override the default "app.activate()"; used by preferences
this._onActivateOverride = onActivateOverride;
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
this.actor.connect('popup-menu', Lang.bind(this, this._onKeyboardPopupMenu));
@@ -588,28 +567,24 @@ AppWellIcon.prototype = {
this.emit('launching');
let modifiers = Shell.get_event_state(event);
if (this._onActivateOverride) {
this._onActivateOverride(event);
if (modifiers & Clutter.ModifierType.CONTROL_MASK
&& this.app.state == Shell.AppState.RUNNING) {
this.app.open_new_window(-1);
} else {
if (modifiers & Clutter.ModifierType.CONTROL_MASK
&& this.app.state == Shell.AppState.RUNNING) {
this.app.open_new_window(-1);
} else {
this.app.activate();
}
this.app.activate(-1);
}
Main.overview.hide();
},
shellWorkspaceLaunch : function(params) {
params = Params.parse(params, { workspace: -1,
timestamp: 0 });
params = Params.parse(params, { workspace: null,
timestamp: null });
this.app.open_new_window(params.workspace);
this.app.open_new_window(params.workspace ? params.workspace.index() : -1);
},
getDragActor: function() {
return this.app.create_icon_texture(Main.overview.dashIconSize);
return this.app.create_icon_texture(Main.overview.dash.iconSize);
},
// Returns the original actor that should align with the actor
@@ -632,7 +607,7 @@ AppIconMenu.prototype = {
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
side = St.Side.RIGHT;
PopupMenu.PopupMenu.prototype._init.call(this, source.actor, 0.5, side);
PopupMenu.PopupMenu.prototype._init.call(this, source.actor, 0.5, side, 0);
// We want to keep the item hovered while the menu is up
this.blockSourceEvents = true;
@@ -673,18 +648,17 @@ AppIconMenu.prototype = {
item._window = windows[i];
}
if (!this._source.app.is_window_backed()) {
if (windows.length > 0)
this._appendSeparator();
let isFavorite = AppFavorites.getAppFavorites().isFavorite(this._source.app.get_id());
this._newWindowMenuItem = this._appendMenuItem(_("New Window"));
if (windows.length > 0)
this._appendSeparator();
this._toggleFavoriteMenuItem = this._appendMenuItem(isFavorite ? _("Remove from Favorites")
: _("Add to Favorites"));
}
let isFavorite = AppFavorites.getAppFavorites().isFavorite(this._source.app.get_id());
this._newWindowMenuItem = this._appendMenuItem(_("New Window"));
this._appendSeparator();
this._toggleFavoriteMenuItem = this._appendMenuItem(isFavorite ? _("Remove from Favorites")
: _("Add to Favorites"));
},
_appendSeparator: function () {

View File

@@ -28,7 +28,7 @@ AppFavorites.prototype = {
let ids = global.settings.get_strv(this.FAVORITE_APPS_KEY);
let appSys = Shell.AppSystem.get_default();
let apps = ids.map(function (id) {
return appSys.lookup_app(id);
return appSys.get_app(id);
}).filter(function (app) {
return app != null;
});
@@ -65,7 +65,7 @@ AppFavorites.prototype = {
if (appId in this._favorites)
return false;
let app = Shell.AppSystem.get_default().lookup_app(appId);
let app = Shell.AppSystem.get_default().get_app(appId);
if (!app)
return false;
@@ -84,9 +84,9 @@ AppFavorites.prototype = {
if (!this._addFavorite(appId, pos))
return;
let app = Shell.AppSystem.get_default().lookup_app(appId);
let app = Shell.AppSystem.get_default().get_app(appId);
Main.overview.setMessage(_("%s has been added to your favorites.").format(app.get_name()), Lang.bind(this, function () {
Main.overview.shellInfo.setMessage(_("%s has been added to your favorites.").format(app.get_name()), Lang.bind(this, function () {
this._removeFavorite(appId);
}));
},
@@ -117,8 +117,8 @@ AppFavorites.prototype = {
if (!this._removeFavorite(appId))
return;
Main.overview.setMessage(_("%s has been removed from your favorites.").format(app.get_name()),
Lang.bind(this, function () {
Main.overview.shellInfo.setMessage(_("%s has been removed from your favorites.").format(app.get_name()),
Lang.bind(this, function () {
this._addFavorite(appId, pos);
}));
}

View File

@@ -1,278 +0,0 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Lang = imports.lang;
const DBus = imports.dbus;
const Mainloop = imports.mainloop;
const Gio = imports.gi.Gio;
const Params = imports.misc.params;
const Main = imports.ui.main;
const ShellMountOperation = imports.ui.shellMountOperation;
const ScreenSaver = imports.misc.screenSaver;
// GSettings keys
const SETTINGS_SCHEMA = 'org.gnome.desktop.media-handling';
const SETTING_ENABLE_AUTOMOUNT = 'automount';
const AUTORUN_EXPIRE_TIMEOUT_SECS = 10;
const ConsoleKitSessionIface = {
name: 'org.freedesktop.ConsoleKit.Session',
methods: [{ name: 'IsActive',
inSignature: '',
outSignature: 'b' }],
signals: [{ name: 'ActiveChanged',
inSignature: 'b' }]
};
const ConsoleKitSessionProxy = DBus.makeProxyClass(ConsoleKitSessionIface);
const ConsoleKitManagerIface = {
name: 'org.freedesktop.ConsoleKit.Manager',
methods: [{ name: 'GetCurrentSession',
inSignature: '',
outSignature: 'o' }]
};
function ConsoleKitManager() {
this._init();
};
ConsoleKitManager.prototype = {
_init: function() {
this.sessionActive = true;
DBus.system.proxifyObject(this,
'org.freedesktop.ConsoleKit',
'/org/freedesktop/ConsoleKit/Manager');
DBus.system.watch_name('org.freedesktop.ConsoleKit',
false, // do not launch a name-owner if none exists
Lang.bind(this, this._onManagerAppeared),
Lang.bind(this, this._onManagerVanished));
},
_onManagerAppeared: function(owner) {
this.GetCurrentSessionRemote(Lang.bind(this, this._onCurrentSession));
},
_onManagerVanished: function(oldOwner) {
this.sessionActive = true;
},
_onCurrentSession: function(session) {
this._ckSession = new ConsoleKitSessionProxy(DBus.system, 'org.freedesktop.ConsoleKit', session);
this._ckSession.connect
('ActiveChanged', Lang.bind(this, function(object, isActive) {
this.sessionActive = isActive;
}));
this._ckSession.IsActiveRemote(Lang.bind(this, function(isActive) {
this.sessionActive = isActive;
}));
}
};
DBus.proxifyPrototype(ConsoleKitManager.prototype, ConsoleKitManagerIface);
function AutomountManager() {
this._init();
}
AutomountManager.prototype = {
_init: function() {
this._settings = new Gio.Settings({ schema: SETTINGS_SCHEMA });
this._volumeQueue = [];
this.ckListener = new ConsoleKitManager();
this._ssProxy = new ScreenSaver.ScreenSaverProxy();
this._ssProxy.connect('ActiveChanged',
Lang.bind(this,
this._screenSaverActiveChanged));
this._volumeMonitor = Gio.VolumeMonitor.get();
this._volumeMonitor.connect('volume-added',
Lang.bind(this,
this._onVolumeAdded));
this._volumeMonitor.connect('volume-removed',
Lang.bind(this,
this._onVolumeRemoved));
this._volumeMonitor.connect('drive-connected',
Lang.bind(this,
this._onDriveConnected));
this._volumeMonitor.connect('drive-disconnected',
Lang.bind(this,
this._onDriveDisconnected));
this._volumeMonitor.connect('drive-eject-button',
Lang.bind(this,
this._onDriveEjectButton));
Mainloop.idle_add(Lang.bind(this, this._startupMountAll));
},
_screenSaverActiveChanged: function(object, isActive) {
if (!isActive) {
this._volumeQueue.forEach(Lang.bind(this, function(volume) {
this._checkAndMountVolume(volume);
}));
}
// clear the queue anyway
this._volumeQueue = [];
},
_startupMountAll: function() {
let volumes = this._volumeMonitor.get_volumes();
volumes.forEach(Lang.bind(this, function(volume) {
this._checkAndMountVolume(volume, { checkSession: false,
useMountOp: false });
}));
return false;
},
_onDriveConnected: function() {
// if we're not in the current ConsoleKit session,
// or screensaver is active, don't play sounds
if (!this.ckListener.sessionActive)
return;
if (this._ssProxy.screenSaverActive)
return;
global.play_theme_sound(0, 'device-added-media');
},
_onDriveDisconnected: function() {
// if we're not in the current ConsoleKit session,
// or screensaver is active, don't play sounds
if (!this.ckListener.sessionActive)
return;
if (this._ssProxy.screenSaverActive)
return;
global.play_theme_sound(0, 'device-removed-media');
},
_onDriveEjectButton: function(monitor, drive) {
// TODO: this code path is not tested, as the GVfs volume monitor
// doesn't emit this signal just yet.
if (!this.ckListener.sessionActive)
return;
// we force stop/eject in this case, so we don't have to pass a
// mount operation object
if (drive.can_stop()) {
drive.stop
(Gio.MountUnmountFlags.FORCE, null, null,
Lang.bind(this, function(drive, res) {
try {
drive.stop_finish(res);
} catch (e) {
log("Unable to stop the drive after drive-eject-button " + e.toString());
}
}));
} else if (drive.can_eject()) {
drive.eject_with_operation
(Gio.MountUnmountFlags.FORCE, null, null,
Lang.bind(this, function(drive, res) {
try {
drive.eject_with_operation_finish(res);
} catch (e) {
log("Unable to eject the drive after drive-eject-button " + e.toString());
}
}));
}
},
_onVolumeAdded: function(monitor, volume) {
this._checkAndMountVolume(volume);
},
_checkAndMountVolume: function(volume, params) {
params = Params.parse(params, { checkSession: true,
useMountOp: true });
if (params.checkSession) {
// if we're not in the current ConsoleKit session,
// don't attempt automount
if (!this.ckListener.sessionActive)
return;
if (this._ssProxy.screenSaverActive) {
if (this._volumeQueue.indexOf(volume) == -1)
this._volumeQueue.push(volume);
return;
}
}
if (!this._settings.get_boolean(SETTING_ENABLE_AUTOMOUNT) ||
!volume.should_automount() ||
!volume.can_mount()) {
// allow the autorun to run anyway; this can happen if the
// mount gets added programmatically later, even if
// should_automount() or can_mount() are false, like for
// blank optical media.
this._allowAutorun(volume);
this._allowAutorunExpire(volume);
return;
}
if (params.useMountOp) {
let operation = new ShellMountOperation.ShellMountOperation(volume);
this._mountVolume(volume, operation.mountOp);
} else {
this._mountVolume(volume, null);
}
},
_mountVolume: function(volume, operation) {
this._allowAutorun(volume);
volume.mount(0, operation, null,
Lang.bind(this, this._onVolumeMounted));
},
_onVolumeMounted: function(volume, res) {
this._allowAutorunExpire(volume);
try {
volume.mount_finish(res);
} catch (e) {
let string = e.toString();
// FIXME: needs proper error code handling instead of this
// See https://bugzilla.gnome.org/show_bug.cgi?id=591480
if (string.indexOf('No key available with this passphrase') != -1)
this._reaskPassword(volume);
else
log('Unable to mount volume ' + volume.get_name() + ': ' + string);
}
},
_onVolumeRemoved: function(monitor, volume) {
this._volumeQueue =
this._volumeQueue.filter(function(element) {
return (element != volume);
});
},
_reaskPassword: function(volume) {
let operation = new ShellMountOperation.ShellMountOperation(volume, { reaskPassword: true });
this._mountVolume(volume, operation.mountOp);
},
_allowAutorun: function(volume) {
volume.allowAutorun = true;
},
_allowAutorunExpire: function(volume) {
Mainloop.timeout_add_seconds(AUTORUN_EXPIRE_TIMEOUT_SECS, function() {
volume.allowAutorun = false;
return false;
});
}
}

View File

@@ -1,635 +0,0 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Lang = imports.lang;
const DBus = imports.dbus;
const Gio = imports.gi.Gio;
const St = imports.gi.St;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const ShellMountOperation = imports.ui.shellMountOperation;
// GSettings keys
const SETTINGS_SCHEMA = 'org.gnome.desktop.media-handling';
const SETTING_DISABLE_AUTORUN = 'autorun-never';
const SETTING_START_APP = 'autorun-x-content-start-app';
const SETTING_IGNORE = 'autorun-x-content-ignore';
const SETTING_OPEN_FOLDER = 'autorun-x-content-open-folder';
const AutorunSetting = {
RUN: 0,
IGNORE: 1,
FILES: 2,
ASK: 3
};
const HOTPLUG_ICON_SIZE = 16;
// misc utils
function ignoreAutorunForMount(mount) {
let root = mount.get_root();
let volume = mount.get_volume();
if ((root.is_native() && !isMountRootHidden(root)) ||
(volume && volume.allowAutorun && volume.should_automount()))
return false;
return true;
}
function isMountRootHidden(root) {
let path = root.get_path();
// skip any mounts in hidden directory hierarchies
return (path.indexOf('/.') != -1);
}
function startAppForMount(app, mount) {
let files = [];
let root = mount.get_root();
let retval = false;
files.push(root);
try {
retval = app.launch(files,
global.create_app_launch_context())
} catch (e) {
log('Unable to launch the application ' + app.get_name()
+ ': ' + e.toString());
}
return retval;
}
/******************************************/
const HotplugSnifferIface = {
name: 'org.gnome.Shell.HotplugSniffer',
methods: [{ name: 'SniffURI',
inSignature: 's',
outSignature: 'as' }]
};
const HotplugSniffer = function() {
this._init();
};
HotplugSniffer.prototype = {
_init: function() {
DBus.session.proxifyObject(this,
'org.gnome.Shell.HotplugSniffer',
'/org/gnome/Shell/HotplugSniffer');
},
};
DBus.proxifyPrototype(HotplugSniffer.prototype, HotplugSnifferIface);
function ContentTypeDiscoverer(callback) {
this._init(callback);
}
ContentTypeDiscoverer.prototype = {
_init: function(callback) {
this._callback = callback;
},
guessContentTypes: function(mount) {
// guess mount's content types using GIO
mount.guess_content_type(false, null,
Lang.bind(this,
this._onContentTypeGuessed));
},
_onContentTypeGuessed: function(mount, res) {
let contentTypes = [];
try {
contentTypes = mount.guess_content_type_finish(res);
} catch (e) {
log('Unable to guess content types on added mount ' + mount.get_name()
+ ': ' + e.toString());
}
if (contentTypes.length) {
this._emitCallback(mount, contentTypes);
} else {
let root = mount.get_root();
let hotplugSniffer = new HotplugSniffer();
hotplugSniffer.SniffURIRemote
(root.get_uri(), DBus.CALL_FLAG_START,
Lang.bind(this, function(contentTypes) {
this._emitCallback(mount, contentTypes);
}));
}
},
_emitCallback: function(mount, contentTypes) {
if (!contentTypes)
contentTypes = [];
// we're not interested in win32 software content types here
contentTypes = contentTypes.filter(function(type) {
return (type != 'x-content/win32-software');
});
let apps = [];
contentTypes.forEach(function(type) {
let app = Gio.app_info_get_default_for_type(type, false);
if (app)
apps.push(app);
});
if (apps.length == 0)
apps.push(Gio.app_info_get_default_for_type('inode/directory', false));
this._callback(mount, apps, contentTypes);
}
}
function AutorunManager() {
this._init();
}
AutorunManager.prototype = {
_init: function() {
this._volumeMonitor = Gio.VolumeMonitor.get();
this._volumeMonitor.connect('mount-added',
Lang.bind(this,
this._onMountAdded));
this._volumeMonitor.connect('mount-removed',
Lang.bind(this,
this._onMountRemoved));
this._transDispatcher = new AutorunTransientDispatcher();
this._createResidentSource();
let mounts = this._volumeMonitor.get_mounts();
mounts.forEach(Lang.bind(this, function (mount) {
let discoverer = new ContentTypeDiscoverer(Lang.bind (this,
function (mount, apps) {
this._residentSource.addMount(mount, apps);
}));
discoverer.guessContentTypes(mount);
}));
},
_createResidentSource: function() {
this._residentSource = new AutorunResidentSource();
this._residentSource.connect('destroy',
Lang.bind(this,
this._createResidentSource));
},
_onMountAdded: function(monitor, mount) {
// don't do anything if our session is not the currently
// active one
if (!Main.automountManager.ckListener.sessionActive)
return;
let discoverer = new ContentTypeDiscoverer(Lang.bind (this,
function (mount, apps, contentTypes) {
this._transDispatcher.addMount(mount, apps, contentTypes);
this._residentSource.addMount(mount, apps);
}));
discoverer.guessContentTypes(mount);
},
_onMountRemoved: function(monitor, mount) {
this._transDispatcher.removeMount(mount);
this._residentSource.removeMount(mount);
},
ejectMount: function(mount) {
let mountOp = new ShellMountOperation.ShellMountOperation(mount);
// first, see if we have a drive
let drive = mount.get_drive();
let volume = mount.get_volume();
if (drive &&
drive.get_start_stop_type() == Gio.DriveStartStopType.SHUTDOWN &&
drive.can_stop()) {
drive.stop(0, mountOp.mountOp, null,
Lang.bind(this, this._onStop));
} else {
if (mount.can_eject()) {
mount.eject_with_operation(0, mountOp.mountOp, null,
Lang.bind(this, this._onEject));
} else if (volume && volume.can_eject()) {
volume.eject_with_operation(0, mountOp.mountOp, null,
Lang.bind(this, this._onEject));
} else if (drive && drive.can_eject()) {
drive.eject_with_operation(0, mountOp.mountOp, null,
Lang.bind(this, this._onEject));
} else if (mount.can_unmount()) {
mount.unmount_with_operation(0, mountOp.mountOp, null,
Lang.bind(this, this._onUnmount));
}
}
},
_onUnmount: function(mount, res) {
try {
mount.unmount_with_operation_finish(res);
} catch (e) {
// FIXME: we need to ignore G_IO_ERROR_FAILED_HANDLED errors here
// but we can't access the error code from JS.
// See https://bugzilla.gnome.org/show_bug.cgi?id=591480
log('Unable to eject the mount ' + mount.get_name()
+ ': ' + e.toString());
}
},
_onEject: function(source, res) {
try {
source.eject_with_operation_finish(res);
} catch (e) {
// FIXME: we need to ignore G_IO_ERROR_FAILED_HANDLED errors here
// but we can't access the error code from JS.
// See https://bugzilla.gnome.org/show_bug.cgi?id=591480
log('Unable to eject the drive ' + source.get_name()
+ ': ' + e.toString());
}
},
_onStop: function(drive, res) {
try {
drive.stop_finish(res);
} catch (e) {
// FIXME: we need to ignore G_IO_ERROR_FAILED_HANDLED errors here
// but we can't access the error code from JS.
// See https://bugzilla.gnome.org/show_bug.cgi?id=591480
log('Unable to stop the drive ' + drive.get_name()
+ ': ' + e.toString());
}
},
}
function AutorunResidentSource() {
this._init();
}
AutorunResidentSource.prototype = {
__proto__: MessageTray.Source.prototype,
_init: function() {
MessageTray.Source.prototype._init.call(this, _('Removable Devices'));
this._mounts = [];
this._notification = new AutorunResidentNotification(this);
this._setSummaryIcon(this.createNotificationIcon(HOTPLUG_ICON_SIZE));
},
addMount: function(mount, apps) {
if (ignoreAutorunForMount(mount))
return;
let filtered = this._mounts.filter(function (element) {
return (element.mount == mount);
});
if (filtered.length != 0)
return;
let element = { mount: mount, apps: apps };
this._mounts.push(element);
this._redisplay();
},
removeMount: function(mount) {
this._mounts =
this._mounts.filter(function (element) {
return (element.mount != mount);
});
this._redisplay();
},
_redisplay: function() {
if (this._mounts.length == 0) {
this._notification.destroy();
this.destroy();
return;
}
this._notification.updateForMounts(this._mounts);
// add ourselves as a source, and push the notification
if (!Main.messageTray.contains(this)) {
Main.messageTray.add(this);
this.pushNotification(this._notification);
}
},
createNotificationIcon: function(iconSize) {
return new St.Icon ({ icon_name: 'media-removable',
icon_type: St.IconType.FULLCOLOR,
icon_size: iconSize ? iconSize : this.ICON_SIZE });
}
}
function AutorunResidentNotification(source) {
this._init(source);
}
AutorunResidentNotification.prototype = {
__proto__: MessageTray.Notification.prototype,
_init: function(source) {
MessageTray.Notification.prototype._init.call(this, source,
source.title, null,
{ customContent: true });
// set the notification as resident
this.setResident(true);
this._layout = new St.BoxLayout ({ style_class: 'hotplug-resident-box',
vertical: true });
this.addActor(this._layout,
{ x_expand: true,
x_fill: true });
},
updateForMounts: function(mounts) {
// remove all the layout content
this._layout.destroy_children();
for (let idx = 0; idx < mounts.length; idx++) {
let element = mounts[idx];
let actor = this._itemForMount(element.mount, element.apps);
this._layout.add(actor, { x_fill: true,
expand: true });
}
},
_itemForMount: function(mount, apps) {
let item = new St.BoxLayout();
// prepare the mount button content
let mountLayout = new St.BoxLayout();
let mountIcon = new St.Icon({ gicon: mount.get_icon(),
style_class: 'hotplug-resident-mount-icon' });
mountLayout.add_actor(mountIcon);
let labelBin = new St.Bin({ y_align: St.Align.MIDDLE });
let mountLabel =
new St.Label({ text: mount.get_name(),
style_class: 'hotplug-resident-mount-label',
track_hover: true,
reactive: true });
labelBin.add_actor(mountLabel);
mountLayout.add_actor(labelBin);
let mountButton = new St.Button({ child: mountLayout,
x_align: St.Align.START,
x_fill: true,
style_class: 'hotplug-resident-mount',
button_mask: St.ButtonMask.ONE });
item.add(mountButton, { x_align: St.Align.START,
expand: true });
let ejectIcon =
new St.Icon({ icon_name: 'media-eject',
style_class: 'hotplug-resident-eject-icon' });
let ejectButton =
new St.Button({ style_class: 'hotplug-resident-eject-button',
button_mask: St.ButtonMask.ONE,
child: ejectIcon });
item.add(ejectButton, { x_align: St.Align.END });
// now connect signals
mountButton.connect('clicked', Lang.bind(this, function(actor, event) {
startAppForMount(apps[0], mount);
}));
ejectButton.connect('clicked', Lang.bind(this, function() {
Main.autorunManager.ejectMount(mount);
}));
return item;
},
}
function AutorunTransientDispatcher() {
this._init();
}
AutorunTransientDispatcher.prototype = {
_init: function() {
this._sources = [];
this._settings = new Gio.Settings({ schema: SETTINGS_SCHEMA });
},
_getAutorunSettingForType: function(contentType) {
let runApp = this._settings.get_strv(SETTING_START_APP);
if (runApp.indexOf(contentType) != -1)
return AutorunSetting.RUN;
let ignore = this._settings.get_strv(SETTING_IGNORE);
if (ignore.indexOf(contentType) != -1)
return AutorunSetting.IGNORE;
let openFiles = this._settings.get_strv(SETTING_OPEN_FOLDER);
if (openFiles.indexOf(contentType) != -1)
return AutorunSetting.FILES;
return AutorunSetting.ASK;
},
_getSourceForMount: function(mount) {
let filtered =
this._sources.filter(function (source) {
return (source.mount == mount);
});
// we always make sure not to add two sources for the same
// mount in addMount(), so it's safe to assume filtered.length
// is always either 1 or 0.
if (filtered.length == 1)
return filtered[0];
return null;
},
_addSource: function(mount, apps) {
// if we already have a source showing for this
// mount, return
if (this._getSourceForMount(mount))
return;
// add a new source
this._sources.push(new AutorunTransientSource(mount, apps));
},
addMount: function(mount, apps, contentTypes) {
// if autorun is disabled globally, return
if (this._settings.get_boolean(SETTING_DISABLE_AUTORUN))
return;
// if the mount doesn't want to be autorun, return
if (ignoreAutorunForMount(mount))
return;
let setting = this._getAutorunSettingForType(contentTypes[0]);
// check at the settings for the first content type
// to see whether we should ask
if (setting == AutorunSetting.IGNORE)
return; // return right away
let success = false;
let app = null;
if (setting == AutorunSetting.RUN) {
app = Gio.app_info_get_default_for_type(type, false);
} else if (setting == AutorunSetting.FILES) {
app = Gio.app_info_get_default_for_type('inode/directory', false);
}
if (app)
success = startAppForMount(app, mount);
// we fallback here also in case the settings did not specify 'ask',
// but we failed launching the default app or the default file manager
if (!success)
this._addSource(mount, apps);
},
removeMount: function(mount) {
let source = this._getSourceForMount(mount);
// if we aren't tracking this mount, don't do anything
if (!source)
return;
// destroy the notification source
source.destroy();
}
}
function AutorunTransientSource(mount, apps) {
this._init(mount, apps);
}
AutorunTransientSource.prototype = {
__proto__: MessageTray.Source.prototype,
_init: function(mount, apps) {
MessageTray.Source.prototype._init.call(this, mount.get_name());
this.mount = mount;
this.apps = apps;
this._notification = new AutorunTransientNotification(this);
this._setSummaryIcon(this.createNotificationIcon(this.ICON_SIZE));
// add ourselves as a source, and popup the notification
Main.messageTray.add(this);
this.notify(this._notification);
},
createNotificationIcon: function(iconSize) {
return new St.Icon({ gicon: this.mount.get_icon(),
icon_size: iconSize ? iconSize : this.ICON_SIZE });
}
}
function AutorunTransientNotification(source) {
this._init(source);
}
AutorunTransientNotification.prototype = {
__proto__: MessageTray.Notification.prototype,
_init: function(source) {
MessageTray.Notification.prototype._init.call(this, source,
source.title, null,
{ customContent: true });
this._box = new St.BoxLayout({ style_class: 'hotplug-transient-box',
vertical: true });
this.addActor(this._box);
this._mount = source.mount;
source.apps.forEach(Lang.bind(this, function (app) {
let actor = this._buttonForApp(app);
if (actor)
this._box.add(actor, { x_fill: true,
x_align: St.Align.START });
}));
this._box.add(this._buttonForEject(), { x_fill: true,
x_align: St.Align.START });
// set the notification to transient and urgent, so that it
// expands out
this.setTransient(true);
this.setUrgency(MessageTray.Urgency.CRITICAL);
},
_buttonForApp: function(app) {
let box = new St.BoxLayout();
let icon = new St.Icon({ gicon: app.get_icon(),
style_class: 'hotplug-notification-item-icon' });
box.add(icon);
let label = new St.Bin({ y_align: St.Align.MIDDLE,
child: new St.Label
({ text: _("Open with %s").format(app.get_display_name()) })
});
box.add(label);
let button = new St.Button({ child: box,
x_fill: true,
x_align: St.Align.START,
button_mask: St.ButtonMask.ONE,
style_class: 'hotplug-notification-item' });
button.connect('clicked', Lang.bind(this, function() {
startAppForMount(app, this._mount);
this.destroy();
}));
return button;
},
_buttonForEject: function() {
let box = new St.BoxLayout();
let icon = new St.Icon({ icon_name: 'media-eject',
style_class: 'hotplug-notification-item-icon' });
box.add(icon);
let label = new St.Bin({ y_align: St.Align.MIDDLE,
child: new St.Label
({ text: _("Eject") })
});
box.add(label);
let button = new St.Button({ child: box,
x_fill: true,
x_align: St.Align.START,
button_mask: St.ButtonMask.ONE,
style_class: 'hotplug-notification-item' });
button.connect('clicked', Lang.bind(this, function() {
Main.autorunManager.ejectMount(this._mount);
}));
return button;
}
}

View File

@@ -6,7 +6,6 @@ const Meta = imports.gi.Meta;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
const Main = imports.ui.main;
const Tweener = imports.ui.tweener;
const POPUP_ANIMATION_TIME = 0.15;
@@ -180,7 +179,7 @@ BoxPointer.prototype = {
this.bin.allocate(childBox, flags);
if (this._sourceActor && this._sourceActor.mapped)
this._reposition(this._sourceActor, this._alignment);
this._reposition(this._sourceActor, this._gap, this._alignment);
},
_drawBorder: function(area) {
@@ -306,18 +305,19 @@ BoxPointer.prototype = {
cr.stroke();
},
setPosition: function(sourceActor, alignment) {
setPosition: function(sourceActor, gap, alignment) {
// We need to show it now to force an allocation,
// so that we can query the correct size.
this.actor.show();
this._sourceActor = sourceActor;
this._gap = gap;
this._alignment = alignment;
this._reposition(sourceActor, alignment);
this._reposition(sourceActor, gap, alignment);
},
_reposition: function(sourceActor, alignment) {
_reposition: function(sourceActor, gap, alignment) {
// Position correctly relative to the sourceActor
let sourceNode = sourceActor.get_theme_node();
let sourceContentBox = sourceNode.get_content_box(sourceActor.get_allocation_box());
@@ -329,7 +329,7 @@ BoxPointer.prototype = {
// We also want to keep it onscreen, and separated from the
// edge by the same distance as the main part of the box is
// separated from its sourceActor
let primary = Main.layoutManager.primaryMonitor;
let primary = global.get_primary_monitor();
let themeNode = this.actor.get_theme_node();
let borderWidth = themeNode.get_length('-arrow-border-width');
let arrowBase = themeNode.get_length('-arrow-base');
@@ -337,9 +337,6 @@ BoxPointer.prototype = {
let margin = (4 * borderRadius + borderWidth + arrowBase);
let halfMargin = margin / 2;
let themeNode = this.actor.get_theme_node();
let gap = themeNode.get_length('-boxpointer-gap');
let resX, resY;
switch (this._arrowSide) {

View File

@@ -351,16 +351,17 @@ function Calendar(eventSource) {
Calendar.prototype = {
_init: function(eventSource) {
if (eventSource) {
this._eventSource = eventSource;
this._eventSource = eventSource;
this._eventSource.connect('changed', Lang.bind(this,
function() {
this._update(false);
}));
}
this._eventSource.connect('changed', Lang.bind(this,
function() {
this._update(false);
}));
this._weekStart = Shell.util_get_week_start();
// FIXME: This is actually the fallback method for GTK+ for the week start;
// GTK+ by preference uses nl_langinfo (NL_TIME_FIRST_WEEKDAY). We probably
// should add a C function so we can do the full handling.
this._weekStart = NaN;
this._weekdate = NaN;
this._digitWidth = NaN;
this._settings = new Gio.Settings({ schema: 'org.gnome.shell.calendar' });
@@ -368,6 +369,16 @@ Calendar.prototype = {
this._settings.connect('changed::' + SHOW_WEEKDATE_KEY, Lang.bind(this, this._onSettingsChange));
this._useWeekdate = this._settings.get_boolean(SHOW_WEEKDATE_KEY);
let weekStartString = Gettext_gtk30.gettext('calendar:week_start:0');
if (weekStartString.indexOf('calendar:week_start:') == 0) {
this._weekStart = parseInt(weekStartString.substring(20));
}
if (isNaN(this._weekStart) || this._weekStart < 0 || this._weekStart > 6) {
log('Translation of "calendar:week_start:0" in GTK+ is not correct');
this._weekStart = 0;
}
// Find the ordering for month/year in the calendar heading
this._headerFormatWithoutYear = '%B';
switch (Gettext_gtk30.gettext('calendar:MY')) {
@@ -556,16 +567,13 @@ Calendar.prototype = {
while (true) {
let button = new St.Button({ label: iter.getDate().toString() });
if (!this._eventSource)
button.reactive = false;
let iterStr = iter.toUTCString();
button.connect('clicked', Lang.bind(this, function() {
let newlySelectedDate = new Date(iterStr);
this.setDate(newlySelectedDate, false);
}));
let hasEvents = this._eventSource && this._eventSource.hasEvents(iter);
let hasEvents = this._eventSource.hasEvents(iter);
let styleClass = 'calendar-day-base calendar-day';
if (_isWorkDay(iter))
styleClass += ' calendar-work-day'
@@ -612,8 +620,7 @@ Calendar.prototype = {
}
// Signal to the event source that we are interested in events
// only from this date range
if (this._eventSource)
this._eventSource.requestRange(beginDate, iter, forceReload);
this._eventSource.requestRange(beginDate, iter, forceReload);
}
};
@@ -631,7 +638,17 @@ EventsList.prototype = {
this._eventSource.connect('changed', Lang.bind(this, this._update));
this._desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
this._desktopSettings.connect('changed', Lang.bind(this, this._update));
this._weekStart = Shell.util_get_week_start();
let weekStartString = Gettext_gtk30.gettext('calendar:week_start:0');
if (weekStartString.indexOf('calendar:week_start:') == 0) {
this._weekStart = parseInt(weekStartString.substring(20));
}
if (isNaN(this._weekStart) ||
this._weekStart < 0 ||
this._weekStart > 6) {
log('Translation of "calendar:week_start:0" in GTK+ is not correct');
this._weekStart = 0;
}
this._update();
},
@@ -650,9 +667,6 @@ EventsList.prototype = {
},
_addPeriod: function(header, begin, end, includeDayName, showNothingScheduled) {
if (!this._eventSource)
return;
let events = this._eventSource.getEvents(begin, end);
let clockFormat = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);;

454
js/ui/chrome.js Normal file
View File

@@ -0,0 +1,454 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const Main = imports.ui.main;
const Params = imports.misc.params;
// This manages the shell "chrome"; the UI that's visible in the
// normal mode (ie, outside the Overview), that surrounds the main
// workspace content.
const defaultParams = {
visibleInOverview: false,
visibleInFullscreen: false,
affectsStruts: true,
affectsInputRegion: true
};
function Chrome() {
this._init();
}
Chrome.prototype = {
_init: function() {
// The group itself has zero size so it doesn't interfere with DND
this.actor = new Shell.GenericContainer({ width: 0, height: 0 });
Main.uiGroup.add_actor(this.actor);
this.actor.connect('allocate', Lang.bind(this, this._allocated));
this._monitors = [];
this._inOverview = false;
this._trackedActors = [];
global.screen.connect('monitors-changed',
Lang.bind(this, this._monitorsChanged));
global.screen.connect('restacked',
Lang.bind(this, this._windowsRestacked));
// Need to update struts on new workspaces when they are added
global.screen.connect('notify::n-workspaces',
Lang.bind(this, this._queueUpdateRegions));
Main.overview.connect('showing',
Lang.bind(this, this._overviewShowing));
Main.overview.connect('hidden',
Lang.bind(this, this._overviewHidden));
this._updateMonitors();
this._updateFullscreen();
this._queueUpdateRegions();
},
_allocated: function(actor, box, flags) {
let children = this.actor.get_children();
for (let i = 0; i < children.length; i++)
children[i].allocate_preferred_size(flags);
},
// addActor:
// @actor: an actor to add to the chrome layer
// @params: (optional) additional params
//
// Adds @actor to the chrome layer and extends the input region
// and window manager struts to include it. (Window manager struts
// will only be affected if @actor is touching a screen edge.)
// Changes in @actor's size and position will automatically result
// in appropriate changes to the input region and struts. Changes
// in its visibility will affect the input region, but NOT the
// struts.
//
// If %visibleInOverview is %true in @params, @actor will remain
// visible when the overview is brought up. Otherwise it will
// automatically be hidden. Likewise, if %visibleInFullscreen is
// %true, the actor will be visible even when a fullscreen window
// should be covering it.
//
// If %affectsStruts or %affectsInputRegion is %false, the actor
// will not have the indicated effect.
addActor: function(actor, params) {
this.actor.add_actor(actor);
this._trackActor(actor, params);
},
// trackActor:
// @actor: a descendant of the chrome to begin tracking
// @params: parameters describing how to track @actor
//
// Tells the chrome to track @actor, which must be a descendant
// of an actor added via addActor(). This can be used to extend the
// struts or input region to cover specific children.
//
// @params can have any of the same values as in addActor(), though
// some possibilities don't make sense (eg, trying to have a
// %visibleInOverview child of a non-%visibleInOverview parent).
// By default, @actor has the same params as its chrome ancestor.
trackActor: function(actor, params) {
let ancestor = actor.get_parent();
let index = this._findActor(ancestor);
while (ancestor && index == -1) {
ancestor = ancestor.get_parent();
index = this._findActor(ancestor);
}
if (!ancestor)
throw new Error('actor is not a descendent of the chrome layer');
let ancestorData = this._trackedActors[index];
if (!params)
params = {};
// We can't use Params.parse here because we want to drop
// the extra values like ancestorData.actor
for (let prop in defaultParams) {
if (!params[prop])
params[prop] = ancestorData[prop];
}
this._trackActor(actor, params);
},
// untrackActor:
// @actor: an actor previously tracked via trackActor()
//
// Undoes the effect of trackActor()
untrackActor: function(actor) {
this._untrackActor(actor);
},
// removeActor:
// @actor: a child of the chrome layer
//
// Removes @actor from the chrome layer
removeActor: function(actor) {
this.actor.remove_actor(actor);
this._untrackActor(actor);
},
_findActor: function(actor) {
for (let i = 0; i < this._trackedActors.length; i++) {
let actorData = this._trackedActors[i];
if (actorData.actor == actor)
return i;
}
return -1;
},
_trackActor: function(actor, params) {
if (this._findActor(actor) != -1)
throw new Error('trying to re-track existing chrome actor');
let actorData = Params.parse(params, defaultParams);
actorData.actor = actor;
actorData.visibleId = actor.connect('notify::visible',
Lang.bind(this, this._queueUpdateRegions));
actorData.allocationId = actor.connect('notify::allocation',
Lang.bind(this, this._queueUpdateRegions));
actorData.parentSetId = actor.connect('parent-set',
Lang.bind(this, this._actorReparented));
// Note that destroying actor will unset its parent, so we don't
// need to connect to 'destroy' too.
this._trackedActors.push(actorData);
this._queueUpdateRegions();
},
_untrackActor: function(actor) {
let i = this._findActor(actor);
if (i == -1)
return;
let actorData = this._trackedActors[i];
this._trackedActors.splice(i, 1);
actor.disconnect(actorData.visibleId);
actor.disconnect(actorData.allocationId);
actor.disconnect(actorData.parentSetId);
this._queueUpdateRegions();
},
_actorReparented: function(actor, oldParent) {
if (!this.actor.contains(actor))
this._untrackActor(actor);
},
_updateVisibility: function() {
for (let i = 0; i < this._trackedActors.length; i++) {
let actorData = this._trackedActors[i];
if (this._inOverview && !actorData.visibleInOverview)
this.actor.set_skip_paint(actorData.actor, true);
else if (!this._inOverview && !actorData.visibleInFullscreen &&
this._findMonitorForActor(actorData.actor).inFullscreen)
this.actor.set_skip_paint(actorData.actor, true);
else
this.actor.set_skip_paint(actorData.actor, false);
}
},
_overviewShowing: function() {
this._inOverview = true;
this._updateVisibility();
this._queueUpdateRegions();
},
_overviewHidden: function() {
this._inOverview = false;
this._updateVisibility();
this._queueUpdateRegions();
},
_updateMonitors: function() {
let monitors = global.get_monitors();
let primary = global.get_primary_monitor();
this._monitors = monitors;
for (let i = 0; i < monitors.length; i++) {
let monitor = monitors[i];
if (monitor.x == primary.x &&
monitor.y == primary.y &&
monitor.width == primary.width &&
monitor.height == primary.height)
this._primaryMonitor = monitor;
}
},
_findMonitorForRect: function(x, y, w, h) {
// First look at what monitor the center of the rectangle is at
let cx = x + w/2;
let cy = y + h/2;
for (let i = 0; i < this._monitors.length; i++) {
let monitor = this._monitors[i];
if (cx >= monitor.x && cx < monitor.x + monitor.width &&
cy >= monitor.y && cy < monitor.y + monitor.height)
return monitor;
}
// If the center is not on a monitor, return the first overlapping monitor
for (let i = 0; i < this._monitors.length; i++) {
let monitor = this._monitors[i];
if (x + w > monitor.x && x < monitor.x + monitor.width &&
y + h > monitor.y && y < monitor.y + monitor.height)
return monitor;
}
// otherwise on no monitor
return null;
},
_findMonitorForWindow: function(window) {
return this._findMonitorForRect(window.x, window.y, window.width, window.height);
},
// This call guarantees that we return some monitor to simplify usage of it
// In practice all tracked actors should be visible on some monitor anyway
_findMonitorForActor: function(actor) {
let [x, y] = actor.get_transformed_position();
let [w, h] = actor.get_transformed_size();
let monitor = this._findMonitorForRect(x, y, w, h);
if (monitor)
return monitor;
return this._primaryMonitor; // Not on any monitor, pretend its on the primary
},
_monitorsChanged: function() {
this._updateMonitors();
// Update everything that depends on monitor positions
this._updateFullscreen();
this._updateVisibility();
this._queueUpdateRegions();
},
_queueUpdateRegions: function() {
if (!this._updateRegionIdle)
this._updateRegionIdle = Mainloop.idle_add(Lang.bind(this, this._updateRegions),
Meta.PRIORITY_BEFORE_REDRAW);
},
_updateFullscreen: function() {
let windows = Main.getWindowActorsForWorkspace(global.screen.get_active_workspace_index());
// Reset all monitors to not fullscreen
for (let i = 0; i < this._monitors.length; i++)
this._monitors[i].inFullscreen = false;
// The chrome layer should be visible unless there is a window
// with layer FULLSCREEN, or a window with layer
// OVERRIDE_REDIRECT that covers the whole screen.
// ('override_redirect' is not actually a layer above all
// other windows, but this seems to be how mutter treats it
// currently...) If we wanted to be extra clever, we could
// figure out when an OVERRIDE_REDIRECT window was trying to
// partially overlap us, and then adjust the input region and
// our clip region accordingly...
// @windows is sorted bottom to top.
for (let i = windows.length - 1; i > -1; i--) {
let window = windows[i];
let layer = window.get_meta_window().get_layer();
if (layer == Meta.StackLayer.FULLSCREEN) {
let monitor = this._findMonitorForWindow(window);
if (monitor)
monitor.inFullscreen = true;
}
if (layer == Meta.StackLayer.OVERRIDE_REDIRECT) {
let monitor = this._findMonitorForWindow(window);
if (monitor &&
window.x <= monitor.x &&
window.x + window.width >= monitor.x + monitor.width &&
window.y <= monitor.y &&
window.y + window.height >= monitor.y + monitor.height)
monitor.inFullscreen = true;
} else
break;
}
},
_windowsRestacked: function() {
let wasInFullscreen = [];
for (let i = 0; i < this._monitors.length; i++)
wasInFullscreen[i] = this._monitors[i].inFullscreen;
this._updateFullscreen();
let changed = false;
for (let i = 0; i < wasInFullscreen.length; i++) {
if (wasInFullscreen[i] != this._monitors[i].inFullscreen) {
changed = true;
break;
}
}
if (changed) {
this._updateVisibility();
this._queueUpdateRegions();
}
// Figure out where the pointer is in case we lost track of
// it during a grab. (In particular, if a trayicon popup menu
// is dismissed, see if we need to close the message tray.)
global.sync_pointer();
},
_updateRegions: function() {
let rects = [], struts = [], i;
delete this._updateRegionIdle;
for (i = 0; i < this._trackedActors.length; i++) {
let actorData = this._trackedActors[i];
if (!actorData.affectsInputRegion && !actorData.affectsStruts)
continue;
let [x, y] = actorData.actor.get_transformed_position();
let [w, h] = actorData.actor.get_transformed_size();
x = Math.round(x);
y = Math.round(y);
w = Math.round(w);
h = Math.round(h);
let rect = new Meta.Rectangle({ x: x, y: y, width: w, height: h});
if (actorData.affectsInputRegion &&
actorData.actor.get_paint_visibility() &&
!this.actor.get_skip_paint(actorData.actor))
rects.push(rect);
if (!actorData.affectsStruts)
continue;
// Limit struts to the size of the screen
let x1 = Math.max(x, 0);
let x2 = Math.min(x + w, global.screen_width);
let y1 = Math.max(y, 0);
let y2 = Math.min(y + h, global.screen_height);
// NetWM struts are not really powerful enought to handle
// a multi-monitor scenario, they only describe what happens
// around the outer sides of the full display region. However
// it can describe a partial region along each side, so
// we can support having the struts only affect the
// primary monitor. This should be enough as we only have
// chrome affecting the struts on the primary monitor so
// far.
//
// Metacity wants to know what side of the screen the
// strut is considered to be attached to. If the actor is
// only touching one edge, or is touching the entire
// border of the primary monitor, then it's obvious which
// side to call it. If it's in a corner, we pick a side
// arbitrarily. If it doesn't touch any edges, or it spans
// the width/height across the middle of the screen, then
// we don't create a strut for it at all.
let side;
let primary = this._primaryMonitor;
if (x1 <= primary.x && x2 >= primary.x + primary.width) {
if (y1 <= primary.y)
side = Meta.Side.TOP;
else if (y2 >= primary.y + primary.height)
side = Meta.Side.BOTTOM;
else
continue;
} else if (y1 <= primary.y && y2 >= primary.y + primary.height) {
if (x1 <= 0)
side = Meta.Side.LEFT;
else if (x2 >= global.screen_width)
side = Meta.Side.RIGHT;
else
continue;
} else if (x1 <= 0)
side = Meta.Side.LEFT;
else if (y1 <= 0)
side = Meta.Side.TOP;
else if (x2 >= global.screen_width)
side = Meta.Side.RIGHT;
else if (y2 >= global.screen_height)
side = Meta.Side.BOTTOM;
else
continue;
// Ensure that the strut rects goes all the way to the screen edge,
// as this really what mutter expects.
switch (side) {
case Meta.Side.TOP:
y1 = 0;
break;
case Meta.Side.BOTTOM:
y2 = global.screen_height;
break;
case Meta.Side.LEFT:
x1 = 0;
break;
case Meta.Side.RIGHT:
x2 = global.screen_width;
break;
}
let strutRect = new Meta.Rectangle({ x: x1, y: y1, width: x2 - x1, height: y2 - y1});
let strut = new Meta.Strut({ rect: strutRect, side: side });
struts.push(strut);
}
global.set_stage_input_region(rects);
let screen = global.screen;
for (let w = 0; w < screen.n_workspaces; w++) {
let workspace = screen.get_workspace_by_index(w);
workspace.set_builtin_struts(struts);
}
return false;
}
};
Signals.addSignalMethods(Chrome.prototype);

View File

@@ -1,179 +0,0 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Folks = imports.gi.Folks
const Lang = imports.lang;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Util = imports.misc.util;
const IconGrid = imports.ui.iconGrid;
const Search = imports.ui.search;
const SearchDisplay = imports.ui.searchDisplay;
const MAX_SEARCH_RESULTS_ROWS = 1;
const ICON_SIZE = 81;
function launchContact(id) {
Util.spawn(['gnome-contacts', '-i', id]);
}
/* This class represents a shown contact search result in the overview */
function Contact(id) {
this._init(id);
}
Contact.prototype = {
_init: function(id) {
this.individual = Shell.ContactSystem.get_default().get_individual(id);
this.actor = new St.Bin({ style_class: 'contact',
reactive: true,
track_hover: true });
let content = new St.BoxLayout( { style_class: 'contact-content',
vertical: false });
this.actor.set_child(content);
let icon = new St.Icon({ icon_type: St.IconType.FULLCOLOR,
icon_size: ICON_SIZE,
style_class: 'contact-icon' });
if (this.individual.avatar != null)
icon.gicon = this.individual.avatar;
else
icon.icon_name = 'avatar-default';
content.add(icon, { x_fill: true,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.MIDDLE });
let details = new St.BoxLayout({ style_class: 'contact-details',
vertical: true });
content.add(details, { x_fill: true,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.MIDDLE });
let aliasText = this.individual.alias || _("Unknown");
let aliasLabel = new St.Label({ text: aliasText,
style_class: 'contact-details-alias' });
details.add(aliasLabel, { x_fill: true,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.START });
let presence = this._createPresence(this.individual.presence_type);
details.add(presence, { x_fill: false,
y_fill: true,
x_align: St.Align.START,
y_align: St.Align.END });
},
_createPresence: function(presence) {
let text;
let iconName;
switch(presence) {
case Folks.PresenceType.AVAILABLE:
text = _("Available");
iconName = 'user-available';
break;
case Folks.PresenceType.AWAY:
case Folks.PresenceType.EXTENDED_AWAY:
text = _("Away");
iconName = 'user-away';
break;
case Folks.PresenceType.BUSY:
text = _("Busy");
iconName = 'user-busy';
break;
default:
text = _("Offline");
iconName = 'user-offline';
}
let icon = new St.Icon({ icon_name: iconName,
icon_type: St.IconType.FULLCOLOR,
icon_size: 16,
style_class: 'contact-details-status-icon' });
let label = new St.Label({ text: text });
let box = new St.BoxLayout({ vertical: false,
style_class: 'contact-details-status' });
box.add(icon, { x_fill: true,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.START });
box.add(label, { x_fill: true,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.START });
return box;
},
createIcon: function(size) {
let tc = St.TextureCache.get_default();
let icon = this.individual.avatar;
if (icon != null) {
return tc.load_gicon(null, icon, size);
} else {
return tc.load_icon_name(null, 'avatar-default', St.IconType.FULLCOLOR, size);
}
},
};
/* Searches for and returns contacts */
function ContactSearchProvider() {
this._init();
}
ContactSearchProvider.prototype = {
__proto__: Search.SearchProvider.prototype,
_init: function() {
Search.SearchProvider.prototype._init.call(this, _("CONTACTS"));
this._contactSys = Shell.ContactSystem.get_default();
},
getResultMeta: function(id) {
let contact = new Contact(id);
return { 'id': id,
'name': contact.alias,
'createIcon': function(size) {
return contact.createIcon(size);
}
};
},
getInitialResultSet: function(terms) {
return this._contactSys.initial_search(terms);
},
getSubsearchResultSet: function(previousResults, terms) {
return this._contactSys.subsearch(previousResults, terms);
},
createResultActor: function(resultMeta, terms) {
let contact = new Contact(resultMeta.id);
return contact.actor;
},
createResultContainerActor: function() {
let grid = new IconGrid.IconGrid({ rowLimit: MAX_SEARCH_RESULTS_ROWS,
xAlign: St.Align.START });
grid.actor.style_class = 'contact-grid';
let actor = new SearchDisplay.GridSearchResults(this, grid);
return actor;
},
activateResult: function(id, params) {
launchContact(id);
}
};

View File

@@ -153,14 +153,14 @@ CtrlAltTabPopup.prototype = {
},
_getPreferredWidth: function (actor, forHeight, alloc) {
let primary = Main.layoutManager.primaryMonitor;
let primary = global.get_primary_monitor();
alloc.min_size = primary.width;
alloc.natural_size = primary.width;
},
_getPreferredHeight: function (actor, forWidth, alloc) {
let primary = Main.layoutManager.primaryMonitor;
let primary = global.get_primary_monitor();
alloc.min_size = primary.height;
alloc.natural_size = primary.height;
@@ -168,7 +168,7 @@ CtrlAltTabPopup.prototype = {
_allocate: function (actor, box, flags) {
let childBox = new Clutter.ActorBox();
let primary = Main.layoutManager.primaryMonitor;
let primary = global.get_primary_monitor();
let leftPadding = this.actor.get_theme_node().get_padding(St.Side.LEFT);
let vPadding = this.actor.get_theme_node().get_vertical_padding();

View File

@@ -207,7 +207,7 @@ RemoveFavoriteIcon.prototype = {
let app = null;
if (source instanceof AppDisplay.AppWellIcon) {
let appSystem = Shell.AppSystem.get_default();
app = appSystem.lookup_app(source.getId());
app = appSystem.get_app(source.getId());
} else if (source.metaWindow) {
let tracker = Shell.WindowTracker.get_default();
app = tracker.get_window_app(source.metaWindow);
@@ -324,13 +324,13 @@ Dash.prototype = {
this._favRemoveTarget = null;
}));
}
DND.removeDragMonitor(this._dragMonitor);
DND.removeMonitor(this._dragMonitor);
},
_onDragMotion: function(dragEvent) {
let app = null;
if (dragEvent.source instanceof AppDisplay.AppWellIcon)
app = this._appSystem.lookup_app(dragEvent.source.getId());
app = this._appSystem.get_app(dragEvent.source.getId());
else if (dragEvent.source.metaWindow)
app = this._tracker.get_window_app(dragEvent.source.metaWindow);
else
@@ -342,10 +342,7 @@ Dash.prototype = {
let srcIsFavorite = (id in favorites);
if (srcIsFavorite &&
dragEvent.source.actor &&
this.actor.contains (dragEvent.source.actor) &&
this._favRemoveTarget == null) {
if (srcIsFavorite && this._favRemoveTarget == null) {
this._favRemoveTarget = new RemoveFavoriteIcon();
this._favRemoveTarget.icon.setIconSize(this.iconSize);
this._box.add(this._favRemoveTarget.actor);
@@ -437,7 +434,6 @@ Dash.prototype = {
let oldIconSize = this.iconSize;
this.iconSize = newIconSize;
this.emit('icon-size-changed');
let scale = oldIconSize / newIconSize;
for (let i = 0; i < iconChildren.length; i++) {
@@ -620,12 +616,12 @@ Dash.prototype = {
handleDragOver : function(source, actor, x, y, time) {
let app = null;
if (source instanceof AppDisplay.AppWellIcon)
app = this._appSystem.lookup_app(source.getId());
app = this._appSystem.get_app(source.getId());
else if (source.metaWindow)
app = this._tracker.get_window_app(source.metaWindow);
// Don't allow favoriting of transient apps
if (app == null || app.is_window_backed())
if (app == null || app.is_transient())
return DND.DragMotionResult.NO_DROP;
let favorites = AppFavorites.getAppFavorites().getFavorites();
@@ -705,13 +701,13 @@ Dash.prototype = {
acceptDrop : function(source, actor, x, y, time) {
let app = null;
if (source instanceof AppDisplay.AppWellIcon) {
app = this._appSystem.lookup_app(source.getId());
app = this._appSystem.get_app(source.getId());
} else if (source.metaWindow) {
app = this._tracker.get_window_app(source.metaWindow);
}
// Don't allow favoriting of transient apps
if (app == null || app.is_window_backed()) {
if (app == null || app.is_transient()) {
return false;
}

View File

@@ -9,13 +9,11 @@ const Clutter = imports.gi.Clutter;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Params = imports.misc.params;
const Util = imports.misc.util;
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const Calendar = imports.ui.calendar;
const UPowerGlib = imports.gi.UPowerGlib;
// in org.gnome.desktop.interface
const CLOCK_FORMAT_KEY = 'clock-format';
@@ -41,19 +39,19 @@ function _onVertSepRepaint (area)
};
function DateMenuButton() {
this._init.apply(this, arguments);
this._init();
}
DateMenuButton.prototype = {
__proto__: PanelMenu.Button.prototype,
_init: function(params) {
params = Params.parse(params, { showEvents: true });
_init: function() {
let item;
let hbox;
let vbox;
this._eventSource = new Calendar.DBusEventSource();
let menuAlignment = 0.25;
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
menuAlignment = 1.0 - menuAlignment;
@@ -62,7 +60,7 @@ DateMenuButton.prototype = {
this._clock = new St.Label();
this.actor.set_child(this._clock);
hbox = new St.BoxLayout({name: 'calendarArea' });
hbox = new St.BoxLayout({name: 'calendarArea'});
this.menu.addActor(hbox);
// Fill up the first column
@@ -75,58 +73,43 @@ DateMenuButton.prototype = {
this._date.style_class = 'datemenu-date-label';
vbox.add(this._date);
if (params.showEvents) {
this._eventSource = new Calendar.DBusEventSource();
this._eventList = new Calendar.EventsList(this._eventSource);
} else {
this._eventSource = null;
this._eventList = null;
}
this._eventList = new Calendar.EventsList(this._eventSource);
// Calendar
this._calendar = new Calendar.Calendar(this._eventSource);
this._calendar.connect('selected-date-changed',
Lang.bind(this, function(calendar, date) {
// we know this._eventList is defined here, because selected-data-changed
// only gets emitted when the user clicks a date in the calendar,
// and the calender makes those dates unclickable when instantiated with
// a null event source
this._eventList.setDate(date);
}));
vbox.add(this._calendar.actor);
item = this.menu.addSettingsAction(_("Date and Time Settings"), 'gnome-datetime-panel.desktop');
if (item) {
let separator = new PopupMenu.PopupSeparatorMenuItem();
separator.setColumnWidths(1);
vbox.add(separator.actor, {y_align: St.Align.END, expand: true, y_fill: false});
item = new PopupMenu.PopupSeparatorMenuItem();
item.setColumnWidths(1);
vbox.add(item.actor, {y_align: St.Align.END, expand: true, y_fill: false});
item = new PopupMenu.PopupMenuItem(_("Date and Time Settings"));
item.connect('activate', Lang.bind(this, this._onPreferencesActivate));
item.actor.can_focus = false;
vbox.add(item.actor);
item.actor.can_focus = false;
item.actor.reparent(vbox);
}
// Add vertical separator
if (params.showEvents) {
// Add vertical separator
item = new St.DrawingArea({ style_class: 'calendar-vertical-separator',
pseudo_class: 'highlighted' });
item.connect('repaint', Lang.bind(this, _onVertSepRepaint));
hbox.add(item);
item = new St.DrawingArea({ style_class: 'calendar-vertical-separator',
pseudo_class: 'highlighted' });
item.connect('repaint', Lang.bind(this, _onVertSepRepaint));
hbox.add(item);
// Fill up the second column
// Fill up the second column
vbox = new St.BoxLayout({name: 'calendarEventsArea',
vertical: true});
hbox.add(vbox, { expand: true });
vbox = new St.BoxLayout({vertical: true});
hbox.add(vbox, { expand: true });
// Event list
vbox.add(this._eventList.actor, { expand: true });
// Event list
vbox.add(this._eventList.actor, { expand: true });
item = new PopupMenu.PopupMenuItem(_("Open Calendar"));
item.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
item.actor.can_focus = false;
vbox.add(item.actor, {y_align: St.Align.END, expand: true, y_fill: false});
}
item = new PopupMenu.PopupMenuItem(_("Open Calendar"));
item.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
item.actor.can_focus = false;
vbox.add(item.actor, {y_align: St.Align.END, expand: true, y_fill: false});
// Whenever the menu is opened, select today
this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) {
@@ -159,10 +142,6 @@ DateMenuButton.prototype = {
this._desktopSettings.connect('changed', Lang.bind(this, this._updateClockAndDate));
this._clockSettings.connect('changed', Lang.bind(this, this._updateClockAndDate));
// https://bugzilla.gnome.org/show_bug.cgi?id=655129
this._upClient = new UPowerGlib.Client();
this._upClient.connect('notify-resume', Lang.bind(this, this._updateClockAndDate));
// Start the clock
this._updateClockAndDate();
},
@@ -178,12 +157,12 @@ DateMenuButton.prototype = {
switch (format) {
case '24h':
if (showDate)
/* Translators: This is the time format with date used
/* Translators: This is the time format with date used
in 24-hour mode. */
clockFormat = showSeconds ? _("%a %b %e, %R:%S")
: _("%a %b %e, %R");
else
/* Translators: This is the time format without date used
/* Translators: This is the time format without date used
in 24-hour mode. */
clockFormat = showSeconds ? _("%a %R:%S")
: _("%a %R");
@@ -191,12 +170,12 @@ DateMenuButton.prototype = {
case '12h':
default:
if (showDate)
/* Translators: This is a time format with date used
/* Translators: This is a time format with date used
for AM/PM. */
clockFormat = showSeconds ? _("%a %b %e, %l:%M:%S %p")
: _("%a %b %e, %l:%M %p");
else
/* Translators: This is a time format without date used
/* Translators: This is a time format without date used
for AM/PM. */
clockFormat = showSeconds ? _("%a %l:%M:%S %p")
: _("%a %l:%M %p");
@@ -217,26 +196,15 @@ DateMenuButton.prototype = {
return false;
},
_onPreferencesActivate: function() {
this.menu.close();
let app = Shell.AppSystem.get_default().get_app('gnome-datetime-panel.desktop');
app.activate(-1);
},
_onOpenCalendarActivate: function() {
this.menu.close();
let calendarSettings = new Gio.Settings({ schema: 'org.gnome.desktop.default-applications.office.calendar' });
let tool = calendarSettings.get_string('exec');
if (tool.length == 0 || tool == 'evolution') {
// TODO: pass the selected day
Util.spawn(['evolution', '-c', 'calendar']);
} else {
let needTerm = calendarSettings.get_boolean('needs-term');
if (needTerm) {
let terminalSettings = new Gio.Settings({ schema: 'org.gnome.desktop.default-applications.terminal' });
let term = terminalSettings.get_string('exec');
let arg = terminalSettings.get_string('exec-arg');
if (arg != '')
Util.spawn([term, arg, tool]);
else
Util.spawn([term, tool]);
} else {
Util.spawnCommandLine(tool)
}
}
// TODO: pass the selected day
Util.spawn(['evolution', '-c', 'calendar']);
}
};

View File

@@ -61,7 +61,7 @@ function addDragMonitor(monitor) {
dragMonitors.push(monitor);
}
function removeDragMonitor(monitor) {
function removeMonitor(monitor) {
for (let i = 0; i < dragMonitors.length; i++)
if (dragMonitors[i] == monitor) {
dragMonitors.splice(i, 1);
@@ -284,13 +284,13 @@ _Draggable.prototype = {
this._dragOffsetY = actorStageY - this._dragStartY;
// Set the actor's scale such that it will keep the same
// transformed size when it's reparented to the uiGroup
// transformed size when it's reparented to the stage
let [scaledWidth, scaledHeight] = this.actor.get_transformed_size();
this.actor.set_scale(scaledWidth / this.actor.width,
scaledHeight / this.actor.height);
}
this._dragActor.reparent(Main.uiGroup);
this._dragActor.reparent(this.actor.get_stage());
this._dragActor.raise_top();
Shell.util_set_hidden_from_pick(this._dragActor, true);
@@ -442,7 +442,7 @@ _Draggable.prototype = {
return true;
// If it accepted the drop without taking the actor,
// handle it ourselves.
if (this._dragActor.get_parent() == Main.uiGroup) {
if (this._dragActor.get_parent() == this._dragActor.get_stage()) {
if (this._restoreOnSuccess) {
this._restoreDragActor(event.get_time());
return true;

View File

@@ -30,11 +30,11 @@ DocSearchProvider.prototype = {
},
activateResult: function(id, params) {
params = Params.parse(params, { workspace: -1,
timestamp: 0 });
params = Params.parse(params, { workspace: null,
timestamp: null });
let docInfo = this._docManager.lookupByUri(id);
docInfo.launch(params.workspace);
docInfo.launch(params.workspace ? params.workspace.index() : -1);
},
getInitialResultSet: function(terms) {

View File

@@ -22,8 +22,8 @@ const DBus = imports.dbus;
const Lang = imports.lang;
const Signals = imports.signals;
const AccountsService = imports.gi.AccountsService;
const Clutter = imports.gi.Clutter;
const Gdm = imports.gi.Gdm;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const Pango = imports.gi.Pango;
@@ -113,7 +113,7 @@ function findAppFromInhibitor(inhibitor) {
let app = null;
for (let i = 0; i < candidateDesktopFiles.length; i++) {
try {
app = appSystem.lookup_app(candidateDesktopFiles[i]);
app = appSystem.get_app(candidateDesktopFiles[i]);
if (app)
break;
@@ -173,7 +173,7 @@ ListItem.prototype = {
_onClicked: function() {
this.emit('activate');
this._app.activate();
this._app.activate(-1);
}
};
Signals.addSignalMethods(ListItem.prototype);
@@ -237,7 +237,7 @@ EndSessionDialog.prototype = {
_init: function() {
ModalDialog.ModalDialog.prototype._init.call(this, { styleClass: 'end-session-dialog' });
this._user = AccountsService.UserManager.get_default().get_user(GLib.get_user_name());
this._user = Gdm.UserManager.ref_default().get_user(GLib.get_user_name());
this._secondsLeft = 0;
this._totalSecondsToStayOpen = 0;

View File

@@ -39,6 +39,35 @@ function _patchContainerClass(containerClass) {
};
}
// Replace @method with something that throws an error instead
function _blockMethod(method, replacement, reason) {
let match = method.match(/^(.+)\.([^.]+)$/);
if (!match)
throw new Error('Bad method name "' + method + '"');
let proto = 'imports.gi.' + match[1] + '.prototype';
let property = match[2];
if (!global.set_property_mutable(proto, property, true))
throw new Error('Bad method name "' + method + '"');
// eval() is evil in general, but we know it's safe here since
// set_property_mutable() would have failed if proto was
// malformed.
let node = eval(proto);
let msg = 'Do not use "' + method + '".';
if (replacement)
msg += ' Use "' + replacement + '" instead.';
if (reason)
msg += ' (' + reason + ')';
node[property] = function() {
throw new Error(msg);
};
global.set_property_mutable(proto, property, false);
}
function init() {
// Add some bindings to the global JS namespace; (gjs keeps the web
// browser convention of having that namespace be called 'window'.)
@@ -70,6 +99,17 @@ function init() {
return base;
};
_blockMethod('Clutter.Event.get_state', 'Shell.get_event_state',
'gjs\'s handling of Clutter.ModifierType is broken. See bug 597292.');
_blockMethod('Gdk.Window.get_device_position', 'global.get_pointer',
'gjs\'s handling of Gdk.ModifierType is broken. See bug 597292.');
// Now close the back door to prevent extensions from trying to
// abuse it. We can't actually delete it since
// Shell.Global.prototype itself is read-only.
global.set_property_mutable('imports.gi.Shell.Global.prototype', 'set_property_mutable', true);
Shell.Global.prototype.set_property_mutable = undefined;
// Work around https://bugzilla.mozilla.org/show_bug.cgi?id=508783
Date.prototype.toLocaleFormat = function(format) {
return Shell.util_format_date(format, this.getTime());

View File

@@ -1,13 +1,9 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Lang = imports.lang;
const Signals = imports.signals;
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
const Soup = imports.gi.Soup;
const Config = imports.misc.config;
@@ -15,12 +11,7 @@ const ExtensionState = {
ENABLED: 1,
DISABLED: 2,
ERROR: 3,
OUT_OF_DATE: 4,
DOWNLOADING: 5,
// Used as an error state for operations on unknown extensions,
// should never be in a real extensionMeta object.
UNINSTALLED: 99
OUT_OF_DATE: 4
};
const ExtensionType = {
@@ -28,40 +19,15 @@ const ExtensionType = {
PER_USER: 2
};
const _httpSession = new Soup.SessionAsync();
// The unfortunate state of gjs, gobject-introspection and libsoup
// means that I have to do a hack to add a feature.
// See: https://bugzilla.gnome.org/show_bug.cgi?id=655189 for context.
if (Soup.Session.prototype.add_feature != null)
Soup.Session.prototype.add_feature.call(_httpSession, new Soup.ProxyResolverDefault());
// Maps uuid -> metadata object
const extensionMeta = {};
// Maps uuid -> importer object (extension directory tree)
const extensions = {};
// Maps uuid -> extension state object (returned from init())
const extensionStateObjs = {};
// Arrays of uuids
var enabledExtensions;
// Array of uuids
var disabledExtensions;
// GFile for user extensions
var userExtensionsDir = null;
// We don't really have a class to add signals on. So, create
// a simple dummy object, add the signal methods, and export those
// publically.
var _signals = {};
Signals.addSignalMethods(_signals);
const connect = Lang.bind(_signals, _signals.connect);
const disconnect = Lang.bind(_signals, _signals.disconnect);
// UUID => Array of error messages
var errors = {};
const ENABLED_EXTENSIONS_KEY = 'enabled-extensions';
/**
* versionCheck:
* @required: an array of versions we're compatible with
@@ -92,150 +58,13 @@ function versionCheck(required, current) {
return false;
}
function installExtensionFromManifestURL(uuid, url) {
_httpSession.queue_message(
Soup.Message.new('GET', url),
function(session, message) {
if (message.status_code != Soup.KnownStatusCode.OK) {
logExtensionError(uuid, 'downloading manifest: ' + message.status_code.toString());
return;
}
let manifest;
try {
manifest = JSON.parse(message.response_body.data);
} catch (e) {
logExtensionError(uuid, 'parsing: ' + e.toString());
return;
}
if (uuid != manifest['uuid']) {
logExtensionError(uuid, 'manifest: manifest uuids do not match');
return;
}
let meta = extensionMeta[uuid] = { uuid: uuid,
state: ExtensionState.DOWNLOADING,
error: '' };
_signals.emit('extension-state-changed', meta);
installExtensionFromManifest(manifest, meta);
});
}
function installExtensionFromManifest(manifest, meta) {
let uuid = manifest['uuid'];
let name = manifest['name'];
if (!versionCheck(manifest['shell-version'], Config.PACKAGE_VERSION)) {
meta.state = ExtensionState.OUT_OF_DATE;
logExtensionError(uuid, 'version: ' + name + ' is not compatible with current GNOME Shell version', meta.state);
return;
}
let url = manifest['__installer'];
_httpSession.queue_message(Soup.Message.new('GET', url),
function(session, message) {
gotExtensionZipFile(session, message, uuid);
});
}
function gotExtensionZipFile(session, message, uuid) {
if (message.status_code != Soup.KnownStatusCode.OK) {
logExtensionError(uuid, 'downloading extension: ' + message.status_code);
return;
}
// FIXME: use a GFile mkstemp-type method once one exists
let fd, tmpzip;
try {
[fd, tmpzip] = GLib.file_open_tmp('XXXXXX.shell-extension.zip');
} catch (e) {
logExtensionError(uuid, 'tempfile: ' + e.toString());
return;
}
let stream = new Gio.UnixOutputStream({ fd: fd });
let dir = userExtensionsDir.get_child(uuid);
Shell.write_soup_message_to_stream(stream, message);
stream.close(null);
let [success, pid] = GLib.spawn_async(null,
['unzip', '-uod', dir.get_path(), '--', tmpzip],
null,
GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD,
null);
if (!success) {
logExtensionError(uuid, 'extract: could not extract');
return;
}
GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, function(pid, status) {
GLib.spawn_close_pid(pid);
loadExtension(dir, true, ExtensionType.PER_USER);
});
}
function disableExtension(uuid) {
let meta = extensionMeta[uuid];
if (!meta)
return;
if (meta.state != ExtensionState.ENABLED)
return;
let extensionState = extensionStateObjs[uuid];
try {
extensionState.disable();
} catch(e) {
logExtensionError(uuid, e.toString());
return;
}
meta.state = ExtensionState.DISABLED;
_signals.emit('extension-state-changed', meta);
}
function enableExtension(uuid) {
let meta = extensionMeta[uuid];
if (!meta)
return;
if (meta.state != ExtensionState.DISABLED)
return;
let extensionState = extensionStateObjs[uuid];
try {
extensionState.enable();
} catch(e) {
logExtensionError(uuid, e.toString());
return;
}
meta.state = ExtensionState.ENABLED;
_signals.emit('extension-state-changed', meta);
}
function logExtensionError(uuid, message, state) {
if (!errors[uuid]) errors[uuid] = [];
errors[uuid].push(message);
global.logError('Extension "%s" had error: %s'.format(uuid, message));
state = state || ExtensionState.ERROR;
_signals.emit('extension-state-changed', { uuid: uuid,
error: message,
state: state });
}
function loadExtension(dir, enabled, type) {
let info;
let uuid = dir.get_basename();
let baseErrorString = 'While loading extension from "' + dir.get_parse_name() + '": ';
let metadataFile = dir.get_child('metadata.json');
if (!metadataFile.query_exists(null)) {
logExtensionError(uuid, 'Missing metadata.json');
global.logError(baseErrorString + 'Missing metadata.json');
return;
}
@@ -243,65 +72,61 @@ function loadExtension(dir, enabled, type) {
try {
metadataContents = Shell.get_file_contents_utf8_sync(metadataFile.get_path());
} catch (e) {
logExtensionError(uuid, 'Failed to load metadata.json: ' + e);
global.logError(baseErrorString + 'Failed to load metadata.json: ' + e);
return;
}
let meta;
try {
meta = JSON.parse(metadataContents);
} catch (e) {
logExtensionError(uuid, 'Failed to parse metadata.json: ' + e);
global.logError(baseErrorString + 'Failed to parse metadata.json: ' + e);
return;
}
let requiredProperties = ['uuid', 'name', 'description', 'shell-version'];
for (let i = 0; i < requiredProperties.length; i++) {
let prop = requiredProperties[i];
if (!meta[prop]) {
logExtensionError(uuid, 'missing "' + prop + '" property in metadata.json');
global.logError(baseErrorString + 'missing "' + prop + '" property in metadata.json');
return;
}
}
if (extensions[uuid] != undefined) {
logExtensionError(uuid, "extension already loaded");
if (extensions[meta.uuid] != undefined) {
global.logError(baseErrorString + "extension already loaded");
return;
}
// Encourage people to add this
if (!meta['url']) {
global.log('Warning: Missing "url" property in metadata.json');
global.log(baseErrorString + 'Warning: Missing "url" property in metadata.json');
}
if (uuid != meta.uuid) {
logExtensionError(uuid, 'uuid "' + meta.uuid + '" from metadata.json does not match directory name "' + uuid + '"');
let base = dir.get_basename();
if (base != meta.uuid) {
global.logError(baseErrorString + 'uuid "' + meta.uuid + '" from metadata.json does not match directory name "' + base + '"');
return;
}
if (!versionCheck(meta['shell-version'], Config.PACKAGE_VERSION) ||
(meta['js-version'] && !versionCheck(meta['js-version'], Config.GJS_VERSION))) {
logExtensionError(uuid, 'extension is not compatible with current GNOME Shell and/or GJS version');
global.logError(baseErrorString + 'extension is not compatible with current GNOME Shell and/or GJS version');
return;
}
extensionMeta[uuid] = meta;
meta.type = type;
meta.path = dir.get_path();
meta.error = '';
extensionMeta[meta.uuid] = meta;
extensionMeta[meta.uuid].type = type;
extensionMeta[meta.uuid].path = dir.get_path();
if (!enabled) {
extensionMeta[meta.uuid].state = ExtensionState.DISABLED;
return;
}
// Default to error, we set success as the last step
meta.state = ExtensionState.ERROR;
if (!versionCheck(meta['shell-version'], Config.PACKAGE_VERSION) ||
(meta['js-version'] && !versionCheck(meta['js-version'], Config.GJS_VERSION))) {
logExtensionError(uuid, 'extension is not compatible with current GNOME Shell and/or GJS version', ExtensionState.OUT_OF_DATE);
meta.state = ExtensionState.OUT_OF_DATE;
return;
}
extensionMeta[meta.uuid].state = ExtensionState.ERROR;
let extensionJs = dir.get_child('extension.js');
if (!extensionJs.query_exists(null)) {
logExtensionError(uuid, 'Missing extension.js');
global.logError(baseErrorString + 'Missing extension.js');
return;
}
let stylesheetPath = null;
@@ -312,94 +137,47 @@ function loadExtension(dir, enabled, type) {
try {
theme.load_stylesheet(stylesheetFile.get_path());
} catch (e) {
logExtensionError(uuid, 'Stylesheet parse error: ' + e);
global.logError(baseErrorString + 'Stylesheet parse error: ' + e);
return;
}
}
let extensionModule;
let extensionState = null;
try {
global.add_extension_importer('imports.ui.extensionSystem.extensions', meta.uuid, dir.get_path());
extensionModule = extensions[meta.uuid].extension;
} catch (e) {
if (stylesheetPath != null)
theme.unload_stylesheet(stylesheetPath);
logExtensionError(uuid, e);
global.logError(baseErrorString + e);
return;
}
if (!extensionModule.init) {
logExtensionError(uuid, 'missing \'init\' function');
if (!extensionModule.main) {
global.logError(baseErrorString + 'missing \'main\' function');
return;
}
try {
extensionState = extensionModule.init(meta);
extensionModule.main(meta);
} catch (e) {
if (stylesheetPath != null)
theme.unload_stylesheet(stylesheetPath);
logExtensionError(uuid, 'Failed to evaluate init function:' + e);
global.logError(baseErrorString + 'Failed to evaluate main function:' + e);
return;
}
if (!extensionState)
extensionState = extensionModule;
extensionStateObjs[uuid] = extensionState;
if (!extensionState.enable) {
logExtensionError(uuid, 'missing \'enable\' function');
return;
}
if (!extensionState.disable) {
logExtensionError(uuid, 'missing \'disable\' function');
return;
}
meta.state = ExtensionState.DISABLED;
if (enabled)
enableExtension(uuid);
_signals.emit('extension-loaded', meta.uuid);
_signals.emit('extension-state-changed', meta);
extensionMeta[meta.uuid].state = ExtensionState.ENABLED;
global.log('Loaded extension ' + meta.uuid);
}
function onEnabledExtensionsChanged() {
let newEnabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
// Find and enable all the newly enabled extensions: UUIDs found in the
// new setting, but not in the old one.
newEnabledExtensions.filter(function(uuid) {
return enabledExtensions.indexOf(uuid) == -1;
}).forEach(function(uuid) {
enableExtension(uuid);
});
// Find and disable all the newly disabled extensions: UUIDs found in the
// old setting, but not in the new one.
enabledExtensions.filter(function(item) {
return newEnabledExtensions.indexOf(item) == -1;
}).forEach(function(uuid) {
disableExtension(uuid);
});
enabledExtensions = newEnabledExtensions;
}
function init() {
let userExtensionsPath = GLib.build_filenamev([global.userdatadir, 'extensions']);
userExtensionsDir = Gio.file_new_for_path(userExtensionsPath);
try {
if (!userExtensionsDir.query_exists(null))
userExtensionsDir.make_directory_with_parents(null);
userExtensionsDir.make_directory_with_parents(null);
} catch (e) {
global.logError('' + e);
}
global.settings.connect('changed::' + ENABLED_EXTENSIONS_KEY, onEnabledExtensionsChanged);
enabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
disabledExtensions = global.settings.get_strv('disabled-extensions', -1);
}
function _loadExtensionsIn(dir, type) {
@@ -417,8 +195,8 @@ function _loadExtensionsIn(dir, type) {
if (fileType != Gio.FileType.DIRECTORY)
continue;
let name = info.get_name();
let enabled = disabledExtensions.indexOf(name) < 0;
let child = dir.get_child(name);
let enabled = enabledExtensions.indexOf(name) != -1;
loadExtension(child, enabled, type);
}
fileEnum.close(null);

500
js/ui/goaClient.js Normal file
View File

@@ -0,0 +1,500 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const Goa = imports.gi.Goa;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const St = imports.gi.St;
const Gettext = imports.gettext.domain('gnome-shell');
const _ = Gettext.gettext;
const C_ = Gettext.pgettext;
const Gtk = imports.gi.Gtk;
const Pango = imports.gi.Pango;
const History = imports.misc.history;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
// ----------------------------------------------------------------------------------------------------
function Client() {
this._init();
}
Client.prototype = {
_init : function() {
this._client = null;
this._accountIdToMailMonitor = {}
this._mailSource = null;
// TODO: need to call refreshAllMonitors() when network-connectivity changes
Goa.Client.new(null, /* cancellable */
Lang.bind(this, this._onClientConstructed));
},
_onClientConstructed : function(object, asyncRes) {
this._client = object.new_finish(asyncRes);
this._updateAccounts();
this._client.connect('account-added', Lang.bind(this, this._updateAccounts));
this._client.connect('account-removed', Lang.bind(this, this._updateAccounts));
this._client.connect('account-changed', Lang.bind(this, this._updateAccounts));
},
_updateAccounts : function () {
let objects = this._client.get_accounts();
let mailIds = {};
// Add monitors for accounts that now exist
for (let n = 0; n < objects.length; n++) {
let object = objects[n];
let id = object.account.id;
if (object.mail) {
mailIds[id] = true;
if (!(id in this._accountIdToMailMonitor)) {
let monitor = new MailMonitor(this, object);
this._accountIdToMailMonitor[id] = monitor;
}
}
}
// Nuke monitors for accounts that are now non-existant
let monitorsToRemove = []
for (let existingMonitorId in this._accountIdToMailMonitor) {
if (!(existingMonitorId in mailIds)) {
monitorsToRemove.push(existingMonitorId);
}
}
for (let n = 0; n < monitorsToRemove.length; n++) {
let id = monitorsToRemove[n];
let monitor = this._accountIdToMailMonitor[id];
delete this._accountIdToMailMonitor[id]
monitor.destroy();
}
},
_ensureMailSource: function() {
if (!this._mailSource) {
this._mailSource = new MailSource(this);
this._mailSource.connect('destroy', Lang.bind(this,
function () {
this._mailSource = null;
}));
Main.messageTray.add(this._mailSource);
}
},
addPendingMessage: function(message) {
this._ensureMailSource();
this._mailSource.addMessage(message);
},
refreshAllMonitors: function() {
log('Refreshing all mail monitors');
for (let id in this._accountIdToMailMonitor) {
let monitor = this._accountIdToMailMonitor[id];
monitor.refresh();
}
}
}
// ----------------------------------------------------------------------------------------------------
function Message(uid, from, subject, excerpt, uri, can_be_marked_as_spam, can_be_starred) {
this._init(uid, from, subject, excerpt, uri, can_be_marked_as_spam, can_be_starred);
}
Message.prototype = {
_init: function(uid, from, subject, excerpt, uri, can_be_marked_as_spam, can_be_starred) {
this.uid = uid;
this.from = from;
this.subject = subject;
this.excerpt = excerpt;
this.uri = uri;
this.can_be_marked_as_spam = can_be_marked_as_spam;
this.can_be_starred = can_be_starred;
this.receivedAt = new Date();
}
}
// ----------------------------------------------------------------------------------------------------
function MailMonitor(client, accountObject) {
this._init(client, accountObject);
}
MailMonitor.prototype = {
_init : function(client, accountObject) {
this._client = client;
this._accountObject = accountObject;
this._account = this._accountObject.get_account();
this._mail = this._accountObject.get_mail();
// Create the remote monitor object
this._proxy = null;
this._cancellable = new Gio.Cancellable();
this._mail.call_create_monitor(this._cancellable, Lang.bind(this, this._onMonitorCreated));
},
destroy : function() {
this._cancellable.cancel();
if (this._proxy) {
// We don't really care if this fails or not
this._proxy.call_close(null, Lang.bind(this, function() { }));
this._proxy.disconnect(this._messageReceivedId);
this._proxy = null;
}
},
refresh : function() {
if (this._proxy) {
// We don't really care if this fails or not
log('Refreshing mail monitor for account ' + this._account.name);
this._proxy.call_refresh(null, Lang.bind(this, function() { }));
}
},
_onMonitorCreated : function(mail, asyncRes) {
// TODO: a (gboolean, object_path) tuple is returned here
// See https://bugzilla.gnome.org/show_bug.cgi?id=649657
let ret = mail.call_create_monitor_finish(asyncRes);
let object_path = ret[1];
Goa.MailMonitorProxy.new_for_bus(Gio.BusType.SESSION,
Gio.DBusProxyFlags.NONE,
'org.gnome.OnlineAccounts',
object_path,
null, /* cancellable */
Lang.bind(this, this._onMonitorProxyConstructed));
},
_onMonitorProxyConstructed : function(monitor, asyncRes) {
this._proxy = monitor.new_for_bus_finish(asyncRes);
// Now listen for changes on the mail monitor proxy
this._messageReceivedId = this._proxy.connect('message-received',
Lang.bind(this, this._onMessageReceived));
},
_onMessageReceived : function(monitor, uid, from, subject, excerpt, uri, can_be_marked_as_spam, can_be_starred) {
let message = new Message(uid, from, subject, excerpt, uri, can_be_marked_as_spam, can_be_starred);
if (!Main.messageTray.getBusy()) {
let source = new Source(this._client, message);
let notification = new Notification(source, this._client, this, message);
// If the user is not marked as busy, present the notification to the user
Main.messageTray.add(source);
source.notify(notification);
} else {
// ... otherwise, if the user is busy, just add it to the MailSource's list
// of pending messages
this._client.addPendingMessage(message);
}
},
MessageAddStar: function (message) {
this._proxy.call_add_star(message.uid,
null, /* cancellable */
Lang.bind(this,
function(object, asyncRes) {
this._proxy.call_add_star_finish(asyncRes);
}));
},
MessageMarkAsSpam: function (message) {
this._proxy.call_mark_as_spam(message.uid,
null, /* cancellable */
Lang.bind(this,
function(object, asyncRes) {
this._proxy.call_add_star_finish(asyncRes);
}));
}
}
// ----------------------------------------------------------------------------------------------------
function Source(client, message) {
this._init(client, message);
}
Source.prototype = {
__proto__: MessageTray.Source.prototype,
_init : function(client, message) {
this._client = client;
this._message = message;
// Init super class and add ourselves to the message tray
MessageTray.Source.prototype._init.call(this, 'Message from ' + _stripEmailAddress(this._message.from));
this.setTransient(true);
this.isChat = true;
this._setSummaryIcon(this.createNotificationIcon());
},
createNotificationIcon : function() {
// TODO: use account icon
let icon = new St.Icon({ icon_type: St.IconType.FULLCOLOR,
icon_size: this.ICON_SIZE,
icon_name: 'mail-send'});
return icon;
}
}
// ----------------------------------------------------------------------------------------------------
function _stripEmailAddress(name_and_addr) {
let bracketStartPos = name_and_addr.indexOf(' <');
if (bracketStartPos == -1) {
return name_and_addr;
} else {
return name_and_addr.slice(0, bracketStartPos);
}
}
function Notification(source, client, monitor, message) {
this._init(source, client, monitor, message);
}
Notification.prototype = {
__proto__: MessageTray.Notification.prototype,
_init : function(source, client, monitor, message) {
this._client = client;
this._monitor = monitor
this._message = message;
this._ignore = false;
this._alreadyExpanded = false;
this._strippedFrom = _stripEmailAddress(this._message.from);
let title = this._strippedFrom;
let banner = this._message.subject + ' \u2014 ' + this._message.excerpt; // — U+2014 EM DASH
// Init super class
MessageTray.Notification.prototype._init.call(this, source, title, banner);
// Change the contents once expanded
this.connect('expanded', Lang.bind (this, this._onExpanded));
this.update(title, banner);
this.setUrgency(MessageTray.Urgency.NORMAL);
this.setTransient(true);
this.addButton('ignore', 'Ignore');
if (message.can_be_starred)
this.addButton('star', 'Star');
if (message.can_be_marked_as_spam)
this.addButton('spam', 'Junk');
if (this._message.uri.length > 0) {
this.addButton('open', 'Open');
}
this.connect('action-invoked', Lang.bind(this,
function(notification, id) {
if (id == 'ignore') {
this._actionIgnore();
} else if (id == 'star') {
this._actionStar();
} else if (id == 'spam') {
this._actionSpam();
} else if (id == 'open') {
this._actionOpen();
}
}));
this.connect('clicked', Lang.bind(this,
function() {
if (this._message.uri.length > 0) {
this._actionOpen();
}
}));
// Hmm, should be ::done-displaying instead?
this.connect('destroy', Lang.bind(this, this._onDestroyed));
},
_onExpanded : function() {
if (this._alreadyExpanded)
return;
this._alreadyExpanded = true;
let escapedExcerpt = GLib.markup_escape_text(this._message.excerpt, -1);
let bannerMarkup = '<b>Subject:</b> ' + this._message.subject + '\n';
// TODO: if available, insert other headers such as Cc
bannerMarkup += '\n' + escapedExcerpt;
this.update(this._strippedFrom, bannerMarkup, {bannerMarkup: true});
},
_onDestroyed : function(reason) {
// If not ignoring the message, push it onto the Mail source
if (!this._ignore) {
this._client.addPendingMessage(this._message);
}
},
_actionIgnore : function() {
this._ignore = true;
},
_actionStar : function() {
this._ignore = true;
this._monitor.MessageAddStar(this._message);
},
_actionSpam : function() {
this._ignore = true;
this._monitor.MessageMarkAsSpam(this._message);
},
_actionOpen : function() {
this._ignore = true;
Gio.app_info_launch_default_for_uri(this._message.uri,
global.create_app_launch_context());
}
}
// ----------------------------------------------------------------------------------------------------
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 _formatRelativeDate(date) {
let ret = ''
let now = new Date();
if (_sameDay(date, now)) {
ret = date.toLocaleFormat("%l:%M %p");
} else {
if (_sameYear(date, now)) {
ret = date.toLocaleFormat("%B %e");
} else {
ret = date.toLocaleFormat("%B %e, %Y");
}
}
return ret;
}
function _addMessageToTable(table, message) {
let formattedExcerpt = message.excerpt.replace(/\r/g, '').replace(/\n/g, ' ');
let formattedDate = _formatRelativeDate(message.receivedAt);
let fromLabel = new St.Label({ style_class: 'goa-message-base goa-message-from',
text: _stripEmailAddress(message.from)});
let hbox = new St.BoxLayout({ style_class: 'goa-message-hbox', vertical: false });
let subjectLabel = new St.Label({ style_class: 'goa-message-base goa-message-subject',
text: message.subject });
let excerptLabel = new St.Label({ style_class: 'goa-message-base goa-message-excerpt',
text: formattedExcerpt });
let dateLabel = new St.Label({ style_class: 'goa-message-base goa-message-date',
text: formattedDate });
excerptLabel.clutter_text.line_wrap = false;
excerptLabel.clutter_text.ellipsize = Pango.EllipsizeMode.END;
hbox.add(subjectLabel, { x_fill: true,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.START });
hbox.add(excerptLabel, { x_fill: true,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.START });
let n = table.get_row_count();
table.add(fromLabel, { x_fill: true, x_expand: true, row: n, col: 0 });
table.add(hbox, { row: n, col: 1 });
table.add(dateLabel, { row: n, col: 2 });
}
function MailSource(client) {
this._init(client);
}
MailSource.prototype = {
__proto__: MessageTray.Source.prototype,
_init : function(client) {
this._client = client;
this._pendingMessages = [];
// Init super class and add ourselves to the message tray
MessageTray.Source.prototype._init.call(this, 'Mail');
// Create the notification
this._notification = new MessageTray.Notification(this)
this._notification.setUrgency(MessageTray.Urgency.NORMAL);
this._notification.setResident(true);
this._updateNotification();
this.pushNotification(this._notification);
// Refresh all monitors everytime the "Mail" notification is displayed
this._notification.connect('expanded', Lang.bind(this,
function() {
this._client.refreshAllMonitors();
}));
},
createNotificationIcon : function() {
let numPending = this._pendingMessages.length;
let baseIcon = new Gio.ThemedIcon({ name: 'mail-mark-unread'});
let numerableIcon = new Gtk.NumerableIcon({ gicon: baseIcon });
numerableIcon.set_count(numPending);
let icon = new St.Icon({ icon_type: St.IconType.FULLCOLOR,
icon_size: this.ICON_SIZE });
icon.set_gicon(numerableIcon);
return icon;
},
_updateNotification: function() {
if (!this._notification)
return
let title = 'Mail';
let banner = ''
let table = new St.Table({ homogeneous: false,
style_class: 'goa-message-table',
reactive: true });
for (let n = 0; n < this._pendingMessages.length; n++)
_addMessageToTable (table, this._pendingMessages[n]);
this._notification.update(title, banner, { clear: true,
icon: this.createNotificationIcon() });
this._notification.addActor(table);
this._notification.addButton('close', 'Close');
this._notification.addButton('ignore-all', 'Ignore All');
this._notification.connect('action-invoked', Lang.bind(this,
function(notification, id) {
if (id == 'close') {
// TODO: Can't find another way hide it
notification._onClicked();
} else if (id == 'ignore-all') {
this.clearMessages();
}
}));
},
addMessage: function(message) {
this._pendingMessages.push(message);
// Update notification
this._updateNotification();
// Update icon with latest pending count
this._setSummaryIcon(this.createNotificationIcon());
},
clearMessages: function() {
let notification = this._notification;
this._notification = null;
if (notification)
notification.destroy();
this.destroy();
},
}

View File

@@ -1,532 +0,0 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Caribou = imports.gi.Caribou;
const Clutter = imports.gi.Clutter;
const DBus = imports.dbus;
const Gdk = imports.gi.Gdk;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const BoxPointer = imports.ui.boxpointer;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const KEYBOARD_SCHEMA = 'org.gnome.shell.keyboard';
const SHOW_KEYBOARD = 'show-keyboard';
const KEYBOARD_TYPE = 'keyboard-type';
// Key constants taken from Antler
// FIXME: ought to be moved into libcaribou
const PRETTY_KEYS = {
'BackSpace': '\u232b',
'space': ' ',
'Return': '\u23ce',
'Caribou_Prefs': '\u2328',
'Caribou_ShiftUp': '\u2b06',
'Caribou_ShiftDown': '\u2b07',
'Caribou_Emoticons': '\u263a',
'Caribou_Symbols': '123',
'Caribou_Symbols_More': '{#*',
'Caribou_Alpha': 'Abc',
'Tab': 'Tab',
'Escape': 'Esc',
'Control_L': 'Ctrl',
'Alt_L': 'Alt'
};
const CaribouKeyboardIface = {
name: 'org.gnome.Caribou.Keyboard',
methods: [ { name: 'Show',
inSignature: 'u',
outSignature: ''
},
{ name: 'Hide',
inSignature: 'u',
outSignature: ''
},
{ name: 'SetCursorLocation',
inSignature: 'iiii',
outSignature: ''
},
{ name: 'SetEntryLocation',
inSignature: 'iiii',
outSignature: ''
} ],
properties: [ { name: 'Name',
signature: 's',
access: 'read' } ]
};
function Key() {
this._init.apply(this, arguments);
}
Key.prototype = {
_init : function(key) {
this._key = key;
this.actor = this._makeKey();
this._extended_keys = this._key.get_extended_keys();
this._extended_keyboard = null;
if (this._key.name == "Control_L" || this._key.name == "Alt_L")
this._key.latch = true;
this._key.connect('key-pressed', Lang.bind(this, function ()
{ this.actor.checked = true }));
this._key.connect('key-released', Lang.bind(this, function ()
{ this.actor.checked = false; }));
if (this._extended_keys.length > 0) {
this._grabbed = false;
this._eventCaptureId = 0;
this._key.connect('notify::show-subkeys', Lang.bind(this, this._onShowSubkeysChanged));
this._boxPointer = new BoxPointer.BoxPointer(St.Side.BOTTOM,
{ x_fill: true,
y_fill: true,
x_align: St.Align.START });
// Adds style to existing keyboard style to avoid repetition
this._boxPointer.actor.add_style_class_name('keyboard-subkeys');
this._getExtendedKeys();
this.actor._extended_keys = this._extended_keyboard;
this._boxPointer.actor.hide();
Main.layoutManager.addChrome(this._boxPointer.actor, { visibleInFullscreen: true });
}
},
_makeKey: function () {
let label = this._key.name;
if (label.length > 1) {
let pretty = PRETTY_KEYS[label];
if (pretty)
label = pretty;
else
label = this._getUnichar(this._key);
}
label = GLib.markup_escape_text(label, -1);
let button = new St.Button ({ label: label,
style_class: 'keyboard-key' });
button.key_width = this._key.width;
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;
},
_getUnichar: function(key) {
let keyval = key.keyval;
let unichar = Gdk.keyval_to_unicode(keyval);
if (unichar) {
return String.fromCharCode(unichar);
} else {
return key.name;
}
},
_getExtendedKeys: function () {
this._extended_keyboard = new St.BoxLayout({ style_class: 'keyboard-layout',
vertical: false });
for (let i = 0; i < this._extended_keys.length; ++i) {
let extended_key = this._extended_keys[i];
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(); }));
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);
},
_onEventCapture: function (actor, event) {
let source = event.get_source();
let type = event.type();
if ((type == Clutter.EventType.BUTTON_PRESS ||
type == Clutter.EventType.BUTTON_RELEASE) &&
this._extended_keyboard.contains(source)) {
source.extended_key.press();
source.extended_key.release();
return false;
}
if (type == Clutter.EventType.BUTTON_PRESS) {
this._boxPointer.actor.hide();
this._ungrab();
return true;
}
return false;
},
_ungrab: function () {
global.stage.disconnect(this._eventCaptureId);
this._eventCaptureId = 0;
this._grabbed = false;
Main.popModal(this.actor);
},
_onShowSubkeysChanged: function () {
if (this._key.show_subkeys) {
this.actor.fake_release();
this._boxPointer.actor.raise_top();
this._boxPointer.setPosition(this.actor, 0.5);
this._boxPointer.show(true);
this.actor.set_hover(false);
if (!this._grabbed) {
Main.pushModal(this.actor);
this._eventCaptureId = global.stage.connect('captured-event', Lang.bind(this, this._onEventCapture));
this._grabbed = true;
}
this._key.release();
} else {
if (this._grabbed)
this._ungrab();
this._boxPointer.hide(true);
}
}
};
function Keyboard() {
this._init.apply(this, arguments);
}
Keyboard.prototype = {
_init: function () {
DBus.session.exportObject('/org/gnome/Caribou/Keyboard', this);
DBus.session.acquire_name('org.gnome.Caribou.Keyboard', 0, null, null);
this._timestamp = global.get_current_time();
this.actor = new St.BoxLayout({ name: 'keyboard', vertical: true, reactive: true });
Main.layoutManager.keyboardBox.add_actor(this.actor);
Main.layoutManager.trackChrome(this.actor);
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._redraw));
this._keyboardSettings = new Gio.Settings({ schema: KEYBOARD_SCHEMA });
this._keyboardSettings.connect('changed', Lang.bind(this, this._settingsChanged));
this._settingsChanged();
},
init: function () {
if (this._enableKeyboard)
this._redraw();
},
_settingsChanged: function () {
this._enableKeyboard = this._keyboardSettings.get_boolean(SHOW_KEYBOARD);
if (!this._enableKeyboard && !this._keyboard)
return;
if (this._enableKeyboard && this._keyboard &&
this._keyboard.keyboard_type == this._keyboardSettings.get_string(KEYBOARD_TYPE))
return;
if (this._keyboard)
this._destroyKeyboard();
if (this._enableKeyboard)
this._setupKeyboard();
else
Main.layoutManager.hideKeyboard(true);
},
_destroyKeyboard: function() {
if (this._keyboardNotifyId)
this._keyboard.disconnect(this._keyboardNotifyId);
if (this._focusNotifyId)
global.stage.disconnect(this._focusNotifyId);
this._keyboard = null;
this.actor.destroy_children();
this._destroySource();
},
_setupKeyboard: function() {
this._keyboard = new Caribou.KeyboardModel({ keyboard_type: this._keyboardSettings.get_string(KEYBOARD_TYPE) });
this._groups = {};
this._current_page = null;
// Initialize keyboard key measurements
this._numOfHorizKeys = 0;
this._numOfVertKeys = 0;
this._addKeys();
this._keyboardNotifyId = this._keyboard.connect('notify::active-group', Lang.bind(this, this._onGroupChanged));
this._focusNotifyId = global.stage.connect('notify::key-focus', Lang.bind(this, this._onKeyFocusChanged));
this._createSource();
},
_onKeyFocusChanged: function () {
let focus = global.stage.key_focus;
if (focus == global.stage || focus == null)
return;
if (focus instanceof Clutter.Text)
this.show();
else {
if (focus._extended_keys == null)
this.hide();
}
},
_addKeys: function () {
let groups = this._keyboard.get_groups();
for (let i = 0; i < groups.length; ++i) {
let gname = groups[i];
let group = this._keyboard.get_group(gname);
group.connect('notify::active-level', Lang.bind(this, this._onLevelChanged));
let layers = {};
let levels = group.get_levels();
for (let j = 0; j < levels.length; ++j) {
let lname = levels[j];
let level = group.get_level(lname);
let layout = new St.BoxLayout({ style_class: 'keyboard-layout',
vertical: true });
this._loadRows(level, layout);
layers[lname] = layout;
this.actor.add(layout, { x_fill: false });
layout.hide();
}
this._groups[gname] = layers;
}
this._setActiveLayer();
},
_getTrayIcon: function () {
let trayButton = new St.Button ({ label: "tray", style_class: 'keyboard-key' });
trayButton.key_width = 1;
trayButton.connect('button-press-event', Lang.bind(this, function () {
Main.messageTray.toggle();
}));
Main.overview.connect('showing', Lang.bind(this, function () {
trayButton.reactive = false;
trayButton.add_style_pseudo_class('grayed');
}));
Main.overview.connect('hiding', Lang.bind(this, function () {
trayButton.reactive = true;
trayButton.remove_style_pseudo_class('grayed');
}));
return trayButton;
},
_addRows : function (keys, layout) {
let keyboard_row = new St.BoxLayout();
for (let i = 0; i < keys.length; ++i) {
let children = keys[i].get_children();
let right_box = new St.BoxLayout({ style_class: 'keyboard-row' });
let left_box = new St.BoxLayout({ style_class: 'keyboard-row' });
for (let j = 0; j < children.length; ++j) {
if (this._numOfHorizKeys == 0)
this._numOfHorizKeys = children.length;
let key = children[j];
let button = new Key(key);
if (key.align == 'right')
right_box.add(button.actor);
else
left_box.add(button.actor);
if (key.name == "Caribou_Prefs") {
key.connect('key-released', Lang.bind(this, this.hide));
// Add new key for hiding message tray
right_box.add(this._getTrayIcon());
}
}
keyboard_row.add(left_box, { expand: true, x_fill: false, x_align: St.Align.START });
keyboard_row.add(right_box, { expand: true, x_fill: false, x_align: St.Align.END });
}
layout.add(keyboard_row);
},
_loadRows : function (level, layout) {
let rows = level.get_rows();
for (let i = 0; i < rows.length; ++i) {
let row = rows[i];
if (this._numOfVertKeys == 0)
this._numOfVertKeys = rows.length;
this._addRows(row.get_columns(), layout);
}
},
_redraw: function () {
let monitor = Main.layoutManager.bottomMonitor;
let maxHeight = monitor.height / 3;
this.actor.width = monitor.width;
let layout = this._current_page;
let verticalSpacing = layout.get_theme_node().get_length('spacing');
let padding = layout.get_theme_node().get_length('padding');
let box = layout.get_children()[0].get_children()[0];
let horizontalSpacing = box.get_theme_node().get_length('spacing');
let allHorizontalSpacing = (this._numOfHorizKeys - 1) * horizontalSpacing;
let keyWidth = Math.floor((this.actor.width - allHorizontalSpacing - 2 * padding) / this._numOfHorizKeys);
let allVerticalSpacing = (this._numOfVertKeys - 1) * verticalSpacing;
let keyHeight = Math.floor((maxHeight - allVerticalSpacing - 2 * padding) / this._numOfVertKeys);
let keySize = Math.min(keyWidth, keyHeight);
this.actor.height = keySize * this._numOfVertKeys + allVerticalSpacing + 2 * padding;
let rows = this._current_page.get_children();
for (let i = 0; i < rows.length; ++i) {
let keyboard_row = rows[i];
let boxes = keyboard_row.get_children();
for (let j = 0; j < boxes.length; ++j) {
let keys = boxes[j].get_children();
for (let k = 0; k < keys.length; ++k) {
let child = keys[k];
child.width = keySize * child.key_width;
child.height = keySize;
if (child._extended_keys) {
let extended_keys = child._extended_keys.get_children();
for (let n = 0; n < extended_keys.length; ++n) {
let extended_key = extended_keys[n];
extended_key.width = keySize;
extended_key.height = keySize;
}
}
}
}
}
},
_onLevelChanged: function () {
this._setActiveLayer();
this._redraw();
},
_onGroupChanged: function () {
this._setActiveLayer();
this._redraw();
},
_setActiveLayer: function () {
let active_group_name = this._keyboard.active_group;
let active_group = this._keyboard.get_group(active_group_name);
let active_level = active_group.active_level;
let layers = this._groups[active_group_name];
if (this._current_page != null) {
this._current_page.hide();
}
this._current_page = layers[active_level];
this._current_page.show();
},
_createSource: function () {
if (this._source == null) {
this._source = new KeyboardSource(this);
this._source.setTransient(true);
Main.messageTray.add(this._source);
}
},
_destroySource: function () {
if (this._source) {
this._source.destroy();
this._source = null;
}
},
show: function () {
this._redraw();
Main.layoutManager.showKeyboard();
this._destroySource();
},
hide: function () {
Main.layoutManager.hideKeyboard();
this._createSource();
},
_moveTemporarily: function () {
let currentWindow = global.screen.get_display().focus_window;
let rect = currentWindow.get_outer_rect();
let newX = rect.x;
let newY = 3 * this.actor.height / 2;
currentWindow.move_frame(true, newX, newY);
},
_setLocation: function (x, y) {
if (y >= 2 * this.actor.height)
this._moveTemporarily();
},
// D-Bus methods
Show: function(timestamp) {
if (timestamp - this._timestamp < 0)
return;
this._timestamp = timestamp;
this.show();
},
Hide: function(timestamp) {
if (timestamp - this._timestamp < 0)
return;
this._timestamp = timestamp;
this.hide();
},
SetCursorLocation: function(x, y, w, h) {
this._setLocation(x, y);
},
SetEntryLocation: function(x, y, w, h) {
this._setLocation(x, y);
},
get Name() {
return 'gnome-shell';
}
};
DBus.conformExport(Keyboard.prototype, CaribouKeyboardIface);
function KeyboardSource() {
this._init.apply(this, arguments);
}
KeyboardSource.prototype = {
__proto__: MessageTray.Source.prototype,
_init: function(keyboard) {
this._keyboard = keyboard;
MessageTray.Source.prototype._init.call(this, _("Keyboard"));
this._setSummaryIcon(this.createNotificationIcon());
},
createNotificationIcon: function() {
return new St.Icon({ icon_name: 'input-keyboard',
icon_type: St.IconType.SYMBOLIC,
icon_size: this.ICON_SIZE });
},
handleSummaryClick: function() {
let event = Clutter.get_current_event();
if (event.type() != Clutter.EventType.BUTTON_RELEASE)
return false;
this.open();
return true;
},
open: function() {
this._keyboard.show();
}
};

View File

@@ -1,928 +0,0 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Clutter = imports.gi.Clutter;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const St = imports.gi.St;
const Main = imports.ui.main;
const Params = imports.misc.params;
const ScreenSaver = imports.misc.screenSaver;
const Tweener = imports.ui.tweener;
const HOT_CORNER_ACTIVATION_TIMEOUT = 0.5;
const STARTUP_ANIMATION_TIME = 0.2;
const KEYBOARD_ANIMATION_TIME = 0.5;
function LayoutManager() {
this._init.apply(this, arguments);
}
LayoutManager.prototype = {
_init: function () {
this._rtl = (St.Widget.get_default_direction() == St.TextDirection.RTL);
this.monitors = [];
this.primaryMonitor = null;
this.primaryIndex = -1;
this._hotCorners = [];
this._leftPanelBarrier = 0;
this._rightPanelBarrier = 0;
this._trayBarrier = 0;
this._chrome = new Chrome(this);
this.panelBox = new St.BoxLayout({ name: 'panelBox',
vertical: true });
this.addChrome(this.panelBox, { affectsStruts: true });
this.panelBox.connect('allocation-changed',
Lang.bind(this, this._updatePanelBarriers));
// bottomBox contains the tray and keyboard (which move
// together, since the tray slides up from the top of the
// keyboard when the keyboard is visible).
this._bottomBox = new St.BoxLayout({ name: 'bottomBox',
vertical: true });
this.addChrome(this._bottomBox, { visibleInFullscreen: true });
this.trayBox = new St.BoxLayout({ name: 'trayBox' });
this.trayBox.connect('allocation-changed',
Lang.bind(this, this._updateTrayBarrier));
this._bottomBox.add_actor(this.trayBox);
this.keyboardBox = new St.BoxLayout({ name: 'keyboardBox' });
this._bottomBox.add_actor(this.keyboardBox);
global.screen.connect('monitors-changed',
Lang.bind(this, this._monitorsChanged));
this._monitorsChanged();
},
// This is called by Main after everything else is constructed;
// Chrome.init() needs access to Main.overview, which didn't exist
// yet when the LayoutManager was constructed.
init: function() {
this._chrome.init();
this._startupAnimation();
},
_updateMonitors: function() {
let screen = global.screen;
this.monitors = [];
let nMonitors = screen.get_n_monitors();
for (let i = 0; i < nMonitors; i++)
this.monitors.push(screen.get_monitor_geometry(i));
if (nMonitors == 1) {
this.primaryIndex = this.bottomIndex = 0;
} else {
// If there are monitors below the primary, then we need
// to split primary from bottom.
this.primaryIndex = this.bottomIndex = screen.get_primary_monitor();
for (let i = 0; i < this.monitors.length; i++) {
let monitor = this.monitors[i];
if (this._isAboveOrBelowPrimary(monitor)) {
if (monitor.y > this.monitors[this.bottomIndex].y)
this.bottomIndex = i;
}
}
}
this.primaryMonitor = this.monitors[this.primaryIndex];
this.bottomMonitor = this.monitors[this.bottomIndex];
},
_updateHotCorners: function() {
// destroy old hot corners
for (let i = 0; i < this._hotCorners.length; i++)
this._hotCorners[i].destroy();
this._hotCorners = [];
// build new hot corners
for (let i = 0; i < this.monitors.length; i++) {
if (i == this.primaryIndex)
continue;
let monitor = this.monitors[i];
let cornerX = this._rtl ? monitor.x + monitor.width : monitor.x;
let cornerY = monitor.y;
let haveTopLeftCorner = true;
// Check if we have a top left (right for RTL) corner.
// I.e. if there is no monitor directly above or to the left(right)
let besideX = this._rtl ? monitor.x + 1 : cornerX - 1;
let besideY = cornerY;
let aboveX = cornerX;
let aboveY = cornerY - 1;
for (let j = 0; j < this.monitors.length; j++) {
if (i == j)
continue;
let otherMonitor = this.monitors[j];
if (besideX >= otherMonitor.x &&
besideX < otherMonitor.x + otherMonitor.width &&
besideY >= otherMonitor.y &&
besideY < otherMonitor.y + otherMonitor.height) {
haveTopLeftCorner = false;
break;
}
if (aboveX >= otherMonitor.x &&
aboveX < otherMonitor.x + otherMonitor.width &&
aboveY >= otherMonitor.y &&
aboveY < otherMonitor.y + otherMonitor.height) {
haveTopLeftCorner = false;
break;
}
}
if (!haveTopLeftCorner)
continue;
let corner = new HotCorner();
this._hotCorners.push(corner);
corner.actor.set_position(cornerX, cornerY);
this._chrome.addActor(corner.actor);
}
},
_updateBoxes: function() {
this.panelBox.set_position(this.primaryMonitor.x, this.primaryMonitor.y);
this.panelBox.set_size(this.primaryMonitor.width, -1);
this._bottomBox.set_position(this.bottomMonitor.x,
this.bottomMonitor.y + this.bottomMonitor.height);
this._bottomBox.set_size(this.bottomMonitor.width, -1);
this.trayBox.width = this.bottomMonitor.width;
// Set trayBox's clip to show things above it, but not below
// it (so it's not visible behind the keyboard). The exact
// height of the clip doesn't matter, as long as it's taller
// than any Notification.actor.
this.trayBox.set_clip(0, -this.bottomMonitor.height,
this.bottomMonitor.width, this.bottomMonitor.height);
},
_updatePanelBarriers: function() {
if (this._leftPanelBarrier)
global.destroy_pointer_barrier(this._leftPanelBarrier);
if (this._rightPanelBarrier)
global.destroy_pointer_barrier(this._rightPanelBarrier);
if (this.panelBox.height) {
let primary = this.primaryMonitor;
this._leftPanelBarrier =
global.create_pointer_barrier(primary.x, primary.y,
primary.x, primary.y + this.panelBox.height,
1 /* BarrierPositiveX */);
this._rightPanelBarrier =
global.create_pointer_barrier(primary.x + primary.width, primary.y,
primary.x + primary.width, primary.y + this.panelBox.height,
4 /* BarrierNegativeX */);
} else {
this._leftPanelBarrier = 0;
this._rightPanelBarrier = 0;
}
},
_updateTrayBarrier: function() {
let monitor = this.bottomMonitor;
if (this._trayBarrier)
global.destroy_pointer_barrier(this._trayBarrier);
if (Main.messageTray) {
this._trayBarrier =
global.create_pointer_barrier(monitor.x + monitor.width, monitor.y + monitor.height - Main.messageTray.actor.height,
monitor.x + monitor.width, monitor.y + monitor.height,
4 /* BarrierNegativeX */);
} else {
this._trayBarrier = 0;
}
},
_monitorsChanged: function() {
this._updateMonitors();
this._updateBoxes();
this._updateHotCorners();
this.emit('monitors-changed');
},
_isAboveOrBelowPrimary: function(monitor) {
let primary = this.monitors[this.primaryIndex];
let monitorLeft = monitor.x, monitorRight = monitor.x + monitor.width;
let primaryLeft = primary.x, primaryRight = primary.x + primary.width;
if ((monitorLeft >= primaryLeft && monitorLeft <= primaryRight) ||
(monitorRight >= primaryLeft && monitorRight <= primaryRight) ||
(primaryLeft >= monitorLeft && primaryLeft <= monitorRight) ||
(primaryRight >= monitorLeft && primaryRight <= monitorRight))
return true;
return false;
},
get focusIndex() {
let focusWindow = global.display.focus_window;
if (focusWindow) {
let wrect = focusWindow.get_outer_rect();
for (let i = 0; i < this.monitors.length; i++) {
let monitor = this.monitors[i];
if (monitor.x <= wrect.x && monitor.y <= wrect.y &&
monitor.x + monitor.width > wrect.x &&
monitor.y + monitor.height > wrect.y)
return i;
}
}
return this.primaryIndex;
},
get focusMonitor() {
return this.monitors[this.focusIndex];
},
_startupAnimation: function() {
this.panelBox.anchor_y = this.panelBox.height;
Tweener.addTween(this.panelBox,
{ anchor_y: 0,
time: STARTUP_ANIMATION_TIME,
transition: 'easeOutQuad'
});
},
showKeyboard: function () {
Main.messageTray.hide();
Tweener.addTween(this._bottomBox,
{ anchor_y: this._bottomBox.height,
time: KEYBOARD_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: this._showKeyboardComplete,
onCompleteScope: this
});
},
_showKeyboardComplete: function() {
// Poke Chrome to update the input shape; it doesn't notice
// anchor point changes
this._chrome.updateRegions();
this._bottomBox.connect('notify::height', Lang.bind(this, function () {
this._bottomBoxAnchor = this._bottomBox.height;
}));
},
hideKeyboard: function (immediate) {
Main.messageTray.hide();
Tweener.addTween(this._bottomBox,
{ anchor_y: 0,
time: immediate ? 0 : KEYBOARD_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: this._showKeyboardComplete,
onCompleteScope: this
});
},
_hideKeyboardComplete: function() {
this._chrome.updateRegions();
},
// addChrome:
// @actor: an actor to add to the chrome layer
// @params: (optional) additional params
//
// Adds @actor to the chrome layer and (unless %affectsInputRegion
// in @params is %false) extends the input region to include it.
// Changes in @actor's size, position, and visibility will
// automatically result in appropriate changes to the input
// region.
//
// If %affectsStruts in @params is %true (and @actor is along a
// screen edge), then @actor's size and position will also affect
// the window manager struts. Changes to @actor's visibility will
// NOT affect whether or not the strut is present, however.
//
// If %visibleInFullscreen in @params is %true, the actor will be
// visible even when a fullscreen window should be covering it.
addChrome: function(actor, params) {
this._chrome.addActor(actor, params);
},
// trackChrome:
// @actor: a descendant of the chrome to begin tracking
// @params: parameters describing how to track @actor
//
// Tells the chrome to track @actor, which must be a descendant
// of an actor added via addChrome(). This can be used to extend the
// struts or input region to cover specific children.
//
// @params can have any of the same values as in addChrome(),
// though some possibilities don't make sense (eg, trying to have
// a %visibleInFullscreen child of a non-%visibleInFullscreen
// parent). By default, @actor has the same params as its chrome
// ancestor.
trackChrome: function(actor, params) {
this._chrome.trackActor(actor, params);
},
// untrackChrome:
// @actor: an actor previously tracked via trackChrome()
//
// Undoes the effect of trackChrome()
untrackChrome: function(actor) {
this._chrome.untrackActor(actor);
},
// removeChrome:
// @actor: a child of the chrome layer
//
// Removes @actor from the chrome layer
removeChrome: function(actor) {
this._chrome.removeActor(actor);
}
};
Signals.addSignalMethods(LayoutManager.prototype);
// HotCorner:
//
// This class manages a "hot corner" that can toggle switching to
// overview.
function HotCorner() {
this._init();
}
HotCorner.prototype = {
_init : function() {
// We use this flag to mark the case where the user has entered the
// hot corner and has not left both the hot corner and a surrounding
// guard area (the "environs"). This avoids triggering the hot corner
// multiple times due to an accidental jitter.
this._entered = false;
this.actor = new Clutter.Group({ name: 'hot-corner-environs',
width: 3,
height: 3,
reactive: true });
this._corner = new Clutter.Rectangle({ name: 'hot-corner',
width: 1,
height: 1,
opacity: 0,
reactive: true });
this._corner._delegate = this;
this.actor.add_actor(this._corner);
if (St.Widget.get_default_direction() == St.TextDirection.RTL) {
this._corner.set_position(this.actor.width - this._corner.width, 0);
this.actor.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
} else {
this._corner.set_position(0, 0);
}
this._activationTime = 0;
this.actor.connect('leave-event',
Lang.bind(this, this._onEnvironsLeft));
// Clicking on the hot corner environs should result in the
// same behavior as clicking on the hot corner.
this.actor.connect('button-release-event',
Lang.bind(this, this._onCornerClicked));
// In addition to being triggered by the mouse enter event,
// the hot corner can be triggered by clicking on it. This is
// useful if the user wants to undo the effect of triggering
// the hot corner once in the hot corner.
this._corner.connect('enter-event',
Lang.bind(this, this._onCornerEntered));
this._corner.connect('button-release-event',
Lang.bind(this, this._onCornerClicked));
this._corner.connect('leave-event',
Lang.bind(this, this._onCornerLeft));
// Cache the three ripples instead of dynamically creating and destroying them.
this._ripple1 = new St.BoxLayout({ style_class: 'ripple-box', opacity: 0 });
this._ripple2 = new St.BoxLayout({ style_class: 'ripple-box', opacity: 0 });
this._ripple3 = new St.BoxLayout({ style_class: 'ripple-box', opacity: 0 });
Main.uiGroup.add_actor(this._ripple1);
Main.uiGroup.add_actor(this._ripple2);
Main.uiGroup.add_actor(this._ripple3);
},
destroy: function() {
this.actor.destroy();
},
_animRipple : function(ripple, delay, time, startScale, startOpacity, finalScale) {
// We draw a ripple by using a source image and animating it scaling
// outwards and fading away. We want the ripples to move linearly
// or it looks unrealistic, but if the opacity of the ripple goes
// linearly to zero it fades away too quickly, so we use Tweener's
// 'onUpdate' to give a non-linear curve to the fade-away and make
// it more visible in the middle section.
ripple._opacity = startOpacity;
if (ripple.get_direction() == St.TextDirection.RTL)
ripple.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
ripple.visible = true;
ripple.opacity = 255 * Math.sqrt(startOpacity);
ripple.scale_x = ripple.scale_y = startScale;
let [x, y] = this._corner.get_transformed_position();
ripple.x = x;
ripple.y = y;
Tweener.addTween(ripple, { _opacity: 0,
scale_x: finalScale,
scale_y: finalScale,
delay: delay,
time: time,
transition: 'linear',
onUpdate: function() { ripple.opacity = 255 * Math.sqrt(ripple._opacity); },
onComplete: function() { ripple.visible = false; } });
},
rippleAnimation: function() {
// Show three concentric ripples expanding outwards; the exact
// parameters were found by trial and error, so don't look
// for them to make perfect sense mathematically
// delay time scale opacity => scale
this._animRipple(this._ripple1, 0.0, 0.83, 0.25, 1.0, 1.5);
this._animRipple(this._ripple2, 0.05, 1.0, 0.0, 0.7, 1.25);
this._animRipple(this._ripple3, 0.35, 1.0, 0.0, 0.3, 1);
},
handleDragOver: function(source, actor, x, y, time) {
if (source != Main.xdndHandler)
return;
if (!Main.overview.visible && !Main.overview.animationInProgress) {
this.rippleAnimation();
Main.overview.showTemporarily();
Main.overview.beginItemDrag(actor);
}
},
_onCornerEntered : function() {
if (!this._entered) {
this._entered = true;
if (!Main.overview.animationInProgress) {
this._activationTime = Date.now() / 1000;
this.rippleAnimation();
Main.overview.toggle();
}
}
return false;
},
_onCornerClicked : function() {
if (this.shouldToggleOverviewOnClick())
Main.overview.toggle();
return true;
},
_onCornerLeft : function(actor, event) {
if (event.get_related() != this.actor)
this._entered = false;
// Consume event, otherwise this will confuse onEnvironsLeft
return true;
},
_onEnvironsLeft : function(actor, event) {
if (event.get_related() != this._corner)
this._entered = false;
return false;
},
// Checks if the Activities button is currently sensitive to
// clicks. The first call to this function within the
// HOT_CORNER_ACTIVATION_TIMEOUT time of the hot corner being
// triggered will return false. This avoids opening and closing
// the overview if the user both triggered the hot corner and
// clicked the Activities button.
shouldToggleOverviewOnClick: function() {
if (Main.overview.animationInProgress)
return false;
if (this._activationTime == 0 || Date.now() / 1000 - this._activationTime > HOT_CORNER_ACTIVATION_TIMEOUT)
return true;
return false;
}
};
// This manages the shell "chrome"; the UI that's visible in the
// normal mode (ie, outside the Overview), that surrounds the main
// workspace content.
const defaultParams = {
visibleInFullscreen: false,
affectsStruts: false,
affectsInputRegion: true
};
function Chrome() {
this._init.apply(this, arguments);
}
Chrome.prototype = {
_init: function(layoutManager) {
this._layoutManager = layoutManager;
// The group itself has zero size so it doesn't interfere with DND
this.actor = new Shell.GenericContainer({ width: 0, height: 0 });
Main.uiGroup.add_actor(this.actor);
this.actor.connect('allocate', Lang.bind(this, this._allocated));
this._monitors = [];
this._inOverview = false;
this._trackedActors = [];
this._layoutManager.connect('monitors-changed',
Lang.bind(this, this._relayout));
global.screen.connect('restacked',
Lang.bind(this, this._windowsRestacked));
// Need to update struts on new workspaces when they are added
global.screen.connect('notify::n-workspaces',
Lang.bind(this, this._queueUpdateRegions));
this._screenSaverProxy = new ScreenSaver.ScreenSaverProxy();
this._screenSaverProxy.connect('ActiveChanged', Lang.bind(this, this._onScreenSaverActiveChanged));
this._screenSaverProxy.GetActiveRemote(Lang.bind(this,
function(result, err) {
if (!err)
this._onScreenSaverActiveChanged(this._screenSaverProxy, result);
}));
this._relayout();
},
init: function() {
Main.overview.connect('showing',
Lang.bind(this, this._overviewShowing));
Main.overview.connect('hidden',
Lang.bind(this, this._overviewHidden));
},
_allocated: function(actor, box, flags) {
let children = this.actor.get_children();
for (let i = 0; i < children.length; i++)
children[i].allocate_preferred_size(flags);
},
addActor: function(actor, params) {
this.actor.add_actor(actor);
this._trackActor(actor, params);
},
trackActor: function(actor, params) {
let ancestor = actor.get_parent();
let index = this._findActor(ancestor);
while (ancestor && index == -1) {
ancestor = ancestor.get_parent();
index = this._findActor(ancestor);
}
if (!ancestor)
throw new Error('actor is not a descendent of the chrome layer');
let ancestorData = this._trackedActors[index];
if (!params)
params = {};
// We can't use Params.parse here because we want to drop
// the extra values like ancestorData.actor
for (let prop in defaultParams) {
if (!params[prop])
params[prop] = ancestorData[prop];
}
this._trackActor(actor, params);
},
untrackActor: function(actor) {
this._untrackActor(actor);
},
removeActor: function(actor) {
this.actor.remove_actor(actor);
this._untrackActor(actor);
},
_findActor: function(actor) {
for (let i = 0; i < this._trackedActors.length; i++) {
let actorData = this._trackedActors[i];
if (actorData.actor == actor)
return i;
}
return -1;
},
_trackActor: function(actor, params) {
if (this._findActor(actor) != -1)
throw new Error('trying to re-track existing chrome actor');
let actorData = Params.parse(params, defaultParams);
actorData.actor = actor;
actorData.visibleId = actor.connect('notify::visible',
Lang.bind(this, this._queueUpdateRegions));
actorData.allocationId = actor.connect('notify::allocation',
Lang.bind(this, this._queueUpdateRegions));
actorData.parentSetId = actor.connect('parent-set',
Lang.bind(this, this._actorReparented));
// Note that destroying actor will unset its parent, so we don't
// need to connect to 'destroy' too.
this._trackedActors.push(actorData);
this._queueUpdateRegions();
},
_untrackActor: function(actor) {
let i = this._findActor(actor);
if (i == -1)
return;
let actorData = this._trackedActors[i];
this._trackedActors.splice(i, 1);
actor.disconnect(actorData.visibleId);
actor.disconnect(actorData.allocationId);
actor.disconnect(actorData.parentSetId);
this._queueUpdateRegions();
},
_actorReparented: function(actor, oldParent) {
if (!this.actor.contains(actor))
this._untrackActor(actor);
},
_updateVisibility: function() {
for (let i = 0; i < this._trackedActors.length; i++) {
let actorData = this._trackedActors[i];
if (!this._inOverview && !actorData.visibleInFullscreen &&
this._findMonitorForActor(actorData.actor).inFullscreen)
this.actor.set_skip_paint(actorData.actor, true);
else
this.actor.set_skip_paint(actorData.actor, false);
}
},
_overviewShowing: function() {
this._inOverview = true;
this._updateVisibility();
this._queueUpdateRegions();
},
_overviewHidden: function() {
this._inOverview = false;
this._updateVisibility();
this._queueUpdateRegions();
},
_relayout: function() {
this._monitors = this._layoutManager.monitors;
this._primaryMonitor = this._layoutManager.primaryMonitor;
this._updateFullscreen();
this._updateVisibility();
this._queueUpdateRegions();
},
_onScreenSaverActiveChanged: function(proxy, screenSaverActive) {
this.actor.visible = !screenSaverActive;
this._queueUpdateRegions();
},
_findMonitorForRect: function(x, y, w, h) {
// First look at what monitor the center of the rectangle is at
let cx = x + w/2;
let cy = y + h/2;
for (let i = 0; i < this._monitors.length; i++) {
let monitor = this._monitors[i];
if (cx >= monitor.x && cx < monitor.x + monitor.width &&
cy >= monitor.y && cy < monitor.y + monitor.height)
return monitor;
}
// If the center is not on a monitor, return the first overlapping monitor
for (let i = 0; i < this._monitors.length; i++) {
let monitor = this._monitors[i];
if (x + w > monitor.x && x < monitor.x + monitor.width &&
y + h > monitor.y && y < monitor.y + monitor.height)
return monitor;
}
// otherwise on no monitor
return null;
},
_findMonitorForWindow: function(window) {
return this._findMonitorForRect(window.x, window.y, window.width, window.height);
},
// This call guarantees that we return some monitor to simplify usage of it
// In practice all tracked actors should be visible on some monitor anyway
_findMonitorForActor: function(actor) {
let [x, y] = actor.get_transformed_position();
let [w, h] = actor.get_transformed_size();
let monitor = this._findMonitorForRect(x, y, w, h);
if (monitor)
return monitor;
return this._primaryMonitor; // Not on any monitor, pretend its on the primary
},
_queueUpdateRegions: function() {
if (!this._updateRegionIdle)
this._updateRegionIdle = Mainloop.idle_add(Lang.bind(this, this.updateRegions),
Meta.PRIORITY_BEFORE_REDRAW);
},
_updateFullscreen: function() {
let windows = Main.getWindowActorsForWorkspace(global.screen.get_active_workspace_index());
// Reset all monitors to not fullscreen
for (let i = 0; i < this._monitors.length; i++)
this._monitors[i].inFullscreen = false;
// The chrome layer should be visible unless there is a window
// with layer FULLSCREEN, or a window with layer
// OVERRIDE_REDIRECT that covers the whole screen.
// ('override_redirect' is not actually a layer above all
// other windows, but this seems to be how mutter treats it
// currently...) If we wanted to be extra clever, we could
// figure out when an OVERRIDE_REDIRECT window was trying to
// partially overlap us, and then adjust the input region and
// our clip region accordingly...
// @windows is sorted bottom to top.
for (let i = windows.length - 1; i > -1; i--) {
let window = windows[i];
let layer = window.get_meta_window().get_layer();
// Skip minimized windows
if (!window.showing_on_its_workspace())
continue;
if (layer == Meta.StackLayer.FULLSCREEN) {
let monitor = this._findMonitorForWindow(window);
if (monitor)
monitor.inFullscreen = true;
}
if (layer == Meta.StackLayer.OVERRIDE_REDIRECT) {
let monitor = this._findMonitorForWindow(window);
if (monitor &&
window.x <= monitor.x &&
window.x + window.width >= monitor.x + monitor.width &&
window.y <= monitor.y &&
window.y + window.height >= monitor.y + monitor.height)
monitor.inFullscreen = true;
} else
break;
}
},
_windowsRestacked: function() {
let wasInFullscreen = [];
for (let i = 0; i < this._monitors.length; i++)
wasInFullscreen[i] = this._monitors[i].inFullscreen;
this._updateFullscreen();
let changed = false;
for (let i = 0; i < wasInFullscreen.length; i++) {
if (wasInFullscreen[i] != this._monitors[i].inFullscreen) {
changed = true;
break;
}
}
if (changed) {
this._updateVisibility();
this._queueUpdateRegions();
}
},
updateRegions: function() {
let rects = [], struts = [], i;
if (this._updateRegionIdle) {
Mainloop.source_remove(this._updateRegionIdle);
delete this._updateRegionIdle;
}
for (i = 0; i < this._trackedActors.length; i++) {
let actorData = this._trackedActors[i];
if (!actorData.affectsInputRegion && !actorData.affectsStruts)
continue;
let [x, y] = actorData.actor.get_transformed_position();
let [w, h] = actorData.actor.get_transformed_size();
x = Math.round(x);
y = Math.round(y);
w = Math.round(w);
h = Math.round(h);
let rect = new Meta.Rectangle({ x: x, y: y, width: w, height: h});
if (actorData.affectsInputRegion &&
actorData.actor.get_paint_visibility() &&
!this.actor.get_skip_paint(actorData.actor))
rects.push(rect);
if (!actorData.affectsStruts)
continue;
// Limit struts to the size of the screen
let x1 = Math.max(x, 0);
let x2 = Math.min(x + w, global.screen_width);
let y1 = Math.max(y, 0);
let y2 = Math.min(y + h, global.screen_height);
// NetWM struts are not really powerful enought to handle
// a multi-monitor scenario, they only describe what happens
// around the outer sides of the full display region. However
// it can describe a partial region along each side, so
// we can support having the struts only affect the
// primary monitor. This should be enough as we only have
// chrome affecting the struts on the primary monitor so
// far.
//
// Metacity wants to know what side of the screen the
// strut is considered to be attached to. If the actor is
// only touching one edge, or is touching the entire
// border of the primary monitor, then it's obvious which
// side to call it. If it's in a corner, we pick a side
// arbitrarily. If it doesn't touch any edges, or it spans
// the width/height across the middle of the screen, then
// we don't create a strut for it at all.
let side;
let primary = this._primaryMonitor;
if (x1 <= primary.x && x2 >= primary.x + primary.width) {
if (y1 <= primary.y)
side = Meta.Side.TOP;
else if (y2 >= primary.y + primary.height)
side = Meta.Side.BOTTOM;
else
continue;
} else if (y1 <= primary.y && y2 >= primary.y + primary.height) {
if (x1 <= 0)
side = Meta.Side.LEFT;
else if (x2 >= global.screen_width)
side = Meta.Side.RIGHT;
else
continue;
} else if (x1 <= 0)
side = Meta.Side.LEFT;
else if (y1 <= 0)
side = Meta.Side.TOP;
else if (x2 >= global.screen_width)
side = Meta.Side.RIGHT;
else if (y2 >= global.screen_height)
side = Meta.Side.BOTTOM;
else
continue;
// Ensure that the strut rects goes all the way to the screen edge,
// as this really what mutter expects.
switch (side) {
case Meta.Side.TOP:
y1 = 0;
break;
case Meta.Side.BOTTOM:
y2 = global.screen_height;
break;
case Meta.Side.LEFT:
x1 = 0;
break;
case Meta.Side.RIGHT:
x2 = global.screen_width;
break;
}
let strutRect = new Meta.Rectangle({ x: x1, y: y1, width: x2 - x1, height: y2 - y1});
let strut = new Meta.Strut({ rect: strutRect, side: side });
struts.push(strut);
}
global.set_stage_input_region(rects);
let screen = global.screen;
for (let w = 0; w < screen.n_workspaces; w++) {
let workspace = screen.get_workspace_by_index(w);
workspace.set_builtin_struts(struts);
}
return false;
}
};

View File

@@ -222,9 +222,10 @@ function WindowList() {
WindowList.prototype = {
_init : function () {
this.actor = new St.BoxLayout({ name: 'Windows', vertical: true, style: 'spacing: 8px' });
let display = global.screen.get_display();
let tracker = Shell.WindowTracker.get_default();
this._updateId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._updateWindowList));
global.display.connect('window-created', Lang.bind(this, this._updateWindowList));
display.connect('window-created', Lang.bind(this, this._updateWindowList));
tracker.connect('tracked-windows-changed', Lang.bind(this, this._updateWindowList));
},
@@ -247,7 +248,7 @@ WindowList.prototype = {
box.add(propsBox);
propsBox.add(new St.Label({ text: 'wmclass: ' + metaWindow.get_wm_class() }));
let app = tracker.get_window_app(metaWindow);
if (app != null && !app.is_window_backed()) {
if (app != null && !app.is_transient()) {
let icon = app.create_icon_texture(22);
let propBox = new St.BoxLayout({ style: 'spacing: 6px; ' });
propsBox.add(propBox);
@@ -435,7 +436,7 @@ Inspector.prototype = {
if (!this._eventHandler)
return;
let primary = Main.layoutManager.primaryMonitor;
let primary = global.get_primary_monitor();
let [minWidth, minHeight, natWidth, natHeight] =
this._eventHandler.get_preferred_size();
@@ -581,53 +582,6 @@ ErrorLog.prototype = {
}
};
function Memory() {
this._init();
}
Memory.prototype = {
_init: function() {
this.actor = new St.BoxLayout({ vertical: true });
this._glibc_uordblks = new St.Label();
this.actor.add(this._glibc_uordblks);
this._js_bytes = new St.Label();
this.actor.add(this._js_bytes);
this._gjs_boxed = new St.Label();
this.actor.add(this._gjs_boxed);
this._gjs_gobject = new St.Label();
this.actor.add(this._gjs_gobject);
this._gjs_function = new St.Label();
this.actor.add(this._gjs_function);
this._gjs_closure = new St.Label();
this.actor.add(this._gjs_closure);
this._gcbutton = new St.Button({ label: 'Full GC',
style_class: 'lg-obj-inspector-button' });
this._gcbutton.connect('clicked', Lang.bind(this, function () { global.gc(); this._renderText(); }));
this.actor.add(this._gcbutton, { x_align: St.Align.START,
x_fill: false });
this.actor.connect('notify::mapped', Lang.bind(this, this._renderText));
},
_renderText: function() {
if (!this.actor.mapped)
return;
let memInfo = global.get_memory_info();
this._glibc_uordblks.text = 'glibc_uordblks: ' + memInfo.glibc_uordblks;
this._js_bytes.text = 'js bytes: ' + memInfo.js_bytes;
this._gjs_boxed.text = 'gjs_boxed: ' + memInfo.gjs_boxed;
this._gjs_gobject.text = 'gjs_gobject: ' + memInfo.gjs_gobject;
this._gjs_function.text = 'gjs_function: ' + memInfo.gjs_function;
this._gjs_closure.text = 'gjs_closure: ' + memInfo.gjs_closure;
}
};
function Extensions() {
this._init();
}
@@ -638,32 +592,23 @@ Extensions.prototype = {
name: 'lookingGlassExtensions' });
this._noExtensions = new St.Label({ style_class: 'lg-extensions-none',
text: _("No extensions installed") });
this._numExtensions = 0;
this._extensionsList = new St.BoxLayout({ vertical: true,
style_class: 'lg-extensions-list' });
this._extensionsList.add(this._noExtensions);
this.actor.add(this._extensionsList);
for (let uuid in ExtensionSystem.extensionMeta)
this._loadExtension(null, uuid);
ExtensionSystem.connect('extension-loaded',
Lang.bind(this, this._loadExtension));
this._loadExtensionList();
},
_loadExtension: function(o, uuid) {
let extension = ExtensionSystem.extensionMeta[uuid];
// There can be cases where we create dummy extension metadata
// that's not really a proper extension. Don't bother with these.
if (!extension.name)
return;
let extensionDisplay = this._createExtensionDisplay(extension);
if (this._numExtensions == 0)
this._extensionsList.remove_actor(this._noExtensions);
this._numExtensions ++;
this._extensionsList.add(extensionDisplay);
_loadExtensionList: function() {
let extensions = ExtensionSystem.extensionMeta;
let totalExtensions = 0;
for (let uuid in extensions) {
let extensionDisplay = this._createExtensionDisplay(extensions[uuid]);
this._extensionsList.add(extensionDisplay);
totalExtensions++;
}
if (totalExtensions == 0) {
this._extensionsList.add(this._noExtensions);
}
},
_onViewSource: function (actor) {
@@ -690,8 +635,6 @@ Extensions.prototype = {
return _("Error");
case ExtensionSystem.ExtensionState.OUT_OF_DATE:
return _("Out of date");
case ExtensionSystem.ExtensionState.DOWNLOADING:
return _("Downloading");
}
return 'Unknown'; // Not translated, shouldn't appear
},
@@ -702,7 +645,7 @@ Extensions.prototype = {
text: meta.name });
box.add(name, { expand: true });
let description = new St.Label({ style_class: 'lg-extension-description',
text: meta.description || 'No description' });
text: meta.description });
box.add(description, { expand: true });
let metaBox = new St.BoxLayout();
@@ -761,9 +704,7 @@ LookingGlass.prototype = {
Lang.bind(this, this._updateFont));
this._updateFont();
// we add it to the chrome because we want it to appear to slide
// out from underneath the panel
Main.layoutManager.addChrome(this.actor);
Main.uiGroup.add_actor(this.actor);
this._objInspector = new ObjInspector();
Main.uiGroup.add_actor(this._objInspector.actor);
@@ -823,9 +764,6 @@ LookingGlass.prototype = {
this._errorLog = new ErrorLog();
notebook.appendPage('Errors', this._errorLog.actor);
this._memory = new Memory();
notebook.appendPage('Memory', this._memory.actor);
this._extensions = new Extensions();
notebook.appendPage('Extensions', this._extensions.actor);
@@ -919,7 +857,7 @@ LookingGlass.prototype = {
},
_resizeTo: function(actor) {
let primary = Main.layoutManager.primaryMonitor;
let primary = global.get_primary_monitor();
let myWidth = primary.width * 0.7;
let myHeight = primary.height * 0.7;
let [srcX, srcY] = actor.get_transformed_position();
@@ -974,7 +912,7 @@ LookingGlass.prototype = {
this._notebook.selectIndex(0);
this.actor.show();
this.actor.lower_bottom();
this.actor.lower(Main.chrome.actor);
this._open = true;
this._history.lastItem();
@@ -1005,7 +943,6 @@ LookingGlass.prototype = {
Main.popModal(this._entry);
this.actor.lower_bottom();
Tweener.addTween(this.actor, { time: 0.5 / St.get_slow_down_factor(),
transition: 'easeOutQuad',
y: this._hiddenY,

View File

@@ -12,27 +12,24 @@ const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const AutomountManager = imports.ui.automountManager;
const AutorunManager = imports.ui.autorunManager;
const Chrome = imports.ui.chrome;
const CtrlAltTab = imports.ui.ctrlAltTab;
const EndSessionDialog = imports.ui.endSessionDialog;
const PolkitAuthenticationAgent = imports.ui.polkitAuthenticationAgent;
const Environment = imports.ui.environment;
const ExtensionSystem = imports.ui.extensionSystem;
const Keyboard = imports.ui.keyboard;
const MessageTray = imports.ui.messageTray;
const Overview = imports.ui.overview;
const Panel = imports.ui.panel;
const PlaceDisplay = imports.ui.placeDisplay;
const RunDialog = imports.ui.runDialog;
const Layout = imports.ui.layout;
const LookingGlass = imports.ui.lookingGlass;
const NetworkAgent = imports.ui.networkAgent;
const NotificationDaemon = imports.ui.notificationDaemon;
const WindowAttentionHandler = imports.ui.windowAttentionHandler;
const Scripting = imports.ui.scripting;
const ShellDBus = imports.ui.shellDBus;
const TelepathyClient = imports.ui.telepathyClient;
const GoaClient = imports.ui.goaClient;
const WindowManager = imports.ui.windowManager;
const Magnifier = imports.ui.magnifier;
const XdndHandler = imports.ui.xdndHandler;
@@ -42,8 +39,7 @@ const Util = imports.misc.util;
const DEFAULT_BACKGROUND_COLOR = new Clutter.Color();
DEFAULT_BACKGROUND_COLOR.from_pixel(0x2266bbff);
let automountManager = null;
let autorunManager = null;
let chrome = null;
let panel = null;
let hotCorners = [];
let placesManager = null;
@@ -55,6 +51,7 @@ let messageTray = null;
let notificationDaemon = null;
let windowAttentionHandler = null;
let telepathyClient = null;
let goaClient = null;
let ctrlAltTabManager = null;
let recorder = null;
let shellDBusService = null;
@@ -64,94 +61,13 @@ let uiGroup = null;
let magnifier = null;
let xdndHandler = null;
let statusIconDispatcher = null;
let keyboard = null;
let layoutManager = null;
let networkAgent = null;
let _errorLogStack = [];
let _startDate;
let _defaultCssStylesheet = null;
let _cssStylesheet = null;
let _gdmCssStylesheet = null;
let background = null;
function _createUserSession() {
// Load the calendar server. Note that we are careful about
// not loading any events until the user presses the clock
global.launch_calendar_server();
placesManager = new PlaceDisplay.PlacesManager();
telepathyClient = new TelepathyClient.Client();
automountManager = new AutomountManager.AutomountManager();
autorunManager = new AutorunManager.AutorunManager();
networkAgent = new NetworkAgent.NetworkAgent();
}
function _createGDMSession() {
// We do this this here instead of at the top to prevent GDM
// related code from getting loaded in normal user sessions
const LoginDialog = imports.gdm.loginDialog;
let loginDialog = new LoginDialog.LoginDialog();
loginDialog.connect('loaded', function() {
loginDialog.open();
});
}
function _initRecorder() {
let recorderSettings = new Gio.Settings({ schema: 'org.gnome.shell.recorder' });
global.screen.connect('toggle-recording', function() {
if (recorder == null) {
recorder = new Shell.Recorder({ stage: global.stage });
}
if (recorder.is_recording()) {
recorder.pause();
Meta.enable_unredirect_for_screen(global.screen);
} else {
// read the parameters from GSettings always in case they have changed
recorder.set_framerate(recorderSettings.get_int('framerate'));
recorder.set_filename('shell-%d%u-%c.' + recorderSettings.get_string('file-extension'));
let pipeline = recorderSettings.get_string('pipeline');
if (!pipeline.match(/^\s*$/))
recorder.set_pipeline(pipeline);
else
recorder.set_pipeline(null);
Meta.disable_unredirect_for_screen(global.screen);
recorder.record();
}
});
}
function _initUserSession() {
_initRecorder();
keyboard.init();
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT, false, -1, 1);
ExtensionSystem.init();
ExtensionSystem.loadExtensions();
let shellwm = global.window_manager;
shellwm.takeover_keybinding('panel_run_dialog');
shellwm.connect('keybinding::panel_run_dialog', function () {
getRunDialog().open();
});
shellwm.takeover_keybinding('panel_main_menu');
shellwm.connect('keybinding::panel_main_menu', function () {
overview.toggle();
});
global.display.connect('overlay-key', Lang.bind(overview, overview.toggle));
}
function start() {
// Monkey patch utility functions into the global proxy;
// This is easier and faster than indirecting down into global
@@ -171,6 +87,10 @@ function start() {
// back into sync ones.
DBus.session.flush();
// Load the calendar server. Note that we are careful about
// not loading any events until the user presses the clock
global.launch_calendar_server();
// Ensure ShellWindowTracker and ShellAppUsage are initialized; this will
// also initialize ShellAppSystem first. ShellAppSystem
// needs to load all the .desktop files, and ShellWindowTracker
@@ -189,9 +109,18 @@ function start() {
global.stage.no_clear_hint = true;
_defaultCssStylesheet = global.datadir + '/theme/gnome-shell.css';
_gdmCssStylesheet = global.datadir + '/theme/gdm.css';
loadTheme();
let shellwm = global.window_manager;
shellwm.takeover_keybinding('panel_main_menu');
shellwm.connect('keybinding::panel_main_menu', function () {
overview.toggle();
});
shellwm.takeover_keybinding('panel_run_dialog');
shellwm.connect('keybinding::panel_run_dialog', function () {
getRunDialog().open();
});
// Set up stage hierarchy to group all UI actors under one container.
uiGroup = new Clutter.Group();
St.set_ui_root(global.stage, uiGroup);
@@ -199,34 +128,52 @@ function start() {
global.overlay_group.reparent(uiGroup);
global.stage.add_actor(uiGroup);
layoutManager = new Layout.LayoutManager();
placesManager = new PlaceDisplay.PlacesManager();
xdndHandler = new XdndHandler.XdndHandler();
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
// This overview object is just a stub for non-user sessions
overview = new Overview.Overview({ isDummy: global.session_type != Shell.SessionType.USER });
overview = new Overview.Overview();
chrome = new Chrome.Chrome();
magnifier = new Magnifier.Magnifier();
statusIconDispatcher = new StatusIconDispatcher.StatusIconDispatcher();
panel = new Panel.Panel();
wm = new WindowManager.WindowManager();
messageTray = new MessageTray.MessageTray();
keyboard = new Keyboard.Keyboard();
notificationDaemon = new NotificationDaemon.NotificationDaemon();
windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();
telepathyClient = new TelepathyClient.Client();
goaClient = new GoaClient.Client();
if (global.session_type == Shell.SessionType.USER)
_createUserSession();
else if (global.session_type == Shell.SessionType.GDM)
_createGDMSession();
panel.startStatusArea();
keyboard.init();
overview.init();
if (global.session_type == Shell.SessionType.USER)
_initUserSession();
statusIconDispatcher.start(messageTray.actor);
_startDate = new Date();
let recorderSettings = new Gio.Settings({ schema: 'org.gnome.shell.recorder' });
global.screen.connect('toggle-recording', function() {
if (recorder == null) {
recorder = new Shell.Recorder({ stage: global.stage });
}
if (recorder.is_recording()) {
recorder.pause();
} else {
// read the parameters from GSettings always in case they have changed
recorder.set_framerate(recorderSettings.get_int('framerate'));
recorder.set_filename('shell-%d%u-%c.' + recorderSettings.get_string('file-extension'));
let pipeline = recorderSettings.get_string('pipeline');
if (!pipeline.match(/^\s*$/))
recorder.set_pipeline(pipeline);
else
recorder.set_pipeline(null);
recorder.record();
}
});
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT, false, -1, 1);
// Provide the bus object for gnome-session to
// initiate logouts.
EndSessionDialog.init();
@@ -234,7 +181,19 @@ function start() {
// Attempt to become a PolicyKit authentication agent
PolkitAuthenticationAgent.init()
_startDate = new Date();
global.screen.connect('monitors-changed', _relayout);
ExtensionSystem.init();
ExtensionSystem.loadExtensions();
// Perform initial relayout here
_relayout();
panel.startStatusArea();
panel.startupAnimation();
let display = global.screen.get_display();
display.connect('overlay-key', Lang.bind(overview, overview.toggle));
global.stage.connect('captured-event', _globalKeyPressHandler);
@@ -252,7 +211,6 @@ function start() {
global.screen.connect('window-entered-monitor', _windowEnteredMonitor);
global.screen.connect('window-left-monitor', _windowLeftMonitor);
global.screen.connect('restacked', _windowsRestacked);
_nWorkspacesChanged();
}
@@ -344,24 +302,17 @@ function _windowRemoved(workspace, window) {
function _windowLeftMonitor(metaScreen, monitorIndex, metaWin) {
// If the window left the primary monitor, that
// might make that workspace empty
if (monitorIndex == layoutManager.primaryIndex)
if (monitorIndex == global.get_primary_monitor_index())
_queueCheckWorkspaces();
}
function _windowEnteredMonitor(metaScreen, monitorIndex, metaWin) {
// If the window entered the primary monitor, that
// might make that workspace non-empty
if (monitorIndex == layoutManager.primaryIndex)
if (monitorIndex == global.get_primary_monitor_index())
_queueCheckWorkspaces();
}
function _windowsRestacked() {
// Figure out where the pointer is in case we lost track of
// it during a grab. (In particular, if a trayicon popup menu
// is dismissed, see if we need to close the message tray.)
global.sync_pointer();
}
function _queueCheckWorkspaces() {
if (_checkWorkspacesId == 0)
_checkWorkspacesId = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, _checkWorkspaces);
@@ -445,40 +396,15 @@ function setThemeStylesheet(cssStylesheet)
*/
function loadTheme() {
let themeContext = St.ThemeContext.get_for_stage (global.stage);
let previousTheme = themeContext.get_theme();
let cssStylesheet = _defaultCssStylesheet;
if (_cssStylesheet != null)
cssStylesheet = _cssStylesheet;
let theme = new St.Theme ({ application_stylesheet: cssStylesheet });
if (global.session_type == Shell.SessionType.GDM)
theme.load_stylesheet(_gdmCssStylesheet);
if (previousTheme) {
let customStylesheets = previousTheme.get_custom_stylesheets();
for (let i = 0; i < customStylesheets.length; i++)
theme.load_stylesheet(customStylesheets[i]);
}
themeContext.set_theme (theme);
}
/**
* notify:
* @msg: A message
* @details: Additional information
*/
function notify(msg, details) {
let source = new MessageTray.SystemNotificationSource();
messageTray.add(source);
let notification = new MessageTray.Notification(source, msg, details);
notification.setTransient(true);
source.notify(notification);
}
/**
* notifyError:
* @msg: An error message
@@ -493,7 +419,11 @@ function notifyError(msg, details) {
else
log("error: " + msg)
notify(msg, details);
let source = new MessageTray.SystemNotificationSource();
messageTray.add(source);
let notification = new MessageTray.Notification(source, msg, details);
notification.setTransient(true);
source.notify(notification);
}
/**
@@ -537,16 +467,80 @@ function _getAndClearErrorStack() {
return errors;
}
function logStackTrace(msg) {
try {
throw new Error();
} catch (e) {
// e.stack must have at least two lines, with the first being
// logStackTrace() (which we strip off), and the second being
// our caller.
let trace = e.stack.substr(e.stack.indexOf('\n') + 1);
log(msg ? (msg + '\n' + trace) : trace);
function _relayout() {
let monitors = global.get_monitors();
// destroy old corners
for (let i = 0; i < hotCorners.length; i++)
hotCorners[i].destroy();
hotCorners = [];
let primary = global.get_primary_monitor();
for (let i = 0; i < monitors.length; i++) {
let monitor = monitors[i];
let isPrimary = (monitor.x == primary.x &&
monitor.y == primary.y &&
monitor.width == primary.width &&
monitor.height == primary.height);
let cornerX = monitor.x;
let cornerY = monitor.y;
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
cornerX += monitor.width;
let haveTopLeftCorner = true;
/* Check if we have a top left (right for RTL) corner.
* I.e. if there is no monitor directly above or to the left(right) */
let besideX;
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
besideX = monitor.x + 1;
else
besideX = cornerX - 1;
let besideY = cornerY;
let aboveX = cornerX;
let aboveY = cornerY - 1;
for (let j = 0; j < monitors.length; j++) {
if (i == j)
continue;
let otherMonitor = monitors[j];
if (besideX >= otherMonitor.x &&
besideX < otherMonitor.x + otherMonitor.width &&
besideY >= otherMonitor.y &&
besideY < otherMonitor.y + otherMonitor.height) {
haveTopLeftCorner = false;
break;
}
if (aboveX >= otherMonitor.x &&
aboveX < otherMonitor.x + otherMonitor.width &&
aboveY >= otherMonitor.y &&
aboveY < otherMonitor.y + otherMonitor.height) {
haveTopLeftCorner = false;
break;
}
}
/* We only want hot corners where there is a natural top-left
* corner, and on the primary monitor */
if (!isPrimary && !haveTopLeftCorner)
continue;
let corner = new Panel.HotCorner(isPrimary ? panel.button : null);
hotCorners.push(corner);
corner.actor.set_position(cornerX, cornerY);
if (isPrimary)
panel.setHotCorner(corner);
}
panel.relayout();
overview.relayout();
// To avoid updating the position and size of the workspaces
// in the overview, we just hide the overview. The positions
// will be updated when it is next shown.
overview.hide();
}
function isWindowActorDisplayedOnWorkspace(win, workspaceIndex) {
@@ -575,8 +569,9 @@ function _globalKeyPressHandler(actor, event) {
let keyCode = event.get_key_code();
let modifierState = Shell.get_event_state(event);
let display = global.screen.get_display();
// This relies on the fact that Clutter.ModifierType is the same as Gdk.ModifierType
let action = global.display.get_keybinding_action(keyCode, modifierState);
let action = display.get_keybinding_action(keyCode, modifierState);
// The screenshot action should always be available (even if a
// modal dialog is present)
@@ -599,15 +594,6 @@ function _globalKeyPressHandler(actor, event) {
return true;
}
if (action == Meta.KeyBindingAction.SWITCH_PANELS) {
ctrlAltTabManager.popup(modifierState & Clutter.ModifierType.SHIFT_MASK);
return true;
}
// None of the other bindings are relevant outside of the user's session
if (global.session_type != Shell.SessionType.USER)
return false;
switch (action) {
// left/right would effectively act as synonyms for up/down if we enabled them;
// but that could be considered confusing; we also disable them in the main view.
@@ -631,6 +617,9 @@ function _globalKeyPressHandler(actor, event) {
case Meta.KeyBindingAction.PANEL_MAIN_MENU:
overview.hide();
return true;
case Meta.KeyBindingAction.SWITCH_PANELS:
ctrlAltTabManager.popup(modifierState & Clutter.ModifierType.SHIFT_MASK);
return true;
}
return false;

View File

@@ -68,19 +68,14 @@ function _fixMarkup(text, allowMarkup) {
// Support &amp;, &quot;, &apos;, &lt; and &gt;, escape all other
// occurrences of '&'.
let _text = text.replace(/&(?!amp;|quot;|apos;|lt;|gt;)/g, '&amp;');
// Support <b>, <i>, and <u>, escape anything else
// so it displays as raw markup.
_text = _text.replace(/<(?!\/?[biu]>)/g, '&lt;');
try {
Pango.parse_markup(_text, -1, '');
return _text;
} catch (e) {}
return _text.replace(/<(\/?[^biu]>|[^>\/][^>])/g, '&lt;$1');
} else {
// Escape everything
let _text = text.replace(/&/g, '&amp;');
return _text.replace(/</g, '&lt;');
}
// !allowMarkup, or invalid markup
return GLib.markup_escape_text(text, -1);
}
function URLHighlighter(text, lineWrap, allowMarkup) {
@@ -234,7 +229,9 @@ FocusGrabber.prototype = {
this.actor = actor;
this._prevFocusedWindow = global.display.focus_window;
let metaDisplay = global.screen.get_display();
this._prevFocusedWindow = metaDisplay.focus_window;
this._prevKeyFocusActor = global.stage.get_key_focus();
if (!Main.overview.visible)
@@ -269,7 +266,7 @@ FocusGrabber.prototype = {
let source = event.get_source();
switch (event.type()) {
case Clutter.EventType.BUTTON_PRESS:
if (!this.actor.contains(source) && !Main.keyboard.actor.contains(source))
if (!this.actor.contains(source))
this.emit('button-pressed', source);
break;
case Clutter.EventType.KEY_PRESS:
@@ -288,6 +285,8 @@ FocusGrabber.prototype = {
if (!this._hasFocus)
return;
let metaDisplay = global.screen.get_display();
if (this._focusActorChangedId > 0) {
global.stage.disconnect(this._focusActorChangedId);
this._focusActorChangedId = 0;
@@ -306,8 +305,8 @@ FocusGrabber.prototype = {
this._hasFocus = false;
this.emit('focus-ungrabbed');
if (this._prevFocusedWindow && !global.display.focus_window) {
global.display.set_input_focus_window(this._prevFocusedWindow, false, global.get_current_time());
if (this._prevFocusedWindow && !metaDisplay.focus_window) {
metaDisplay.set_input_focus_window(this._prevFocusedWindow, false, global.get_current_time());
this._prevFocusedWindow = null;
}
if (this._prevKeyFocusActor) {
@@ -396,8 +395,6 @@ function Notification(source, title, banner, params) {
}
Notification.prototype = {
IMAGE_SIZE: 125,
_init: function(source, title, banner, params) {
this.source = source;
this.urgency = Urgency.NORMAL;
@@ -414,7 +411,6 @@ Notification.prototype = {
this._titleDirection = St.TextDirection.NONE;
this._spacing = 0;
this._scrollPolicy = Gtk.PolicyType.AUTOMATIC;
this._imageBin = null;
source.connect('destroy', Lang.bind(this,
function (source, reason) {
@@ -444,19 +440,9 @@ Notification.prototype = {
this._bannerBox.connect('allocate', Lang.bind(this, this._bannerBoxAllocate));
this._table.add(this._bannerBox, { row: 0,
col: 1,
col_span: 2,
x_expand: false,
y_expand: false,
y_fill: false });
// This is an empty cell that overlaps with this._bannerBox cell to ensure
// that this._bannerBox cell expands horizontally, while not forcing the
// this._imageBin that is also in col: 2 to expand horizontally.
this._table.add(new St.Bin(), { row: 0,
col: 2,
y_expand: false,
y_fill: false });
this._titleLabel = new St.Label();
this._bannerBox.add_actor(this._titleLabel);
this._bannerUrlHighlighter = new URLHighlighter();
@@ -508,10 +494,7 @@ Notification.prototype = {
this._actionArea = null;
this._buttonBox = null;
}
if (this._imageBin && params.clear)
this.unsetImage();
if (!this._scrollArea && !this._actionArea && !this._imageBin)
if (!this._scrollArea && !this._actionArea)
this._table.remove_style_class_name('multi-line-notification');
this._icon = params.icon || this.source.createNotificationIcon();
@@ -574,10 +557,8 @@ Notification.prototype = {
this._scrollArea = new St.ScrollView({ name: 'notification-scrollview',
vscrollbar_policy: this._scrollPolicy,
hscrollbar_policy: Gtk.PolicyType.NEVER,
style_class: 'vfade' });
this._table.add(this._scrollArea, { row: 1,
col: 2 });
this._updateLastColumnSettings();
vfade: true });
this._table.add(this._scrollArea, { row: 1, col: 1 });
this._contentArea = new St.BoxLayout({ name: 'notification-body',
vertical: true });
this._scrollArea.add_actor(this._contentArea);
@@ -654,52 +635,13 @@ Notification.prototype = {
if (!props)
props = {};
props.row = 2;
props.col = 2;
props.col = 1;
this._table.add_style_class_name('multi-line-notification');
this._table.add(this._actionArea, props);
this._updateLastColumnSettings();
this._updated();
},
_updateLastColumnSettings: function() {
if (this._scrollArea)
this._table.child_set(this._scrollArea, { col: this._imageBin ? 2 : 1,
col_span: this._imageBin ? 1 : 2 });
if (this._actionArea)
this._table.child_set(this._actionArea, { col: this._imageBin ? 2 : 1,
col_span: this._imageBin ? 1 : 2 });
},
setImage: function(image) {
if (this._imageBin)
this.unsetImage();
this._imageBin = new St.Bin();
this._imageBin.child = image;
this._imageBin.opacity = 230;
this._table.add_style_class_name('multi-line-notification');
this._table.add_style_class_name('notification-with-image');
this._updateLastColumnSettings();
this._table.add(this._imageBin, { row: 1,
col: 1,
row_span: 2,
x_expand: false,
y_expand: false,
x_fill: false,
y_fill: false });
},
unsetImage: function() {
if (this._imageBin) {
this._table.remove_style_class_name('notification-with-image');
this._table.remove_actor(this._imageBin);
this._imageBin = null;
this._updateLastColumnSettings();
if (!this._scrollArea && !this._actionArea)
this._table.remove_style_class_name('multi-line-notification');
}
},
// addButton:
// @id: the action ID
// @label: the label for the action's button
@@ -715,9 +657,7 @@ Notification.prototype = {
let box = new St.BoxLayout({ name: 'notification-actions' });
this.setActionArea(box, { x_expand: false,
y_expand: false,
x_fill: false,
y_fill: false,
x_align: St.Align.END });
this._buttonBox = box;
}
@@ -889,7 +829,6 @@ Notification.prototype = {
// Restore banner opacity in case the notification is shown in the
// banner mode again on update.
this._bannerLabel.opacity = 255;
this.emit('collapsed');
},
_onActionInvoked: function(actor, mouseButtonClicked, id) {
@@ -940,96 +879,20 @@ Source.prototype = {
_init: function(title) {
this.title = title;
this.actor = new Shell.GenericContainer();
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
this.actor.connect('allocate', Lang.bind(this, this._allocate));
this.actor.connect('destroy', Lang.bind(this,
function() {
this._actorDestroyed = true;
}));
this._actorDestroyed = false;
this._counterLabel = new St.Label();
this._counterBin = new St.Bin({ style_class: 'summary-source-counter',
child: this._counterLabel });
this._counterBin.hide();
this._iconBin = new St.Bin({ width: this.ICON_SIZE,
height: this.ICON_SIZE,
x_fill: true,
y_fill: true });
this.actor.add_actor(this._iconBin);
this.actor.add_actor(this._counterBin);
this.isTransient = false;
this.isChat = false;
this.notifications = [];
},
_getPreferredWidth: function (actor, forHeight, alloc) {
let [min, nat] = this._iconBin.get_preferred_width(forHeight);
alloc.min_size = min; alloc.nat_size = nat;
},
_getPreferredHeight: function (actor, forWidth, alloc) {
let [min, nat] = this._iconBin.get_preferred_height(forWidth);
alloc.min_size = min; alloc.nat_size = nat;
},
_allocate: function(actor, box, flags) {
// the iconBin should fill our entire box
this._iconBin.allocate(box, flags);
let childBox = new Clutter.ActorBox();
let [minWidth, minHeight, naturalWidth, naturalHeight] = this._counterBin.get_preferred_size();
let direction = this.actor.get_direction();
if (direction == St.TextDirection.LTR) {
// allocate on the right in LTR
childBox.x1 = box.x2 - naturalWidth;
childBox.x2 = box.x2;
} else {
// allocate on the left in RTL
childBox.x1 = 0;
childBox.x2 = naturalWidth;
}
childBox.y1 = box.y2 - naturalHeight;
childBox.y2 = box.y2;
this._counterBin.allocate(childBox, flags);
},
_setCount: function(count, visible) {
if (isNaN(parseInt(count)))
throw new Error("Invalid notification count: " + count);
if (this._actorDestroyed)
return;
this._counterBin.visible = visible;
this._counterLabel.set_text(count.toString());
},
_updateCount: function() {
let count = this.notifications.length;
this._setCount(count, count > 1);
},
setTransient: function(isTransient) {
this.isTransient = isTransient;
},
setTitle: function(newTitle) {
this.title = newTitle;
this.emit('title-changed');
},
// Called to create a new icon actor (of size this.ICON_SIZE).
// Must be overridden by the subclass if you do not pass icons
// explicitly to the Notification() constructor.
@@ -1040,7 +903,7 @@ Source.prototype = {
// Unlike createNotificationIcon, this always returns the same actor;
// there is only one summary icon actor for a Source.
getSummaryIcon: function() {
return this.actor;
return this._iconBin;
},
pushNotification: function(notification) {
@@ -1059,11 +922,7 @@ Source.prototype = {
this.notifications.splice(index, 1);
if (this.notifications.length == 0)
this._lastNotificationRemoved();
this._updateCount();
}));
this._updateCount();
},
notify: function(notification) {
@@ -1100,8 +959,6 @@ Source.prototype = {
for (let i = this.notifications.length - 1; i >= 0; i--)
if (!this.notifications[i].resident)
this.notifications[i].destroy();
this._updateCount();
},
// Default implementation is to destroy this source, but subclasses can override
@@ -1138,11 +995,6 @@ SummaryItem.prototype = {
this._sourceTitleBin.child = this._sourceTitle;
this._sourceTitleBin.width = 0;
this.source.connect('title-changed',
Lang.bind(this, function() {
this._sourceTitle.text = source.title;
}));
this._sourceBox.add(this._sourceIcon, { y_fill: false });
this._sourceBox.add(this._sourceTitleBin, { expand: true, y_fill: false });
this.actor.child = this._sourceBox;
@@ -1150,7 +1002,7 @@ SummaryItem.prototype = {
this.notificationStackView = new St.ScrollView({ name: source.isChat ? '' : 'summary-notification-stack-scrollview',
vscrollbar_policy: source.isChat ? Gtk.PolicyType.NEVER : Gtk.PolicyType.AUTOMATIC,
hscrollbar_policy: Gtk.PolicyType.NEVER,
style_class: 'vfade' });
vfade: true });
this.notificationStack = new St.BoxLayout({ name: 'summary-notification-stack',
vertical: true });
this.notificationStackView.add_actor(this.notificationStack);
@@ -1334,11 +1186,12 @@ MessageTray.prototype = {
this._summaryMotionId = 0;
this._summaryBoxPointer = new BoxPointer.BoxPointer(St.Side.BOTTOM,
{ reactive: true,
track_hover: true });
{ reactive: true,
track_hover: true });
this._summaryBoxPointer.actor.style_class = 'summary-boxpointer';
this.actor.add_actor(this._summaryBoxPointer.actor);
this._summaryBoxPointer.actor.lower_bottom();
this._summaryBoxPointer.actor.hide();
Main.layoutManager.addChrome(this._summaryBoxPointer.actor, { visibleInFullscreen: true });
this._summaryBoxPointerItem = null;
this._summaryBoxPointerContentUpdatedId = 0;
@@ -1373,7 +1226,6 @@ MessageTray.prototype = {
this._trayState = State.HIDDEN;
this._locked = false;
this._traySummoned = false;
this._useLongerTrayLeftTimeout = false;
this._trayLeftTimeoutId = 0;
this._pointerInTray = false;
@@ -1389,12 +1241,12 @@ MessageTray.prototype = {
this._notificationRemoved = false;
this._reNotifyAfterHideNotification = null;
Main.layoutManager.trayBox.add_actor(this.actor);
this.actor.y = -1;
Main.layoutManager.trackChrome(this.actor);
Main.layoutManager.trackChrome(this._notificationBin);
Main.chrome.addActor(this.actor, { affectsStruts: false,
visibleInOverview: true });
Main.chrome.trackActor(this._notificationBin);
Main.chrome.trackActor(this._summaryBoxPointer.actor);
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._setSizePosition));
global.screen.connect('monitors-changed', Lang.bind(this, this._setSizePosition));
this._setSizePosition();
@@ -1430,11 +1282,27 @@ MessageTray.prototype = {
},
_setSizePosition: function() {
let monitor = Main.layoutManager.bottomMonitor;
let primary = global.get_primary_monitor();
this.actor.x = primary.x;
this.actor.y = primary.y + primary.height - 1;
this.actor.width = primary.width;
this._notificationBin.x = 0;
this._notificationBin.width = monitor.width;
this._notificationBin.width = primary.width;
this._summaryBin.x = 0;
this._summaryBin.width = monitor.width;
this._summaryBin.width = primary.width;
if (this._pointerBarrier)
global.destroy_pointer_barrier(this._pointerBarrier);
this._pointerBarrier =
global.create_pointer_barrier(primary.x + primary.width, primary.y + primary.height - this.actor.height,
primary.x + primary.width, primary.y + primary.height,
4 /* BarrierNegativeX */);
},
getBusy: function(source) {
return this._busy;
},
contains: function(source) {
@@ -1580,19 +1448,7 @@ MessageTray.prototype = {
if (!this._locked)
return;
this._locked = false;
this._pointerInTray = this.actor.hover;
this._updateState();
},
toggle: function() {
this._traySummoned = !this._traySummoned;
this._updateState();
},
hide: function() {
this._traySummoned = false;
this.actor.set_hover(false);
this._summary.set_hover(false);
this._pointerInTray = this.actor.hover && !this._summaryBoxPointer.bin.hover;
this._updateState();
},
@@ -1753,8 +1609,6 @@ MessageTray.prototype = {
this._clickedSummaryItemMouseButton != button) {
this._clickedSummaryItem = summaryItem;
this._clickedSummaryItemMouseButton = button;
summaryItem.source.emit('summary-item-clicked', button);
} else {
this._unsetClickedSummaryItem();
}
@@ -1780,6 +1634,13 @@ MessageTray.prototype = {
if (this._useLongerTrayLeftTimeout && !this._trayLeftTimeoutId)
return;
// Don't do anything if the mouse is over the summary notification as this should be considered as
// leaving the tray. The tray is locked when the summary notification is visible anyway, but we
// should treat the mouse being over the summary notification as the tray being left for collapsing
// any expanded summary item other than the one related to the notification.
if (this._summaryBoxPointer.bin.hover)
return;
this._useLongerTrayLeftTimeout = false;
if (this._trayLeftTimeoutId) {
Mainloop.source_remove(this._trayLeftTimeoutId);
@@ -1903,7 +1764,7 @@ MessageTray.prototype = {
}
// Summary
let summarySummoned = this._pointerInSummary || this._overviewVisible || this._traySummoned;
let summarySummoned = this._pointerInSummary || this._overviewVisible;
let summaryPinned = this._summaryTimeoutId != 0 || this._pointerInTray || summarySummoned || this._locked;
let summaryHovered = this._pointerInTray || this._pointerInSummary;
let summaryVisibleWithNoHover = (this._overviewVisible || this._locked) && !summaryHovered;
@@ -1997,16 +1858,18 @@ MessageTray.prototype = {
},
_showTray: function() {
let primary = global.get_primary_monitor();
this._tween(this.actor, '_trayState', State.SHOWN,
{ y: -this.actor.height,
{ y: primary.y + primary.height - this.actor.height,
time: ANIMATION_TIME,
transition: 'easeOutQuad'
});
},
_hideTray: function() {
let primary = global.get_primary_monitor();
this._tween(this.actor, '_trayState', State.HIDDEN,
{ y: -1,
{ y: primary.y + primary.height - 1,
time: ANIMATION_TIME,
transition: 'easeOutQuad'
});
@@ -2094,7 +1957,7 @@ MessageTray.prototype = {
_notificationTimeout: function() {
let [x, y, mods] = global.get_pointer();
if (y > this._lastSeenMouseY + 10 && !this.actor.hover) {
if (y > this._lastSeenMouseY + 10 && y < this.actor.y) {
// The mouse is moving towards the notification, so don't
// hide it yet. (We just create a new timeout (and destroy
// the old one) each time because the bookkeeping is
@@ -2152,15 +2015,9 @@ MessageTray.prototype = {
this._notification.expand(!autoExpanding);
},
_onNotificationExpanded: function() {
_onNotificationExpanded: function() {
let expandedY = this.actor.height - this._notificationBin.height;
// Don't animate the notification to its new position if it has shrunk:
// there will be a very visible "gap" that breaks the illusion.
if (this._notificationBin.y < expandedY)
this._notificationBin.y = expandedY;
else if (this._notification.y != expandedY)
if (this._notificationBin.y != expandedY)
this._tween(this._notificationBin, '_notificationState', State.SHOWN,
{ y: expandedY,
time: ANIMATION_TIME,
@@ -2175,6 +2032,7 @@ MessageTray.prototype = {
},
_showSummary: function(timeout) {
let primary = global.get_primary_monitor();
this._summaryBin.opacity = 0;
this._summaryBin.y = this.actor.height;
this._tween(this._summaryBin, '_summaryState', State.SHOWN,

View File

@@ -18,7 +18,6 @@ const Main = imports.ui.main;
const Tweener = imports.ui.tweener;
const OPEN_AND_CLOSE_TIME = 0.1;
const FADE_IN_BUTTONS_TIME = 0.33;
const FADE_OUT_DIALOG_TIME = 1.0;
const State = {
@@ -35,12 +34,10 @@ function ModalDialog() {
ModalDialog.prototype = {
_init: function(params) {
params = Params.parse(params, { shellReactive: false,
styleClass: null });
params = Params.parse(params, { styleClass: null });
this.state = State.CLOSED;
this._hasModal = false;
this._shellReactive = params.shellReactive;
this._group = new St.Group({ visible: false,
x: 0,
@@ -56,30 +53,26 @@ ModalDialog.prototype = {
this._actionKeys = {};
this._group.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
this._lightbox = new Lightbox.Lightbox(this._group,
{ inhibitEvents: true });
this._backgroundBin = new St.Bin();
this._group.add_actor(this._backgroundBin);
this._lightbox.highlight(this._backgroundBin);
this._backgroundStack = new Shell.Stack();
this._backgroundBin.child = this._backgroundStack;
this._eventBlocker = new Clutter.Group({ reactive: true });
this._backgroundStack.add_actor(this._eventBlocker);
this._dialogLayout = new St.BoxLayout({ style_class: 'modal-dialog',
vertical: true });
if (params.styleClass != null) {
this._dialogLayout.add_style_class_name(params.styleClass);
}
if (!this._shellReactive) {
this._lightbox = new Lightbox.Lightbox(this._group,
{ inhibitEvents: true });
this._lightbox.highlight(this._backgroundBin);
let stack = new Shell.Stack();
this._backgroundBin.child = stack;
this._eventBlocker = new Clutter.Group({ reactive: true });
stack.add_actor(this._eventBlocker);
stack.add_actor(this._dialogLayout);
} else {
this._backgroundBin.child = this._dialogLayout;
}
this._backgroundStack.add_actor(this._dialogLayout);
this.contentLayout = new St.BoxLayout({ vertical: true });
this._dialogLayout.add(this.contentLayout,
@@ -89,6 +82,7 @@ ModalDialog.prototype = {
y_align: St.Align.START });
this._buttonLayout = new St.BoxLayout({ style_class: 'modal-dialog-button-box',
opacity: 220,
vertical: false });
this._dialogLayout.add(this._buttonLayout,
{ expand: true,
@@ -100,13 +94,7 @@ ModalDialog.prototype = {
this._savedKeyFocus = null;
},
destroy: function() {
this._group.destroy();
},
setButtons: function(buttons) {
let hadChildren = this._buttonLayout.get_children() > 0;
this._buttonLayout.destroy_children();
this._actionKeys = {};
@@ -117,10 +105,10 @@ ModalDialog.prototype = {
let action = buttonInfo['action'];
let key = buttonInfo['key'];
buttonInfo.button = new St.Button({ style_class: 'modal-dialog-button',
reactive: true,
can_focus: true,
label: label });
let button = new St.Button({ style_class: 'modal-dialog-button',
reactive: true,
can_focus: true,
label: label });
let x_alignment;
if (buttons.length == 1)
@@ -132,36 +120,20 @@ ModalDialog.prototype = {
else
x_alignment = St.Align.MIDDLE;
this._initialKeyFocus = buttonInfo.button;
this._buttonLayout.add(buttonInfo.button,
this._initialKeyFocus = button;
this._buttonLayout.add(button,
{ expand: true,
x_fill: false,
y_fill: false,
x_align: x_alignment,
y_align: St.Align.MIDDLE });
buttonInfo.button.connect('clicked', action);
button.connect('clicked', action);
if (key)
this._actionKeys[key] = action;
i++;
}
// Fade in buttons if there weren't any before
if (!hadChildren && i > 0) {
this._buttonLayout.opacity = 0;
Tweener.addTween(this._buttonLayout,
{ opacity: 255,
time: FADE_IN_BUTTONS_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() {
this.emit('buttons-set');
})
});
} else {
this.emit('buttons-set');
}
},
_onKeyPressEvent: function(object, keyPressEvent) {
@@ -177,7 +149,7 @@ ModalDialog.prototype = {
},
_fadeOpen: function() {
let monitor = Main.layoutManager.focusMonitor;
let monitor = global.get_focus_monitor();
this._backgroundBin.set_position(monitor.x, monitor.y);
this._backgroundBin.set_size(monitor.width, monitor.height);
@@ -185,8 +157,7 @@ ModalDialog.prototype = {
this.state = State.OPENING;
this._dialogLayout.opacity = 255;
if (this._lightbox)
this._lightbox.show();
this._lightbox.show();
this._group.opacity = 0;
this._group.show();
Tweener.addTween(this._group,
@@ -252,8 +223,7 @@ ModalDialog.prototype = {
global.gdk_screen.get_display().sync();
this._hasModal = false;
if (!this._shellReactive)
this._eventBlocker.raise_top();
this._eventBlocker.raise_top();
},
pushModal: function (timestamp) {
@@ -269,8 +239,7 @@ ModalDialog.prototype = {
} else
this._initialKeyFocus.grab_key_focus();
if (!this._shellReactive)
this._eventBlocker.lower_bottom();
this._eventBlocker.lower_bottom();
return true;
},

View File

@@ -1,400 +0,0 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*-
*
* Copyright 2011 Giovanni Campagna <scampa.giovanni@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
*/
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const NetworkManager = imports.gi.NetworkManager;
const NMClient = imports.gi.NMClient;
const Pango = imports.gi.Pango;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const ModalDialog = imports.ui.modalDialog;
const PopupMenu = imports.ui.popupMenu;
function NetworkSecretDialog() {
this._init.apply(this, arguments);
}
NetworkSecretDialog.prototype = {
__proto__: ModalDialog.ModalDialog.prototype,
_init: function(agent, requestId, connection, settingName, hints) {
ModalDialog.ModalDialog.prototype._init.call(this, { styleClass: 'polkit-dialog' });
this._agent = agent;
this._requestId = requestId;
this._connection = connection;
this._settingName = settingName;
this._hints = hints;
this._content = this._getContent();
let mainContentBox = new St.BoxLayout({ style_class: 'polkit-dialog-main-layout',
vertical: false });
this.contentLayout.add(mainContentBox,
{ x_fill: true,
y_fill: true });
let icon = new St.Icon({ icon_name: 'dialog-password-symbolic' });
mainContentBox.add(icon,
{ x_fill: true,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.START });
let messageBox = new St.BoxLayout({ style_class: 'polkit-dialog-message-layout',
vertical: true });
mainContentBox.add(messageBox,
{ y_align: St.Align.START });
let subjectLabel = new St.Label({ style_class: 'polkit-dialog-headline',
text: this._content.title });
messageBox.add(subjectLabel,
{ y_fill: false,
y_align: St.Align.START });
if (this._content.message != null) {
let descriptionLabel = new St.Label({ style_class: 'polkit-dialog-description',
text: this._content.message,
// HACK: for reasons unknown to me, the label
// is not asked the correct height for width,
// and thus is underallocated
// place a fixed height to avoid overflowing
style: 'height: 3em'
});
descriptionLabel.clutter_text.line_wrap = true;
messageBox.add(descriptionLabel,
{ y_fill: true,
y_align: St.Align.START,
expand: true });
}
let secretTable = new St.Table({ style_class: 'network-dialog-secret-table' });
let pos = 0;
for (let i = 0; i < this._content.secrets.length; i++) {
let secret = this._content.secrets[i];
let label = new St.Label({ style_class: 'polkit-dialog-password-label',
text: secret.label });
let reactive = secret.key != null;
secret.entry = new St.Entry({ style_class: 'polkit-dialog-password-entry',
text: secret.value, can_focus: reactive,
reactive: reactive });
if (secret.validate)
secret.valid = secret.validate(secret);
else // no special validation, just ensure it's not empty
secret.valid = secret.value.length > 0;
if (reactive) {
secret.entry.clutter_text.connect('text-changed', Lang.bind(this, function() {
secret.value = secret.entry.get_text();
if (secret.validate)
secret.valid = secret.validate(secret);
else
secret.valid = secret.value.length > 0;
this._updateOkButton();
}));
} else
secret.valid = true;
secretTable.add(label, { row: pos, col: 0, x_align: St.Align.START, y_align: St.Align.START });
secretTable.add(secret.entry, { row: pos, col: 1, x_expand: true, x_fill: true, y_align: St.Align.END });
pos++;
if (secret.password) {
secret.entry.clutter_text.set_password_char('\u25cf');
// FIXME: need a real checkbox here
let button = new St.Button({ button_mask: St.ButtonMask.ONE,
can_focus: true });
let checkbox = new St.BoxLayout({ vertical: false,
style_class: 'network-dialog-show-password-checkbox'
});
let _switch = new PopupMenu.Switch(false);
checkbox.add(_switch.actor);
checkbox.add(new St.Label({ text: _("Show password") }), { expand: true });
button.connect('clicked', function() {
_switch.toggle();
if (_switch.state)
secret.entry.clutter_text.set_password_char('');
else
secret.entry.clutter_text.set_password_char('\u25cf');
});
button.child = checkbox;
secretTable.add(button, { row: pos, col: 1, x_expand: true, x_fill: true, y_fill: true })
pos++;
}
}
messageBox.add(secretTable);
this._okButton = { label: _("Connect"),
action: Lang.bind(this, this._onOk),
key: Clutter.KEY_Return,
};
this.setButtons([{ label: _("Cancel"),
action: Lang.bind(this, this.cancel),
key: Clutter.KEY_Escape,
},
this._okButton]);
},
_updateOkButton: function() {
let valid = true;
for (let i = 0; i < this._content.secrets.length; i++) {
let secret = this._content.secrets[i];
valid = valid && secret.valid;
}
this._okButton.button.reactive = valid;
this._okButton.button.can_focus = valid;
if (valid)
this._okButton.button.remove_style_pseudo_class('disabled');
else
this._okButton.button.add_style_pseudo_class('disabled');
},
_onOk: function() {
let valid = true;
for (let i = 0; i < this._content.secrets.length; i++) {
let secret = this._content.secrets[i];
valid = valid && secret.valid;
if (secret.key != null)
this._agent.set_password(this._requestId, secret.key, secret.value);
}
if (valid) {
this._agent.respond(this._requestId, false);
this.close(global.get_current_time());
}
// do nothing if not valid
},
cancel: function() {
this._agent.respond(this._requestId, true);
this.close(global.get_current_time());
},
_validateWpaPsk: function(secret) {
let value = secret.value;
if (value.length == 64) {
// must be composed of hexadecimal digits only
for (let i = 0; i < 64; i++) {
if (!((value[i] >= 'a' && value[i] <= 'f')
|| (value[i] >= 'A' && value[i] <= 'F')
|| (value[i] >= '0' && value[i] <= '9')))
return false;
}
return true;
}
return (value.length >= 8 && value.length <= 63);
},
_validateStaticWep: function(secret) {
let value = secret.value;
if (secret.wep_key_type == NetworkManager.WepKeyType.KEY) {
if (value.length == 10 || value.length == 26) {
for (let i = 0; i < value.length; i++) {
if (!((value[i] >= 'a' && value[i] <= 'f')
|| (value[i] >= 'A' && value[i] <= 'F')
|| (value[i] >= '0' && value[i] <= '9')))
return false;
}
} else if (value.length == 5 || value.length == 13) {
for (let i = 0; i < value.length; i++) {
if (!((value[i] >= 'a' && value[i] <= 'z')
|| (value[i] >= 'A' && value[i] <= 'Z')))
return false;
}
} else
return false;
} else if (secret.wep_key_type == NetworkManager.WepKeyType.PASSPHRASE) {
if (value.length < 0 || value.length > 64)
return false;
}
return true;
},
_getWirelessSecrets: function(secrets, wirelessSetting) {
let wirelessSecuritySetting = this._connection.get_setting_wireless_security();
switch (wirelessSecuritySetting.key_mgmt) {
// First the easy ones
case 'wpa-none':
case 'wpa-psk':
secrets.push({ label: _("Password: "), key: 'psk',
value: wirelessSecuritySetting.psk || '',
validate: this._validateWpaPsk, password: true });
break;
case 'none': // static WEP
secrets.push({ label: _("Key: "), key: 'wep-key' + wirelessSecuritySetting.wep_tx_keyidx,
value: wirelessSecuritySetting.get_wep_key(wirelessSecuritySetting.wep_tx_keyidx) || '',
wep_key_type: wirelessSecuritySetting.wep_key_type,
validate: this._validateStaticWep, password: true });
break;
case 'ieee8021x':
if (wirelessSecuritySetting.auth_alg == 'leap') // Cisco LEAP
secrets.push({ label: _("Password: "), key: 'leap-password',
value: wirelessSecuritySetting.leap_password || '', password: true });
else // Dynamic (IEEE 802.1x) WEP
this._get8021xSecrets(secrets);
break;
case 'wpa-eap':
this._get8021xSecrets(secrets);
break;
default:
log('Invalid wireless key management: ' + wirelessSecuritySetting.key_mgmt);
}
},
_get8021xSecrets: function(secrets) {
let ieee8021xSetting = this._connection.get_setting_802_1x();
let phase2method;
switch (ieee8021xSetting.get_eap_method(0)) {
case 'md5':
case 'leap':
case 'ttls':
case 'peap':
// 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)
secrets.push({ label: _("Username: "), key: null,
value: ieee8021xSetting.identity || '', password: false });
secrets.push({ label: _("Password: "), key: 'password',
value: ieee8021xSetting.password || '', password: true });
break;
case 'tls':
secrets.push({ label: _("Identity: "), key: null,
value: ieee8021xSetting.identity || '', password: false });
secrets.push({ label: _("Private key password: "), key: 'private-key-password',
value: ieee8021xSetting.private_key_password || '', password: true });
break;
default:
log('Invalid EAP/IEEE802.1x method: ' + ieee8021xSetting.get_eap_method(0));
}
},
_getPPPoESecrets: function(secrets) {
let pppoeSetting = this._connection.get_setting_pppoe();
secrets.push({ label: _("Username: "), key: 'username',
value: pppoeSetting.username || '', password: false });
secrets.push({ label: _("Service: "), key: 'service',
value: pppoeSetting.service || '', password: false });
secrets.push({ label: _("Password: "), key: 'password',
value: pppoeSetting.password || '', password: true });
},
_getMobileSecrets: function(secrets, connectionType) {
let setting;
if (connectionType == 'bluetooth')
setting = this._connection.get_setting_cdma() || this._connection.get_setting_gsm();
else
setting = this._connection.get_setting_by_name(connectionType);
secrets.push({ label: _("Password: "), key: 'password',
value: setting.value || '', password: true });
},
_getContent: function() {
let connectionSetting = this._connection.get_setting_connection();
let connectionType = connectionSetting.get_connection_type();
let wirelessSetting;
let ssid;
let content = { };
content.secrets = [ ];
switch (connectionType) {
case '802-11-wireless':
wirelessSetting = this._connection.get_setting_wireless();
ssid = NetworkManager.utils_ssid_to_utf8(wirelessSetting.get_ssid());
content.title = _("Authentication required by wireless network");
content.message = _("Passwords or encryption keys are required to access the wireless network '%s'.").format(ssid);
this._getWirelessSecrets(content.secrets, wirelessSetting);
break;
case '802-3-ethernet':
content.title = _("Wired 802.1X authentication");
content.message = null;
content.secrets.push({ label: _("Network name: "), key: null,
value: connectionSetting.get_id(), password: false });
this._get8021xSecrets(content.secrets);
break;
case 'pppoe':
content.title = _("DSL authentication");
content.message = null;
this._getPPPoESecrets(content.secrets);
break;
case 'gsm':
if (this._hints.indexOf('pin') != -1) {
let gsmSetting = this._connection.get_setting_gsm();
content.title = _("PIN code required");
content.message = _("PIN code is needed for the mobile broadband device");
content.secrets.push({ label: _("PIN: "), key: 'pin',
value: gsmSetting.pin || '', password: true });
}
// fall through
case 'cdma':
case 'bluetooth':
content.title = _("Mobile broadband network password");
content.message = _("A password is required to connect to '%s'.").format(connectionSetting.get_id());
this._getMobileSecrets(content.secrets);
break;
default:
log('Invalid connection type: ' + connectionType);
};
return content;
}
};
function NetworkAgent() {
this._init.apply(this, arguments);
}
NetworkAgent.prototype = {
_init: function() {
this._native = new Shell.NetworkAgent({ auto_register: true,
identifier: 'org.gnome.Shell.NetworkAgent' });
this._dialogs = { };
this._native.connect('new-request', Lang.bind(this, this._newRequest));
this._native.connect('cancel-request', Lang.bind(this, this._cancelRequest));
},
_newRequest: function(agent, requestId, connection, settingName, hints) {
let dialog = new NetworkSecretDialog(agent, requestId, connection, settingName, hints);
dialog.connect('destroy', Lang.bind(this, function() {
delete this._dialogs[requestId];
}));
this._dialogs[requestId] = dialog;
dialog.open(global.get_current_time());
},
_cancelRequest: function(agent, requestId) {
this._dialogs[requestId].close(global.get_current_time());
this._dialogs[requestId].destroy();
}
};

View File

@@ -119,6 +119,11 @@ NotificationDaemon.prototype = {
return new St.Icon({ icon_name: icon,
icon_type: St.IconType.FULLCOLOR,
icon_size: size });
} else if (hints['image-data']) {
let [width, height, rowStride, hasAlpha,
bitsPerSample, nChannels, data] = hints['image-data'];
return textureCache.load_from_raw(data, data.length, hasAlpha,
width, height, rowStride, size);
} else {
let stockIcon;
switch (hints.urgency) {
@@ -143,7 +148,7 @@ NotificationDaemon.prototype = {
//
// Either a pid or ndata.notification is needed to retrieve or
// create a source.
_getSource: function(title, pid, ndata, sender) {
_getSource: function(title, pid, ndata) {
if (!pid && !(ndata && ndata.notification))
return null;
@@ -160,13 +165,10 @@ NotificationDaemon.prototype = {
// with a transient one from the same sender, so we
// always create a new source object for new transient notifications
// and never add it to this._sources .
if (!isForTransientNotification && this._sources[pid]) {
let source = this._sources[pid];
source.setTitle(title);
return source;
}
if (!isForTransientNotification && this._sources[pid])
return this._sources[pid];
let source = new Source(title, pid, sender);
let source = new Source(title, pid);
source.setTransient(isForTransientNotification);
if (!isForTransientNotification) {
@@ -185,13 +187,9 @@ NotificationDaemon.prototype = {
actions, hints, timeout) {
let id;
// Filter out chat, presence, calls and invitation notifications from
// Empathy, since we handle that information from telepathyClient.js
// Filter out chat and presence notifications from Empathy, since we
// handle that information from telepathyClient.js
if (appName == 'Empathy' && (hints['category'] == 'im.received' ||
hints['category'] == 'x-empathy.im.room-invitation' ||
hints['category'] == 'x-empathy.call.incoming' ||
hints['category'] == 'x-empathy.call.incoming"' ||
hints['category'] == 'x-empathy.im.subscription-request' ||
hints['category'] == 'presence.online' ||
hints['category'] == 'presence.offline')) {
// Ignore replacesId since we already sent back a
@@ -215,18 +213,14 @@ NotificationDaemon.prototype = {
hints = Params.parse(hints, { urgency: Urgency.NORMAL }, true);
// Be compatible with the various hints for image data and image path
// 'image-data' and 'image-path' are the latest name of these hints, introduced in 1.2
if (!hints['image-path'] && hints['image_path'])
hints['image-path'] = hints['image_path']; // version 1.1 of the spec
if (!hints['image-data'])
// Be compatible with the various hints for image data
// 'image-data' is the latest name of this hint, introduced in 1.2
if (!hints['image-data']) {
if (hints['image_data'])
hints['image-data'] = hints['image_data']; // version 1.1 of the spec
else if (hints['icon_data'] && !hints['image-path'])
// early versions of the spec; 'icon_data' should only be used if 'image-path' is not available
hints['image-data'] = hints['icon_data'];
else if (hints['icon_data'])
hints['image-data'] = hints['icon_data']; // previous versions of the spec
}
let ndata = { appName: appName,
icon: icon,
@@ -247,7 +241,7 @@ NotificationDaemon.prototype = {
let sender = DBus.getCurrentMessageContext().sender;
let pid = this._senderToPid[sender];
let source = this._getSource(appName, pid, ndata, sender);
let source = this._getSource(appName, pid, ndata);
if (source) {
this._notifyForSource(source, ndata);
@@ -268,7 +262,7 @@ NotificationDaemon.prototype = {
if (!ndata)
return;
source = this._getSource(appName, pid, ndata, sender);
source = this._getSource(appName, pid, ndata);
// We only store sender-pid entries for persistent sources.
// Removing the entries once the source is destroyed
@@ -305,7 +299,7 @@ NotificationDaemon.prototype = {
ndata.notification = notification;
notification.connect('destroy', Lang.bind(this,
function(n, reason) {
delete this._notifications[ndata.id];
delete this._notifications[id];
let notificationClosedReason;
switch (reason) {
case MessageTray.NotificationDestroyedReason.EXPIRED:
@@ -318,11 +312,11 @@ NotificationDaemon.prototype = {
notificationClosedReason = NotificationClosedReason.APP_CLOSED;
break;
}
this._emitNotificationClosed(ndata.id, notificationClosedReason);
this._emitNotificationClosed(id, notificationClosedReason);
}));
notification.connect('action-invoked', Lang.bind(this,
function(n, actionId) {
this._emitActionInvoked(ndata.id, actionId);
this._emitActionInvoked(id, actionId);
}));
} else {
notification.update(summary, body, { icon: iconActor,
@@ -330,34 +324,10 @@ NotificationDaemon.prototype = {
clear: true });
}
if (hints['image-data'] || hints['image-path']) {
let image = null;
if (hints['image-data']) {
let [width, height, rowStride, hasAlpha,
bitsPerSample, nChannels, data] = hints['image-data'];
image = St.TextureCache.get_default().load_from_raw(data, hasAlpha,
width, height, rowStride, notification.IMAGE_SIZE);
} else if (hints['image-path']) {
image = St.TextureCache.get_default().load_uri_async(GLib.filename_to_uri(hints['image-path'], null),
notification.IMAGE_SIZE,
notification.IMAGE_SIZE);
}
notification.setImage(image);
} else {
notification.unsetImage();
}
if (actions.length) {
notification.setUseActionIcons(hints['action-icons'] == true);
for (let i = 0; i < actions.length - 1; i += 2) {
if (actions[i] == 'default')
notification.connect('clicked', Lang.bind(this,
function() {
this._emitActionInvoked(ndata.id, "default");
}));
else
notification.addButton(actions[i], actions[i + 1]);
}
for (let i = 0; i < actions.length - 1; i += 2)
notification.addButton(actions[i], actions[i + 1]);
}
switch (hints.urgency) {
case Urgency.LOW:
@@ -441,7 +411,7 @@ NotificationDaemon.prototype = {
},
_onTrayIconAdded: function(o, icon) {
let source = this._getSource(icon.title || icon.wm_class || _("Unknown"), icon.pid, null, null);
let source = this._getSource(icon.title || icon.wm_class || _("Unknown"), icon.pid, null);
source.setTrayIcon(icon);
},
@@ -454,28 +424,18 @@ NotificationDaemon.prototype = {
DBus.conformExport(NotificationDaemon.prototype, NotificationDaemonIface);
function Source(title, pid, sender) {
this._init(title, pid, sender);
function Source(title, pid) {
this._init(title, pid);
}
Source.prototype = {
__proto__: MessageTray.Source.prototype,
_init: function(title, pid, sender) {
_init: function(title, pid) {
MessageTray.Source.prototype._init.call(this, title);
this._pid = pid;
if (sender)
// TODO: dbus-glib implementation of watch_name() doesnt return an id to be used for
// unwatch_name() or implement unwatch_name(), however when we move to using GDBus implementation,
// we should save the id here and call unwatch_name() with it in destroy().
// Moving to GDBus is the work in progress: https://bugzilla.gnome.org/show_bug.cgi?id=648651
// and https://bugzilla.gnome.org/show_bug.cgi?id=622921 .
DBus.session.watch_name(sender,
false,
null,
Lang.bind(this, this._onNameVanished));
this._appStateChangedId = 0;
this._setApp();
if (this.app)
this.title = this.app.get_name();
@@ -484,16 +444,6 @@ Source.prototype = {
this._trayIcon = null;
},
_onNameVanished: function() {
// Destroy the notification source when its sender is removed from DBus.
// Only do so if this.app is set to avoid removing "notify-send" sources, senders
// of which аre removed from DBus immediately.
// Sender being removed from DBus would normally result in a tray icon being removed,
// so allow the code path that handles the tray icon being removed to handle that case.
if (!this.trayIcon && this.app)
this.destroy();
},
processNotification: function(notification, icon) {
if (!this.app)
this._setApp();
@@ -546,6 +496,10 @@ Source.prototype = {
if (!this.app)
return;
// We only update the app if this.app is null, so we can't disconnect the old this._appStateChangedId
// even if it were non-zero for some reason.
this._appStateChangedId = this.app.connect('notify::state', Lang.bind(this, this._appStateChanged));
// 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) {
@@ -570,6 +524,19 @@ Source.prototype = {
this.destroy();
},
_appStateChanged: function() {
// Destroy notification sources when their apps exit.
// The app exiting would normally result in a tray icon being removed,
// so the associated source would be destroyed through the code path
// that handles the tray icon being removed. We should not destroy
// the source associated with a tray icon when the application state
// is Shell.AppState.STOPPED because running applications that have
// no open windows would also have that state. This is often the case
// for applications that use tray icons.
if (!this._trayIcon && this.app.get_state() == Shell.AppState.STOPPED)
this.destroy();
},
openApp: function() {
if (this.app == null)
return;
@@ -582,6 +549,10 @@ Source.prototype = {
},
destroy: function() {
if (this.app && this._appStateChangedId) {
this.app.disconnect(this._appStateChangedId);
this._appStateChangedId = 0;
}
MessageTray.Source.prototype.destroy.call(this);
}
};

View File

@@ -11,7 +11,6 @@ const Shell = imports.gi.Shell;
const Gdk = imports.gi.Gdk;
const AppDisplay = imports.ui.appDisplay;
const ContactDisplay = imports.ui.contactDisplay;
const Dash = imports.ui.dash;
const DND = imports.ui.dnd;
const DocDisplay = imports.ui.docDisplay;
@@ -19,7 +18,6 @@ const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const Panel = imports.ui.panel;
const Params = imports.misc.params;
const PlaceDisplay = imports.ui.placeDisplay;
const Tweener = imports.ui.tweener;
const ViewSelector = imports.ui.viewSelector;
@@ -98,30 +96,14 @@ ShellInfo.prototype = {
};
function Overview() {
this._init.apply(this, arguments);
this._init();
}
Overview.prototype = {
_init : function(params) {
params = Params.parse(params, { isDummy: false });
this.isDummy = params.isDummy;
// We only have an overview in user sessions, so
// create a dummy overview in other cases
if (this.isDummy) {
this.animationInProgress = false;
this.visible = false;
this.workspaces = null;
return;
}
// The main BackgroundActor is inside global.window_group which is
// hidden when displaying the overview, so we create a new
// one. Instances of this class share a single CoglTexture behind the
// scenes which allows us to show the background with different
// rendering options without duplicating the texture data.
this._background = Meta.BackgroundActor.new_for_screen(global.screen);
_init : function() {
// The actual global.background_actor is inside global.window_group,
// which is hidden when displaying the overview, so we display a clone.
this._background = new Clutter.Clone({ source: global.background_actor });
this._background.hide();
global.overlay_group.add_actor(this._background);
@@ -139,7 +121,7 @@ Overview.prototype = {
let spacing = node.get_length('spacing');
if (spacing != this._spacing) {
this._spacing = spacing;
this._relayout();
this.relayout();
}
}));
@@ -193,51 +175,33 @@ Overview.prototype = {
// signal handlers and so forth. So we create them after
// construction in this init() method.
init: function() {
if (this.isDummy)
return;
this.shellInfo = new ShellInfo();
this._shellInfo = new ShellInfo();
this._viewSelector = new ViewSelector.ViewSelector();
this._group.add_actor(this._viewSelector.actor);
this.viewSelector = new ViewSelector.ViewSelector();
this._group.add_actor(this.viewSelector.actor);
this._workspacesDisplay = new WorkspacesView.WorkspacesDisplay();
this._viewSelector.addViewTab('windows', _("Windows"), this._workspacesDisplay.actor, 'text-x-generic');
this.viewSelector.addViewTab('windows', _("Windows"), this._workspacesDisplay.actor, 'text-x-generic');
let appView = new AppDisplay.AllAppDisplay();
this._viewSelector.addViewTab('applications', _("Applications"), appView.actor, 'system-run');
this.viewSelector.addViewTab('applications', _("Applications"), appView.actor, 'system-run');
// Default search providers
this._viewSelector.addSearchProvider(new AppDisplay.AppSearchProvider());
this._viewSelector.addSearchProvider(new AppDisplay.SettingsSearchProvider());
this._viewSelector.addSearchProvider(new PlaceDisplay.PlaceSearchProvider());
this._viewSelector.addSearchProvider(new DocDisplay.DocSearchProvider());
this._viewSelector.addSearchProvider(new ContactDisplay.ContactSearchProvider());
this.viewSelector.addSearchProvider(new AppDisplay.AppSearchProvider());
this.viewSelector.addSearchProvider(new AppDisplay.PrefsSearchProvider());
this.viewSelector.addSearchProvider(new PlaceDisplay.PlaceSearchProvider());
this.viewSelector.addSearchProvider(new DocDisplay.DocSearchProvider());
// TODO - recalculate everything when desktop size changes
this._dash = new Dash.Dash();
this._group.add_actor(this._dash.actor);
this._dash.actor.add_constraint(this._viewSelector.constrainY);
this._dash.actor.add_constraint(this._viewSelector.constrainHeight);
this.dashIconSize = this._dash.iconSize;
this._dash.connect('icon-size-changed',
Lang.bind(this, function() {
this.dashIconSize = this._dash.iconSize;
}));
this.dash = new Dash.Dash();
this._group.add_actor(this.dash.actor);
this.dash.actor.add_constraint(this.viewSelector.constrainY);
this.dash.actor.add_constraint(this.viewSelector.constrainHeight);
// Translators: this is the name of the dock/favorites area on
// the left of the overview
Main.ctrlAltTabManager.addGroup(this._dash.actor, _("Dash"), 'user-bookmarks');
Main.ctrlAltTabManager.addGroup(this.dash.actor, _("Dash"), 'user-bookmarks');
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._relayout));
this._relayout();
},
setMessage: function(text, undoCallback, undoLabel) {
if (this.isDummy)
return;
this._shellInfo.setMessage(text, undoCallback, undoLabel);
},
_onDragBegin: function() {
@@ -256,7 +220,7 @@ Overview.prototype = {
}
this._resetWindowSwitchTimeout();
this._lastHoveredWindow = null;
DND.removeDragMonitor(this._dragMonitor);
DND.removeMonitor(this._dragMonitor);
this.endItemDrag();
},
@@ -309,9 +273,6 @@ Overview.prototype = {
},
setScrollAdjustment: function(adjustment, direction) {
if (this.isDummy)
return;
this._scrollAdjustment = adjustment;
if (this._scrollAdjustment == null)
this._scrollDirection = SwipeScrollDirection.NONE;
@@ -320,8 +281,7 @@ Overview.prototype = {
},
_onButtonPress: function(actor, event) {
if (this._scrollDirection == SwipeScrollDirection.NONE
|| event.get_button() != 1)
if (this._scrollDirection == SwipeScrollDirection.NONE)
return;
let [stageX, stageY] = event.get_coords();
@@ -430,7 +390,7 @@ Overview.prototype = {
[stageX, stageY] = event.get_coords();
let dx = this._dragX - stageX;
let dy = this._dragY - stageY;
let primary = Main.layoutManager.primaryMonitor;
let primary = global.get_primary_monitor();
this._dragX = stageX;
this._dragY = stageY;
@@ -477,13 +437,8 @@ Overview.prototype = {
return clone;
},
_relayout: function () {
// To avoid updating the position and size of the workspaces
// we just hide the overview. The positions will be updated
// when it is next shown.
this.hide();
let primary = Main.layoutManager.primaryMonitor;
relayout: function () {
let primary = global.get_primary_monitor();
let rtl = (St.Widget.get_default_direction () == St.TextDirection.RTL);
let contentY = Main.panel.actor.height;
@@ -504,15 +459,15 @@ Overview.prototype = {
// Set the dash's x position - y is handled by a constraint
let dashX;
if (rtl) {
this._dash.actor.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
this.dash.actor.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
dashX = primary.width;
} else {
dashX = 0;
}
this._dash.actor.set_x(dashX);
this.dash.actor.set_x(dashX);
this._viewSelector.actor.set_position(viewX, viewY);
this._viewSelector.actor.set_size(viewWidth, viewHeight);
this.viewSelector.actor.set_position(viewX, viewY);
this.viewSelector.actor.set_size(viewWidth, viewHeight);
},
//// Public methods ////
@@ -545,8 +500,6 @@ Overview.prototype = {
//
// Animates the overview visible and grabs mouse and keyboard input
show : function() {
if (this.isDummy)
return;
if (this._shown)
return;
// Do this manually instead of using _syncInputMode, to handle failure
@@ -574,9 +527,6 @@ Overview.prototype = {
//
// If we switched to displaying the actors in the Overview rather than
// clones of them, this would obviously no longer be necessary.
//
// Disable unredirection while in the overview
Meta.disable_unredirect_for_screen(global.screen);
global.window_group.hide();
this._group.show();
this._background.show();
@@ -608,12 +558,6 @@ Overview.prototype = {
onCompleteScope: this
});
Tweener.addTween(this._background,
{ dim_factor: 0.4,
time: ANIMATION_TIME,
transition: 'easeOutQuad'
});
this._coverPane.raise_top();
this._coverPane.show();
this.emit('showing');
@@ -626,9 +570,6 @@ Overview.prototype = {
// will result in the overview not being hidden until hideTemporarily() is
// called.
showTemporarily: function() {
if (this.isDummy)
return;
if (this._shownTemporarily)
return;
@@ -641,9 +582,6 @@ Overview.prototype = {
//
// Reverses the effect of show()
hide: function() {
if (this.isDummy)
return;
if (!this._shown)
return;
@@ -662,9 +600,6 @@ Overview.prototype = {
//
// Reverses the effect of showTemporarily()
hideTemporarily: function() {
if (this.isDummy)
return;
if (!this._shownTemporarily)
return;
@@ -676,9 +611,6 @@ Overview.prototype = {
},
toggle: function() {
if (this.isDummy)
return;
if (this._shown)
this.hide();
else
@@ -744,12 +676,6 @@ Overview.prototype = {
onCompleteScope: this
});
Tweener.addTween(this._background,
{ dim_factor: 1.0,
time: ANIMATION_TIME,
transition: 'easeOutQuad'
});
this._coverPane.raise_top();
this._coverPane.show();
this.emit('hiding');
@@ -770,9 +696,6 @@ Overview.prototype = {
},
_hideDone: function() {
// Re-enable unredirection
Meta.enable_unredirect_for_screen(global.screen);
global.window_group.show();
this.workspaces.destroy();

View File

@@ -12,23 +12,26 @@ const Signals = imports.signals;
const Config = imports.misc.config;
const CtrlAltTab = imports.ui.ctrlAltTab;
const Layout = imports.ui.layout;
const Overview = imports.ui.overview;
const PopupMenu = imports.ui.popupMenu;
const PanelMenu = imports.ui.panelMenu;
const UserMenu = imports.ui.userMenu;
const StatusMenu = imports.ui.statusMenu;
const DateMenu = imports.ui.dateMenu;
const Main = imports.ui.main;
const Tweener = imports.ui.tweener;
const PANEL_ICON_SIZE = 24;
const STARTUP_ANIMATION_TIME = 0.2;
const HOT_CORNER_ACTIVATION_TIMEOUT = 0.5;
const BUTTON_DND_ACTIVATION_TIMEOUT = 250;
const ANIMATED_ICON_UPDATE_TIMEOUT = 100;
const SPINNER_ANIMATION_TIME = 0.2;
const STANDARD_TRAY_ICON_ORDER = ['a11y', 'keyboard', 'volume', 'bluetooth', 'network', 'battery'];
const STANDARD_TRAY_ICON_ORDER = ['a11y', 'display', 'keyboard', 'volume', 'bluetooth', 'network', 'battery'];
const STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION = {
'a11y': imports.ui.status.accessibility.ATIndicator,
'volume': imports.ui.status.volume.Indicator,
@@ -45,14 +48,6 @@ try {
log('NMApplet is not supported. It is possible that your NetworkManager version is too old');
}
const GDM_TRAY_ICON_ORDER = ['a11y', 'display', 'keyboard', 'volume', 'battery'];
const GDM_TRAY_ICON_SHELL_IMPLEMENTATION = {
'a11y': imports.ui.status.accessibility.ATIndicator,
'volume': imports.ui.status.volume.Indicator,
'battery': imports.ui.status.power.Indicator,
'keyboard': imports.ui.status.keyboard.XKBIndicator
};
// To make sure the panel corners blend nicely with the panel,
// we draw background and borders the same way, e.g. drawing
// them as filled shapes from the outside inwards instead of
@@ -243,6 +238,7 @@ AppMenuButton.prototype = {
_init: function() {
PanelMenu.Button.prototype._init.call(this, 0.0);
this._metaDisplay = global.screen.get_display();
this._startingApps = [];
this._targetApp = null;
@@ -306,7 +302,6 @@ AppMenuButton.prototype = {
this._visible = true;
this.actor.show();
this.actor.reactive = true;
if (!this._targetIsCurrent)
return;
@@ -323,7 +318,6 @@ AppMenuButton.prototype = {
return;
this._visible = false;
this.actor.reactive = false;
if (!this._targetIsCurrent) {
this.actor.hide();
return;
@@ -548,249 +542,17 @@ AppMenuButton.prototype = {
Signals.addSignalMethods(AppMenuButton.prototype);
// Activities button. Because everything else in the top bar is a
// PanelMenu.Button, it simplifies some things to make this be one too.
// We just hack it up to not actually have a menu attached to it.
function ActivitiesButton() {
this._init.apply(this, arguments);
}
ActivitiesButton.prototype = {
__proto__: PanelMenu.Button.prototype,
_init: function() {
PanelMenu.Button.prototype._init.call(this, 0.0);
let container = new Shell.GenericContainer();
container.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
container.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
container.connect('allocate', Lang.bind(this, this._allocate));
this.actor.child = container;
this.actor.name = 'panelActivities';
/* Translators: If there is no suitable word for "Activities"
in your language, you can use the word for "Overview". */
this._label = new St.Label({ text: _("Activities") });
container.add_actor(this._label);
this._hotCorner = new Layout.HotCorner();
container.add_actor(this._hotCorner.actor);
// Hack up our menu...
this.menu.open = Lang.bind(this, this._onMenuOpenRequest);
this.menu.close = Lang.bind(this, this._onMenuCloseRequest);
this.menu.toggle = Lang.bind(this, this._onMenuToggleRequest);
this.actor.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
this.actor.connect_after('button-release-event', Lang.bind(this, this._onButtonRelease));
this.actor.connect_after('key-release-event', Lang.bind(this, this._onKeyRelease));
Main.overview.connect('showing', Lang.bind(this, function() {
this.actor.add_style_pseudo_class('overview');
this._escapeMenuGrab();
}));
Main.overview.connect('hiding', Lang.bind(this, function() {
this.actor.remove_style_pseudo_class('overview');
this._escapeMenuGrab();
}));
this._xdndTimeOut = 0;
},
_getPreferredWidth: function(actor, forHeight, alloc) {
[alloc.min_size, alloc.natural_size] = this._label.get_preferred_width(forHeight);
},
_getPreferredHeight: function(actor, forWidth, alloc) {
[alloc.min_size, alloc.natural_size] = this._label.get_preferred_height(forWidth);
},
_allocate: function(actor, box, flags) {
this._label.allocate(box, flags);
// The hot corner needs to be outside any padding/alignment
// that has been imposed on us
let primary = Main.layoutManager.primaryMonitor;
let hotBox = new Clutter.ActorBox();
let ok, x, y;
if (actor.get_direction() == St.TextDirection.LTR) {
[ok, x, y] = actor.transform_stage_point(primary.x, primary.y)
} else {
[ok, x, y] = actor.transform_stage_point(primary.x + primary.width, primary.y);
// hotCorner.actor has northeast gravity, so we don't need
// to adjust x for its width
}
hotBox.x1 = Math.round(x);
hotBox.x2 = hotBox.x1 + this._hotCorner.actor.width;
hotBox.y1 = Math.round(y);
hotBox.y2 = hotBox.y1 + this._hotCorner.actor.height;
this._hotCorner.actor.allocate(hotBox, flags);
},
handleDragOver: function(source, actor, x, y, time) {
if (source != Main.xdndHandler)
return;
if (this._xdndTimeOut != 0)
Mainloop.source_remove(this._xdndTimeOut);
this._xdndTimeOut = Mainloop.timeout_add(BUTTON_DND_ACTIVATION_TIMEOUT,
Lang.bind(this, this._xdndShowOverview, actor));
},
_escapeMenuGrab: function() {
if (this.menu.isOpen)
this.menu.close();
},
_onCapturedEvent: function(actor, event) {
if (event.type() == Clutter.EventType.BUTTON_PRESS) {
if (!this._hotCorner.shouldToggleOverviewOnClick())
return true;
}
return false;
},
_onMenuOpenRequest: function() {
this.menu.isOpen = true;
this.menu.emit('open-state-changed', true);
},
_onMenuCloseRequest: function() {
this.menu.isOpen = false;
this.menu.emit('open-state-changed', false);
},
_onMenuToggleRequest: function() {
this.menu.isOpen = !this.menu.isOpen;
this.menu.emit('open-state-changed', this.menu.isOpen);
},
_onButtonRelease: function() {
if (this.menu.isOpen) {
this.menu.close();
Main.overview.toggle();
}
},
_onKeyRelease: function(actor, event) {
let symbol = event.get_key_symbol();
if (symbol == Clutter.KEY_Return || symbol == Clutter.KEY_space) {
if (this.menu.isOpen)
this.menu.close();
Main.overview.toggle();
}
},
_xdndShowOverview: function(actor) {
let [x, y, mask] = global.get_pointer();
let pickedActor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y);
if (pickedActor == this.actor) {
if (!Main.overview.visible && !Main.overview.animationInProgress) {
Main.overview.showTemporarily();
Main.overview.beginItemDrag(actor);
}
}
Mainloop.source_remove(this._xdndTimeOut);
this._xdndTimeOut = 0;
}
};
function PanelCorner(panel, side) {
this._init(panel, side);
function PanelCorner(side) {
this._init(side);
}
PanelCorner.prototype = {
_init: function(box, side) {
_init: function(side) {
this._side = side;
this._box = box;
this._box.connect('style-changed', Lang.bind(this, this._boxStyleChanged));
this.actor = new St.DrawingArea({ style_class: 'panel-corner' });
this.actor.connect('style-changed', Lang.bind(this, this._styleChanged));
this.actor.connect('repaint', Lang.bind(this, this._repaint));
},
_findRightmostButton: function(container) {
if (!container.get_children)
return null;
let children = container.get_children();
if (!children || children.length == 0)
return null;
// Start at the back and work backward
let index = children.length - 1;
while (!children[index].visible && index >= 0)
index--;
if (index < 0)
return null;
if (!(children[index].has_style_class_name('panel-menu')) &&
!(children[index].has_style_class_name('panel-button')))
return this._findRightmostButton(children[index]);
return children[index];
},
_findLeftmostButton: function(container) {
if (!container.get_children)
return null;
let children = container.get_children();
if (!children || children.length == 0)
return null;
// Start at the front and work forward
let index = 0;
while (!children[index].visible && index < children.length)
index++;
if (index == children.length)
return null;
if (!(children[index].has_style_class_name('panel-menu')) &&
!(children[index].has_style_class_name('panel-button')))
return this._findLeftmostButton(children[index]);
return children[index];
},
_boxStyleChanged: function() {
let button;
if (this._side == St.Side.LEFT)
button = this._findLeftmostButton(this._box);
else if (this._side == St.Side.RIGHT)
button = this._findRightmostButton(this._box);
if (button) {
if (this._button && this._buttonStyleChangedSignalId)
this._button.disconnect(this._buttonStyleChangedSignalId);
this._button = button;
button.connect('destroy', Lang.bind(this,
function() {
if (this._button == button) {
this._button = null;
this._buttonStyleChangedSignalId = 0;
}
}));
// Synchronize the locate button's pseudo classes with this corner
this._buttonStyleChangedSignalId = button.connect('style-changed', Lang.bind(this,
function(actor) {
let pseudoClass = button.get_style_pseudo_class();
this.actor.set_style_pseudo_class(pseudoClass);
}));
}
this.actor.connect('style-changed', Lang.bind(this, this.relayout));
},
_repaint: function() {
@@ -853,17 +615,194 @@ PanelCorner.prototype = {
cr.restore();
},
_styleChanged: function() {
relayout: function() {
let node = this.actor.get_theme_node();
let cornerRadius = node.get_length("-panel-corner-radius");
let innerBorderWidth = node.get_length('-panel-corner-inner-border-width');
this.actor.set_size(cornerRadius, innerBorderWidth + cornerRadius);
this.actor.set_anchor_point(0, innerBorderWidth);
this.actor.set_size(cornerRadius,
innerBorderWidth + cornerRadius);
if (this._side == St.Side.LEFT)
this.actor.set_position(Main.panel.actor.x,
Main.panel.actor.y + Main.panel.actor.height - innerBorderWidth);
else
this.actor.set_position(Main.panel.actor.x + Main.panel.actor.width - cornerRadius,
Main.panel.actor.y + Main.panel.actor.height - innerBorderWidth);
}
};
/**
* HotCorner:
*
* This class manages the "hot corner" that can toggle switching to
* overview.
*/
function HotCorner(button) {
this._init(button);
}
HotCorner.prototype = {
_init : function(button) {
// This is the activities button associated with this hot corner,
// if this is on the primary monitor (or null with the corner is
// on a different monitor)
this._button = button;
// We use this flag to mark the case where the user has entered the
// hot corner and has not left both the hot corner and a surrounding
// guard area (the "environs"). This avoids triggering the hot corner
// multiple times due to an accidental jitter.
this._entered = false;
this.actor = new Clutter.Group({ width: 3,
height: 3,
reactive: true });
this._corner = new Clutter.Rectangle({ width: 1,
height: 1,
opacity: 0,
reactive: true });
this.actor.add_actor(this._corner);
if (St.Widget.get_default_direction() == St.TextDirection.RTL) {
this._corner.set_position(this.actor.width - this._corner.width, 0);
this.actor.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
} else {
this._corner.set_position(0, 0);
}
this._activationTime = 0;
this.actor.connect('enter-event',
Lang.bind(this, this._onEnvironsEntered));
this.actor.connect('leave-event',
Lang.bind(this, this._onEnvironsLeft));
// Clicking on the hot corner environs should result in the same bahavior
// as clicking on the hot corner.
this.actor.connect('button-release-event',
Lang.bind(this, this._onCornerClicked));
// In addition to being triggered by the mouse enter event, the hot corner
// can be triggered by clicking on it. This is useful if the user wants to
// undo the effect of triggering the hot corner once in the hot corner.
this._corner.connect('enter-event',
Lang.bind(this, this._onCornerEntered));
this._corner.connect('button-release-event',
Lang.bind(this, this._onCornerClicked));
this._corner.connect('leave-event',
Lang.bind(this, this._onCornerLeft));
this._corner._delegate = this._corner;
this._corner.handleDragOver = Lang.bind(this,
function(source, actor, x, y, time) {
if (source == Main.xdndHandler) {
if(!Main.overview.visible && !Main.overview.animationInProgress) {
this.rippleAnimation();
Main.overview.showTemporarily();
Main.overview.beginItemDrag(actor);
}
}
});
Main.chrome.addActor(this.actor, { visibleInOverview: true, affectsStruts: false });
},
destroy: function() {
this.actor.destroy();
},
_addRipple : function(delay, time, startScale, startOpacity, finalScale, finalOpacity) {
// We draw a ripple by using a source image and animating it scaling
// outwards and fading away. We want the ripples to move linearly
// or it looks unrealistic, but if the opacity of the ripple goes
// linearly to zero it fades away too quickly, so we use Tweener's
// 'onUpdate' to give a non-linear curve to the fade-away and make
// it more visible in the middle section.
let [x, y] = this._corner.get_transformed_position();
let ripple = new St.BoxLayout({ style_class: 'ripple-box',
opacity: 255 * Math.sqrt(startOpacity),
scale_x: startScale,
scale_y: startScale,
x: x,
y: y });
ripple._opacity = startOpacity;
if (ripple.get_direction() == St.TextDirection.RTL)
ripple.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
Tweener.addTween(ripple, { _opacity: finalOpacity,
scale_x: finalScale,
scale_y: finalScale,
delay: delay,
time: time,
transition: 'linear',
onUpdate: function() { ripple.opacity = 255 * Math.sqrt(ripple._opacity); },
onComplete: function() { ripple.destroy(); } });
Main.uiGroup.add_actor(ripple);
},
rippleAnimation: function() {
// Show three concentric ripples expanding outwards; the exact
// parameters were found by trial and error, so don't look
// for them to make perfect sense mathematically
// delay time scale opacity => scale opacity
this._addRipple(0.0, 0.83, 0.25, 1.0, 1.5, 0.0);
this._addRipple(0.05, 1.0, 0.0, 0.7, 1.25, 0.0);
this._addRipple(0.35, 1.0, 0.0, 0.3, 1, 0.0);
},
_onEnvironsEntered : function() {
if (this._button)
this._button.hover = true;
},
_onCornerEntered : function() {
if (!this._entered) {
this._entered = true;
if (!Main.overview.animationInProgress) {
this._activationTime = Date.now() / 1000;
this.rippleAnimation();
Main.overview.toggle();
}
}
return false;
},
_onCornerClicked : function() {
if (!Main.overview.animationInProgress)
this.maybeToggleOverviewOnClick();
return false;
},
_onCornerLeft : function(actor, event) {
if (event.get_related() != this.actor)
this._entered = false;
// Consume event, otherwise this will confuse onEnvironsLeft
return true;
},
_onEnvironsLeft : function(actor, event) {
if (this._button)
this._button.hover = false;
if (event.get_related() != this._corner)
this._entered = false;
return false;
},
// Toggles the overview unless this is the first click on the Activities button within the HOT_CORNER_ACTIVATION_TIMEOUT time
// of the hot corner being triggered. This check avoids opening and closing the overview if the user both triggered the hot corner
// and clicked the Activities button.
maybeToggleOverviewOnClick: function() {
if (this._activationTime == 0 || Date.now() / 1000 - this._activationTime > HOT_CORNER_ACTIVATION_TIMEOUT)
Main.overview.toggle();
this._activationTime = 0;
}
}
function Panel() {
this._init();
@@ -871,8 +810,9 @@ function Panel() {
Panel.prototype = {
_init : function() {
this.actor = new Shell.GenericContainer({ name: 'panel',
reactive: true });
this.actor = new St.BoxLayout({ style_class: 'menu-bar',
name: 'panel',
reactive: true });
this.actor._delegate = this;
this._statusArea = {};
@@ -884,60 +824,131 @@ Panel.prototype = {
this.actor.remove_style_class_name('in-overview');
}));
if (global.session_type == Shell.SessionType.GDM) {
this._tray_icon_order = GDM_TRAY_ICON_ORDER;
this._tray_icon_shell_implementation = GDM_TRAY_ICON_SHELL_IMPLEMENTATION;
} else {
this._tray_icon_order = STANDARD_TRAY_ICON_ORDER;
this._tray_icon_shell_implementation = STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION;
}
this._leftPointerBarrier = 0;
this._rightPointerBarrier = 0;
this._menus = new PopupMenu.PopupMenuManager(this);
this._leftBox = new St.BoxLayout({ name: 'panelLeft' });
this.actor.add_actor(this._leftBox);
this._centerBox = new St.BoxLayout({ name: 'panelCenter' });
this.actor.add_actor(this._centerBox);
this._rightBox = new St.BoxLayout({ name: 'panelRight' });
this.actor.add_actor(this._rightBox);
if (this.actor.get_direction() == St.TextDirection.RTL)
this._leftCorner = new PanelCorner(this._rightBox, St.Side.LEFT);
else
this._leftCorner = new PanelCorner(this._leftBox, St.Side.LEFT);
this._leftCorner = new PanelCorner(St.Side.LEFT);
this._rightCorner = new PanelCorner(St.Side.RIGHT);
this.actor.add_actor(this._leftCorner.actor);
/* This box container ensures that the centerBox is positioned in the *absolute*
* center, but can be pushed aside if necessary. */
this._boxContainer = new Shell.GenericContainer();
this.actor.add(this._boxContainer, { expand: true });
this._boxContainer.add_actor(this._leftBox);
this._boxContainer.add_actor(this._centerBox);
this._boxContainer.add_actor(this._rightBox);
this._boxContainer.connect('get-preferred-width', Lang.bind(this, function(box, forHeight, alloc) {
let children = box.get_children();
for (let i = 0; i < children.length; i++) {
let [childMin, childNatural] = children[i].get_preferred_width(forHeight);
alloc.min_size += childMin;
alloc.natural_size += childNatural;
}
}));
this._boxContainer.connect('get-preferred-height', Lang.bind(this, function(box, forWidth, alloc) {
let children = box.get_children();
for (let i = 0; i < children.length; i++) {
let [childMin, childNatural] = children[i].get_preferred_height(forWidth);
if (childMin > alloc.min_size)
alloc.min_size = childMin;
if (childNatural > alloc.natural_size)
alloc.natural_size = childNatural;
}
}));
this._boxContainer.connect('allocate', Lang.bind(this, function(container, box, flags) {
let allocWidth = box.x2 - box.x1;
let allocHeight = box.y2 - box.y1;
let [leftMinWidth, leftNaturalWidth] = this._leftBox.get_preferred_width(-1);
let [centerMinWidth, centerNaturalWidth] = this._centerBox.get_preferred_width(-1);
let [rightMinWidth, rightNaturalWidth] = this._rightBox.get_preferred_width(-1);
if (this.actor.get_direction() == St.TextDirection.RTL)
this._rightCorner = new PanelCorner(this._leftBox, St.Side.RIGHT);
else
this._rightCorner = new PanelCorner(this._rightBox, St.Side.RIGHT);
this.actor.add_actor(this._rightCorner.actor);
let sideWidth, centerWidth;
centerWidth = centerNaturalWidth;
sideWidth = (allocWidth - centerWidth) / 2;
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
this.actor.connect('allocate', Lang.bind(this, this._allocate));
let childBox = new Clutter.ActorBox();
childBox.y1 = 0;
childBox.y2 = allocHeight;
if (this.actor.get_direction() == St.TextDirection.RTL) {
childBox.x1 = allocWidth - Math.min(Math.floor(sideWidth),
leftNaturalWidth);
childBox.x2 = allocWidth;
} else {
childBox.x1 = 0;
childBox.x2 = Math.min(Math.floor(sideWidth),
leftNaturalWidth);
}
this._leftBox.allocate(childBox, flags);
childBox.x1 = Math.ceil(sideWidth);
childBox.y1 = 0;
childBox.x2 = childBox.x1 + centerWidth;
childBox.y2 = allocHeight;
this._centerBox.allocate(childBox, flags);
childBox.y1 = 0;
childBox.y2 = allocHeight;
if (this.actor.get_direction() == St.TextDirection.RTL) {
childBox.x1 = 0;
childBox.x2 = Math.min(Math.floor(sideWidth),
rightNaturalWidth);
} else {
childBox.x1 = allocWidth - Math.min(Math.floor(sideWidth),
rightNaturalWidth);
childBox.x2 = allocWidth;
}
this._rightBox.allocate(childBox, flags);
}));
/* Button on the left side of the panel. */
if (global.session_type == Shell.SessionType.USER) {
this._activitiesButton = new ActivitiesButton();
this._activities = this._activitiesButton.actor;
this._leftBox.add(this._activities);
/* Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview". */
let label = new St.Label({ text: _("Activities") });
this.button = new St.Button({ name: 'panelActivities',
style_class: 'panel-button',
reactive: true,
can_focus: true });
this._activities = this.button;
this.button.set_child(label);
this.button._delegate = this.button;
this.button._xdndTimeOut = 0;
this.button.handleDragOver = Lang.bind(this,
function(source, actor, x, y, time) {
if (source == Main.xdndHandler) {
if (this.button._xdndTimeOut != 0)
Mainloop.source_remove(this.button._xdndTimeOut);
this.button._xdndTimeOut = Mainloop.timeout_add(BUTTON_DND_ACTIVATION_TIMEOUT,
Lang.bind(this,
function() {
this._xdndShowOverview(actor);
}));
}
});
this._leftBox.add(this.button);
// The activities button has a pretend menu, so as to integrate
// more cleanly with the rest of the panel
this._menus.addMenu(this._activitiesButton.menu);
// Synchronize the buttons pseudo classes with its corner
this.button.connect('style-changed', Lang.bind(this,
function(actor) {
let rtl = actor.get_direction() == St.TextDirection.RTL;
let corner = rtl ? this._rightCorner : this._leftCorner;
let pseudoClass = actor.get_style_pseudo_class();
corner.actor.set_style_pseudo_class(pseudoClass);
}));
this._appMenu = new AppMenuButton();
this._leftBox.add(this._appMenu.actor);
this._menus.addMenu(this._appMenu.menu);
}
this._hotCorner = null;
this._appMenu = new AppMenuButton();
this._leftBox.add(this._appMenu.actor);
this._menus.addMenu(this._appMenu.menu);
/* center */
if (global.session_type == Shell.SessionType.USER)
this._dateMenu = new DateMenu.DateMenuButton({ showEvents: true });
else
this._dateMenu = new DateMenu.DateMenuButton({ showEvents: false });
this._dateMenu = new DateMenu.DateMenuButton();
this._centerBox.add(this._dateMenu.actor, { y_fill: true });
this._menus.addMenu(this._dateMenu.menu);
@@ -953,145 +964,161 @@ Panel.prototype = {
this._rightBox.add(this._trayBox);
this._rightBox.add(this._statusBox);
if (global.session_type == Shell.SessionType.USER) {
this._userMenu = new UserMenu.UserMenuButton();
this._userMenu.actor.name = 'panelStatus';
this._rightBox.add(this._userMenu.actor);
}
this._userMenu = new StatusMenu.StatusMenuButton();
this._userMenu.actor.name = 'panelStatus';
this._rightBox.add(this._userMenu.actor);
// Synchronize the buttons pseudo classes with its corner
this._userMenu.actor.connect('style-changed', Lang.bind(this,
function(actor) {
let rtl = actor.get_direction() == St.TextDirection.RTL;
let corner = rtl ? this._leftCorner : this._rightCorner;
let pseudoClass = actor.get_style_pseudo_class();
corner.actor.set_style_pseudo_class(pseudoClass);
}));
Main.statusIconDispatcher.connect('status-icon-added', Lang.bind(this, this._onTrayIconAdded));
Main.statusIconDispatcher.connect('status-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
Main.layoutManager.panelBox.add(this.actor);
// TODO: decide what to do with the rest of the panel in the Overview mode (make it fade-out, become non-reactive, etc.)
// We get into the Overview mode on button-press-event as opposed to button-release-event because eventually we'll probably
// have the Overview act like a menu that allows the user to release the mouse on the activity the user wants
// to switch to.
this.button.connect('clicked', Lang.bind(this, function(b) {
if (!Main.overview.animationInProgress) {
this._hotCorner.maybeToggleOverviewOnClick();
return true;
} else {
return false;
}
}));
// In addition to pressing the button, the Overview can be entered and exited by other means, such as
// pressing the System key, Alt+F1 or Esc. We want the button to be pressed in when the Overview is entered
// and to be released when it is exited regardless of how it was triggered.
Main.overview.connect('showing', Lang.bind(this, function() {
this.button.checked = true;
}));
Main.overview.connect('hiding', Lang.bind(this, function() {
this.button.checked = false;
}));
Main.chrome.addActor(this.actor, { visibleInOverview: true });
Main.chrome.addActor(this._leftCorner.actor, { visibleInOverview: true,
affectsStruts: false,
affectsInputRegion: false });
Main.chrome.addActor(this._rightCorner.actor, { visibleInOverview: true,
affectsStruts: false,
affectsInputRegion: false });
Main.ctrlAltTabManager.addGroup(this.actor, _("Top Bar"), 'start-here',
{ sortGroup: CtrlAltTab.SortGroup.TOP });
},
_getPreferredWidth: function(actor, forHeight, alloc) {
alloc.min_size = -1;
alloc.natural_size = Main.layoutManager.primaryMonitor.width;
_xdndShowOverview: function (actor) {
let [x, y, mask] = global.get_pointer();
let pickedActor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y);
if (pickedActor != this.button) {
Mainloop.source_remove(this.button._xdndTimeOut);
this.button._xdndTimeOut = 0;
return;
}
if(!Main.overview.visible && !Main.overview.animationInProgress) {
Main.overview.showTemporarily();
Main.overview.beginItemDrag(actor);
}
Mainloop.source_remove(this.button._xdndTimeOut);
this.button._xdndTimeOut = 0;
},
_getPreferredHeight: function(actor, forWidth, alloc) {
// We don't need to implement this; it's forced by the CSS
alloc.min_size = -1;
alloc.natural_size = -1;
},
_allocate: function(actor, box, flags) {
let allocWidth = box.x2 - box.x1;
let allocHeight = box.y2 - box.y1;
let [leftMinWidth, leftNaturalWidth] = this._leftBox.get_preferred_width(-1);
let [centerMinWidth, centerNaturalWidth] = this._centerBox.get_preferred_width(-1);
let [rightMinWidth, rightNaturalWidth] = this._rightBox.get_preferred_width(-1);
let sideWidth, centerWidth;
centerWidth = centerNaturalWidth;
sideWidth = (allocWidth - centerWidth) / 2;
let childBox = new Clutter.ActorBox();
childBox.y1 = 0;
childBox.y2 = allocHeight;
if (this.actor.get_direction() == St.TextDirection.RTL) {
childBox.x1 = allocWidth - Math.min(Math.floor(sideWidth),
leftNaturalWidth);
childBox.x2 = allocWidth;
} else {
childBox.x1 = 0;
childBox.x2 = Math.min(Math.floor(sideWidth),
leftNaturalWidth);
}
this._leftBox.allocate(childBox, flags);
childBox.x1 = Math.ceil(sideWidth);
childBox.y1 = 0;
childBox.x2 = childBox.x1 + centerWidth;
childBox.y2 = allocHeight;
this._centerBox.allocate(childBox, flags);
childBox.y1 = 0;
childBox.y2 = allocHeight;
if (this.actor.get_direction() == St.TextDirection.RTL) {
childBox.x1 = 0;
childBox.x2 = Math.min(Math.floor(sideWidth),
rightNaturalWidth);
} else {
childBox.x1 = allocWidth - Math.min(Math.floor(sideWidth),
rightNaturalWidth);
childBox.x2 = allocWidth;
}
this._rightBox.allocate(childBox, flags);
let [cornerMinWidth, cornerWidth] = this._leftCorner.actor.get_preferred_width(-1);
let [cornerMinHeight, cornerHeight] = this._leftCorner.actor.get_preferred_width(-1);
childBox.x1 = 0;
childBox.x2 = cornerWidth;
childBox.y1 = allocHeight;
childBox.y2 = allocHeight + cornerHeight;
this._leftCorner.actor.allocate(childBox, flags);
let [cornerMinWidth, cornerWidth] = this._rightCorner.actor.get_preferred_width(-1);
let [cornerMinHeight, cornerHeight] = this._rightCorner.actor.get_preferred_width(-1);
childBox.x1 = allocWidth - cornerWidth;
childBox.x2 = allocWidth;
childBox.y1 = allocHeight;
childBox.y2 = allocHeight + cornerHeight;
this._rightCorner.actor.allocate(childBox, flags);
// While there can be multiple hotcorners (one per monitor), the hot corner
// that is on top of the Activities button is special since it needs special
// coordination with clicking on that button
setHotCorner: function(corner) {
this._hotCorner = corner;
},
startStatusArea: function() {
for (let i = 0; i < this._tray_icon_order.length; i++) {
let role = this._tray_icon_order[i];
let constructor = this._tray_icon_shell_implementation[role];
for (let i = 0; i < STANDARD_TRAY_ICON_ORDER.length; i++) {
let role = STANDARD_TRAY_ICON_ORDER[i];
let constructor = STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION[role];
if (!constructor) {
// This icon is not implemented (this is a bug)
continue;
}
let indicator = new constructor();
this.addToStatusArea(role, indicator, i);
this._statusBox.add(indicator.actor);
this._menus.addMenu(indicator.menu);
this._statusArea[role] = indicator;
}
// PopupMenuManager depends on menus being added in order for
// keyboard navigation
if (this._userMenu)
this._menus.addMenu(this._userMenu.menu);
this._menus.addMenu(this._userMenu.menu);
},
addToStatusArea: function(role, indicator, position) {
if (this._statusArea[role])
throw new Error('Extension point conflict: there is already a status indicator for role ' + role);
startupAnimation: function() {
let oldY = this.actor.y;
this.actor.y = oldY - this.actor.height;
Tweener.addTween(this.actor,
{ y: oldY,
time: STARTUP_ANIMATION_TIME,
transition: 'easeOutQuad'
});
if (!(indicator instanceof PanelMenu.Button))
throw new TypeError('Status indicator must be an instance of PanelMenu.Button');
let oldCornerY = this._leftCorner.actor.y;
this._leftCorner.actor.y = oldCornerY - this.actor.height;
this._rightCorner.actor.y = oldCornerY - this.actor.height;
Tweener.addTween(this._leftCorner.actor,
{ y: oldCornerY,
time: STARTUP_ANIMATION_TIME,
transition: 'easeOutQuad'
});
Tweener.addTween(this._rightCorner.actor,
{ y: oldCornerY,
time: STARTUP_ANIMATION_TIME,
transition: 'easeOutQuad'
});
},
if (!position)
position = 0;
relayout: function() {
let primary = global.get_primary_monitor();
this._statusBox.insert_actor(indicator.actor, position);
this._menus.addMenu(indicator.menu);
this.actor.set_position(primary.x, primary.y);
this.actor.set_size(primary.width, -1);
this._statusArea[role] = indicator;
let destroyId = indicator.connect('destroy', Lang.bind(this, function(emitter) {
this._statusArea[role] = null;
emitter.disconnect(destroyId);
}));
if (this._leftPointerBarrier)
global.destroy_pointer_barrier(this._leftPointerBarrier);
if (this._rightPointerBarrier)
global.destroy_pointer_barrier(this._rightPointerBarrier);
return indicator;
this._leftPointerBarrier =
global.create_pointer_barrier(primary.x, primary.y,
primary.x, primary.y + this.actor.height,
1 /* BarrierPositiveX */);
this._rightPointerBarrier =
global.create_pointer_barrier(primary.x + primary.width, primary.y,
primary.x + primary.width, primary.y + this.actor.height,
4 /* BarrierNegativeX */);
this._leftCorner.relayout();
this._rightCorner.relayout();
},
_onTrayIconAdded: function(o, icon, role) {
icon.height = PANEL_ICON_SIZE;
if (this._tray_icon_shell_implementation[role]) {
if (STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION[role]) {
// This icon is legacy, and replaced by a Shell version
// Hide it
return;
}
// Figure out the index in our well-known order for this icon
let position = this._tray_icon_order.indexOf(role);
let position = STANDARD_TRAY_ICON_ORDER.indexOf(role);
icon._rolePosition = position;
let children = this._trayBox.get_children();
let i;

View File

@@ -2,7 +2,6 @@
const Clutter = imports.gi.Clutter;
const Gtk = imports.gi.Gtk;
const Signals = imports.signals;
const St = imports.gi.St;
const Lang = imports.lang;
@@ -24,11 +23,11 @@ Button.prototype = {
this.actor._delegate = this;
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
this.actor.connect('key-press-event', Lang.bind(this, this._onSourceKeyPress));
this.menu = new PopupMenu.PopupMenu(this.actor, menuAlignment, St.Side.TOP);
this.menu.actor.add_style_class_name('panel-menu');
this.menu = new PopupMenu.PopupMenu(this.actor, menuAlignment, St.Side.TOP, 0);
this.menu.connect('open-state-changed', Lang.bind(this, this._onOpenStateChanged));
this.menu.actor.connect('key-press-event', Lang.bind(this, this._onMenuKeyPress));
Main.uiGroup.add_actor(this.menu.actor);
Main.chrome.addActor(this.menu.actor, { visibleInOverview: true,
affectsStruts: false });
this.menu.actor.hide();
},
@@ -37,7 +36,7 @@ Button.prototype = {
// Setting the max-height won't do any good if the minimum height of the
// menu is higher then the screen; it's useful if part of the menu is
// scrollable so the minimum height is smaller than the natural height
let monitor = Main.layoutManager.primaryMonitor;
let monitor = global.get_primary_monitor();
this.menu.actor.style = ('max-height: ' +
Math.round(monitor.height - Main.panel.actor.height) +
'px;');
@@ -81,18 +80,8 @@ Button.prototype = {
this.actor.add_style_pseudo_class('active');
else
this.actor.remove_style_pseudo_class('active');
},
destroy: function() {
this.actor._delegate = null;
this.menu.destroy();
this.actor.destroy();
this.emit('destroy');
}
};
Signals.addSignalMethods(Button.prototype);
/* SystemStatusButton:
*

View File

@@ -60,13 +60,13 @@ PlaceInfo.prototype = {
// Helper function to translate launch parameters into a GAppLaunchContext
function _makeLaunchContext(params)
{
params = Params.parse(params, { workspace: -1,
timestamp: 0 });
params = Params.parse(params, { workspace: null,
timestamp: null });
let launchContext = global.create_app_launch_context();
if (params.workspace != -1)
launchContext.set_desktop(params.workspace);
if (params.timestamp != 0)
if (params.workspace != null)
launchContext.set_desktop(params.workspace.index());
if (params.timestamp != null)
launchContext.set_timestamp(params.timestamp);
return launchContext;
@@ -118,9 +118,9 @@ PlaceDeviceInfo.prototype = {
this._mount.unmount_finish(res);
} catch (e) {
let message = _("Failed to unmount '%s'").format(o.get_name());
Main.overview.setMessage(message,
Lang.bind(this, this.remove),
_("Retry"));
Main.overview.shellInfo.setMessage(message,
Lang.bind(this, this.remove),
_("Retry"));
}
}
};
@@ -268,7 +268,10 @@ PlacesManager.prototype = {
if (!GLib.file_test(this._bookmarksPath, GLib.FileTest.EXISTS))
return;
let bookmarksContent = Shell.get_file_contents_utf8_sync(this._bookmarksPath);
let [success, bookmarksContent, len] = GLib.file_get_contents(this._bookmarksPath);
if (!success)
return;
let bookmarks = bookmarksContent.split('\n');

View File

@@ -23,10 +23,10 @@
const Lang = imports.lang;
const Signals = imports.signals;
const Shell = imports.gi.Shell;
const AccountsService = imports.gi.AccountsService;
const Clutter = imports.gi.Clutter;
const St = imports.gi.St;
const Pango = imports.gi.Pango;
const Gdm = imports.gi.Gdm;
const Gio = imports.gi.Gio;
const Mainloop = imports.mainloop;
const Polkit = imports.gi.Polkit;
@@ -92,7 +92,7 @@ AuthenticationDialog.prototype = {
let userName = userNames[0];
this._user = AccountsService.UserManager.get_default().get_user(userName);
this._user = Gdm.UserManager.ref_default().get_user(userName);
let userRealName = this._user.get_real_name()
this._userLoadedId = this._user.connect('notify::is_loaded',
Lang.bind(this, this._onUserChanged));

View File

@@ -15,17 +15,6 @@ const Tweener = imports.ui.tweener;
const SLIDER_SCROLL_STEP = 0.05; /* Slider scrolling step in % */
function _ensureStyle(actor) {
if (actor.get_children) {
let children = actor.get_children();
for (let i = 0; i < children.length; i++)
_ensureStyle(children[i]);
}
if (actor instanceof St.Widget)
actor.ensure_style();
}
function PopupBaseMenuItem(params) {
this._init(params);
}
@@ -715,36 +704,11 @@ PopupSwitchMenuItem.prototype = {
this._switch = new Switch(active);
this.addActor(this.label);
this.addActor(this._switch.actor, { align: St.Align.END });
this._statusBin = new St.Bin({ x_align: St.Align.END });
this.addActor(this._statusBin,
{ expand: true, span: -1, align: St.Align.END });
this._statusLabel = new St.Label({ text: '',
style_class: 'popup-inactive-menu-item'
});
this._statusBin.child = this._switch.actor;
},
setStatus: function(text) {
if (text != null) {
this._statusLabel.text = text;
this._statusBin.child = this._statusLabel;
this.actor.reactive = false;
this.actor.can_focus = false;
} else {
this._statusBin.child = this._switch.actor;
this.actor.reactive = true;
this.actor.can_focus = true;
}
},
activate: function(event) {
if (this._switch.actor.mapped) {
this.connect('activate', Lang.bind(this,function(from) {
this.toggle();
}
PopupBaseMenuItem.prototype.activate.call(this, event);
}));
},
toggle: function() {
@@ -812,58 +776,14 @@ PopupMenuBase.prototype = {
this.passEvents = false;
this._activeMenuItem = null;
this._childMenus = [];
},
addAction: function(title, callback) {
let menuItem = new PopupMenuItem(title);
var menuItem = new PopupMenuItem(title);
this.addMenuItem(menuItem);
menuItem.connect('activate', Lang.bind(this, function (menuItem, event) {
callback(event);
}));
return menuItem;
},
addSettingsAction: function(title, desktopFile) {
// Don't allow user settings to get edited unless we're in a user session
if (global.session_type != Shell.SessionType.USER)
return null;
let menuItem = this.addAction(title, function() {
let app = Shell.AppSystem.get_default().lookup_setting(desktopFile);
if (!app) {
log('Settings panel for desktop file ' + desktopFile + ' could not be loaded!');
return;
}
Main.overview.hide();
app.activate();
});
return menuItem;
},
isChildMenu: function(menu) {
return this._childMenus.indexOf(menu) != -1;
},
addChildMenu: function(menu) {
if (this.isChildMenu(menu))
return;
this._childMenus.push(menu);
this.emit('child-menu-added', menu);
},
removeChildMenu: function(menu) {
let index = this._childMenus.indexOf(menu);
if (index == -1)
return;
this._childMenus.splice(index, 1);
this.emit('child-menu-removed', menu);
},
/**
@@ -916,39 +836,6 @@ PopupMenuBase.prototype = {
}));
},
_updateSeparatorVisibility: function(menuItem) {
let children = this.box.get_children();
let index = children.indexOf(menuItem.actor);
if (index < 0)
return;
let childBeforeIndex = index - 1;
while (childBeforeIndex >= 0 && !children[childBeforeIndex].visible)
childBeforeIndex--;
if (childBeforeIndex < 0
|| children[childBeforeIndex]._delegate instanceof PopupSeparatorMenuItem) {
menuItem.actor.hide();
return;
}
let childAfterIndex = index + 1;
while (childAfterIndex < children.length && !children[childAfterIndex].visible)
childAfterIndex++;
if (childAfterIndex >= children.length
|| children[childAfterIndex]._delegate instanceof PopupSeparatorMenuItem) {
menuItem.actor.hide();
return;
}
menuItem.actor.show();
},
addMenuItem: function(menuItem, position) {
let before_item = null;
if (position == undefined) {
@@ -980,14 +867,6 @@ PopupMenuBase.prototype = {
if (!open)
menuItem.menu.close(false);
});
} else if (menuItem instanceof PopupSeparatorMenuItem) {
this._connectItemSignals(menuItem);
// updateSeparatorVisibility needs to get called any time the
// separator's adjacent siblings change visibility or position.
// open-state-changed isn't exactly that, but doing it in more
// precise ways would require a lot more bookkeeping.
this.connect('open-state-changed', Lang.bind(this, function() { this._updateSeparatorVisibility(menuItem); }));
} else if (menuItem instanceof PopupBaseMenuItem)
this._connectItemSignals(menuItem);
else
@@ -1043,18 +922,6 @@ PopupMenuBase.prototype = {
});
},
get firstMenuItem() {
let items = this._getMenuItems();
if (items.length)
return items[0];
else
return null;
},
get numMenuItems() {
return this._getMenuItems().length;
},
removeAll: function() {
let children = this._getMenuItems();
for (let i = 0; i < children.length; i++) {
@@ -1086,11 +953,12 @@ function PopupMenu() {
PopupMenu.prototype = {
__proto__: PopupMenuBase.prototype,
_init: function(sourceActor, alignment, arrowSide) {
_init: function(sourceActor, alignment, arrowSide, gap) {
PopupMenuBase.prototype._init.call (this, sourceActor, 'popup-menu-content');
this._alignment = alignment;
this._arrowSide = arrowSide;
this._gap = gap;
this._boxPointer = new BoxPointer.BoxPointer(arrowSide,
{ x_fill: true,
@@ -1148,11 +1016,9 @@ PopupMenu.prototype = {
this.isOpen = true;
this._boxPointer.setPosition(this.sourceActor, this._alignment);
this._boxPointer.setPosition(this.sourceActor, this._gap, this._alignment);
this._boxPointer.show(animate);
this.actor.raise_top();
this.emit('open-state-changed', true);
},
@@ -1420,193 +1286,6 @@ PopupSubMenuMenuItem.prototype = {
}
};
function PopupComboMenu() {
this._init.apply(this, arguments);
}
PopupComboMenu.prototype = {
__proto__: PopupMenuBase.prototype,
_init: function(sourceActor) {
PopupMenuBase.prototype._init.call(this,
sourceActor, 'popup-combo-menu');
this.actor = this.box;
this.actor._delegate = this;
this.actor.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
this.actor.connect('key-focus-in', Lang.bind(this, this._onKeyFocusIn));
this._activeItemPos = -1;
global.focus_manager.add_group(this.actor);
},
_onKeyPressEvent: function(actor, event) {
if (event.get_key_symbol() == Clutter.Escape) {
this.close(true);
return true;
}
return false;
},
_onKeyFocusIn: function(actor) {
let items = this._getMenuItems();
let activeItem = items[this._activeItemPos];
activeItem.actor.grab_key_focus();
},
open: function() {
if (this.isOpen)
return;
this.isOpen = true;
let [sourceX, sourceY] = this.sourceActor.get_transformed_position();
let items = this._getMenuItems();
let activeItem = items[this._activeItemPos];
this.actor.set_position(sourceX, sourceY - activeItem.actor.y);
this.actor.width = Math.max(this.actor.width, this.sourceActor.width);
this.actor.raise_top();
this.actor.opacity = 0;
this.actor.show();
Tweener.addTween(this.actor,
{ opacity: 255,
transition: 'linear',
time: BoxPointer.POPUP_ANIMATION_TIME });
this.emit('open-state-changed', true);
},
close: function() {
if (!this.isOpen)
return;
this.isOpen = false;
Tweener.addTween(this.actor,
{ opacity: 0,
transition: 'linear',
time: BoxPointer.POPUP_ANIMATION_TIME,
onComplete: Lang.bind(this,
function() {
this.actor.hide();
})
});
this.emit('open-state-changed', false);
},
setActiveItem: function(position) {
this._activeItemPos = position;
},
setItemVisible: function(position, visible) {
if (!visible && position == this._activeItemPos) {
log('Trying to hide the active menu item.');
return;
}
this._getMenuItems()[position].actor.visible = visible;
}
};
function PopupComboBoxMenuItem() {
this._init.apply(this, arguments);
}
PopupComboBoxMenuItem.prototype = {
__proto__: PopupBaseMenuItem.prototype,
_init: function (params) {
PopupBaseMenuItem.prototype._init.call(this, params);
this._itemBox = new Shell.Stack();
this.addActor(this._itemBox);
let expander = new St.Label({ text: '\u2304' });
this.addActor(expander, { align: St.Align.END });
this._menu = new PopupComboMenu(this.actor);
Main.uiGroup.add_actor(this._menu.actor);
this._menu.actor.hide();
if (params.style_class)
this._menu.actor.add_style_class_name(params.style_class);
this._activeItemPos = -1;
this._items = [];
},
_getTopMenu: function() {
let actor = this.actor.get_parent();
while (actor) {
if (actor._delegate &&
(actor._delegate instanceof PopupMenu ||
actor._delegate instanceof PopupComboMenu))
return actor._delegate;
actor = actor.get_parent();
}
return null;
},
activate: function(event) {
let topMenu = this._getTopMenu();
if (!topMenu)
return;
topMenu.addChildMenu(this._menu);
this._menu.toggle();
},
addMenuItem: function(menuItem, position) {
if (position === undefined)
position = this._menu.numMenuItems;
this._menu.addMenuItem(menuItem, position);
_ensureStyle(this._menu.actor);
let item = new St.BoxLayout({ style_class: 'popup-combobox-item' });
let children = menuItem.actor.get_children();
for (let i = 0; i < children.length; i++) {
let clone = new Clutter.Clone({ source: children[i] });
item.add(clone, { y_fill: false });
}
let oldItem = this._items[position];
if (oldItem)
this._itemBox.remove_actor(oldItem);
this._items[position] = item;
this._itemBox.add_actor(item);
menuItem.connect('activate',
Lang.bind(this, this._itemActivated, position));
},
setActiveItem: function(position) {
let item = this._items[position];
if (!item)
return;
if (this._activeItemPos == position)
return;
this._menu.setActiveItem(position);
this._activeItemPos = position;
for (let i = 0; i < this._items.length; i++)
this._items[i].visible = (i == this._activeItemPos);
},
setItemVisible: function(position, visible) {
this._menu.setItemVisible(position, visible);
},
_itemActivated: function(menuItem, event, position) {
this.setActiveItem(position);
this.emit('active-item-changed', position);
}
};
/* Basic implementation of a menu manager.
* Call addMenu to add menus
@@ -1626,7 +1305,6 @@ PopupMenuManager.prototype = {
this._keyFocusNotifyId = 0;
this._activeMenu = null;
this._menus = [];
this._menuStack = [];
this._preGrabInputMode = null;
this._grabbedFromKeynav = false;
},
@@ -1635,8 +1313,6 @@ PopupMenuManager.prototype = {
let menudata = {
menu: menu,
openStateChangeId: menu.connect('open-state-changed', Lang.bind(this, this._onMenuOpenState)),
childMenuAddedId: menu.connect('child-menu-added', Lang.bind(this, this._onChildMenuAdded)),
childMenuRemovedId: menu.connect('child-menu-removed', Lang.bind(this, this._onChildMenuRemoved)),
destroyId: menu.connect('destroy', Lang.bind(this, this._onMenuDestroy)),
enterId: 0,
focusInId: 0
@@ -1664,8 +1340,6 @@ PopupMenuManager.prototype = {
let menudata = this._menus[position];
menu.disconnect(menudata.openStateChangeId);
menu.disconnect(menudata.childMenuAddedId);
menu.disconnect(menudata.childMenuRemovedId);
menu.disconnect(menudata.destroyId);
if (menudata.enterId)
@@ -1703,20 +1377,8 @@ PopupMenuManager.prototype = {
},
_onMenuOpenState: function(menu, open) {
if (open) {
if (this._activeMenu && this._activeMenu.isChildMenu(menu)) {
this._menuStack.push(this._activeMenu);
menu.actor.grab_key_focus();
}
if (open)
this._activeMenu = menu;
} else {
if (this._menuStack.length > 0) {
this._activeMenu = this._menuStack.pop();
if (menu.sourceActor)
menu.sourceActor.grab_key_focus();
this._didPop = true;
}
}
// Check what the focus was before calling pushModal/popModal
let focus = global.stage.key_focus;
@@ -1749,14 +1411,6 @@ PopupMenuManager.prototype = {
}
},
_onChildMenuAdded: function(menu, childMenu) {
this.addMenu(childMenu);
},
_onChildMenuRemoved: function(menu, childMenu) {
this.removeMenu(childMenu);
},
// change the currently-open menu without dropping grab
_changeMenu: function(newMenu) {
if (this._activeMenu) {
@@ -1765,8 +1419,6 @@ PopupMenuManager.prototype = {
// before closing it to keep that from happening
let oldMenu = this._activeMenu;
this._activeMenu = null;
for (let i = this._menuStack.length - 1; i >= 0; i--)
this._menuStack[i].close(false);
oldMenu.close(false);
newMenu.open(false);
} else
@@ -1777,15 +1429,6 @@ PopupMenuManager.prototype = {
if (!this.grabbed || menu == this._activeMenu)
return false;
if (this._activeMenu && this._activeMenu.isChildMenu(menu))
return false;
if (this._menuStack.indexOf(menu) != -1)
return false;
if (this._menuStack.length > 0 && this._menuStack[0].isChildMenu(menu))
return false;
this._changeMenu(menu);
return false;
},
@@ -1798,8 +1441,6 @@ PopupMenuManager.prototype = {
if (focus) {
if (this._activeMenuContains(focus))
return;
if (this._menuStack.length > 0)
return;
if (focus._delegate && focus._delegate.menu &&
this._findMenu(focus._delegate.menu) != -1)
return;
@@ -1858,11 +1499,6 @@ PopupMenuManager.prototype = {
if (this._activeMenu != null && this._activeMenu.passEvents)
return false;
if (this._didPop) {
this._didPop = false;
return true;
}
let activeMenuContains = this._eventIsOnActiveMenu(event);
let eventType = event.type();

View File

@@ -23,10 +23,6 @@ const HISTORY_KEY = 'command-history';
const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown';
const DISABLE_COMMAND_LINE_KEY = 'disable-command-line';
const TERMINAL_SCHEMA = 'org.gnome.desktop.default-applications.terminal';
const EXEC_KEY = 'exec';
const EXEC_ARG_KEY = 'exec-arg';
const DIALOG_GROW_TIME = 0.1;
function CommandCompleter() {
@@ -173,7 +169,6 @@ __proto__: ModalDialog.ModalDialog.prototype,
ModalDialog.ModalDialog.prototype._init.call(this, { styleClass: 'run-dialog' });
this._lockdownSettings = new Gio.Settings({ schema: LOCKDOWN_SCHEMA });
this._terminalSettings = new Gio.Settings({ schema: TERMINAL_SCHEMA });
global.settings.connect('changed::development-tools', Lang.bind(this, function () {
this._enableInternalCommands = global.settings.get_boolean('development-tools');
}));
@@ -194,7 +189,7 @@ __proto__: ModalDialog.ModalDialog.prototype,
}),
'debugexit': Lang.bind(this, function() {
Meta.quit(Meta.ExitCode.ERROR);
Meta.exit(Meta.ExitCode.ERROR);
}),
// rt is short for "reload theme"
@@ -314,11 +309,8 @@ __proto__: ModalDialog.ModalDialog.prototype,
f();
} else if (input) {
try {
if (inTerminal) {
let exec = this._terminalSettings.get_string(EXEC_KEY);
let exec_arg = this._terminalSettings.get_string(EXEC_ARG_KEY);
command = exec + ' ' + exec_arg + ' ' + input;
}
if (inTerminal)
command = 'gnome-terminal -x ' + input;
Util.trySpawnCommandLine(command);
} catch (e) {
// Mmmh, that failed - see if @input matches an existing file
@@ -333,46 +325,33 @@ __proto__: ModalDialog.ModalDialog.prototype,
if (GLib.file_test(path, GLib.FileTest.EXISTS)) {
let file = Gio.file_new_for_path(path);
try {
Gio.app_info_launch_default_for_uri(file.get_uri(),
global.create_app_launch_context());
} catch (e) {
// The exception from gjs contains an error string like:
// Error invoking Gio.app_info_launch_default_for_uri: No application
// is registered as handling this file
// We are only interested in the part after the first colon.
let message = e.message.replace(/[^:]*: *(.+)/, '$1');
this._showError(message);
}
Gio.app_info_launch_default_for_uri(file.get_uri(),
global.create_app_launch_context());
} else {
this._showError(e.message);
this._commandError = true;
this._errorMessage.set_text(e.message);
if (!this._errorBox.visible) {
let [errorBoxMinHeight, errorBoxNaturalHeight] = this._errorBox.get_preferred_height(-1);
let parentActor = this._errorBox.get_parent();
Tweener.addTween(parentActor,
{ height: parentActor.height + errorBoxNaturalHeight,
time: DIALOG_GROW_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this,
function() {
parentActor.set_height(-1);
this._errorBox.show();
})
});
}
}
}
}
},
_showError : function(message) {
this._commandError = true;
this._errorMessage.set_text(message);
if (!this._errorBox.visible) {
let [errorBoxMinHeight, errorBoxNaturalHeight] = this._errorBox.get_preferred_height(-1);
let parentActor = this._errorBox.get_parent();
Tweener.addTween(parentActor,
{ height: parentActor.height + errorBoxNaturalHeight,
time: DIALOG_GROW_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this,
function() {
parentActor.set_height(-1);
this._errorBox.show();
})
});
}
},
open: function() {
this._history.lastItem();
this._errorBox.hide();

View File

@@ -3,11 +3,10 @@
const DBus = imports.dbus;
const Gio = imports.gi.Gio;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const Main = imports.ui.main;
// This module provides functionality for driving the shell user interface
// in an automated fashion. The primary current use case for this is
// automated performance testing (see runPerfScript()), but it could
@@ -247,14 +246,18 @@ function _collect(scriptModule, outputFile) {
Shell.write_string_to_stream(out, '"events":\n');
Shell.PerfLog.get_default().dump_events(out);
let monitors = Main.layoutManager.monitors;
let primary = Main.layoutManager.primaryIndex;
let monitors = global.get_monitors();
let primary = global.get_primary_monitor();
Shell.write_string_to_stream(out, ',\n"monitors":\n[');
for (let i = 0; i < monitors.length; i++) {
let monitor = monitors[i];
let is_primary = (monitor.x == primary.x &&
monitor.y == primary.y &&
monitor.width == primary.width &&
monitor.height == primary.height);
if (i != 0)
Shell.write_string_to_stream(out, ', ');
Shell.write_string_to_stream(out, '"%s%dx%d+%d+%d"'.format(i == primary ? "*" : "",
Shell.write_string_to_stream(out, '"%s%dx%d+%d+%d"'.format(is_primary ? "*" : "",
monitor.width, monitor.height,
monitor.x, monitor.y));
}

View File

@@ -112,43 +112,6 @@ function SearchProvider(title) {
SearchProvider.prototype = {
_init: function(title) {
this.title = title;
this.searchSystem = null;
this.searchAsync = false;
},
_asyncCancelled: function() {
},
startAsync: function() {
this.searchAsync = true;
},
tryCancelAsync: function() {
if (!this.searchAsync)
return;
this._asyncCancelled();
this.searchAsync = false;
},
/**
* addItems:
* @items: an array of result identifier strings representing
* items which match the last given search terms.
*
* This should be used for something that requires a bit more
* logic; it's designed to be an asyncronous way to add a result
* to the current search.
*/
addItems: function(items) {
if (!this.searchSystem)
throw new Error('Search provider not registered');
if (!items.length)
return;
this.tryCancelAsync();
this.searchSystem.addProviderItems(this, items);
},
/**
@@ -194,7 +157,7 @@ SearchProvider.prototype = {
},
/**
* getResultMeta:
* getResultInfo:
* @id: Result identifier string
*
* Return an object with 'id', 'name', (both strings) and 'createIcon'
@@ -352,7 +315,6 @@ SearchSystem.prototype = {
},
registerProvider: function (provider) {
provider.searchSystem = this;
this._providers.push(provider);
},
@@ -369,23 +331,12 @@ SearchSystem.prototype = {
this._previousResults = [];
},
addProviderItems: function(provider, items) {
this.emit('search-updated', provider, items);
},
updateSearch: function(searchString) {
searchString = searchString.replace(/^\s+/g, '').replace(/\s+$/g, '');
if (searchString == '')
return;
return [];
let terms = searchString.split(/\s+/);
this.updateSearchResults(terms);
},
updateSearchResults: function(terms) {
if (!terms)
return;
let isSubSearch = terms.length == this._previousTerms.length;
if (isSubSearch) {
for (let i = 0; i < terms.length; i++) {
@@ -398,12 +349,12 @@ SearchSystem.prototype = {
let results = [];
if (isSubSearch) {
for (let i = 0; i < this._providers.length; i++) {
for (let i = 0; i < this._previousResults.length; i++) {
let [provider, previousResults] = this._previousResults[i];
provider.tryCancelAsync();
try {
let providerResults = provider.getSubsearchResultSet(previousResults, terms);
results.push([provider, providerResults]);
if (providerResults.length > 0)
results.push([provider, providerResults]);
} catch (error) {
global.log ('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message);
}
@@ -411,10 +362,10 @@ SearchSystem.prototype = {
} else {
for (let i = 0; i < this._providers.length; i++) {
let provider = this._providers[i];
provider.tryCancelAsync();
try {
let providerResults = provider.getInitialResultSet(terms);
results.push([provider, providerResults]);
if (providerResults.length > 0)
results.push([provider, providerResults]);
} catch (error) {
global.log ('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message);
}
@@ -423,7 +374,8 @@ SearchSystem.prototype = {
this._previousTerms = terms;
this._previousResults = results;
this.emit('search-completed', results);
},
return results;
}
};
Signals.addSignalMethods(SearchSystem.prototype);

View File

@@ -28,7 +28,6 @@ SearchResult.prototype = {
x_align: St.Align.START,
y_fill: true });
this.actor._delegate = this;
this._dragActorSource = null;
let content = provider.createResultActor(metaInfo, terms);
if (content == null) {
@@ -38,11 +37,7 @@ SearchResult.prototype = {
let icon = new IconGrid.BaseIcon(this.metaInfo['name'],
{ createIcon: this.metaInfo['createIcon'] });
content.set_child(icon.actor);
this._dragActorSource = icon.icon;
this.actor.label_actor = icon.label;
} else {
if (content._delegate && content._delegate.getDragActorSource)
this._dragActorSource = content._delegate.getDragActorSource();
}
this._content = content;
this.actor.set_child(content);
@@ -81,14 +76,12 @@ SearchResult.prototype = {
},
getDragActorSource: function() {
if (this._dragActorSource)
return this._dragActorSource;
// not exactly right, but alignment problems are hard to notice
return this._content;
},
getDragActor: function(stageX, stageY) {
return this.metaInfo['createIcon'](Main.overview.dashIconSize);
return this.metaInfo['createIcon'](Main.overview.dash.iconSize);
},
shellWorkspaceLaunch: function(params) {
@@ -100,17 +93,17 @@ SearchResult.prototype = {
};
function GridSearchResults(provider, grid) {
this._init(provider, grid);
function GridSearchResults(provider) {
this._init(provider);
}
GridSearchResults.prototype = {
__proto__: Search.SearchResultDisplay.prototype,
_init: function(provider, grid) {
_init: function(provider) {
Search.SearchResultDisplay.prototype._init.call(this, provider);
this._grid = grid || new IconGrid.IconGrid({ rowLimit: MAX_SEARCH_RESULTS_ROWS,
xAlign: St.Align.START });
this._grid = new IconGrid.IconGrid({ rowLimit: MAX_SEARCH_RESULTS_ROWS,
xAlign: St.Align.START });
this.actor = new St.Bin({ x_align: St.Align.START });
this.actor.set_child(this._grid.actor);
@@ -189,8 +182,6 @@ function SearchResults(searchSystem, openSearchSystem) {
SearchResults.prototype = {
_init: function(searchSystem, openSearchSystem) {
this._searchSystem = searchSystem;
this._searchSystem.connect('search-updated', Lang.bind(this, this._updateCurrentResults));
this._searchSystem.connect('search-completed', Lang.bind(this, this._updateResults));
this._openSearchSystem = openSearchSystem;
this.actor = new St.BoxLayout({ name: 'searchResults',
@@ -201,7 +192,7 @@ SearchResults.prototype = {
let scrollView = new St.ScrollView({ x_fill: true,
y_fill: false,
style_class: 'vfade' });
vfade: true });
scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
scrollView.add_actor(this._content);
@@ -225,11 +216,9 @@ SearchResults.prototype = {
this._selectedProvider = -1;
this._providers = this._searchSystem.getProviders();
this._providerMeta = [];
this._providerMetaResults = {};
for (let i = 0; i < this._providers.length; i++) {
for (let i = 0; i < this._providers.length; i++)
this.createProviderMeta(this._providers[i]);
this._providerMetaResults[this.providers[i].title] = [];
}
this._searchProvidersBox = new St.BoxLayout({ style_class: 'search-providers-box' });
this.actor.add(this._searchProvidersBox);
@@ -309,12 +298,6 @@ SearchResults.prototype = {
}
},
_clearDisplayForProvider: function(index) {
let meta = this._providerMeta[index];
meta.resultDisplay.clear();
meta.actor.hide();
},
reset: function() {
this._searchSystem.reset();
this._statusText.hide();
@@ -329,24 +312,15 @@ SearchResults.prototype = {
this._statusText.show();
},
doSearch: function (searchString) {
this._searchSystem.updateSearch(searchString);
},
_metaForProvider: function(provider) {
return this._providerMeta[this._providers.indexOf(provider)];
},
_updateCurrentResults: function(searchSystem, provider, results) {
let terms = searchSystem.getTerms();
let meta = this._metaForProvider(provider);
meta.resultDisplay.clear();
meta.actor.show();
meta.resultDisplay.renderResults(results, terms);
return true;
},
updateSearch: function (searchString) {
let results = this._searchSystem.updateSearch(searchString);
this._clearDisplay();
_updateResults: function(searchSystem, results) {
if (results.length == 0) {
this._statusText.set_text(_("No matching results."));
this._statusText.show();
@@ -356,7 +330,7 @@ SearchResults.prototype = {
this._statusText.hide();
}
let terms = searchSystem.getTerms();
let terms = this._searchSystem.getTerms();
this._openSearchSystem.setSearchTerms(terms);
// To avoid CSS transitions causing flickering
@@ -368,15 +342,9 @@ SearchResults.prototype = {
for (let i = 0; i < results.length; i++) {
let [provider, providerResults] = results[i];
if (providerResults.length == 0) {
this._clearDisplayForProvider(i);
} else {
this._providerMetaResults[provider.title] = providerResults;
this._clearDisplayForProvider(i);
let meta = this._metaForProvider(provider);
meta.actor.show();
meta.resultDisplay.renderResults(providerResults, terms);
}
let meta = this._metaForProvider(provider);
meta.actor.show();
meta.resultDisplay.renderResults(providerResults, terms);
}
if (this._selectedOpenSearchButton == -1)

View File

@@ -1,10 +1,7 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const DBus = imports.dbus;
const Lang = imports.lang;
const Config = imports.misc.config;
const ExtensionSystem = imports.ui.extensionSystem;
const Main = imports.ui.main;
const GnomeShellIface = {
@@ -12,55 +9,12 @@ const GnomeShellIface = {
methods: [{ name: 'Eval',
inSignature: 's',
outSignature: 'bs'
},
{ name: 'ListExtensions',
inSignature: '',
outSignature: 'a{sa{sv}}'
},
{ name: 'GetExtensionInfo',
inSignature: 's',
outSignature: 'a{sv}'
},
{ name: 'GetExtensionErrors',
inSignature: 's',
outSignature: 'as'
},
{ name: 'ScreenshotArea',
inSignature: 'iiiis',
outSignature: 'b'
},
{ name: 'ScreenshotWindow',
inSignature: 'bs',
outSignature: 'b'
},
{ name: 'Screenshot',
inSignature: 's',
outSignature: 'b'
},
{ name: 'EnableExtension',
inSignature: 's',
outSignature: ''
},
{ name: 'DisableExtension',
inSignature: 's',
outSignature: ''
},
{ name: 'InstallRemoteExtension',
inSignature: 's',
outSignature: ''
}
],
signals: [{ name: 'ExtensionStatusChanged',
inSignature: 'sis' }],
signals: [],
properties: [{ name: 'OverviewActive',
signature: 'b',
access: 'readwrite' },
{ name: 'ApiVersion',
signature: 'i',
access: 'read' },
{ name: 'ShellVersion',
signature: 's',
access: 'read' }]
access: 'readwrite' }]
};
function GnomeShell() {
@@ -70,8 +24,6 @@ function GnomeShell() {
GnomeShell.prototype = {
_init: function() {
DBus.session.exportObject('/org/gnome/Shell', this);
ExtensionSystem.connect('extension-state-changed',
Lang.bind(this, this._extensionStateChanged));
},
/**
@@ -104,80 +56,6 @@ GnomeShell.prototype = {
return [success, returnValue];
},
/**
* ScreenshotArea:
* @x: The X coordinate of the area
* @y: The Y coordinate of the area
* @width: The width of the area
* @height: The height of the area
* @filename: The filename for the screenshot
*
* Takes a screenshot of the passed in area and saves it
* in @filename as png image, it returns a boolean
* indicating whether the operation was successful or not.
*
*/
ScreenshotAreaAsync : function (x, y, width, height, filename, callback) {
global.screenshot_area (x, y, width, height, filename, function (obj, result) { callback(result); });
},
/**
* ScreenshotWindow:
* @include_frame: Whether to include the frame or not
* @filename: The filename for the screenshot
*
* Takes a screenshot of the focused window (optionally omitting the frame)
* and saves it in @filename as png image, it returns a boolean
* indicating whether the operation was successful or not.
*
*/
ScreenshotWindow : function (include_frame, filename) {
return global.screenshot_window (include_frame, filename);
},
/**
* Screenshot:
* @filename: The filename for the screenshot
*
* Takes a screenshot of the whole screen and saves it
* in @filename as png image, it returns a boolean
* indicating whether the operation was successful or not.
*
*/
ScreenshotAsync : function (filename, callback) {
global.screenshot(filename, function (obj, result) { callback(result); });
},
ListExtensions: function() {
return ExtensionSystem.extensionMeta;
},
GetExtensionInfo: function(uuid) {
return ExtensionSystem.extensionMeta[uuid] || {};
},
GetExtensionErrors: function(uuid) {
return ExtensionSystem.errors[uuid] || [];
},
EnableExtension: function(uuid) {
let enabledExtensions = global.settings.get_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY);
if (enabledExtensions.indexOf(uuid) == -1)
enabledExtensions.push(uuid);
global.settings.set_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY, enabledExtensions);
},
DisableExtension: function(uuid) {
let enabledExtensions = global.settings.get_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY);
while (enabledExtensions.indexOf(uuid) != -1)
enabledExtensions.splice(enabledExtensions.indexOf(uuid), 1);
global.settings.set_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY, enabledExtensions);
},
InstallRemoteExtension: function(uuid, url) {
ExtensionSystem.installExtensionFromManifestURL(uuid, url);
},
get OverviewActive() {
return Main.overview.visible;
},
@@ -187,17 +65,6 @@ GnomeShell.prototype = {
Main.overview.show();
else
Main.overview.hide();
},
ApiVersion: 1,
ShellVersion: Config.PACKAGE_VERSION,
_extensionStateChanged: function(_, newState) {
DBus.session.emit_signal('/org/gnome/Shell',
'org.gnome.Shell',
'ExtensionStatusChanged', 'sis',
[newState.uuid, newState.state, newState.error]);
}
};

View File

@@ -1,405 +0,0 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Lang = imports.lang;
const Signals = imports.signals;
const Gio = imports.gi.Gio;
const Gtk = imports.gi.Gtk;
const Pango = imports.gi.Pango;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const ModalDialog = imports.ui.modalDialog;
const Params = imports.misc.params;
const LIST_ITEM_ICON_SIZE = 48;
/* ------ Common Utils ------- */
function _setLabelText(label, text) {
if (text) {
label.set_text(text);
label.show();
} else {
label.set_text('');
label.hide();
}
}
function _setButtonsForChoices(dialog, choices) {
let buttons = [];
for (let idx = 0; idx < choices.length; idx++) {
let button = idx;
buttons.unshift({ label: choices[idx],
action: Lang.bind(dialog, function() {
dialog.emit('response', button);
})});
}
dialog.setButtons(buttons);
}
function _setLabelsForMessage(dialog, message) {
let labels = message.split('\n');
_setLabelText(dialog.subjectLabel, labels[0]);
if (labels.length > 1)
_setLabelText(dialog.descriptionLabel, labels[1]);
}
/* -------------------------------------------------------- */
function ListItem(app) {
this._init(app);
}
ListItem.prototype = {
_init: function(app) {
this._app = app;
let layout = new St.BoxLayout({ vertical: false});
this.actor = new St.Button({ style_class: 'show-processes-dialog-app-list-item',
can_focus: true,
child: layout,
reactive: true,
x_align: St.Align.START,
x_fill: true });
this._icon = this._app.create_icon_texture(LIST_ITEM_ICON_SIZE);
let iconBin = new St.Bin({ style_class: 'show-processes-dialog-app-list-item-icon',
child: this._icon });
layout.add(iconBin);
this._nameLabel = new St.Label({ text: this._app.get_name(),
style_class: 'show-processes-dialog-app-list-item-name' });
let labelBin = new St.Bin({ y_align: St.Align.MIDDLE,
child: this._nameLabel });
layout.add(labelBin);
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
},
_onClicked: function() {
this.emit('activate');
this._app.activate();
}
};
Signals.addSignalMethods(ListItem.prototype);
function ShellMountOperation(source, params) {
this._init(source, params);
}
ShellMountOperation.prototype = {
_init: function(source, params) {
params = Params.parse(params, { reaskPassword: false });
this._reaskPassword = params.reaskPassword;
this._dialog = null;
this._processesDialog = null;
this.mountOp = new Shell.MountOperation();
this.mountOp.connect('ask-question',
Lang.bind(this, this._onAskQuestion));
this.mountOp.connect('ask-password',
Lang.bind(this, this._onAskPassword));
this.mountOp.connect('show-processes-2',
Lang.bind(this, this._onShowProcesses2));
this.mountOp.connect('aborted',
Lang.bind(this, this._onAborted));
this._icon = new St.Icon({ gicon: source.get_icon(),
style_class: 'shell-mount-operation-icon' });
},
_onAskQuestion: function(op, message, choices) {
this._dialog = new ShellMountQuestionDialog(this._icon);
this._dialog.connect('response',
Lang.bind(this, function(object, choice) {
this.mountOp.set_choice(choice);
this.mountOp.reply(Gio.MountOperationResult.HANDLED);
this._dialog.close(global.get_current_time());
this._dialog = null;
}));
this._dialog.update(message, choices);
this._dialog.open(global.get_current_time());
},
_onAskPassword: function(op, message) {
this._notificationShowing = true;
this._source = new ShellMountPasswordSource(message, this._icon, this._reaskPassword);
this._source.connect('password-ready',
Lang.bind(this, function(source, password) {
this.mountOp.set_password(password);
this.mountOp.reply(Gio.MountOperationResult.HANDLED);
this._notificationShowing = false;
this._source.destroy();
}));
this._source.connect('destroy',
Lang.bind(this, function() {
if (!this._notificationShowing)
return;
this._notificationShowing = false;
this.mountOp.reply(Gio.MountOperationResult.ABORTED);
}));
},
_onAborted: function(op) {
if (!this._dialog)
return;
this._dialog.close(global.get_current_time());
this._dialog = null;
},
_onShowProcesses2: function(op) {
let processes = op.get_show_processes_pids();
let choices = op.get_show_processes_choices();
let message = op.get_show_processes_message();
if (!this._processesDialog) {
this._processesDialog = new ShellProcessesDialog(this._icon);
this._dialog = this._processesDialog;
this._processesDialog.connect('response',
Lang.bind(this, function(object, choice) {
if (choice == -1) {
this.mountOp.reply(Gio.MountOperationResult.ABORTED);
} else {
this.mountOp.set_choice(choice);
this.mountOp.reply(Gio.MountOperationResult.HANDLED);
}
this._processesDialog.close(global.get_current_time());
this._dialog = null;
}));
this._processesDialog.open(global.get_current_time());
}
this._processesDialog.update(message, processes, choices);
},
}
function ShellMountQuestionDialog(icon) {
this._init(icon);
}
ShellMountQuestionDialog.prototype = {
__proto__: ModalDialog.ModalDialog.prototype,
_init: function(icon) {
ModalDialog.ModalDialog.prototype._init.call(this, { styleClass: 'mount-question-dialog' });
let mainContentLayout = new St.BoxLayout();
this.contentLayout.add(mainContentLayout, { x_fill: true,
y_fill: false });
this._iconBin = new St.Bin({ child: icon });
mainContentLayout.add(this._iconBin,
{ x_fill: true,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.MIDDLE });
let messageLayout = new St.BoxLayout({ vertical: true });
mainContentLayout.add(messageLayout,
{ y_align: St.Align.START });
this.subjectLabel = new St.Label({ style_class: 'mount-question-dialog-subject' });
messageLayout.add(this.subjectLabel,
{ y_fill: false,
y_align: St.Align.START });
this.descriptionLabel = new St.Label({ style_class: 'mount-question-dialog-description' });
this.descriptionLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
this.descriptionLabel.clutter_text.line_wrap = true;
messageLayout.add(this.descriptionLabel,
{ y_fill: true,
y_align: St.Align.START });
},
update: function(message, choices) {
_setLabelsForMessage(this, message);
_setButtonsForChoices(this, choices);
}
}
Signals.addSignalMethods(ShellMountQuestionDialog.prototype);
function ShellMountPasswordSource(message, icon, reaskPassword) {
this._init(message, icon, reaskPassword);
}
ShellMountPasswordSource.prototype = {
__proto__: MessageTray.Source.prototype,
_init: function(message, icon, reaskPassword) {
let strings = message.split('\n');
MessageTray.Source.prototype._init.call(this, strings[0]);
this._notification = new ShellMountPasswordNotification(this, strings, icon, reaskPassword);
// add ourselves as a source, and popup the notification
Main.messageTray.add(this);
this.notify(this._notification);
},
}
Signals.addSignalMethods(ShellMountPasswordSource.prototype);
function ShellMountPasswordNotification(source, strings, icon, reaskPassword) {
this._init(source, strings, icon, reaskPassword);
}
ShellMountPasswordNotification.prototype = {
__proto__: MessageTray.Notification.prototype,
_init: function(source, strings, icon, reaskPassword) {
MessageTray.Notification.prototype._init.call(this, source,
strings[0], null,
{ customContent: true,
icon: icon });
// set the notification to transient and urgent, so that it
// expands out
this.setTransient(true);
this.setUrgency(MessageTray.Urgency.CRITICAL);
if (strings[1])
this.addBody(strings[1]);
if (reaskPassword) {
let label = new St.Label({ style_class: 'mount-password-reask',
text: _("Wrong password, please try again") });
this.addActor(label);
}
this._responseEntry = new St.Entry({ style_class: 'mount-password-entry',
can_focus: true });
this.setActionArea(this._responseEntry);
this._responseEntry.clutter_text.connect('activate',
Lang.bind(this, this._onEntryActivated));
this._responseEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
this._responseEntry.grab_key_focus();
},
_onEntryActivated: function() {
let text = this._responseEntry.get_text();
if (text == '')
return;
this.source.emit('password-ready', text);
}
}
function ShellProcessesDialog(icon) {
this._init(icon);
}
ShellProcessesDialog.prototype = {
__proto__: ModalDialog.ModalDialog.prototype,
_init: function(icon) {
ModalDialog.ModalDialog.prototype._init.call(this, { styleClass: 'show-processes-dialog' });
let mainContentLayout = new St.BoxLayout();
this.contentLayout.add(mainContentLayout, { x_fill: true,
y_fill: false });
this._iconBin = new St.Bin({ child: icon });
mainContentLayout.add(this._iconBin,
{ x_fill: true,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.MIDDLE });
let messageLayout = new St.BoxLayout({ vertical: true });
mainContentLayout.add(messageLayout,
{ y_align: St.Align.START });
this.subjectLabel = new St.Label({ style_class: 'show-processes-dialog-subject' });
messageLayout.add(this.subjectLabel,
{ y_fill: false,
y_align: St.Align.START });
this.descriptionLabel = new St.Label({ style_class: 'show-processes-dialog-description' });
this.descriptionLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
this.descriptionLabel.clutter_text.line_wrap = true;
messageLayout.add(this.descriptionLabel,
{ y_fill: true,
y_align: St.Align.START });
let scrollView = new St.ScrollView({ style_class: 'show-processes-dialog-app-list'});
scrollView.set_policy(Gtk.PolicyType.NEVER,
Gtk.PolicyType.AUTOMATIC);
this.contentLayout.add(scrollView,
{ x_fill: true,
y_fill: true });
scrollView.hide();
this._applicationList = new St.BoxLayout({ vertical: true });
scrollView.add_actor(this._applicationList,
{ x_fill: true,
y_fill: true,
x_align: St.Align.START,
y_align: St.Align.MIDDLE });
this._applicationList.connect('actor-added',
Lang.bind(this, function() {
if (this._applicationList.get_children().length == 1)
scrollView.show();
}));
this._applicationList.connect('actor-removed',
Lang.bind(this, function() {
if (this._applicationList.get_children().length == 0)
scrollView.hide();
}));
},
_setAppsForPids: function(pids) {
// remove all the items
this._applicationList.destroy_children();
pids.forEach(Lang.bind(this, function(pid) {
let tracker = Shell.WindowTracker.get_default();
let app = tracker.get_app_from_pid(pid);
if (!app)
return;
let item = new ListItem(app);
this._applicationList.add(item.actor, { x_fill: true });
item.connect('activate',
Lang.bind(this, function() {
// use -1 to indicate Cancel
this.emit('response', -1);
}));
}));
},
update: function(message, processes, choices) {
this._setAppsForPids(processes);
_setLabelsForMessage(this, message);
_setButtonsForChoices(this, choices);
}
}
Signals.addSignalMethods(ShellProcessesDialog.prototype);

View File

@@ -10,7 +10,6 @@ const Shell = imports.gi.Shell;
const Signals = imports.signals;
const St = imports.gi.St;
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const Util = imports.misc.util;
@@ -88,7 +87,10 @@ ATIndicator.prototype = {
this.menu.addMenuItem(mouseKeys);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addSettingsAction(_("Universal Access Settings"), 'gnome-universal-access-panel.desktop');
this.menu.addAction(_("Universal Access Settings"), function() {
let app = Shell.AppSystem.get_default().get_app('gnome-universal-access-panel.desktop');
app.activate(-1);
});
},
_buildItemExtended: function(string, initial_value, writable, on_set) {

View File

@@ -67,6 +67,7 @@ Indicator.prototype = {
new PopupMenu.PopupMenuItem(_("Set up a New Device...")),
new PopupMenu.PopupSeparatorMenuItem()];
this._hasDevices = false;
this._deviceSep = this._fullMenuItems[0]; // hidden if no device exists
this._fullMenuItems[1].connect('activate', function() {
GLib.spawn_command_line_async('bluetooth-sendto');
@@ -88,7 +89,10 @@ Indicator.prototype = {
this._applet.connect('notify::show-full-menu', Lang.bind(this, this._updateFullMenu));
this._updateFullMenu();
this.menu.addSettingsAction(_("Bluetooth Settings"), 'bluetooth-properties.desktop');
this.menu.addAction(_("Bluetooth Settings"), function() {
let app = Shell.AppSystem.get_default().get_app('bluetooth-properties.desktop');
app.activate(-1);
});
this._applet.connect('pincode-request', Lang.bind(this, this._pinRequest));
this._applet.connect('confirm-request', Lang.bind(this, this._confirmRequest));
@@ -104,11 +108,7 @@ Indicator.prototype = {
current_state != GnomeBluetoothApplet.KillswitchState.HARD_BLOCKED;
this._killswitch.setToggleState(on);
if (can_toggle)
this._killswitch.setStatus(null);
else
/* TRANSLATORS: this means that bluetooth was disabled by hardware rfkill */
this._killswitch.setStatus(_("hardware disabled"));
this._killswitch.actor.reactive = can_toggle;
if (has_adapter)
this.actor.show();
@@ -157,6 +157,10 @@ Indicator.prototype = {
this._hasDevices = true;
}
}
if (this._hasDevices)
this._deviceSep.actor.show();
else
this._deviceSep.actor.hide();
},
_updateDeviceItem: function(item, device) {
@@ -206,15 +210,14 @@ Indicator.prototype = {
if (device.can_connect) {
item._connected = device.connected;
item._connectedMenuitem = new PopupMenu.PopupSwitchMenuItem(_("Connection"), device.connected);
item._connectedMenuitem.connect('toggled', Lang.bind(this, function() {
if (item._connected > ConnectionState.CONNECTED) {
// operation already in progress, revert
// (should not happen anyway)
menuitem.setToggleState(menuitem.state);
}
if (item._connected) {
item._connected = ConnectionState.DISCONNECTING;
menuitem.setStatus(_("disconnecting..."));
this._applet.disconnect_device(item._device.device_path, function(applet, success) {
if (success) { // apply
item._connected = ConnectionState.DISCONNECTED;
@@ -223,11 +226,9 @@ Indicator.prototype = {
item._connected = ConnectionState.CONNECTED;
menuitem.setToggleState(true);
}
menuitem.setStatus(null);
});
} else {
item._connected = ConnectionState.CONNECTING;
menuitem.setStatus(_("connecting..."));
this._applet.connect_device(item._device.device_path, function(applet, success) {
if (success) { // apply
item._connected = ConnectionState.CONNECTED;
@@ -236,7 +237,6 @@ Indicator.prototype = {
item._connected = ConnectionState.DISCONNECTED;
menuitem.setToggleState(false);
}
menuitem.setStatus(null);
});
}
}));
@@ -268,15 +268,21 @@ Indicator.prototype = {
switch (device.type) {
case GnomeBluetoothApplet.Type.KEYBOARD:
item.menu.addSettingsAction(_("Keyboard Settings"), 'gnome-keyboard-panel.desktop');
item.menu.addAction(_("Keyboard Settings"), function() {
GLib.spawn_command_line_async('gnome-control-center keyboard');
});
break;
case GnomeBluetoothApplet.Type.MOUSE:
item.menu.addSettingsAction(_("Mouse Settings"), 'gnome-mouse-panel.desktop');
item.menu.addAction(_("Mouse Settings"), function() {
GLib.spawn_command_line_async('gnome-control-center mouse');
});
break;
case GnomeBluetoothApplet.Type.HEADSET:
case GnomeBluetoothApplet.Type.HEADPHONES:
case GnomeBluetoothApplet.Type.OTHER_AUDIO:
item.menu.addSettingsAction(_("Sound Settings"), 'gnome-sound-panel.desktop');
item.menu.addAction(_("Sound Settings"), function() {
GLib.spawn_command_line_async('gnome-control-center sound');
});
break;
default:
break;
@@ -288,6 +294,8 @@ Indicator.prototype = {
this._showAll(this._fullMenuItems);
if (this._hasDevices)
this._showAll(this._deviceItems);
else
this._deviceSep.actor.hide();
} else {
this._hideAll(this._fullMenuItems);
this._hideAll(this._deviceItems);

View File

@@ -9,7 +9,6 @@ const Lang = imports.lang;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Main = imports.ui.main;
const PopupMenu = imports.ui.popupMenu;
const PanelMenu = imports.ui.panelMenu;
const Util = imports.misc.util;
@@ -68,33 +67,13 @@ XKBIndicator.prototype = {
this._sync_config();
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addAction(_("Show Keyboard Layout"), Lang.bind(this, function() {
Main.overview.hide();
this.menu.addAction(_("Show Keyboard Layout..."), Lang.bind(this, function() {
Util.spawn(['gkbd-keyboard-display', '-g', String(this._config.get_current_group() + 1)]);
}));
this.menu.addSettingsAction(_("Region and Language Settings"), 'gnome-region-panel.desktop');
},
_adjust_group_names: function(names) {
// Disambiguate duplicate names with a subscript
// This is O(N^2) to avoid sorting names
// but N <= 4 so who cares?
for (let i = 0; i < names.length; i++) {
let name = names[i];
let cnt = 0;
for (let j = i + 1; j < names.length; j++) {
if (names[j] == name) {
cnt++;
// U+2081 SUBSCRIPT ONE
names[j] = name + String.fromCharCode(0x2081 + cnt);
}
}
if (cnt != 0)
names[i] = name + '\u2081';
}
return names;
this.menu.addAction(_("Localization Settings"), function() {
let app = Shell.AppSystem.get_default().get_app('gnome-region-panel.desktop');
app.activate(-1);
});
},
_sync_config: function() {
@@ -119,7 +98,7 @@ XKBIndicator.prototype = {
for (let i = 0; i < this._labelActors.length; i++)
this._labelActors[i].destroy();
let short_names = this._adjust_group_names(this._config.get_short_group_names());
let short_names = this._config.get_short_group_names();
this._selectedLayout = null;
this._layoutItems = [ ];

View File

@@ -45,6 +45,17 @@ const NM80211ApSecurityFlags = NetworkManager['80211ApSecurityFlags'];
// (the remaining are placed into More...)
const NUM_VISIBLE_NETWORKS = 5;
const NMAppletHelperInterface = {
name: 'org.gnome.network_manager_applet',
methods: [
{ name: 'ConnectToHiddenNetwork', inSignature: '', outSignature: '' },
{ name: 'CreateWifiNetwork', inSignature: '', outSignature: '' },
{ name: 'ConnectTo8021xNetwork', inSignature: 'oo', outSignature: '' },
{ name: 'ConnectTo3gNetwork', inSignature: 'o', outSignature: '' }
],
};
const NMAppletProxy = DBus.makeProxyClass(NMAppletHelperInterface);
function macToArray(string) {
return string.split(':').map(function(el) {
return parseInt(el, 16);
@@ -91,13 +102,6 @@ function sortAccessPoints(accessPoints) {
});
}
function ssidToLabel(ssid) {
let label = NetworkManager.utils_ssid_to_utf8(ssid);
if (!label)
label = _("<unknown>");
return label;
}
function NMNetworkMenuItem() {
this._init.apply(this, arguments);
}
@@ -113,7 +117,10 @@ NMNetworkMenuItem.prototype = {
if (!title) {
let ssid = this.bestAP.get_ssid();
title = ssidToLabel(ssid);
if (ssid)
title = NetworkManager.utils_ssid_to_utf8(ssid);
if (!title)
title = _("<unknown>");
}
this._label = new St.Label({ text: title });
@@ -185,7 +192,63 @@ NMNetworkMenuItem.prototype = {
apObj.updateId = 0;
}
PopupMenu.PopupBaseMenuItem.prototype.destroy.call(this);
PopupMenu.PopupImageMenuItem.prototype.destroy.call(this);
}
};
function NMDeviceTitleMenuItem() {
this._init.apply(this, arguments);
}
NMDeviceTitleMenuItem.prototype = {
__proto__: PopupMenu.PopupBaseMenuItem.prototype,
_init: function(description, params) {
PopupMenu.PopupBaseMenuItem.prototype._init.call(this, params);
this._descriptionLabel = new St.Label({ text: description,
style_class: 'popup-subtitle-menu-item'
});
this.addActor(this._descriptionLabel);
this._statusBin = new St.Bin({ x_align: St.Align.END });
this.addActor(this._statusBin, { align: St.Align.END });
this._statusLabel = new St.Label({ text: '',
style_class: 'popup-inactive-menu-item'
});
this._switch = new PopupMenu.Switch(false);
this._statusBin.child = this._switch.actor;
},
setStatus: function(text) {
if (text != null) {
this._statusLabel.text = text;
this._statusBin.child = this._statusLabel;
this.actor.reactive = false;
this.actor.can_focus = false;
} else {
this._statusBin.child = this._switch.actor;
this.actor.reactive = true;
this.actor.can_focus = true;
}
},
activate: function(event) {
if (this._switch.actor.mapped) {
this._switch.toggle();
this.emit('toggled', this._switch.state);
}
PopupMenu.PopupBaseMenuItem.prototype.activate.call(this, event);
},
get state() {
return this._switch.state;
},
setToggleState: function(newval) {
this._switch.setToggleState(newval);
}
};
@@ -194,13 +257,7 @@ function NMWiredSectionTitleMenuItem() {
}
NMWiredSectionTitleMenuItem.prototype = {
__proto__: PopupMenu.PopupSwitchMenuItem.prototype,
_init: function(label, params) {
params = params || { };
params.style_class = 'popup-subtitle-menu-item';
PopupMenu.PopupSwitchMenuItem.prototype._init.call(this, label, false, params);
},
__proto__: NMDeviceTitleMenuItem.prototype,
updateForDevice: function(device) {
if (device) {
@@ -212,7 +269,7 @@ NMWiredSectionTitleMenuItem.prototype = {
},
activate: function(event) {
PopupMenu.PopupSwitchMenuItem.prototype.activate.call(this, event);
NMDeviceTitleMenuItem.prototype.activate.call(this, event);
if (!this._device) {
log('Section title activated when there is more than one device, should be non reactive');
@@ -238,12 +295,10 @@ function NMWirelessSectionTitleMenuItem() {
}
NMWirelessSectionTitleMenuItem.prototype = {
__proto__: PopupMenu.PopupSwitchMenuItem.prototype,
__proto__: NMDeviceTitleMenuItem.prototype,
_init: function(client, property, title, params) {
params = params || { };
params.style_class = 'popup-subtitle-menu-item';
PopupMenu.PopupSwitchMenuItem.prototype._init.call(this, title, false, params);
NMDeviceTitleMenuItem.prototype._init.call(this, title, params);
this._client = client;
this._property = property + '_enabled';
@@ -269,7 +324,7 @@ NMWirelessSectionTitleMenuItem.prototype = {
},
activate: function(event) {
PopupMenu.PopupSwitchMenuItem.prototype.activate.call(this, event);
NMDeviceTitleMenuItem.prototype.activate.call(this, event);
this._client[this._setEnabledFunc](this._switch.state);
},
@@ -318,14 +373,15 @@ NMDevice.prototype = {
};
this._connections.push(obj);
}
this._connections.sort(this._connectionSortFunction);
this._connections.sort(function(one, two) {
return two.timestamp - one.timestamp;
});
this._activeConnection = null;
this._activeConnectionItem = null;
this._autoConnectionItem = null;
this._overflowItem = null;
if (this.device) {
this.statusItem = new PopupMenu.PopupSwitchMenuItem(this._getDescription(), this.connected, { style_class: 'popup-subtitle-menu-item' });
this.statusItem = new NMDeviceTitleMenuItem(this._getDescription());
this._statusChanged = this.statusItem.connect('toggled', Lang.bind(this, function(item, state) {
if (state)
this.activate();
@@ -426,7 +482,9 @@ NMDevice.prototype = {
timestamp: connection._timestamp,
};
this._connections.push(obj);
this._connections.sort(this._connectionSortFunction);
this._connections.sort(function(one, two) {
return two.timestamp - one.timestamp;
});
this._clearSection();
this._createSection();
@@ -461,13 +519,6 @@ NMDevice.prototype = {
return this.device.connection_valid(connection);
},
_connectionSortFunction: function(one, two) {
if (one.timestamp == two.timestamp)
return GLib.utf8_collate(one.name, two.name);
return two.timestamp - one.timestamp;
},
setEnabled: function(enabled) {
// do nothing by default, we want to keep the conneciton list visible
// in the majority of cases (wired, wwan, vpn)
@@ -475,15 +526,11 @@ NMDevice.prototype = {
getStatusLabel: function() {
switch(this.device.state) {
case NetworkManager.DeviceState.UNMANAGED:
case NetworkManager.DeviceState.DISCONNECTED:
case NetworkManager.DeviceState.DEACTIVATING:
case NetworkManager.DeviceState.ACTIVATED:
return null;
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) */
return _("unmanaged");
case NetworkManager.DeviceState.DEACTIVATING:
return _("disconnecting...");
case NetworkManager.DeviceState.PREPARE:
case NetworkManager.DeviceState.CONFIG:
case NetworkManager.DeviceState.IP_CONFIG:
@@ -542,7 +589,6 @@ NMDevice.prototype = {
this.section.removeAll();
this._autoConnectionItem = null;
this._activeConnectionItem = null;
this._overflowItem = null;
for (let i = 0; i < this._connections.length; i++) {
this._connections[i].item = null;
}
@@ -561,23 +607,13 @@ NMDevice.prototype = {
this.section.addMenuItem(this._activeConnectionItem);
}
if (this._connections.length > 0) {
let activeOffset = this._activeConnectionItem ? 1 : 0;
for(let j = 0; j < this._connections.length; ++j) {
let obj = this._connections[j];
if (this._activeConnection &&
obj.connection == this._activeConnection._connection)
continue;
obj.item = this._createConnectionItem(obj);
if (j + activeOffset >= NUM_VISIBLE_NETWORKS) {
if (!this._overflowItem) {
this._overflowItem = new PopupMenu.PopupSubMenuMenuItem(_("More..."));
this.section.addMenuItem(this._overflowItem);
}
this._overflowItem.menu.addMenuItem(obj.item);
} else
this.section.addMenuItem(obj.item);
this.section.addMenuItem(obj.item);
}
} else if (this._autoConnectionName) {
this._autoConnectionItem = new PopupMenu.PopupMenuItem(this._autoConnectionName);
@@ -624,8 +660,15 @@ NMDevice.prototype = {
this.emit('network-lost');
}
if (newstate == NetworkManager.DeviceState.FAILED) {
switch(newstate) {
case NetworkManager.DeviceState.NEED_AUTH:
// FIXME: make this have a real effect
// (currently we rely on a running nm-applet)
this.emit('need-auth');
break;
case NetworkManager.DeviceState.FAILED:
this.emit('activation-failed', reason);
break;
}
this._updateStatusItem();
@@ -660,7 +703,7 @@ NMDevice.prototype = {
let dev_product = this.device.get_product();
let dev_vendor = this.device.get_vendor();
if (!dev_product || !dev_vendor)
return '';
return null;
let product = Util.fixupPCIDescription(dev_product);
let vendor = Util.fixupPCIDescription(dev_vendor);
@@ -734,6 +777,10 @@ NMDeviceModem.prototype = {
this.mobileDevice = null;
this._connectionType = 'ppp';
this._applet_proxy = new NMAppletProxy(DBus.session,
'org.gnome.network_manager_applet',
'/org/gnome/network_manager_applet');
this._capabilities = device.current_capabilities;
if (this._capabilities & NetworkManager.DeviceModemCapabilities.GSM_UMTS) {
is_wwan = true;
@@ -774,7 +821,7 @@ NMDeviceModem.prototype = {
}));
}
NMDevice.prototype._init.call(this, client, device, connections);
NMDevice.prototype._init.call(this, client, device, connections, 1);
},
setEnabled: function(enabled) {
@@ -791,7 +838,7 @@ NMDeviceModem.prototype = {
},
get connected() {
return this._enabled && this.device.state == NetworkManager.DeviceState.ACTIVATED;
return this._enabled && this.device.state == NetworkManager.DeviceState.CONNECTED;
},
destroy: function() {
@@ -835,10 +882,12 @@ NMDeviceModem.prototype = {
},
_createAutomaticConnection: function() {
// Mobile wizard is too complex for the shell UI and
// is handled by the network panel
Util.spawn(['gnome-control-center', 'network',
'connect-3g', this.device.get_path()]);
// Mobile wizard is handled by nm-applet for now...
this._applet_proxy.ConnectTo3gNetworkRemote(this.device.get_path(),
Lang.bind(this, function(results, err) {
if (err)
log(err);
}));
return null;
}
};
@@ -950,6 +999,10 @@ NMDeviceWireless.prototype = {
this._overflowItem = null;
this._networks = [ ];
this._applet_proxy = new NMAppletProxy(DBus.session,
'org.gnome.network_manager_applet',
'/org/gnome/network_manager_applet');
// breaking the layers with this, but cannot call
// this.connectionValid until I have a device
this.device = device;
@@ -961,16 +1014,6 @@ NMDeviceWireless.prototype = {
for (let i = 0; i < accessPoints.length; i++) {
// Access points are grouped by network
let ap = accessPoints[i];
if (ap.get_ssid() == null) {
// hidden access point cannot be added, we need to know
// the SSID and security details to connect
// nevertheless, the access point can acquire a SSID when
// NetworkManager connects to it (via nmcli or the control-center)
ap._notifySsidId = ap.connect('notify::ssid', Lang.bind(this, this._notifySsidCb));
continue;
}
let pos = this._findNetwork(ap);
let obj;
if (pos != -1) {
@@ -984,7 +1027,7 @@ NMDeviceWireless.prototype = {
item: null,
accessPoints: [ ap ]
};
obj.ssidText = ssidToLabel(obj.ssid);
obj.ssidText = NetworkManager.utils_ssid_to_utf8(obj.ssid);
this._networks.push(obj);
}
@@ -997,14 +1040,8 @@ NMDeviceWireless.prototype = {
}
}
}
if (this.device.active_access_point) {
let networkPos = this._findNetwork(this.device.active_access_point);
if (networkPos == -1) // the connected access point is invisible
this._activeNetwork = null;
else
this._activeNetwork = this._networks[networkPos];
this._activeNetwork = this._networks[this._findNetwork(this.device.active_access_point)];
} else {
this._activeNetwork = null;
}
@@ -1090,14 +1127,6 @@ NMDeviceWireless.prototype = {
}
},
_notifySsidCb: function(accessPoint) {
if (accessPoint.get_ssid() != null) {
accessPoint.disconnect(accessPoint._notifySsidId);
accessPoint._notifySsidId = 0;
this._accessPointAdded(this.device, accessPoint);
}
},
_activeApChanged: function() {
this._activeNetwork = null;
@@ -1105,9 +1134,7 @@ NMDeviceWireless.prototype = {
if (activeAp) {
let pos = this._findNetwork(activeAp);
if (pos != -1)
this._activeNetwork = this._networks[pos];
this._activeNetwork = this._networks[pos];
}
// we don't refresh the view here, setActiveConnection will
@@ -1182,9 +1209,6 @@ NMDeviceWireless.prototype = {
},
_findNetwork: function(accessPoint) {
if (accessPoint.get_ssid() == null)
return -1;
for (let i = 0; i < this._networks.length; i++) {
if (this._networkCompare(this._networks[i], accessPoint))
return i;
@@ -1193,13 +1217,6 @@ NMDeviceWireless.prototype = {
},
_accessPointAdded: function(device, accessPoint) {
if (accessPoint.get_ssid() == null) {
// This access point is not visible yet
// Wait for it to get a ssid
accessPoint._notifySsidId = accessPoint.connect('notify::ssid', Lang.bind(this, this._notifySsidCb));
return;
}
let pos = this._findNetwork(accessPoint);
let apObj;
let needsupdate = false;
@@ -1222,7 +1239,7 @@ NMDeviceWireless.prototype = {
item: null,
accessPoints: [ accessPoint ]
};
apObj.ssidText = ssidToLabel(apObj.ssid);
apObj.ssidText = NetworkManager.utils_ssid_to_utf8(apObj.ssid);
needsupdate = true;
}
@@ -1293,29 +1310,12 @@ NMDeviceWireless.prototype = {
if (apObj.accessPoints.length == 0) {
if (apObj.item)
apObj.item.destroy();
if (this._overflowItem) {
if (!apObj.isMore) {
// we removed an item in the main menu, and we have a more submenu
// we need to extract the first item in more and move it to the submenu
let apObj = this._overflowItem.menu.firstMenuItem;
if (apObj.item) {
apObj.item.destroy();
this._createNetworkItem(apObj, NUM_VISIBLE_NETWORKS-1);
}
}
// This can happen if the removed connection is from the overflow
// menu, or if we just moved the last connection out from the menu
if (this._overflowItem.menu.length == 0) {
this._overflowItem.destroy();
this._overflowItem = null;
}
}
this._networks.splice(pos, 1);
if (this._overflowItem &&
this._overflowItem.menu.length == 0) {
this._overflowItem.destroy();
this._overflowItem = null;
}
} else if (apObj.item)
apObj.item.updateAccessPoints(apObj.accessPoints);
},
@@ -1435,12 +1435,13 @@ NMDeviceWireless.prototype = {
},
_createActiveConnectionItem: function() {
let activeAp = this.device.active_access_point;
let icon, title;
if (this._activeConnection._connection) {
let connection = this._activeConnection._connection;
if (this._activeNetwork)
this._activeConnectionItem = new NMNetworkMenuItem(this._activeNetwork.accessPoints, undefined,
{ reactive: false });
if (activeAp)
this._activeConnectionItem = new NMNetworkMenuItem([ activeAp ], undefined,
{ reactive: false });
else
this._activeConnectionItem = new PopupMenu.PopupImageMenuItem(connection._name,
'network-wireless-connected',
@@ -1448,9 +1449,9 @@ NMDeviceWireless.prototype = {
} else {
// We cannot read the connection (due to ACL, or API incompatibility), but we still show signal if we have it
let menuItem;
if (this._activeNetwork)
this._activeConnectionItem = new NMNetworkMenuItem(this._activeNetwork.accessPoints, undefined,
{ reactive: false });
if (activeAp)
this._activeConnectionItem = new NMNetworkMenuItem([ activeAp ], undefined,
{ reactive: false });
else
this._activeConnectionItem = new PopupMenu.PopupImageMenuItem(_("Connected (private)"),
'network-wireless-connected',
@@ -1497,26 +1498,27 @@ NMDeviceWireless.prototype = {
let accessPoints = sortAccessPoints(apObj.accessPoints);
if ( (accessPoints[0]._secType == NMAccessPointSecurity.WPA2_ENT)
|| (accessPoints[0]._secType == NMAccessPointSecurity.WPA_ENT)) {
// 802.1x-enabled APs require further configuration, so they're
// handled in gnome-control-center
Util.spawn(['gnome-control-center', 'network', 'connect-8021x-wifi',
this.device.get_path(), accessPoints[0].dbus_path]);
// 802.1x-enabled APs get handled by nm-applet for now...
this._applet_proxy.ConnectTo8021xNetworkRemote(this.device.get_path(),
accessPoints[0].dbus_path,
Lang.bind(this, function(results, err) {
if (err)
log(err);
}));
} else {
let connection = this._createAutomaticConnection(apObj);
this._client.add_and_activate_connection(connection, this.device, accessPoints[0].dbus_path, null)
}
}));
}
if (position < NUM_VISIBLE_NETWORKS) {
apObj.isMore = false;
this.section.addMenuItem(apObj.item, position);
} else {
if (position < NUM_VISIBLE_NETWORKS)
this.section.addMenuItem(apObj.item);
else {
if (!this._overflowItem) {
this._overflowItem = new PopupMenu.PopupSubMenuMenuItem(_("More..."));
this.section.addMenuItem(this._overflowItem);
}
this._overflowItem.menu.addMenuItem(apObj.item, position - NUM_VISIBLE_NETWORKS);
apObj.isMore = true;
}
},
@@ -1558,9 +1560,9 @@ NMApplet.prototype = {
this._statusSection.addAction(_("Enable networking"), Lang.bind(this, function() {
this._client.networking_enabled = true;
}));
this._statusSection.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._statusSection.actor.hide();
this.menu.addMenuItem(this._statusSection);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._devices = { };
@@ -1571,9 +1573,9 @@ NMApplet.prototype = {
};
this._devices.wired.section.addMenuItem(this._devices.wired.item);
this._devices.wired.section.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._devices.wired.section.actor.hide();
this.menu.addMenuItem(this._devices.wired.section);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._devices.wireless = {
section: new PopupMenu.PopupMenuSection(),
@@ -1581,9 +1583,9 @@ NMApplet.prototype = {
item: this._makeToggleItem('wireless', _("Wireless"))
};
this._devices.wireless.section.addMenuItem(this._devices.wireless.item);
this._devices.wireless.section.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._devices.wireless.section.actor.hide();
this.menu.addMenuItem(this._devices.wireless.section);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._devices.wwan = {
section: new PopupMenu.PopupMenuSection(),
@@ -1591,9 +1593,9 @@ NMApplet.prototype = {
item: this._makeToggleItem('wwan', _("Mobile broadband"))
};
this._devices.wwan.section.addMenuItem(this._devices.wwan.item);
this._devices.wwan.section.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._devices.wwan.section.actor.hide();
this.menu.addMenuItem(this._devices.wwan.section);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._devices.vpn = {
section: new PopupMenu.PopupMenuSection(),
@@ -1606,10 +1608,14 @@ NMApplet.prototype = {
this._devices.vpn.item.updateForDevice(this._devices.vpn.device);
this._devices.vpn.section.addMenuItem(this._devices.vpn.item);
this._devices.vpn.section.addMenuItem(this._devices.vpn.device.section);
this._devices.vpn.section.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._devices.vpn.section.actor.hide();
this.menu.addMenuItem(this._devices.vpn.section);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addSettingsAction(_("Network Settings"), 'gnome-network-panel.desktop');
this.menu.addAction(_("Network Settings"), function() {
let app = Shell.AppSystem.get_default().get_app('gnome-network-panel.desktop');
app.activate(-1);
});
this._activeConnections = [ ];
this._connections = [ ];
@@ -1663,7 +1669,8 @@ NMApplet.prototype = {
_ensureSource: function() {
if (!this._source) {
this._source = new NMMessageTraySource();
this._source.connect('destroy', Lang.bind(this, function() {
this._source._destroyId = this._source.connect('destroy', Lang.bind(this, function() {
this._source._destroyId = 0;
this._source = null;
}));
Main.messageTray.add(this._source);
@@ -1684,18 +1691,21 @@ NMApplet.prototype = {
_syncSectionTitle: function(category) {
let devices = this._devices[category].devices;
let managedDevices = devices.filter(function(dev) {
return dev.device.state != NetworkManager.DeviceState.UNMANAGED;
});
let item = this._devices[category].item;
let section = this._devices[category].section;
if (devices.length == 0)
if (managedDevices.length == 0)
section.actor.hide();
else {
section.actor.show();
if (devices.length == 1) {
let dev = devices[0];
if (managedDevices.length == 1) {
let dev = managedDevices[0];
dev.statusItem.actor.hide();
item.updateForDevice(dev);
} else {
devices.forEach(function(dev) {
managedDevices.forEach(function(dev) {
dev.statusItem.actor.show();
});
// remove status text from the section title item
@@ -1711,28 +1721,6 @@ NMApplet.prototype = {
}
},
_notifyForDevice: function(device, iconName, title, text, urgency) {
if (device._notification)
device._notification.destroy();
/* must call after destroying previous notification,
or this._source will be cleared */
this._ensureSource();
let icon = new St.Icon({ icon_name: iconName,
icon_type: St.IconType.SYMBOLIC,
icon_size: this._source.ICON_SIZE
});
device._notification = new MessageTray.Notification(this._source, title, text,
{ icon: icon });
device._notification.setUrgency(urgency);
device._notification.setTransient(true);
device._notification.connect('destroy', function() {
device._notification = null;
});
this._source.notify(device._notification);
},
_deviceAdded: function(client, device) {
if (device._delegate) {
// already seen, not adding again
@@ -1742,29 +1730,42 @@ NMApplet.prototype = {
if (wrapperClass) {
let wrapper = new wrapperClass(this._client, device, this._connections);
wrapper._networkLostId = wrapper.connect('network-lost', Lang.bind(this, function(device) {
this._notifyForDevice(device, 'network-offline',
_("Connectivity lost"),
_("You're no longer connected to the network"),
// set critical urgency to popup the notification automatically
MessageTray.Urgency.CRITICAL);
// FIXME: these notifications are duplicate with those exposed by nm-applet
// uncomment this code in 3.2, when we'll conflict with and kill nm-applet
/* wrapper._networkLostId = wrapper.connect('network-lost', Lang.bind(this, function(emitter) {
this._ensureSource();
let icon = new St.Icon({ icon_name: 'network-offline',
icon_type: St.IconType.SYMBOLIC,
icon_size: this._source.ICON_SIZE
});
let notification = new MessageTray.Notification(this._source,
_("Connectivity lost"),
_("You're no longer connected to the network"),
{ icon: icon });
this._source.notify(notification);
}));
wrapper._activationFailedId = wrapper.connect('activation-failed', Lang.bind(this, function(device, reason) {
wrapper._activationFailedId = wrapper.connect('activation-failed', Lang.bind(this, function(wrapper, reason) {
this._ensureSource();
let icon = new St.Icon({ icon_name: 'network-error',
icon_type: St.IconType.SYMBOLIC,
icon_size: this._source.ICON_SIZE,
});
let banner;
// XXX: nm-applet has no special text depending on reason
// but I'm not sure of this generic message
this._notifyForDevice(device, 'network-error',
_("Connection failed"),
_("Activation of network connection failed"),
MessageTray.Urgency.HIGH);
}));
let notification = new MessageTray.Notification(this._source,
_("Connection failed"),
_("Activation of network connection failed"),
{ icon: icon });
this._source.notify(notification);
})); */
wrapper._deviceStateChangedId = wrapper.connect('state-changed', Lang.bind(this, function(dev) {
this._syncSectionTitle(dev.category);
}));
wrapper._destroyId = wrapper.connect('destroy', function(wrapper) {
wrapper.disconnect(wrapper._networkLostId);
wrapper.disconnect(wrapper._activationFailedId);
//wrapper.disconnect(wrapper._networkLostId);
//wrapper.disconnect(wrapper._activationFailedId);
wrapper.disconnect(wrapper._deviceStateChangedId);
wrapper.disconnect(wrapper._destroyId);
});
let section = this._devices[wrapper.category].section;
let devices = this._devices[wrapper.category].devices;
@@ -1809,8 +1810,11 @@ NMApplet.prototype = {
active._primaryDevice.setActiveConnection(null);
active._primaryDevice = null;
}
if (active._inited) {
if (active._notifyStateId) {
active.disconnect(active._notifyStateId);
active._notifyStateId = 0;
}
if (active._inited) {
active.disconnect(active._notifyDefaultId);
active.disconnect(active._notifyDefault6Id);
active._inited = false;
@@ -1828,7 +1832,14 @@ NMApplet.prototype = {
if (!a._inited) {
a._notifyDefaultId = a.connect('notify::default', Lang.bind(this, this._updateIcon));
a._notifyDefault6Id = a.connect('notify::default6', Lang.bind(this, this._updateIcon));
a._notifyStateId = a.connect('notify::state', Lang.bind(this, this._updateIcon));
if (a.state == NetworkManager.ActiveConnectionState.ACTIVATING) // prepare to notify to the user
a._notifyStateId = a.connect('notify::state', Lang.bind(this, this._notifyActiveConnection));
else {
// notify as soon as possible
Mainloop.idle_add(Lang.bind(this, function() {
this._notifyActiveConnection(a);
}));
}
a._inited = true;
}
@@ -1878,6 +1889,63 @@ NMApplet.prototype = {
this._mainConnection = activating || default_ip4 || default_ip6 || this._activeConnections[0] || null;
},
_notifyActiveConnection: function(active) {
// FIXME: duplicate notifications when nm-applet is running
// This code will come back when nm-applet is killed
this._syncNMState();
return;
if (active.state == NetworkManager.ActiveConnectionState.ACTIVATED) {
// notify only connections that are visible
if (active._connection) {
this._ensureSource();
let icon;
let banner;
switch (active._section) {
case NMConnectionCategory.WWAN:
icon = 'network-cellular-signal-excellent';
banner = _("You're now connected to mobile broadband connection '%s'").format(active._connection._name);
break;
case NMConnectionCategory.WIRELESS:
icon = 'network-wireless-signal-excellent';
banner = _("You're now connected to wireless network '%s'").format(active._connection._name);
break;
case NMConnectionCategory.WIRED:
icon = 'network-wired';
banner = _("You're now connected to wired network '%s'").format(active._connection._name);
break;
case NMConnectionCategory.VPN:
icon = 'network-vpn';
banner = _("You're now connected to VPN network '%s'").format(active._connection._name);
break;
default:
// a fallback for a generic 'connected' icon
icon = 'network-transmit-receive';
banner = _("You're now connected to '%s'").format(active._connection._name);
}
let iconActor = new St.Icon({ icon_name: icon,
icon_type: St.IconType.SYMBOLIC,
icon_size: this._source.ICON_SIZE
});
let notification = new MessageTray.Notification(this._source,
_("Connection established"),
banner,
{ icon: iconActor });
this._source.notify(notification);
}
if (active._stateChangeId) {
active.disconnect(active._stateChangeId);
active._stateChangeId = 0;
}
}
this._syncNMState();
},
_readConnections: function() {
let connections = this._settings.list_connections();
for (let i = 0; i < connections.length; i++) {

View File

@@ -7,13 +7,12 @@ const Mainloop = imports.mainloop;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const Util = imports.misc.util;
const BUS_NAME = 'org.gnome.SettingsDaemon';
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Power';
const BUS_NAME = 'org.gnome.PowerManager';
const OBJECT_PATH = '/org/gnome/PowerManager';
const UPDeviceType = {
UNKNOWN: 0,
@@ -41,7 +40,7 @@ const UPDeviceState = {
};
const PowerManagerInterface = {
name: 'org.gnome.SettingsDaemon.Power',
name: 'org.gnome.PowerManager',
methods: [
{ name: 'GetDevices', inSignature: '', outSignature: 'a(susbut)' },
{ name: 'GetPrimaryDevice', inSignature: '', outSignature: '(susbut)' },
@@ -75,11 +74,15 @@ Indicator.prototype = {
this._batteryItem.addActor(this._primaryPercentage, { align: St.Align.END });
this.menu.addMenuItem(this._batteryItem);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._deviceSep = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addMenuItem(this._deviceSep);
this._otherDevicePosition = 2;
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addSettingsAction(_("Power Settings"), 'gnome-power-panel.desktop');
this.menu.addAction(_("Power Settings"),function() {
let app = Shell.AppSystem.get_default().get_app('gnome-power-panel.desktop');
app.activate(-1);
});
this._proxy.connect('Changed', Lang.bind(this, this._devicesChanged));
this._devicesChanged();
@@ -88,9 +91,11 @@ Indicator.prototype = {
_readPrimaryDevice: function() {
this._proxy.GetPrimaryDeviceRemote(Lang.bind(this, function(device, error) {
if (error) {
this._checkError(error);
this._hasPrimary = false;
this._primaryDeviceId = null;
this._batteryItem.actor.hide();
this._deviceSep.actor.hide();
return;
}
let [device_id, device_type, icon, percentage, state, seconds] = device;
@@ -120,9 +125,12 @@ Indicator.prototype = {
}
this._primaryPercentage.text = Math.round(percentage) + '%';
this._batteryItem.actor.show();
if (this._deviceItems.length > 0)
this._deviceSep.actor.show();
} else {
this._hasPrimary = false;
this._batteryItem.actor.hide();
this._deviceSep.actor.hide();
}
this._primaryDeviceId = device_id;
@@ -135,6 +143,8 @@ Indicator.prototype = {
this._deviceItems = [];
if (error) {
this._checkError(error);
this._deviceSep.actor.hide();
return;
}
@@ -149,6 +159,11 @@ Indicator.prototype = {
this.menu.addMenuItem(item, this._otherDevicePosition + position);
position++;
}
if (this._hasPrimary && position > 0)
this._deviceSep.actor.show();
else
this._deviceSep.actor.hide();
}));
},
@@ -159,12 +174,21 @@ Indicator.prototype = {
this.setGIcon(gicon);
this.actor.show();
} else {
this._checkError(error);
this.menu.close();
this.actor.hide();
}
}));
this._readPrimaryDevice();
this._readOtherDevices();
},
_checkError: function(error) {
if (!this._restarted && error && error.message.match(/org\.freedesktop\.DBus\.Error\.(UnknownMethod|InvalidArgs)/)) {
Util.killall('gnome-power-manager');
Util.spawn(['gnome-power-manager']);
this._restarted = true;
}
}
};

View File

@@ -9,7 +9,6 @@ const Gvc = imports.gi.Gvc;
const Signals = imports.signals;
const St = imports.gi.St;
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const Util = imports.misc.util;
@@ -29,7 +28,7 @@ Indicator.prototype = {
PanelMenu.SystemStatusButton.prototype._init.call(this, 'audio-volume-muted', null);
this._control = new Gvc.MixerControl({ name: 'GNOME Shell Volume Control' });
this._control.connect('state-changed', Lang.bind(this, this._onControlStateChanged));
this._control.connect('ready', Lang.bind(this, this._onControlReady));
this._control.connect('default-sink-changed', Lang.bind(this, this._readOutput));
this._control.connect('default-source-changed', Lang.bind(this, this._readInput));
this._control.connect('stream-added', Lang.bind(this, this._maybeShowInput));
@@ -47,7 +46,8 @@ Indicator.prototype = {
this.menu.addMenuItem(this._outputTitle);
this.menu.addMenuItem(this._outputSlider);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._separator = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addMenuItem(this._separator);
this._input = null;
this._inputVolumeId = 0;
@@ -60,7 +60,10 @@ Indicator.prototype = {
this.menu.addMenuItem(this._inputSlider);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addSettingsAction(_("Sound Settings"), 'gnome-sound-panel.desktop');
this.menu.addAction(_("Sound Settings"), function() {
let app = Shell.AppSystem.get_default().get_app('gnome-sound-panel.desktop');
app.activate(-1);
});
this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
this._control.open();
@@ -97,14 +100,9 @@ Indicator.prototype = {
this._notifyVolumeChange();
},
_onControlStateChanged: function() {
if (this._control.get_state() == Gvc.MixerControlState.READY) {
this._readOutput();
this._readInput();
this.actor.show();
} else {
this.actor.hide();
}
_onControlReady: function() {
this._readOutput();
this._readInput();
},
_readOutput: function() {
@@ -140,6 +138,7 @@ Indicator.prototype = {
this._mutedChanged (null, null, '_input');
this._volumeChanged (null, null, '_input');
} else {
this._separator.actor.hide();
this._inputTitle.actor.hide();
this._inputSlider.actor.hide();
}
@@ -162,9 +161,11 @@ Indicator.prototype = {
}
}
if (showInput) {
this._separator.actor.show();
this._inputTitle.actor.show();
this._inputSlider.actor.show();
} else {
this._separator.actor.hide();
this._inputTitle.actor.hide();
this._inputSlider.actor.hide();
}

305
js/ui/statusMenu.js Normal file
View File

@@ -0,0 +1,305 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Gdm = imports.gi.Gdm;
const DBus = imports.dbus;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Tp = imports.gi.TelepathyGLib;
const UPowerGlib = imports.gi.UPowerGlib;
const GnomeSession = imports.misc.gnomeSession;
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const Util = imports.misc.util;
const BUS_NAME = 'org.gnome.ScreenSaver';
const OBJECT_PATH = '/org/gnome/ScreenSaver';
const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown';
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 ScreenSaverInterface = {
name: BUS_NAME,
methods: [ { name: 'Lock', inSignature: '' } ]
};
let ScreenSaverProxy = DBus.makeProxyClass(ScreenSaverInterface);
// Adapted from gdm/gui/user-switch-applet/applet.c
//
// Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
// Copyright (C) 2008,2009 Red Hat, Inc.
function StatusMenuButton() {
this._init();
}
StatusMenuButton.prototype = {
__proto__: PanelMenu.Button.prototype,
_init: function() {
PanelMenu.Button.prototype._init.call(this, 0.0);
let box = new St.BoxLayout({ name: 'panelStatusMenu' });
this.actor.set_child(box);
this._lockdownSettings = new Gio.Settings({ schema: LOCKDOWN_SCHEMA });
this._gdm = Gdm.UserManager.ref_default();
this._gdm.queue_load();
this._user = this._gdm.get_user(GLib.get_user_name());
this._presence = new GnomeSession.Presence();
this._presenceItems = {};
this._session = new GnomeSession.SessionManager();
this._account_mgr = Tp.AccountManager.dup()
this._upClient = new UPowerGlib.Client();
this._screenSaverProxy = new ScreenSaverProxy(DBus.session, BUS_NAME, OBJECT_PATH);
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
this._iconBox = new St.Bin();
box.add(this._iconBox, { y_align: St.Align.MIDDLE, y_fill: false });
let textureCache = St.TextureCache.get_default();
this._availableIcon = new St.Icon({ icon_name: 'user-available', style_class: 'popup-menu-icon' });
this._busyIcon = new St.Icon({ icon_name: 'user-busy', style_class: 'popup-menu-icon' });
this._invisibleIcon = new St.Icon({ icon_name: 'user-invisible', style_class: 'popup-menu-icon' });
this._idleIcon = new St.Icon({ icon_name: 'user-idle', style_class: 'popup-menu-icon' });
this._presence.connect('StatusChanged', Lang.bind(this, this._updatePresenceIcon));
this._presence.getStatus(Lang.bind(this, this._updatePresenceIcon));
this._name = new St.Label();
box.add(this._name, { y_align: St.Align.MIDDLE, y_fill: false });
this._userLoadedId = this._user.connect('notify::is-loaded', Lang.bind(this, this._updateUserName));
this._userChangedId = this._user.connect('changed', Lang.bind(this, this._updateUserName));
this._createSubMenu();
this._gdm.connect('notify::is-loaded', Lang.bind(this, this._updateSwitchUser));
this._gdm.connect('user-added', Lang.bind(this, this._updateSwitchUser));
this._gdm.connect('user-removed', Lang.bind(this, this._updateSwitchUser));
this._lockdownSettings.connect('changed::' + DISABLE_USER_SWITCH_KEY,
Lang.bind(this, this._updateSwitchUser));
this._lockdownSettings.connect('changed::' + DISABLE_LOG_OUT_KEY,
Lang.bind(this, this._updateLogout));
this._lockdownSettings.connect('changed::' + DISABLE_LOCK_SCREEN_KEY,
Lang.bind(this, this._updateLockScreen));
this._updateSwitchUser();
this._updateLogout();
this._updateLockScreen();
this._upClient.connect('notify::can-suspend', Lang.bind(this, this._updateSuspendOrPowerOff));
},
_onDestroy: function() {
this._user.disconnect(this._userLoadedId);
this._user.disconnect(this._userChangedId);
},
_updateUserName: function() {
if (this._user.is_loaded)
this._name.set_text(this._user.get_real_name());
else
this._name.set_text("");
},
_updateSessionSeparator: function() {
let showSeparator = this._loginScreenItem.actor.visible ||
this._logoutItem.actor.visible ||
this._lockScreenItem.actor.visible;
if (showSeparator)
this._sessionSeparator.actor.show();
else
this._sessionSeparator.actor.hide();
},
_updateSwitchUser: function() {
let allowSwitch = !this._lockdownSettings.get_boolean(DISABLE_USER_SWITCH_KEY);
if (allowSwitch && this._gdm.can_switch ())
this._loginScreenItem.actor.show();
else
this._loginScreenItem.actor.hide();
this._updateSessionSeparator();
},
_updateLogout: function() {
let allowLogout = !this._lockdownSettings.get_boolean(DISABLE_LOG_OUT_KEY);
if (allowLogout)
this._logoutItem.actor.show();
else
this._logoutItem.actor.hide();
this._updateSessionSeparator();
},
_updateLockScreen: function() {
let allowLockScreen = !this._lockdownSettings.get_boolean(DISABLE_LOCK_SCREEN_KEY);
if (allowLockScreen)
this._lockScreenItem.actor.show();
else
this._lockScreenItem.actor.hide();
this._updateSessionSeparator();
},
_updateSuspendOrPowerOff: function() {
this._haveSuspend = this._upClient.get_can_suspend();
if (!this._suspendOrPowerOffItem)
return;
// If we can't suspend show Power Off... instead
// and disable the alt key
if (!this._haveSuspend) {
this._suspendOrPowerOffItem.updateText(_("Power Off..."), null);
} else {
this._suspendOrPowerOffItem.updateText(_("Suspend"), _("Power Off..."));
}
},
_updatePresenceIcon: function(presence, status) {
if (status == GnomeSession.PresenceStatus.AVAILABLE)
this._iconBox.child = this._availableIcon;
else if (status == GnomeSession.PresenceStatus.BUSY)
this._iconBox.child = this._busyIcon;
else if (status == GnomeSession.PresenceStatus.INVISIBLE)
this._iconBox.child = this._invisibleIcon;
else
this._iconBox.child = this._idleIcon;
for (let itemStatus in this._presenceItems)
this._presenceItems[itemStatus].setShowDot(itemStatus == status);
},
_createSubMenu: function() {
let item;
item = new PopupMenu.PopupImageMenuItem(_("Available"), 'user-available');
item.connect('activate', Lang.bind(this, this._setPresenceStatus, GnomeSession.PresenceStatus.AVAILABLE));
this.menu.addMenuItem(item);
this._presenceItems[GnomeSession.PresenceStatus.AVAILABLE] = item;
item = new PopupMenu.PopupImageMenuItem(_("Busy"), 'user-busy');
item.connect('activate', Lang.bind(this, this._setPresenceStatus, GnomeSession.PresenceStatus.BUSY));
this.menu.addMenuItem(item);
this._presenceItems[GnomeSession.PresenceStatus.BUSY] = item;
item = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addMenuItem(item);
item = new PopupMenu.PopupMenuItem(_("My Account"));
item.connect('activate', Lang.bind(this, this._onMyAccountActivate));
this.menu.addMenuItem(item);
item = new PopupMenu.PopupMenuItem(_("System Settings"));
item.connect('activate', Lang.bind(this, this._onPreferencesActivate));
this.menu.addMenuItem(item);
item = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addMenuItem(item);
item = new PopupMenu.PopupMenuItem(_("Lock Screen"));
item.connect('activate', Lang.bind(this, this._onLockScreenActivate));
this.menu.addMenuItem(item);
this._lockScreenItem = item;
item = new PopupMenu.PopupMenuItem(_("Switch User"));
item.connect('activate', Lang.bind(this, this._onLoginScreenActivate));
this.menu.addMenuItem(item);
this._loginScreenItem = item;
item = new PopupMenu.PopupMenuItem(_("Log Out..."));
item.connect('activate', Lang.bind(this, this._onQuitSessionActivate));
this.menu.addMenuItem(item);
this._logoutItem = item;
item = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addMenuItem(item);
this._sessionSeparator = item;
item = new PopupMenu.PopupAlternatingMenuItem(_("Suspend"),
_("Power Off..."));
this.menu.addMenuItem(item);
this._suspendOrPowerOffItem = item;
item.connect('activate', Lang.bind(this, this._onSuspendOrPowerOffActivate));
this._updateSuspendOrPowerOff();
},
_setPresenceStatus: function(item, event, status) {
this._presence.setStatus(status);
this._setIMStatus(status);
},
_onMyAccountActivate: function() {
Main.overview.hide();
let app = Shell.AppSystem.get_default().get_app('gnome-user-accounts-panel.desktop');
app.activate(-1);
},
_onPreferencesActivate: function() {
Main.overview.hide();
let app = Shell.AppSystem.get_default().get_app('gnome-control-center.desktop');
app.activate(-1);
},
_onLockScreenActivate: function() {
Main.overview.hide();
this._screenSaverProxy.LockRemote();
},
_onLoginScreenActivate: function() {
Main.overview.hide();
this._gdm.goto_login_session();
this._onLockScreenActivate();
},
_onQuitSessionActivate: function() {
Main.overview.hide();
this._session.LogoutRemote(0);
},
_onSuspendOrPowerOffActivate: function() {
Main.overview.hide();
if (this._haveSuspend &&
this._suspendOrPowerOffItem.state == PopupMenu.PopupAlternatingMenuItemState.DEFAULT) {
this._screenSaverProxy.LockRemote(Lang.bind(this, function() {
this._upClient.suspend_sync(null);
}));
} else {
this._session.ShutdownRemote();
}
},
_setIMStatus: function(session_status) {
let [presence_type, presence_status, msg] = this._account_mgr.get_most_available_presence();
let type, status;
// We change the IM presence only if there are connected accounts
if (presence_type == Tp.ConnectionPresenceType.UNSET ||
presence_type == Tp.ConnectionPresenceType.OFFLINE ||
presence_type == Tp.ConnectionPresenceType.UNKNOWN ||
presence_type == Tp.ConnectionPresenceType.ERROR)
return;
if (session_status == GnomeSession.PresenceStatus.AVAILABLE) {
type = Tp.ConnectionPresenceType.AVAILABLE;
status = "available";
}
else if (session_status == GnomeSession.PresenceStatus.BUSY) {
type = Tp.ConnectionPresenceType.BUSY;
status = "busy";
}
else {
return;
}
this._account_mgr.set_all_requested_presences(type, status, msg);
}
};

File diff suppressed because it is too large Load Diff

View File

@@ -1,646 +0,0 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const AccountsService = imports.gi.AccountsService;
const DBus = imports.dbus;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Tp = imports.gi.TelepathyGLib;
const UPowerGlib = imports.gi.UPowerGlib;
const GnomeSession = imports.misc.gnomeSession;
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const ScreenSaver = imports.misc.screenSaver;
const Util = imports.misc.util;
const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown';
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 WRAP_WIDTH = 150;
const DIALOG_ICON_SIZE = 64;
const IMStatus = {
AVAILABLE: 0,
BUSY: 1,
HIDDEN: 2,
AWAY: 3,
IDLE: 4,
OFFLINE: 5,
LAST: 6
};
// Adapted from gdm/gui/user-switch-applet/applet.c
//
// Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
// Copyright (C) 2008,2009 Red Hat, Inc.
function IMStatusItem(label, iconName) {
this._init(label, iconName);
}
IMStatusItem.prototype = {
__proto__: PopupMenu.PopupBaseMenuItem.prototype,
_init: function(label, iconName) {
PopupMenu.PopupBaseMenuItem.prototype._init.call(this);
this.actor.add_style_class_name('status-chooser-status-item');
this._icon = new St.Icon({ style_class: 'popup-menu-icon' });
this.addActor(this._icon);
if (iconName)
this._icon.icon_name = iconName;
this.label = new St.Label({ text: label });
this.addActor(this.label);
}
};
function IMUserNameItem() {
this._init();
}
IMUserNameItem.prototype = {
__proto__: PopupMenu.PopupBaseMenuItem.prototype,
_init: function() {
PopupMenu.PopupBaseMenuItem.prototype._init.call(this,
{ reactive: false,
style_class: 'status-chooser-user-name' });
this._wrapper = new Shell.GenericContainer();
this._wrapper.connect('get-preferred-width',
Lang.bind(this, this._wrapperGetPreferredWidth));
this._wrapper.connect('get-preferred-height',
Lang.bind(this, this._wrapperGetPreferredHeight));
this._wrapper.connect('allocate',
Lang.bind(this, this._wrapperAllocate));
this.addActor(this._wrapper, { expand: true, span: -1 });
this.label = new St.Label();
this.label.clutter_text.set_line_wrap(true);
this._wrapper.add_actor(this.label);
},
_wrapperGetPreferredWidth: function(actor, forHeight, alloc) {
[alloc.min_size, alloc.natural_size] = this.label.get_preferred_width(-1);
if (alloc.natural_size > WRAP_WIDTH)
alloc.natural_size = WRAP_WIDTH;
},
_wrapperGetPreferredHeight: function(actor, forWidth, alloc) {
let minWidth, natWidth;
[alloc.min_size, alloc.natural_size] = this.label.get_preferred_height(forWidth);
[minWidth, natWidth] = this.label.get_preferred_width(-1);
if (natWidth > WRAP_WIDTH) {
alloc.min_size *= 2;
alloc.natural_size *= 2;
}
},
_wrapperAllocate: function(actor, box, flags) {
let availWidth = box.x2 - box.x1;
let availHeight = box.y2 - box.y1;
this.label.allocate(box, flags);
}
};
function IMStatusChooserItem() {
this._init();
}
IMStatusChooserItem.prototype = {
__proto__: PopupMenu.PopupBaseMenuItem.prototype,
_init: function() {
PopupMenu.PopupBaseMenuItem.prototype._init.call (this,
{ reactive: false,
style_class: 'status-chooser' });
this._iconBin = new St.Button({ style_class: 'status-chooser-user-icon' });
this.addActor(this._iconBin);
this._iconBin.connect('clicked', Lang.bind(this,
function() {
this.activate();
}));
this._section = new PopupMenu.PopupMenuSection();
this.addActor(this._section.actor);
this._name = new IMUserNameItem();
this._section.addMenuItem(this._name);
this._combo = new PopupMenu.PopupComboBoxMenuItem({ style_class: 'status-chooser-combo' });
this._section.addMenuItem(this._combo);
let item;
item = new IMStatusItem(_("Available"), 'user-available');
this._combo.addMenuItem(item, IMStatus.AVAILABLE);
item = new IMStatusItem(_("Busy"), 'user-busy');
this._combo.addMenuItem(item, IMStatus.BUSY);
item = new IMStatusItem(_("Hidden"), 'user-invisible');
this._combo.addMenuItem(item, IMStatus.HIDDEN);
item = new IMStatusItem(_("Away"), 'user-away');
this._combo.addMenuItem(item, IMStatus.AWAY);
item = new IMStatusItem(_("Idle"), 'user-idle');
this._combo.addMenuItem(item, IMStatus.IDLE);
item = new IMStatusItem(_("Unavailable"), 'user-offline');
this._combo.addMenuItem(item, IMStatus.OFFLINE);
this._combo.connect('active-item-changed',
Lang.bind(this, this._changeIMStatus));
this._presence = new GnomeSession.Presence();
this._presence.getStatus(Lang.bind(this, this._sessionStatusChanged));
this._presence.connect('StatusChanged',
Lang.bind(this, this._sessionStatusChanged));
this._previousPresence = undefined;
this._accountMgr = Tp.AccountManager.dup()
this._accountMgr.connect('most-available-presence-changed',
Lang.bind(this, this._IMStatusChanged));
this._accountMgr.prepare_async(null, Lang.bind(this,
function(mgr) {
let [presence, s, msg] = mgr.get_most_available_presence();
this._previousPresence = presence;
this._IMStatusChanged(mgr, presence, s, msg);
}));
this._userManager = AccountsService.UserManager.get_default();
this._user = this._userManager.get_user(GLib.get_user_name());
this._userLoadedId = this._user.connect('notify::is-loaded',
Lang.bind(this,
this._updateUser));
this._userChangedId = this._user.connect('changed',
Lang.bind(this,
this._updateUser));
},
// Override getColumnWidths()/setColumnWidths() to make the item
// independent from the overall column layout of the menu
getColumnWidths: function() {
return [];
},
setColumnWidths: function(widths) {
this._columnWidths = PopupMenu.PopupBaseMenuItem.prototype.getColumnWidths.call(this);
let sectionWidths = this._section.getColumnWidths();
this._section.setColumnWidths(sectionWidths);
},
_updateUser: function() {
let iconFile = null;
if (this._user.is_loaded) {
this._name.label.set_text(this._user.get_real_name());
iconFile = this._user.get_icon_file();
if (!GLib.file_test(iconFile, GLib.FileTest.EXISTS))
iconFile = null;
} else {
this._name.label.set_text("");
}
if (iconFile)
this._setIconFromFile(iconFile);
else
this._setIconFromName('avatar-default');
},
_setIconFromFile: function(iconFile) {
this._iconBin.set_style('background-image: url("' + iconFile + '");');
this._iconBin.child = null;
},
_setIconFromName: function(iconName) {
this._iconBin.set_style(null);
if (iconName != null) {
let textureCache = St.TextureCache.get_default();
let icon = textureCache.load_icon_name(this._iconBin.get_theme_node(),
iconName,
St.IconType.SYMBOLIC,
DIALOG_ICON_SIZE);
this._iconBin.child = icon;
this._iconBin.show();
} else {
this._iconBin.child = null;
this._iconBin.hide();
}
},
_statusForPresence: function(presence) {
switch(presence) {
case Tp.ConnectionPresenceType.AVAILABLE:
return 'available';
case Tp.ConnectionPresenceType.BUSY:
return 'busy';
case Tp.ConnectionPresenceType.OFFLINE:
return 'offline';
case Tp.ConnectionPresenceType.HIDDEN:
return 'hidden';
case Tp.ConnectionPresenceType.AWAY:
return 'away';
case Tp.ConnectionPresenceType.EXTENDED_AWAY:
return 'xa';
default:
return 'unknown';
}
},
_IMStatusChanged: function(accountMgr, presence, status, message) {
if (presence == Tp.ConnectionPresenceType.AVAILABLE)
this._presence.setStatus(GnomeSession.PresenceStatus.AVAILABLE);
if (!this._expectedPresence || presence != this._expectedPresence)
this._previousPresence = presence;
else
this._expectedPresence = undefined;
let activatedItem;
if (presence == Tp.ConnectionPresenceType.AVAILABLE)
activatedItem = IMStatus.AVAILABLE;
else if (presence == Tp.ConnectionPresenceType.BUSY)
activatedItem = IMStatus.BUSY;
else if (presence == Tp.ConnectionPresenceType.HIDDEN)
activatedItem = IMStatus.HIDDEN;
else if (presence == Tp.ConnectionPresenceType.AWAY)
activatedItem = IMStatus.AWAY;
else if (presence == Tp.ConnectionPresenceType.EXTENDED_AWAY)
activatedItem = IMStatus.IDLE;
else
activatedItem = IMStatus.OFFLINE;
this._combo.setActiveItem(activatedItem);
for (let i = 0; i < IMStatus.LAST; i++) {
if (i == IMStatus.AVAILABLE || i == IMStatus.OFFLINE)
continue; // always visible
this._combo.setItemVisible(i, i == activatedItem);
}
},
_changeIMStatus: function(menuItem, id) {
let [presence, s, msg] = this._accountMgr.get_most_available_presence();
let newPresence, status;
if (id == IMStatus.AVAILABLE) {
newPresence = Tp.ConnectionPresenceType.AVAILABLE;
} else if (id == IMStatus.OFFLINE) {
newPresence = Tp.ConnectionPresenceType.OFFLINE;
} else
return;
status = this._statusForPresence(newPresence);
msg = msg ? msg : "";
this._accountMgr.set_all_requested_presences(newPresence, status, msg);
},
_sessionStatusChanged: function(sessionPresence, sessionStatus) {
let [presence, s, msg] = this._accountMgr.get_most_available_presence();
let newPresence, status;
if (sessionStatus == GnomeSession.PresenceStatus.AVAILABLE) {
newPresence = this._previousPresence;
} else if (sessionStatus == GnomeSession.PresenceStatus.BUSY) {
// Only change presence if the current one is "more present" than
// busy, or if coming back from idle
if (presence == Tp.ConnectionPresenceType.AVAILABLE ||
presence == Tp.ConnectionPresenceType.EXTENDED_AWAY) {
newPresence = Tp.ConnectionPresenceType.BUSY;
} else {
return;
}
} else if (sessionStatus == GnomeSession.PresenceStatus.IDLE) {
// Only change presence if the current one is "more present" than
// idle
if (presence != Tp.ConnectionPresenceType.OFFLINE)
newPresence = Tp.ConnectionPresenceType.EXTENDED_AWAY;
else
return;
} else {
return;
}
if (newPresence == undefined)
return;
status = this._statusForPresence(newPresence);
msg = msg ? msg : "";
this._expectedPresence = newPresence;
this._accountMgr.set_all_requested_presences(newPresence, status, msg);
}
};
function UserMenuButton() {
this._init();
}
UserMenuButton.prototype = {
__proto__: PanelMenu.Button.prototype,
_init: function() {
PanelMenu.Button.prototype._init.call(this, 0.0);
let box = new St.BoxLayout({ name: 'panelStatusMenu' });
this.actor.set_child(box);
this._lockdownSettings = new Gio.Settings({ schema: LOCKDOWN_SCHEMA });
this._userManager = AccountsService.UserManager.get_default();
this._user = this._userManager.get_user(GLib.get_user_name());
this._presence = new GnomeSession.Presence();
this._session = new GnomeSession.SessionManager();
this._haveShutdown = true;
this._account_mgr = Tp.AccountManager.dup()
this._upClient = new UPowerGlib.Client();
this._screenSaverProxy = new ScreenSaver.ScreenSaverProxy();
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
this._iconBox = new St.Bin();
box.add(this._iconBox, { y_align: St.Align.MIDDLE, y_fill: false });
let textureCache = St.TextureCache.get_default();
this._offlineIcon = new St.Icon({ icon_name: 'user-offline',
style_class: 'popup-menu-icon' });
this._availableIcon = new St.Icon({ icon_name: 'user-available',
style_class: 'popup-menu-icon' });
this._busyIcon = new St.Icon({ icon_name: 'user-busy',
style_class: 'popup-menu-icon' });
this._invisibleIcon = new St.Icon({ icon_name: 'user-invisible',
style_class: 'popup-menu-icon' });
this._awayIcon = new St.Icon({ icon_name: 'user-away',
style_class: 'popup-menu-icon' });
this._idleIcon = new St.Icon({ icon_name: 'user-idle',
style_class: 'popup-menu-icon' });
this._presence.connect('StatusChanged',
Lang.bind(this, this._updateSwitch));
this._presence.getStatus(Lang.bind(this, this._updateSwitch));
this._account_mgr.connect('most-available-presence-changed',
Lang.bind(this, this._updatePresenceIcon));
this._account_mgr.prepare_async(null, Lang.bind(this,
function(mgr) {
let [presence, s, msg] = mgr.get_most_available_presence();
this._updatePresenceIcon(mgr, presence, s, msg);
}));
this._name = new St.Label();
box.add(this._name, { y_align: St.Align.MIDDLE, y_fill: false });
this._userLoadedId = this._user.connect('notify::is-loaded', Lang.bind(this, this._updateUserName));
this._userChangedId = this._user.connect('changed', Lang.bind(this, this._updateUserName));
this._createSubMenu();
this._userManager.connect('notify::is-loaded',
Lang.bind(this, this._updateSwitchUser));
this._userManager.connect('user-added',
Lang.bind(this, this._updateSwitchUser));
this._userManager.connect('user-removed',
Lang.bind(this, this._updateSwitchUser));
this._lockdownSettings.connect('changed::' + DISABLE_USER_SWITCH_KEY,
Lang.bind(this, this._updateSwitchUser));
this._lockdownSettings.connect('changed::' + DISABLE_LOG_OUT_KEY,
Lang.bind(this, this._updateLogout));
this._lockdownSettings.connect('changed::' + DISABLE_LOCK_SCREEN_KEY,
Lang.bind(this, this._updateLockScreen));
this._updateSwitchUser();
this._updateLogout();
this._updateLockScreen();
// Whether shutdown is available or not depends on both lockdown
// settings (disable-log-out) and Polkit policy - the latter doesn't
// notify, so we update the menu item each time the menu opens or
// the lockdown setting changes, which should be close enough.
this.menu.connect('open-state-changed', Lang.bind(this,
function(menu, open) {
if (open)
this._updateHaveShutdown();
}));
this._lockdownSettings.connect('changed::' + DISABLE_LOG_OUT_KEY,
Lang.bind(this, this._updateHaveShutdown));
this._upClient.connect('notify::can-suspend', Lang.bind(this, this._updateSuspendOrPowerOff));
},
_onDestroy: function() {
this._user.disconnect(this._userLoadedId);
this._user.disconnect(this._userChangedId);
},
_updateUserName: function() {
if (this._user.is_loaded)
this._name.set_text(this._user.get_real_name());
else
this._name.set_text("");
},
_updateSwitchUser: function() {
let allowSwitch = !this._lockdownSettings.get_boolean(DISABLE_USER_SWITCH_KEY);
if (allowSwitch && this._userManager.can_switch ())
this._loginScreenItem.actor.show();
else
this._loginScreenItem.actor.hide();
},
_updateLogout: function() {
let allowLogout = !this._lockdownSettings.get_boolean(DISABLE_LOG_OUT_KEY);
if (allowLogout)
this._logoutItem.actor.show();
else
this._logoutItem.actor.hide();
},
_updateLockScreen: function() {
let allowLockScreen = !this._lockdownSettings.get_boolean(DISABLE_LOCK_SCREEN_KEY);
if (allowLockScreen)
this._lockScreenItem.actor.show();
else
this._lockScreenItem.actor.hide();
},
_updateHaveShutdown: function() {
this._session.CanShutdownRemote(Lang.bind(this,
function(result, error) {
if (!error) {
this._haveShutdown = result;
this._updateSuspendOrPowerOff();
}
}));
},
_updateSuspendOrPowerOff: function() {
this._haveSuspend = this._upClient.get_can_suspend();
if (!this._suspendOrPowerOffItem)
return;
if (!this._haveShutdown && !this._haveSuspend)
this._suspendOrPowerOffItem.actor.hide();
else
this._suspendOrPowerOffItem.actor.show();
// If we can't suspend show Power Off... instead
// and disable the alt key
if (!this._haveSuspend) {
this._suspendOrPowerOffItem.updateText(_("Power Off..."), null);
} else if (!this._haveShutdown) {
this._suspendOrPowerOffItem.updateText(_("Suspend"), null);
} else {
this._suspendOrPowerOffItem.updateText(_("Suspend"), _("Power Off..."));
}
},
_updateSwitch: function(presence, status) {
let active = status == GnomeSession.PresenceStatus.BUSY;
this._dontDisturbSwitch.setToggleState(active);
},
_updatePresenceIcon: function(accountMgr, presence, status, message) {
if (presence == Tp.ConnectionPresenceType.AVAILABLE)
this._iconBox.child = this._availableIcon;
else if (presence == Tp.ConnectionPresenceType.BUSY)
this._iconBox.child = this._busyIcon;
else if (presence == Tp.ConnectionPresenceType.HIDDEN)
this._iconBox.child = this._invisibleIcon;
else if (presence == Tp.ConnectionPresenceType.AWAY)
this._iconBox.child = this._awayIcon;
else if (presence == Tp.ConnectionPresenceType.EXTENDED_AWAY)
this._iconBox.child = this._idleIcon;
else
this._iconBox.child = this._offlineIcon;
},
_createSubMenu: function() {
let item;
item = new IMStatusChooserItem();
item.connect('activate', Lang.bind(this, this._onMyAccountActivate));
this.menu.addMenuItem(item);
item = new PopupMenu.PopupSwitchMenuItem(_("Do Not Disturb"));
item.connect('activate', Lang.bind(this, this._updatePresenceStatus));
this.menu.addMenuItem(item);
this._dontDisturbSwitch = item;
item = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addMenuItem(item);
item = new PopupMenu.PopupMenuItem(_("Online Accounts"));
item.connect('activate', Lang.bind(this, this._onOnlineAccountsActivate));
this.menu.addMenuItem(item);
item = new PopupMenu.PopupMenuItem(_("System Settings"));
item.connect('activate', Lang.bind(this, this._onPreferencesActivate));
this.menu.addMenuItem(item);
item = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addMenuItem(item);
item = new PopupMenu.PopupMenuItem(_("Lock Screen"));
item.connect('activate', Lang.bind(this, this._onLockScreenActivate));
this.menu.addMenuItem(item);
this._lockScreenItem = item;
item = new PopupMenu.PopupMenuItem(_("Switch User"));
item.connect('activate', Lang.bind(this, this._onLoginScreenActivate));
this.menu.addMenuItem(item);
this._loginScreenItem = item;
item = new PopupMenu.PopupMenuItem(_("Log Out..."));
item.connect('activate', Lang.bind(this, this._onQuitSessionActivate));
this.menu.addMenuItem(item);
this._logoutItem = item;
item = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addMenuItem(item);
item = new PopupMenu.PopupAlternatingMenuItem(_("Suspend"),
_("Power Off..."));
this.menu.addMenuItem(item);
this._suspendOrPowerOffItem = item;
item.connect('activate', Lang.bind(this, this._onSuspendOrPowerOffActivate));
this._updateSuspendOrPowerOff();
},
_updatePresenceStatus: function(item, event) {
let status = item.state ? GnomeSession.PresenceStatus.BUSY
: GnomeSession.PresenceStatus.AVAILABLE;
this._presence.setStatus(status);
},
_onMyAccountActivate: function() {
Main.overview.hide();
let app = Shell.AppSystem.get_default().lookup_setting('gnome-user-accounts-panel.desktop');
app.activate();
},
_onOnlineAccountsActivate: function() {
Main.overview.hide();
let app = Shell.AppSystem.get_default().lookup_setting('gnome-online-accounts-panel.desktop');
app.activate(-1);
},
_onPreferencesActivate: function() {
Main.overview.hide();
let app = Shell.AppSystem.get_default().lookup_app('gnome-control-center.desktop');
app.activate();
},
_onLockScreenActivate: function() {
Main.overview.hide();
this._screenSaverProxy.LockRemote();
},
_onLoginScreenActivate: function() {
Main.overview.hide();
// Ensure we only move to GDM after the screensaver has activated; in some
// OS configurations, the X server may block event processing on VT switch
this._screenSaverProxy.SetActiveRemote(true, Lang.bind(this, function() {
this._userManager.goto_login_session();
}));
},
_onQuitSessionActivate: function() {
Main.overview.hide();
this._session.LogoutRemote(0);
},
_onSuspendOrPowerOffActivate: function() {
Main.overview.hide();
if (this._haveSuspend &&
this._suspendOrPowerOffItem.state == PopupMenu.PopupAlternatingMenuItemState.DEFAULT) {
// Ensure we only suspend after the screensaver has activated
this._screenSaverProxy.SetActiveRemote(true, Lang.bind(this, function() {
this._upClient.suspend_sync(null);
}));
} else {
this._session.ShutdownRemote();
}
}
};

View File

@@ -141,19 +141,13 @@ SearchTab.prototype = {
'edit-find');
this._text.connect('text-changed', Lang.bind(this, this._onTextChanged));
this._text.connect('key-press-event', Lang.bind(this, function (o, e) {
// We can't connect to 'activate' here because search providers
// might want to do something with the modifiers in activateSelected.
let symbol = e.get_key_symbol();
if (symbol == Clutter.Return || symbol == Clutter.KP_Enter) {
if (this._searchTimeoutId > 0) {
Mainloop.source_remove(this._searchTimeoutId);
this._doSearch();
}
this._searchResults.activateSelected();
return true;
this._text.connect('activate', Lang.bind(this, function (se) {
if (this._searchTimeoutId > 0) {
Mainloop.source_remove(this._searchTimeoutId);
this._doSearch();
}
return false;
this._searchResults.activateSelected();
return true;
}));
this._entry.connect('notify::mapped', Lang.bind(this, this._onMapped));
@@ -297,7 +291,7 @@ SearchTab.prototype = {
_doSearch: function () {
this._searchTimeoutId = 0;
let text = this._text.get_text().replace(/^\s+/g, '').replace(/\s+$/g, '');
this._searchResults.doSearch(text);
this._searchResults.updateSearch(text);
return false;
}

View File

@@ -16,7 +16,8 @@ WindowAttentionHandler.prototype = {
this._tracker = Shell.WindowTracker.get_default();
this._tracker.connect('startup-sequence-changed', Lang.bind(this, this._onStartupSequenceChanged));
global.display.connect('window-demands-attention', Lang.bind(this, this._onWindowDemandsAttention));
let display = global.screen.get_display();
display.connect('window-demands-attention', Lang.bind(this, this._onWindowDemandsAttention));
},
_onStartupSequenceChanged : function(tracker) {

View File

@@ -6,7 +6,6 @@ const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Meta = imports.gi.Meta;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
const AltTab = imports.ui.altTab;
const WorkspaceSwitcherPopup = imports.ui.workspaceSwitcherPopup;
@@ -23,7 +22,8 @@ function getDimShader() {
if (dimShader === null)
return null;
if (!dimShader) {
let source = Shell.get_file_contents_utf8_sync(global.datadir + '/shaders/dim-window.glsl');
let [success, source, length] = GLib.file_get_contents(global.datadir +
'/shaders/dim-window.glsl');
try {
let shader = new Clutter.Shader();
shader.set_fragment_source(source, -1);
@@ -119,8 +119,6 @@ WindowManager.prototype = {
this.setKeybindingHandler('switch_to_workspace_down', Lang.bind(this, this._showWorkspaceSwitcher));
this.setKeybindingHandler('switch_windows', Lang.bind(this, this._startAppSwitcher));
this.setKeybindingHandler('switch_group', Lang.bind(this, this._startAppSwitcher));
this.setKeybindingHandler('switch_windows_backward', Lang.bind(this, this._startAppSwitcher));
this.setKeybindingHandler('switch_group_backward', Lang.bind(this, this._startAppSwitcher));
this.setKeybindingHandler('switch_panels', Lang.bind(this, this._startA11ySwitcher));
Main.overview.connect('showing', Lang.bind(this, function() {
@@ -182,7 +180,7 @@ WindowManager.prototype = {
*/
this._minimizing.push(actor);
let primary = Main.layoutManager.primaryMonitor;
let primary = global.get_primary_monitor();
let xDest = primary.x;
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
xDest += primary.width;
@@ -239,7 +237,7 @@ WindowManager.prototype = {
_hasAttachedDialogs: function(window, ignoreWindow) {
var count = 0;
window.foreach_transient(function(win) {
if (win != ignoreWindow && win.is_attached_dialog())
if (win != ignoreWindow && win.get_window_type() == Meta.WindowType.MODAL_DIALOG)
count++;
return false;
});
@@ -247,7 +245,7 @@ WindowManager.prototype = {
},
_checkDimming: function(window, ignoreWindow) {
let shouldDim = this._hasAttachedDialogs(window, ignoreWindow);
let shouldDim = Meta.prefs_get_attach_modal_dialogs() && this._hasAttachedDialogs(window, ignoreWindow);
if (shouldDim && !window._dimmed) {
window._dimmed = true;
@@ -309,7 +307,9 @@ WindowManager.prototype = {
actor._windowType = type;
}));
if (actor.meta_window.is_attached_dialog()) {
if (actor.meta_window.get_window_type() == Meta.WindowType.MODAL_DIALOG
&& Meta.prefs_get_attach_modal_dialogs()
&& actor.get_meta_window().get_transient_for()) {
this._checkDimming(actor.get_meta_window().get_transient_for());
if (this._shouldAnimate()) {
actor.set_scale(1.0, 0.0);
@@ -371,6 +371,7 @@ WindowManager.prototype = {
_destroyWindow : function(shellwm, actor) {
let window = actor.meta_window;
let parent = window.get_transient_for();
if (actor._notifyWindowTypeSignalId) {
window.disconnect(actor._notifyWindowTypeSignalId);
actor._notifyWindowTypeSignalId = 0;
@@ -380,14 +381,12 @@ WindowManager.prototype = {
return win != window;
});
}
if (window.is_attached_dialog()) {
let parent = window.get_transient_for();
while (window.get_window_type() == Meta.WindowType.MODAL_DIALOG
&& parent) {
this._checkDimming(parent, window);
if (!this._shouldAnimate()) {
shellwm.completed_destroy(actor);
return;
}
if (!Meta.prefs_get_attach_modal_dialogs()
|| !this._shouldAnimate())
break;
actor.set_scale(1.0, 1.0);
actor.show();
this._destroying.push(actor);
@@ -535,7 +534,7 @@ WindowManager.prototype = {
let tabPopup = new AltTab.AltTabPopup();
if (!tabPopup.show(backwards, binding))
if (!tabPopup.show(backwards, binding == 'switch_group'))
tabPopup.destroy();
},

View File

@@ -1,7 +1,6 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Clutter = imports.gi.Clutter;
const GConf = imports.gi.GConf;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
@@ -28,8 +27,6 @@ const CLOSE_BUTTON_FADE_TIME = 0.1;
const DRAGGING_WINDOW_OPACITY = 100;
const BUTTON_LAYOUT_KEY = '/desktop/gnome/shell/windows/button_layout';
// Define a layout scheme for small window counts. For larger
// counts we fall back to an algorithm. We need more schemes here
// unless we have a really good algorithm.
@@ -94,38 +91,22 @@ function WindowClone(realWindow) {
WindowClone.prototype = {
_init : function(realWindow) {
this.actor = new Clutter.Clone({ source: realWindow.get_texture(),
reactive: true,
x: realWindow.x,
y: realWindow.y });
this.actor._delegate = this;
this.realWindow = realWindow;
this.metaWindow = realWindow.meta_window;
this.metaWindow._delegate = this;
let [borderX, borderY] = this._getInvisibleBorderPadding();
this._windowClone = new Clutter.Clone({ source: realWindow.get_texture(),
x: -borderX,
y: -borderY });
this.origX = realWindow.x + borderX;
this.origY = realWindow.y + borderY;
let outerRect = realWindow.meta_window.get_outer_rect();
// The MetaShapedTexture that we clone has a size that includes
// the invisible border; this is inconvenient; rather than trying
// to compensate all over the place we insert a ClutterGroup into
// the hierarchy that is sized to only the visible portion.
this.actor = new Clutter.Group({ reactive: true,
x: this.origX,
y: this.origY,
width: outerRect.width,
height: outerRect.height });
this.actor.add_actor(this._windowClone);
this.actor._delegate = this;
this.origX = realWindow.x;
this.origY = realWindow.y;
this._stackAbove = null;
this._sizeChangedId = this.realWindow.connect('size-changed',
Lang.bind(this, this._onRealWindowSizeChanged));
this._sizeChangedId = this.realWindow.connect('size-changed', Lang.bind(this, function() {
this.emit('size-changed');
}));
this._realWindowDestroyId = this.realWindow.connect('destroy',
Lang.bind(this, this._disconnectRealWindowSignals));
@@ -177,7 +158,7 @@ WindowClone.prototype = {
// will look funny.
if (!this._selected &&
this.metaWindow != global.display.focus_window)
this.metaWindow != global.screen.get_display().focus_window)
this._zoomEnd();
}
},
@@ -192,32 +173,6 @@ WindowClone.prototype = {
this._realWindowDestroyId = 0;
},
_getInvisibleBorderPadding: function() {
// We need to adjust the position of the actor because of the
// consequences of invisible borders -- in reality, the texture
// has an extra set of "padding" around it that we need to trim
// down.
// The outer rect paradoxically is the smaller rectangle,
// containing the positions of the visible frame. The input
// rect contains everything, including the invisible border
// padding.
let outerRect = this.metaWindow.get_outer_rect();
let inputRect = this.metaWindow.get_input_rect();
let [borderX, borderY] = [outerRect.x - inputRect.x,
outerRect.y - inputRect.y];
return [borderX, borderY];
},
_onRealWindowSizeChanged: function() {
let [borderX, borderY] = this._getInvisibleBorderPadding();
let outerRect = this.metaWindow.get_outer_rect();
this.actor.set_size(outerRect.width, outerRect.height);
this._windowClone.set_position(-borderX, -borderY);
this.emit('size-changed');
},
_onDestroy: function() {
this._disconnectRealWindowSignals();
@@ -267,12 +222,8 @@ WindowClone.prototype = {
let [width, height] = this.actor.get_transformed_size();
let monitorIndex = this.metaWindow.get_monitor();
let monitor = Main.layoutManager.monitors[monitorIndex];
let availArea = new Meta.Rectangle({ x: monitor.x,
y: monitor.y,
width: monitor.width,
height: monitor.height });
if (monitorIndex == Main.layoutManager.primaryIndex) {
let availArea = global.get_monitors()[monitorIndex];
if (monitorIndex == global.get_primary_monitor_index()) {
availArea.y += Main.panel.actor.height;
availArea.height -= Main.panel.actor.height;
}
@@ -354,21 +305,6 @@ WindowClone.prototype = {
this.emit('drag-begin');
},
_getWorkspaceActor : function() {
let index = this.metaWindow.get_workspace().index();
return Main.overview.workspaces.getWorkspaceByIndex(index);
},
handleDragOver : function(source, actor, x, y, time) {
let workspace = this._getWorkspaceActor();
return workspace.handleDragOver(source, actor, x, y, time);
},
acceptDrop : function(source, actor, x, y, time) {
let workspace = this._getWorkspaceActor();
workspace.acceptDrop(source, actor, x, y, time);
},
_onDragCancelled : function (draggable, time) {
this.emit('drag-cancelled');
},
@@ -499,20 +435,9 @@ WindowOverlay.prototype = {
let button = this.closeButton;
let title = this.title;
let gconf = GConf.Client.get_default();
let layout = gconf.get_string(BUTTON_LAYOUT_KEY);
let rtl = St.Widget.get_default_direction() == St.TextDirection.RTL;
let split = layout.split(":");
let side;
if (split[0].indexOf("close") > -1)
side = rtl ? St.Side.RIGHT : St.Side.LEFT;
else
side = rtl ? St.Side.LEFT : St.Side.RIGHT;
let buttonX;
let buttonY = cloneY - (button.height - button._overlap);
if (side == St.Side.LEFT)
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
buttonX = cloneX - (button.width - button._overlap);
else
buttonX = cloneX + (cloneWidth - button._overlap);
@@ -639,7 +564,7 @@ Workspace.prototype = {
this._height = 0;
this.monitorIndex = monitorIndex;
this._monitor = Main.layoutManager.monitors[this.monitorIndex];
this._monitor = global.get_monitors()[this.monitorIndex];
this._windowOverlaysGroup = new Clutter.Group();
// Without this the drop area will be overlapped.
this._windowOverlaysGroup.set_size(0, 0);
@@ -694,7 +619,6 @@ Workspace.prototype = {
function () {
this._dropRect.set_position(x, y);
this._dropRect.set_size(width, height);
this.positionWindows(WindowPositionFlags.ANIMATE);
return false;
}));
@@ -717,6 +641,16 @@ Workspace.prototype = {
return this._windows.length == 0;
},
/**
* setReactive:
* @reactive: %true iff the workspace should be reactive
*
* Set the workspace (desktop) reactive
**/
setReactive: function(reactive) {
this.actor.reactive = reactive;
},
// Only use this for n <= 20 say
_factorial: function(n) {
let result = 1;
@@ -1482,7 +1416,7 @@ Workspace.prototype = {
time);
return true;
} else if (source.shellWorkspaceLaunch) {
source.shellWorkspaceLaunch({ workspace: this.metaWorkspace ? this.metaWorkspace.index() : -1,
source.shellWorkspaceLaunch({ workspace: this.metaWorkspace,
timestamp: time });
return true;
}

View File

@@ -56,7 +56,7 @@ WorkspaceSwitcherPopup.prototype = {
_getPreferredHeight : function (actor, forWidth, alloc) {
let children = this._list.get_children();
let primary = Main.layoutManager.primaryMonitor;
let primary = global.get_primary_monitor();
let availHeight = primary.height;
availHeight -= Main.panel.actor.height;
@@ -82,7 +82,7 @@ WorkspaceSwitcherPopup.prototype = {
},
_getPreferredWidth : function (actor, forHeight, alloc) {
let primary = Main.layoutManager.primaryMonitor;
let primary = global.get_primary_monitor();
this._childWidth = Math.round(this._childHeight * primary.width / primary.height);
alloc.min_size = this._childWidth;
@@ -125,7 +125,7 @@ WorkspaceSwitcherPopup.prototype = {
},
_position: function() {
let primary = Main.layoutManager.primaryMonitor;
let primary = global.get_primary_monitor();
this._container.x = primary.x + Math.floor((primary.width - this._container.width) / 2);
this._container.y = primary.y + Main.panel.actor.height +
Math.floor(((primary.height - Main.panel.actor.height) - this._container.height) / 2);

View File

@@ -146,7 +146,7 @@ function WorkspaceThumbnail(metaWorkspace) {
WorkspaceThumbnail.prototype = {
_init : function(metaWorkspace) {
this.metaWorkspace = metaWorkspace;
this.monitorIndex = Main.layoutManager.primaryIndex;
this.monitorIndex = global.get_primary_monitor_index();
this.actor = new St.Group({ reactive: true,
clip_to_allocation: true,
@@ -167,10 +167,10 @@ WorkspaceThumbnail.prototype = {
return true;
}));
this._background = Meta.BackgroundActor.new_for_screen(global.screen);
this._background = new Clutter.Clone({ source: global.background_actor });
this._contents.add_actor(this._background);
let monitor = Main.layoutManager.primaryMonitor;
let monitor = global.get_primary_monitor();
this.setPorthole(monitor.x, monitor.y, monitor.width, monitor.height);
let windows = global.get_window_actors().filter(this._isMyWindow, this);
@@ -178,11 +178,6 @@ WorkspaceThumbnail.prototype = {
// Create clones for windows that should be visible in the Overview
this._windows = [];
for (let i = 0; i < windows.length; i++) {
windows[i].meta_window._minimizedChangedId =
windows[i].meta_window.connect('notify::minimized',
Lang.bind(this,
this._updateMinimized));
if (this._isOverviewWindow(windows[i])) {
this._addWindowClone(windows[i]);
}
@@ -262,18 +257,11 @@ WorkspaceThumbnail.prototype = {
return;
// Check if window still should be here
if (win && this._isMyWindow(win) && this._isOverviewWindow(win))
if (win && this._isMyWindow(win))
return;
let clone = this._windows[index];
this._windows.splice(index, 1);
if (win && this._isOverviewWindow(win)) {
if (metaWin._minimizedChangedId) {
metaWin.disconnect(metaWin._minimizedChangedId);
delete metaWin._minimizedChangedId;
}
}
clone.destroy();
},
@@ -302,11 +290,6 @@ WorkspaceThumbnail.prototype = {
if (this._lookupIndex (metaWin) != -1)
return;
if (!metaWin._minimizedChangedId)
metaWin._minimizedChangedId = metaWin.connect('notify::minimized',
Lang.bind(this,
this._updateMinimized));
if (!this._isMyWindow(win) || !this._isOverviewWindow(win))
return;
@@ -333,13 +316,6 @@ WorkspaceThumbnail.prototype = {
}
},
_updateMinimized: function(metaWin) {
if (metaWin.minimized)
this._doRemoveWindow(metaWin);
else
this._doAddWindow(metaWin);
},
destroy : function() {
this.actor.destroy();
},
@@ -350,14 +326,6 @@ WorkspaceThumbnail.prototype = {
global.screen.disconnect(this._windowEnteredMonitorId);
global.screen.disconnect(this._windowLeftMonitorId);
for (let i = 0; i < this._windows.length; i++) {
let metaWin = this._windows[i].metaWindow;
if (metaWin._minimizedChangedId) {
metaWin.disconnect(metaWin._minimizedChangedId);
delete metaWin._minimizedChangedId;
}
}
this._windows = [];
this.actor = null;
},
@@ -371,8 +339,7 @@ WorkspaceThumbnail.prototype = {
// Tests if @win should be shown in the Overview
_isOverviewWindow : function (win) {
let tracker = Shell.WindowTracker.get_default();
return tracker.is_window_interesting(win.get_meta_window()) &&
win.get_meta_window().showing_on_its_workspace();
return tracker.is_window_interesting(win.get_meta_window());
},
// Create a clone of a (non-desktop) window and add it to the window list
@@ -452,7 +419,7 @@ WorkspaceThumbnail.prototype = {
time);
return true;
} else if (source.shellWorkspaceLaunch) {
source.shellWorkspaceLaunch({ workspace: this.metaWorkspace ? this.metaWorkspace.index() : -1,
source.shellWorkspaceLaunch({ workspace: this.metaWorkspace,
timestamp: time });
return true;
}
@@ -528,7 +495,7 @@ ThumbnailsBox.prototype = {
// The "porthole" is the portion of the screen that we show in the workspaces
let panelHeight = Main.panel.actor.height;
let monitor = Main.layoutManager.primaryMonitor;
let monitor = global.get_primary_monitor();
this._porthole = {
x: monitor.x,
y: monitor.y + panelHeight,

View File

@@ -49,12 +49,9 @@ WorkspacesView.prototype = {
this._height = 0;
this._x = 0;
this._y = 0;
this._clipX = 0;
this._clipY = 0;
this._clipWidth = 0;
this._clipHeight = 0;
this._workspaceRatioSpacing = 0;
this._spacing = 0;
this._lostWorkspaces = [];
this._animating = false; // tweening
this._scrolling = false; // swipe-scrolling
this._animatingScroll = false; // programatically updating the adjustment
@@ -70,10 +67,10 @@ WorkspacesView.prototype = {
this._workspaces[activeWorkspaceIndex].actor.raise_top();
this._extraWorkspaces = [];
let monitors = Main.layoutManager.monitors;
let monitors = global.get_monitors();
let m = 0;
for (let i = 0; i < monitors.length; i++) {
if (i == Main.layoutManager.primaryIndex)
if (i == global.get_primary_monitor_index())
continue;
let ws = new Workspace.Workspace(null, i);
this._extraWorkspaces[m++] = ws;
@@ -96,8 +93,7 @@ WorkspacesView.prototype = {
this._overviewShownId =
Main.overview.connect('shown',
Lang.bind(this, function() {
this.actor.set_clip(this._clipX, this._clipY,
this._clipWidth, this._clipHeight);
this.actor.set_clip(this._x, this._y, this._width, this._height);
}));
this._scrollAdjustment = new St.Adjustment({ value: activeWorkspaceIndex,
@@ -141,13 +137,6 @@ WorkspacesView.prototype = {
this._workspaces[i].setGeometry(x, y, width, height);
},
setClipRect: function(x, y, width, height) {
this._clipX = x;
this._clipY = y;
this._clipWidth = width;
this._clipHeight = height;
},
_lookupWorkspaceForMetaWindow: function (metaWindow) {
for (let i = 0; i < this._workspaces.length; i++) {
if (this._workspaces[i].containsMetaWindow(metaWindow))
@@ -161,10 +150,6 @@ WorkspacesView.prototype = {
return this._workspaces[active];
},
getWorkspaceByIndex: function(index) {
return this._workspaces[index];
},
hide: function() {
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
let activeWorkspace = this._workspaces[activeWorkspaceIndex];
@@ -239,6 +224,27 @@ WorkspacesView.prototype = {
this._updateVisibility();
}
}
for (let l = 0; l < this._lostWorkspaces.length; l++) {
let workspace = this._lostWorkspaces[l];
Tweener.removeTweens(workspace.actor);
workspace.actor.show();
workspace.hideWindowsOverlays();
if (showAnimation) {
Tweener.addTween(workspace.actor,
{ y: workspace.x,
time: WORKSPACE_SWITCH_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this,
this._cleanWorkspaces)
});
} else {
this._cleanWorkspaces();
}
}
},
_updateVisibility: function() {
@@ -259,6 +265,17 @@ WorkspacesView.prototype = {
}
},
_cleanWorkspaces: function() {
if (this._lostWorkspaces.length == 0)
return;
for (let l = 0; l < this._lostWorkspaces.length; l++)
this._lostWorkspaces[l].destroy();
this._lostWorkspaces = [];
this._updateWorkspaceActors(false);
},
_updateScrollAdjustment: function(index, showAnimation) {
if (this._scrolling)
return;
@@ -281,9 +298,12 @@ WorkspacesView.prototype = {
}
},
updateWorkspaces: function(oldNumWorkspaces, newNumWorkspaces) {
updateWorkspaces: function(oldNumWorkspaces, newNumWorkspaces, lostWorkspaces) {
let active = global.screen.get_active_workspace_index();
for (let l = 0; l < lostWorkspaces.length; l++)
lostWorkspaces[l].disconnectAll();
Tweener.addTween(this._scrollAdjustment,
{ upper: newNumWorkspaces,
time: WORKSPACE_SWITCH_TIME,
@@ -291,13 +311,12 @@ WorkspacesView.prototype = {
});
if (newNumWorkspaces > oldNumWorkspaces) {
for (let w = oldNumWorkspaces; w < newNumWorkspaces; w++) {
this._workspaces[w].setGeometry(this._x, this._y,
this._width, this._height);
for (let w = oldNumWorkspaces; w < newNumWorkspaces; w++)
this.actor.add_actor(this._workspaces[w].actor);
}
this._updateWorkspaceActors(false);
} else {
this._lostWorkspaces = lostWorkspaces;
}
this._scrollToActive(true);
@@ -383,7 +402,7 @@ WorkspacesView.prototype = {
this._extraWorkspaces[i].setReservedSlot(dragEvent.dragActor._delegate);
}
let primary = Main.layoutManager.primaryMonitor;
let primary = global.get_primary_monitor();
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
let topWorkspace, bottomWorkspace;
@@ -449,7 +468,7 @@ WorkspacesView.prototype = {
Mainloop.source_remove(this._timeoutId);
this._timeoutId = 0;
}
DND.removeDragMonitor(this._dragMonitor);
DND.removeMonitor(this._dragMonitor);
this._inDrag = false;
for (let i = 0; i < this._workspaces.length; i++)
@@ -550,7 +569,8 @@ WorkspacesDisplay.prototype = {
controls.connect('scroll-event',
Lang.bind(this, this._onScrollEvent));
this._monitorIndex = Main.layoutManager.primaryIndex;
this._monitorIndex = global.get_primary_monitor_index();
this._monitor = global.get_monitors()[this._monitorIndex];
this._thumbnailsBox = new WorkspaceThumbnail.ThumbnailsBox();
controls.add_actor(this._thumbnailsBox.actor);
@@ -566,10 +586,7 @@ WorkspacesDisplay.prototype = {
this._updateAlwaysZoom();
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._updateAlwaysZoom));
global.screen.connect('notify::n-workspaces',
Lang.bind(this, this._workspacesChanged));
global.screen.connect('monitors-changed', Lang.bind(this, this._updateAlwaysZoom));
Main.xdndHandler.connect('drag-begin', Lang.bind(this, function(){
this._alwaysZoomOut = true;
}));
@@ -579,6 +596,7 @@ WorkspacesDisplay.prototype = {
this._updateAlwaysZoom();
}));
this._nWorkspacesNotifyId = 0;
this._switchWorkspaceNotifyId = 0;
this._itemDragBeginId = 0;
@@ -608,6 +626,10 @@ WorkspacesDisplay.prototype = {
this.workspacesView = new WorkspacesView(this._workspaces);
this._updateWorkspacesGeometry();
this._nWorkspacesNotifyId =
global.screen.connect('notify::n-workspaces',
Lang.bind(this, this._workspacesChanged));
this._restackedNotifyId =
global.screen.connect('restacked',
Lang.bind(this, this._onRestacked));
@@ -638,6 +660,10 @@ WorkspacesDisplay.prototype = {
this._controls.hide();
this._thumbnailsBox.hide();
if (this._nWorkspacesNotifyId > 0) {
global.screen.disconnect(this._nWorkspacesNotifyId);
this._nWorkspacesNotifyId = 0;
}
if (this._restackedNotifyId > 0){
global.screen.disconnect(this._restackedNotifyId);
this._restackedNotifyId = 0;
@@ -686,15 +712,10 @@ WorkspacesDisplay.prototype = {
},
_updateAlwaysZoom: function() {
// Always show the pager if workspaces are actually used,
// e.g. there are windows on more than one
this._alwaysZoomOut = global.screen.n_workspaces > 2;
this._alwaysZoomOut = false;
if (this._alwaysZoomOut)
return;
let monitors = Main.layoutManager.monitors;
let primary = Main.layoutManager.primaryMonitor;
let monitors = global.get_monitors();
let primary = global.get_primary_monitor();
/* Look for any monitor to the right of the primary, if there is
* one, we always keep zoom out, otherwise its hard to reach
@@ -762,13 +783,6 @@ WorkspacesDisplay.prototype = {
let rtl = (St.Widget.get_default_direction () == St.TextDirection.RTL);
let clipWidth = width - controlsVisible;
let clipHeight = (fullHeight / fullWidth) * clipWidth;
let clipX = rtl ? x + controlsVisible : x;
let clipY = y + (fullHeight - clipHeight) / 2;
this.workspacesView.setClipRect(clipX, clipY, clipWidth, clipHeight);
if (this._zoomOut) {
width -= controlsNatural;
if (rtl)
@@ -807,12 +821,6 @@ WorkspacesDisplay.prototype = {
if (oldNumWorkspaces == newNumWorkspaces)
return;
this._updateAlwaysZoom();
this._updateZoom();
if (this.workspacesView == null)
return;
let lostWorkspaces = [];
if (newNumWorkspaces > oldNumWorkspaces) {
// Assume workspaces are only added at the end
@@ -838,23 +846,24 @@ WorkspacesDisplay.prototype = {
lostWorkspaces = this._workspaces.splice(removedIndex,
removedNum);
for (let l = 0; l < lostWorkspaces.length; l++) {
lostWorkspaces[l].disconnectAll();
lostWorkspaces[l].destroy();
}
// Don't let the user try to select this workspace as it's
// making its exit.
for (let l = 0; l < lostWorkspaces.length; l++)
lostWorkspaces[l].setReactive(false);
this._thumbnailsBox.removeThumbmails(removedIndex, removedNum);
}
this.workspacesView.updateWorkspaces(oldNumWorkspaces,
newNumWorkspaces);
newNumWorkspaces,
lostWorkspaces);
},
_updateZoom : function() {
if (Main.overview.animationInProgress)
return;
let shouldZoom = this._alwaysZoomOut || this._controls.hover;
let shouldZoom = this._alwaysZoomOut || this._controls.hover || (this._inDrag && !this._cancelledDrag);
if (shouldZoom != this._zoomOut) {
this._zoomOut = shouldZoom;
this._updateWorkspacesGeometry();
@@ -878,22 +887,12 @@ WorkspacesDisplay.prototype = {
_dragBegin: function() {
this._inDrag = true;
this._cancelledDrag = false;
this._dragMonitor = {
dragMotion: Lang.bind(this, this._onDragMotion)
};
DND.addDragMonitor(this._dragMonitor);
this._updateZoom();
},
_dragCancelled: function() {
this._cancelledDrag = true;
DND.removeDragMonitor(this._dragMonitor);
},
_onDragMotion: function(dragEvent) {
let controlsHovered = this._controls.contains(dragEvent.targetActor);
this._controls.set_hover(controlsHovered);
return DND.DragMotionResult.CONTINUE;
this._updateZoom();
},
_dragEnd: function() {

View File

@@ -36,48 +36,53 @@ visually attractive and easy to use experience.
.SH OPTIONS
.TP
.B \-\-replace
Replace the running window manager
.B \-r, \-\-replace
Replace the running metacity/gnome-panel
.br
.TP
.B \-\-sm-disable
Disable connection to the session manager
.B \-v, \-\-verbose
Shows details about the results of running `gnome-shell'.
.br
.TP
.B \-\-sm-client-id=ID
Specify session management ID
.B \-g, \-\-debug
Run under a debugger
.br
.TP
.B \-\-sm-save-file=FILE
Initialize session from savefile
.br
.TP
.B \-\-screen=SCREEN
X screen to use
.br
.TP
.B \-d, \-\-display=DISPLAY
X display to use
.B \-\-debug\-command
Command to use for debugging (defaults to 'gdb \-\-args')
.br
.TP
.B \-\-sync
Make X calls synchronous
.br
Make X calls synchronously, useful when debugging down X errors
.br
.TP
.B \-\-version
Print version and exit
.B \-\-xephyr
Run a debugging instance inside Xephyr
.br
.TP
.B \-\-help
Display help and exit
.B \-\-geometry
Specify Xephyr screen geometry
.br
.TP
.B \-w, \-\-wide
Use widescreen (1280x800) with Xephyr
.br
.TP
.B \-\-create\-extension
Create a new GNOME Shell extension
.TP
.B \-\-eval\-file
Evaluate the contents of the given JavaScript file
.br
.SH BUGS

View File

@@ -1,12 +1,10 @@
af
an
ar
be
bg
bn
bn_IN
ca
ca@valencia
cs
da
de
@@ -33,7 +31,6 @@ kn
lt
lv
mr
ms
nb
nl
nn
@@ -49,7 +46,6 @@ sr
sr@latin
sv
ta
te
th
tr
ug

View File

@@ -1,21 +1,15 @@
data/gnome-shell.desktop.in.in
data/org.gnome.shell.gschema.xml.in
js/gdm/loginDialog.js
js/misc/util.js
js/ui/appDisplay.js
js/ui/appFavorites.js
js/ui/autorunManager.js
js/ui/calendar.js
js/ui/contactDisplay.js
js/ui/dash.js
js/ui/dateMenu.js
js/ui/docDisplay.js
js/ui/endSessionDialog.js
js/ui/keyboard.js
js/ui/lookingGlass.js
js/ui/messageTray.js
js/ui/networkAgent.js
js/ui/notificationDaemon.js
js/ui/overview.js
js/ui/panel.js
js/ui/placeDisplay.js
@@ -23,7 +17,7 @@ js/ui/polkitAuthenticationAgent.js
js/ui/popupMenu.js
js/ui/runDialog.js
js/ui/searchDisplay.js
js/ui/shellMountOperation.js
js/ui/statusMenu.js
js/ui/status/accessibility.js
js/ui/status/bluetooth.js
js/ui/status/keyboard.js
@@ -31,10 +25,11 @@ js/ui/status/network.js
js/ui/status/power.js
js/ui/status/volume.js
js/ui/telepathyClient.js
js/ui/userMenu.js
js/ui/viewSelector.js
js/ui/windowAttentionHandler.js
js/ui/workspacesView.js
src/gvc/gvc-mixer-control.c
src/gdmuser/gdm-user.c
src/main.c
src/shell-app.c
src/shell-app-system.c

1217
po/be.po

File diff suppressed because it is too large Load Diff

719
po/bg.po

File diff suppressed because it is too large Load Diff

553
po/ca.po

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

739
po/de.po

File diff suppressed because it is too large Load Diff

1118
po/es.po

File diff suppressed because it is too large Load Diff

849
po/fa.po

File diff suppressed because it is too large Load Diff

1248
po/ga.po

File diff suppressed because it is too large Load Diff

717
po/gl.po

File diff suppressed because it is too large Load Diff

699
po/he.po

File diff suppressed because it is too large Load Diff

836
po/id.po

File diff suppressed because it is too large Load Diff

619
po/it.po

File diff suppressed because it is too large Load Diff

868
po/lt.po

File diff suppressed because it is too large Load Diff

609
po/lv.po

File diff suppressed because it is too large Load Diff

1348
po/ms.po

File diff suppressed because it is too large Load Diff

643
po/nb.po

File diff suppressed because it is too large Load Diff

767
po/pa.po

File diff suppressed because it is too large Load Diff

1129
po/ru.po

File diff suppressed because it is too large Load Diff

669
po/sk.po

File diff suppressed because it is too large Load Diff

586
po/sl.po

File diff suppressed because it is too large Load Diff

756
po/sv.po

File diff suppressed because it is too large Load Diff

1254
po/te.po

File diff suppressed because it is too large Load Diff

750
po/vi.po

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

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