Compare commits

..

27 Commits

Author SHA1 Message Date
Cosimo Cecchi
3124e82838 search: make sure to pass a timestamp to LaunchSearch()
Do not repeat past mistakes while we're still in time.

https://bugzilla.gnome.org/show_bug.cgi?id=690009
2012-12-10 17:44:28 -05:00
Cosimo Cecchi
6c4daaaa71 overview: streamline sides state change
Instead of dirty tricks like connecting to "notify::visible" on the dash
after we hide it, split the page-change signal into
before/after-page-change, and turn setSideControlsVisibility() into a
better state machine.
2012-12-10 15:51:02 -05:00
Cosimo Cecchi
3768e85673 workspaceThumbnails: restore previous slide-out behavior
Start with the workspace selector half-visible, then slide it out either
on hover, when in drag, or when showing multiple workspaces/when using
an external monitor with windows on it.
2012-12-10 15:51:02 -05:00
Cosimo Cecchi
d1c72e1e4c overview: also hide the messsage tray ghost when switching to search 2012-12-10 15:51:02 -05:00
Tanner Doshier
2498497cc1 overview, viewSelector: show/hide workspaces on apps button 2012-12-10 15:51:02 -05:00
Tanner Doshier
7c3c6da368 overview: Connect item-drag signals to show/hide overview elements
Anytime we begin dragging an app launcher, ensure the overview elements are
showing. While searching, if an item-drag ends or is cancelled, hide the
overview elements, but don't switch back to windows (which was the old
behavior).

https://bugzilla.gnome.org/show_bug.cgi?id=682050
2012-12-10 15:51:02 -05:00
Tanner Doshier
09e7ab5611 overview: hide side controls when searching
Hide the elements when the search is active. Show them if the search
is cancelled.

https://bugzilla.gnome.org/show_bug.cgi?id=682050
2012-12-10 15:51:02 -05:00
Tanner Doshier
53cff07eef messageTray: don't assume we're always visible in the overview
Since we'll hide the message tray when searching in a future commit.

https://bugzilla.gnome.org/show_bug.cgi?id=682050
2012-12-10 15:51:02 -05:00
Tanner Doshier
cfed6e401d viewSelector: this.active --> this._searchActive
'active' isn't terribly clear about just what is active; also, make it
private, remove an useless extra object state we were saving, and
refactor some messy code.

https://bugzilla.gnome.org/show_bug.cgi?id=682050
2012-12-10 15:51:02 -05:00
Tanner Doshier
12b041a569 dash: Add show/hide methods
https://bugzilla.gnome.org/show_bug.cgi?id=682050
2012-12-10 15:51:02 -05:00
Jasper St. Pierre
f2dd94776c overview: Center the main overview group accordingly
Using the same logic as the panel which smartly centers everything,
smartly center the overview contents if we have enough space to do so.
2012-12-10 15:51:02 -05:00
Jasper St. Pierre
c0f9c52ba6 panel: Abstract the centered panel logic out into a ClutterLayoutManager
Since we want to use this in the overview as well, put it into centerLayout.js
2012-12-10 15:51:02 -05:00
Tanner Doshier
2ee8b0e427 workspaceThumbnail: Add keyboard nav to ThumbnailsBox
https://bugzilla.gnome.org/show_bug.cgi?id=682050
2012-12-10 15:51:02 -05:00
Tanner Doshier
b250a72fcf overview, workspacesView: Use ThumbnailsBox for independent workspace thumbnails
Both WorkspacesDisplay and ThumbnailsBox need to know when windows have been
restacked. Instead of each tracking changes on their own or trying to call
each other, have the overview keep track and do the calculations, emitting
a signal with the result.

https://bugzilla.gnome.org/show_bug.cgi?id=682050
2012-12-10 15:51:01 -05:00
Tanner Doshier
cf08745f6c workspaceThumbnail: Rename show(), hide() and add new methods
show() and hide() now slide the thumbnails in and out, respectively. The old
show and hide are now _createThumbnails and _destroyThumbnails. We only care
about these thumbnails while in the overview, so create them when entering and
destroy them on exit.

https://bugzilla.gnome.org/show_bug.cgi?id=682050
2012-12-10 15:51:01 -05:00
Tanner Doshier
58023c81ad workspaceThumbnail: React to scroll event
https://bugzilla.gnome.org/show_bug.cgi?id=682050
2012-12-10 15:51:01 -05:00
Tanner Doshier
7e8968da52 workspaceThumbnail: Make ThumbnailsBox track workspace changes itself
https://bugzilla.gnome.org/show_bug.cgi?id=682050
2012-12-10 15:51:01 -05:00
Tanner Doshier
fce1d8157e overview: overview as boxlayouts
https://bugzilla.gnome.org/show_bug.cgi?id=682286
2012-12-10 15:51:00 -05:00
Cosimo Cecchi
3b5de01ed6 search: remove more dead code
https://bugzilla.gnome.org/show_bug.cgi?id=681797
2012-12-10 15:49:04 -05:00
Cosimo Cecchi
a61da42aa7 search: remove SearchProvider base class
This is causing more confusion than anything else these days; the DBus
API is properly documented now and that's what people are expected to
use, the rest are implementation details we're not interested in
exposing.

https://bugzilla.gnome.org/show_bug.cgi?id=681797
2012-12-10 15:49:04 -05:00
Cosimo Cecchi
81acfdbfc3 search: remove SearchResultDisplay base class
It's unused, and the clear() method is just wrong. Remove it.

https://bugzilla.gnome.org/show_bug.cgi?id=681797
2012-12-10 15:49:04 -05:00
Tanner Doshier
159b789443 appDisplay: Don't use icons in results for settings
The search results are not necessarily improved by including icons, so don't.

https://bugzilla.gnome.org/show_bug.cgi?id=681797
2012-12-10 15:49:04 -05:00
Tanner Doshier
c07b715a3a remoteSearch: We do not need a fallback for createIcon
Remote providers no longer have access to a grid layout, where an icon is
a requirement. If they don't specify an icon, don't create one.

https://bugzilla.gnome.org/show_bug.cgi?id=681797
2012-12-10 15:49:04 -05:00
Tanner Doshier
1bb09fd0f1 searchDisplay: Add ListSearchResult and ListSearchResults
These are for all search results except apps (and Wanda).
We also simplify a bit the packing of search results, which removes some
ugly code in navigateFocus() where we needed to call
st_widget_navigate_focus() twice, since the grid icon was composed by
two nested boxes, both focusable.

https://bugzilla.gnome.org/show_bug.cgi?id=681797
2012-12-10 15:49:04 -05:00
Cosimo Cecchi
4441541da6 searchDisplay: simplify drag actor source code
Make sure this._dragActorSource is always set from _init() to siplify
code.

https://bugzilla.gnome.org/show_bug.cgi?id=681797
2012-12-10 15:49:04 -05:00
Tanner Doshier
afee4d6925 popupMenu: Break separator drawing code out of PopupSeparatorMenuItem
And into a separate HorizontalSeparatorClass.

https://bugzilla.gnome.org/show_bug.cgi?id=681797
2012-12-10 15:49:04 -05:00
Tanner Doshier
b62bd0e62f searchDisplay, and others: Switch from provider title to provider icon
Display a '+' icon on the provider icon if there are more results that are
hidden. If the provider icon is clicked, ask the provider to launch itself and
perform a search with the current terms.

https://bugzilla.gnome.org/show_bug.cgi?id=681797
2012-12-10 15:49:04 -05:00
138 changed files with 15683 additions and 21448 deletions

View File

@@ -16,7 +16,6 @@ EXTRA_DIST = \
# These are files checked into Git that we don't want to distribute
DIST_EXCLUDE = \
.gitignore \
.gitmodules \
gnome-shell.doap \
HACKING \
MAINTAINERS \

154
NEWS
View File

@@ -1,157 +1,3 @@
3.7.5
=====
* MessageTray: pass keyboard events to tray icons [Giovanni; #687425]
* network: add support for virtual devices (vlan, bond, bridge) [Dan; #677144]
* gdm: Allow right-clicking buttons for left-handed users [Jasper; #688748]
* Make list search results span all available horizontal space [Tanner; #691967]
* Make Show-Applications button depress when held down [Hashem; #692319]
* Set a max width on search results [Cosimo; #692453]
* Reserve scrollbar allocation for automatic policy [Cosimo; #686881]
* Improve scaling algorithm for window thumbnails [Jasper; #686944]
* Fix launching settings panels after g-c-c changes [Jasper; #692483]
* Stop launching applications from empty searches [Hashem; #692391]
* Implement per-source notification filtering [Giovanni; #685926]
* ScreenShield: Omit ActiveChanged() signal at end of fade [Giovanni; #691964]
* ScreenShield: Lower the shield on idle before locking [Giovanni; #692560]
* Make previews of minimized windows translucent in overview [Florian; #692999]
* windowManager: Respect icon geometry when minimizing [Florian; #692997]
* ScreenShield: Only show lock icon when actually locked [Giovanni; #693007]
* general: Use & instead of 'and' for Settings panels [Jeremy; #689590]
* network: Add support for new ModemManager1 interface [Aleksander; #687359]
* network: Handle LTE-only modems as GSM ones [Aleksander; #688144]
* mobile-providers: Port to libnm-gtk [Aleksander; #688943]
* general: Consistently use Title Case in top bar [Jeremy; #689589]
* panel: Add :overview pseudo class while in overview [Florian; #693218]
* sessionMode: Add support for mode-specific styling [Florian; #693219]
* loginManager: Make suspend a NOP in the ConsoleKit patch [Florian; #693162]
* screenShield: Inhibit suspend until the screen is locked [Florian; #686482]
* Misc bug fixes and cleanups [Jasper, Giovanni, Rui, Cosimo, Florian, Stefano,
Adel, Yanko; #691745, #691731, #690171, #689091, #691976, #691963, #684279,
#692052, #692091, #642831, #692454, #692715, #692678, #692723, #692677,
#683986, #692693, #692749, #692948, #692995, #692996, #692994, #677215,
#692586, #693067, #693031, #693049, #643111, #693161, #693220]
Contributors:
Jeremy Bicha, Giovanni Campagna, Cosimo Cecchi, Tanner Doshier,
Stefano Facchini, Adel Gadllah, Yanko Kaneti, Rui Matos, Aleksander Morgado,
Florian Müllner, Hashem Nasarat, Jasper St. Pierre, Dan Winship
Translations:
Duarte Loreto [pt], Daniel Mustieles [es], Kjartan Maraas [nb],
Nilamdyuti Goswami [as], Мирослав Николић [sr,sr@latin],
Tobias Endrigkeit [de], Fabio Tomat [fur], Matej Urbančič [sl], A S Alam [pa],
Inaki Larranaga Murgoitio [eu], Piotr Drąg [pl], Wouter Bolsterlee [nl],
Gheyret Kenji [ug], Yaron Shahrabani [he], Chao-Hsiung Liao [zh_HK,zh_TW],
Milo Casagrande [it], Benjamin Steinwender [de]
3.7.4.1
=======
* userMenu: Use show-full-name-in-top-bar setting [Bastien; #689561]
* dateMenu: Add "Open Clocks" entry [Mathieu; #644390]
* screenshot: Immediately show the flash spot [Jasper; #691875]
* Misc. bug fixes [Rico, Jeremy]
Contributors:
Jeremy Bicha, Mathieu Bridon, Bastien Nocera, Jasper St. Pierre,
Rico Tzschichholz
Translations:
Ihar Hrachyshka [be]
3.7.4
=====
* Make menu separators crisp [Giovanni, Allan; #641745]
* power: Update for new D-Bus name [Bastien; #690506]
* Add smooth scrolling support [Jasper; #687573]
* Tweak notification layout [Allan; #688506]
* Ping the active window when using the app menu [Giovanni; #684340]
* Make password entries insensitive after submission [Jasper; #690594, #690895]
* Honor lock-delay GSettings key [Giovanni, Matthias; #690766, #691170]
* Use text/calendar preferred app as the calendar app [Giovanni; #690767]
* lookingGlass: Move to an inspect() function [Jasper; #690726]
* Make OSK animation quicker, snappier [Rui; #688642]
* Allow to close chat notifications with Escape [Jasper; #690897]
* Honor org.gnome.desktop.screensaver.user-switch-enabled [Giovanni; #691042]
* Add a SelectArea() DBus method [Cosimo; #687954]
* Support non-absolute paths when saving screenshots [Cosimo; #688004]
* OSK: Fix extended keys popups [Rui; #674955]
* Don't hide or show the keyboard immediately [Rui; #688646]
* Improve padding in power menu [Giovanni; #689297]
* Add per-window input source switching [Rui; #691414]
* Misc bug fixes and cleanups [Rico, Jasper, Giovanni, Rui, Florian, Dan;
#690608, #690589, #690539, #687081, #690667, #690665, #690666, #685856,
#690858, #690895, #680414, #690965, #691019, #690590, #681376, #690180,
#685513, #689263, #691553, #691720, #691743, #691750]
Contributors:
Giovanni Campagna, Cosimo Cecchi, Matthias Clasen, Allan Day, Rui Matos,
Florian Müllner, Bastien Nocera, Jasper St. Pierre, Rico Tzschichholz,
Dan Winship
Translations:
Matej Urbančič [sl], Kjartan Maraas [nb], Mattias Põldaru [et],
Yaron Shahrabani [he], Aurimas Černius [lt], Khaled Hosny [ar],
Fran Diéguez [gl], Daniel Mustieles [es], Piotr Drąg [pl], Balázs Úr [hu],
Baurzhan Muftakhidinov [kk], Tobias Endrigkeit [de], Dušan Kazik [sk],
Aron Xu [zh_CN], Gheyret Kenji [ug]
3.7.3
=====
* Add 'No Messages' label when message tray is empty [Victoria; #686738]
* Use better icons in Ctrl-Alt-Tab popup [Stéphane; #641303]
* Show the OSK on the monitor where the focused window lives [Giovanni; #685856]
* Highlight window clone and caption when hovered [Giovanni, Marc; #665310]
* Improve login process indication [Stéphane; #687113]
* Omit empty categories in apps view [Stéphane; #687970]
* Style panel differently according to mode [Florian; #684573]
* Make it possible to hide the user name [Matthias; #688577]
* Consolidate and improve chat connection notifications [Giovanni; #687213]
* Improve notification scrollbar appearance [Carlos; #688393]
* Fade scroll view fade near scrolling edges [Jasper; #689249]
* Add a read-only org.gnome.Shell.Mode property [Debarshi; #689300]
* Don't close message tray after using context menus [Giovanni; #689296]
* Port swipe-scrolling to ClutterPanAction [Jasper, Florian; #689062, #689552]
* Remember state of 'Remember Password' checkbox [Ron; #688039]
* Improve timestamp format in chat notifications [Carlos; #680989]
* Improve style of missed-messages counter [Carlos; #686472]
* Omit connection failure notifications if cancelled by user [Giovanni; #684823]
* Add window-based Alt-Tab popup [Florian; #688913]
* Support external session mode definitions [Florian; #689304]
* Support session-mode-specific extensions [Florian; #689305]
* Support 'parentMode' property in session modes [Florian; #689308]
* Support a new org.gnome.ShellSearchProvider2 DBus interface
[Cosimo; #689735, #690009]
* Add "windows" to Ctrl-Alt-Tab popup [Jasper; #689653]
* Port PopupMenu to GrabHelper [Jasper; #689109, #689954]
* Show headphone icon when headphones are plugged in [Giovanni; #675902]
* Display (non-app) search results as list [Tanner, Cosimo; #681797]
* Skip diacritical marks in search terms [Aleksander; #648587]
* Expose all engine options in input sources [Giovanni, Rui; #682318]
* Add input source switcher popup [Rui; #682315]
* Add minimal support for InfiniBand in network menu [Dan; #677150]
* Misc bug fixes and cleanups [Sebastian, Aleksander, Giovanni, Tim, Cosimo,
Florian, Matthias, Rui, Lionel, Colin, Piotr, Guillaume, Bastien, Tanner,
Carlos, Stéphane, Jakub; #688422, #688379, #688750, #688771, #686800,
#688133, #688895, #688966, #683986, #688004, #689108, #689029, #683449,
#688196, #689304, #689243, #689295, #689325, #689400, #679168, #689568,
#689537, #689528, #689749, #689789, #689353, #689820, #689868, #689778,
#689959, #688589, #688589, #689955, #687250, #689965, #690046, #690049,
#689884, #682286, #690173, #690174, #672941, #689876, #687881, #690171,
#690241, #690312, #690175, #687955, #650843, #688234, #690427
Contributors:
Giovanni Campagna, Cosimo Cecchi, Matthias Clasen, Stéphane Démurget,
Guillaume Desmottes, Tanner Doshier, Piotr Drąg, Sebastian Keller,
Lionel Landwerlin, Tim Lunn, Victoria Martínez de la Cruz, Aleksander Morgado,
Florian Müllner, Bastien Nocera, Marc Plano-Lesay, Carlos Soriano Sánchez,
Jakub Steiner, Jasper St. Pierre, Colin Walters, Dan Winship, Ron Yorston
Translations:
Yuri Myasoedov [ru], Wouter Bolsterlee [nl], Yaron Shahrabani [he],
Nilamdyuti Goswami [as], Ani Peter [ml], Kjartan Maraas [nb],
Dr.T.Vasudevan [ta], A S Alam [pa], Shankar Prasad [kn], Khaled Hosny [ar],
Daniel Mustieles [es], Dušan Kazik [sk]
3.7.2
=====
* Enforce RTL in he for messages that might end up as LTR [Florian; #686630]

View File

@@ -1,5 +1,5 @@
AC_PREREQ(2.63)
AC_INIT([gnome-shell],[3.7.5],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
AC_INIT([gnome-shell],[3.7.2],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_SRCDIR([src/shell-global.c])
@@ -16,6 +16,8 @@ m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
# Checks for programs.
AC_PROG_CC
# Needed for per-target cflags, like in gnomeshell-taskpanel
AM_PROG_CC_C_O
# Initialize libtool
LT_PREREQ([2.2.6])
@@ -60,14 +62,15 @@ fi
AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
CLUTTER_MIN_VERSION=1.13.4
CLUTTER_MIN_VERSION=1.11.11
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
GJS_MIN_VERSION=1.35.4
MUTTER_MIN_VERSION=3.7.5
GJS_MIN_VERSION=1.33.2
MUTTER_MIN_VERSION=3.7.2
GTK_MIN_VERSION=3.3.9
GIO_MIN_VERSION=2.35.0
LIBECAL_MIN_VERSION=3.5.3
LIBEDATASERVER_MIN_VERSION=3.5.3
LIBEDATASERVERUI_MIN_VERSION=3.5.3
TELEPATHY_GLIB_MIN_VERSION=0.17.5
TELEPATHY_LOGGER_MIN_VERSION=0.2.4
POLKIT_MIN_VERSION=0.100
@@ -75,8 +78,7 @@ STARTUP_NOTIFICATION_MIN_VERSION=0.11
GCR_MIN_VERSION=3.3.90
GNOME_DESKTOP_REQUIRED_VERSION=3.7.1
GNOME_MENUS_REQUIRED_VERSION=3.5.3
NETWORKMANAGER_MIN_VERSION=0.9.6
PULSE_MIN_VERS=2.0
NETWORKMANAGER_MIN_VERSION=0.9.7
# Collect more than 20 libraries for a prize!
PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
@@ -93,7 +95,7 @@ PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
clutter-glx-1.0 >= $CLUTTER_MIN_VERSION
libstartup-notification-1.0 >= $STARTUP_NOTIFICATION_MIN_VERSION
gobject-introspection-1.0 >= $GOBJECT_INTROSPECTION_MIN_VERSION
libcanberra libcanberra-gtk3
libcanberra
telepathy-glib >= $TELEPATHY_GLIB_MIN_VERSION
telepathy-logger-0.2 >= $TELEPATHY_LOGGER_MIN_VERSION
polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes
@@ -101,14 +103,30 @@ PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
libnm-gtk >= $NETWORKMANAGER_MIN_VERSION
gnome-keyring-1 gcr-3 >= $GCR_MIN_VERSION)
PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0)
PKG_CHECK_MODULES(SHELL_HOTPLUG_SNIFFER, gio-2.0 gdk-pixbuf-2.0)
PKG_CHECK_MODULES(BROWSER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION json-glib-1.0 >= 0.13.2)
GNOME_KEYBINDINGS_KEYSDIR=`$PKG_CONFIG --variable keysdir gnome-keybindings`
AC_SUBST([GNOME_KEYBINDINGS_KEYSDIR])
GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION])
saved_CFLAGS=$CFLAGS
saved_LIBS=$LIBS
CFLAGS=$GNOME_SHELL_CFLAGS
LIBS=$GNOME_SHELL_LIBS
AC_CHECK_FUNCS(JS_NewGlobalObject XFixesCreatePointerBarrier)
CFLAGS=$saved_CFLAGS
LIBS=$saved_LIBS
PKG_CHECK_MODULES(GNOME_SHELL_JS, gio-2.0 gjs-internals-1.0 >= $GJS_MIN_VERSION)
PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 >= 0.6.8 x11)
PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0)
PKG_CHECK_MODULES(SHELL_HOTPLUG_SNIFFER, gio-2.0 gdk-pixbuf-2.0)
PKG_CHECK_MODULES(BROWSER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION json-glib-1.0 >= 0.13.2)
PKG_CHECK_MODULES(TRAY, gtk+-3.0)
PKG_CHECK_MODULES(GVC, libpulse >= $PULSE_MIN_VERS libpulse-mainloop-glib gobject-2.0)
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.7.4)
PKG_CHECK_MODULES(GVC, libpulse libpulse-mainloop-glib gobject-2.0)
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.7.2)
AC_MSG_CHECKING([for bluetooth support])
PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.1.0],
@@ -128,18 +146,32 @@ PKG_CHECK_MODULES(CALENDAR_SERVER, libecal-1.2 >= $LIBECAL_MIN_VERSION libedatas
AC_SUBST(CALENDAR_SERVER_CFLAGS)
AC_SUBST(CALENDAR_SERVER_LIBS)
GNOME_KEYBINDINGS_KEYSDIR=`$PKG_CONFIG --variable keysdir gnome-keybindings`
AC_SUBST([GNOME_KEYBINDINGS_KEYSDIR])
AC_ARG_WITH(systemd,
AS_HELP_STRING([--with-systemd],
[Add systemd support]),
[with_systemd=$withval], [with_systemd=auto])
GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION])
PKG_CHECK_MODULES(SYSTEMD,
[libsystemd-login libsystemd-daemon],
[have_systemd=yes], [have_systemd=no])
saved_CFLAGS=$CFLAGS
saved_LIBS=$LIBS
CFLAGS=$GNOME_SHELL_CFLAGS
LIBS=$GNOME_SHELL_LIBS
AC_CHECK_FUNCS(XFixesCreatePointerBarrier)
CFLAGS=$saved_CFLAGS
LIBS=$saved_LIBS
if test "x$with_systemd" = "xauto" ; then
if test x$have_systemd = xno ; then
use_systemd=no
else
use_systemd=yes
fi
else
use_systemd=$with_systemd
fi
if test "x$use_systemd" = "xyes"; then
if test "x$have_systemd" = "xno"; then
AC_MSG_ERROR([Systemd support explicitly required, but systemd not found])
fi
AC_DEFINE(WITH_SYSTEMD, 1, [systemd support])
fi
MUTTER_GIR_DIR=`$PKG_CONFIG --variable=girdir libmutter`
MUTTER_TYPELIB_DIR=`$PKG_CONFIG --variable=typelibdir libmutter`
@@ -166,6 +198,16 @@ fi
# Sets GLIB_GENMARSHAL and GLIB_MKENUMS
AM_PATH_GLIB_2_0()
G_IR_SCANNER=`$PKG_CONFIG --variable=g_ir_scanner gobject-introspection-1.0`
AC_SUBST(G_IR_SCANNER)
G_IR_COMPILER=`$PKG_CONFIG --variable=g_ir_compiler gobject-introspection-1.0`
AC_SUBST(G_IR_COMPILER)
G_IR_GENERATE=`$PKG_CONFIG --variable=g_ir_generate gobject-introspection-1.0`
AC_SUBST(G_IR_GENERATE)
GIRDIR=`$PKG_CONFIG --variable=girdir gobject-introspection-1.0`
AC_SUBST(GIRDIR)
TYPELIBDIR="$($PKG_CONFIG --variable=typelibdir gobject-introspection-1.0)"
AC_SUBST(TYPELIBDIR)
GTK_DOC_CHECK([1.15], [--flavour no-tmpl])
@@ -181,7 +223,32 @@ if test "$enable_man" != no; then
fi
AM_CONDITIONAL(ENABLE_MAN, test "$enable_man" != no)
GNOME_COMPILE_WARNINGS([error])
# Stay command-line compatible with the gnome-common configure option. Here
# minimum/yes/maximum are the same, however.
AC_ARG_ENABLE(compile_warnings,
AS_HELP_STRING([--enable-compile-warnings=@<:@no/minimum/yes/maximum/error@:>@],[Turn on compiler warnings]),,
enable_compile_warnings=error)
changequote(,)dnl
if test "$enable_compile_warnings" != no ; then
if test "x$GCC" = "xyes"; then
case " $CFLAGS " in
*[\ \ ]-Wall[\ \ ]*) ;;
*) CFLAGS="$CFLAGS -Wall" ;;
esac
case " $CFLAGS " in
*[\ \ ]-Wmissing-prototypes[\ \ ]*) ;;
*) CFLAGS="$CFLAGS -Wmissing-prototypes" ;;
esac
if test "$enable_compile_warnings" = error ; then
case " $CFLAGS " in
*[\ \ ]-Werror[\ \ ]*) ;;
*) CFLAGS="$CFLAGS -Werror -Wno-error=deprecated-declarations" ;;
esac
fi
fi
fi
changequote([,])dnl
AC_ARG_ENABLE(jhbuild-wrapper-script,
AS_HELP_STRING([--enable-jhbuild-wrapper-script],[Make "gnome-shell" script work for jhbuild]),,enable_jhbuild_wrapper_script=no)

View File

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

View File

@@ -1,128 +0,0 @@
<!DOCTYPE node PUBLIC
'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
<node>
<!--
org.gnome.Shell.Screenshot:
@short_description: Screenshot interface
The interface used to capture pictures of the screen contents.
-->
<interface name="org.gnome.Shell.Screenshot">
<!--
Screenshot:
@filename: The filename for the screenshot
@include_cursor: Whether to include the cursor image or not
@flash: Whether to flash the screen or not
@success: whether the screenshot was captured
@filename_used: the file where the screenshot was saved
Takes a screenshot of the whole screen and saves it
in @filename as png image, it returns a boolean
indicating whether the operation was successful or not.
@filename can either be an absolute path or a basename, in
which case the screenshot will be saved in the $XDG_PICTURES_DIR
or the home directory if it doesn't exist. The filename used
to save the screenshot will be returned in @filename_used.
-->
<method name="Screenshot">
<arg type="b" direction="in" name="include_cursor"/>
<arg type="b" direction="in" name="flash"/>
<arg type="s" direction="in" name="filename"/>
<arg type="b" direction="out" name="success"/>
<arg type="s" direction="out" name="filename_used"/>
</method>
<!--
ScreenshotWindow:
@include_frame: Whether to include the frame or not
@include_cursor: Whether to include the cursor image or not
@flash: Whether to flash the window area or not
@filename: The filename for the screenshot
@success: whether the screenshot was captured
@filename_used: the file where the screenshot was saved
Takes a screenshot of the focused window (optionally omitting the frame)
and saves it in @filename as png image, it returns a boolean
indicating whether the operation was successful or not.
@filename can either be an absolute path or a basename, in
which case the screenshot will be saved in the $XDG_PICTURES_DIR
or the home directory if it doesn't exist. The filename used
to save the screenshot will be returned in @filename_used.
-->
<method name="ScreenshotWindow">
<arg type="b" direction="in" name="include_frame"/>
<arg type="b" direction="in" name="include_cursor"/>
<arg type="b" direction="in" name="flash"/>
<arg type="s" direction="in" name="filename"/>
<arg type="b" direction="out" name="success"/>
<arg type="s" direction="out" name="filename_used"/>
</method>
<!--
ScreenshotArea:
@x: the X coordinate of the area to capture
@y: the Y coordinate of the area to capture
@width: the width of the area to capture
@height: the height of the area to capture
@flash: whether to flash the area or not
@filename: the filename for the screenshot
@success: whether the screenshot was captured
@filename_used: the file where the screenshot was saved
Takes a screenshot of the passed in area and saves it
in @filename as png image, it returns a boolean
indicating whether the operation was successful or not.
@filename can either be an absolute path or a basename, in
which case the screenshot will be saved in the $XDG_PICTURES_DIR
or the home directory if it doesn't exist. The filename used
to save the screenshot will be returned in @filename_used.
-->
<method name="ScreenshotArea">
<arg type="i" direction="in" name="x"/>
<arg type="i" direction="in" name="y"/>
<arg type="i" direction="in" name="width"/>
<arg type="i" direction="in" name="height"/>
<arg type="b" direction="in" name="flash"/>
<arg type="s" direction="in" name="filename"/>
<arg type="b" direction="out" name="success"/>
<arg type="s" direction="out" name="filename_used"/>
</method>
<!--
FlashArea:
@x: the X coordinate of the area to flash
@y: the Y coordinate of the area to flash
@width: the width of the area to flash
@height: the height of the area to flash
Renders a flash spot effect in the specified rectangle of the screen.
-->
<method name="FlashArea">
<arg type="i" direction="in" name="x"/>
<arg type="i" direction="in" name="y"/>
<arg type="i" direction="in" name="width"/>
<arg type="i" direction="in" name="height"/>
</method>
<!--
SelectArea:
@x: the X coordinate of the selected area
@y: the Y coordinate of the selected area
@width: the width of the selected area
@height: the height of the selected area
Interactively allows the user to select a rectangular area of
the screen, and returns its coordinates.
-->
<method name="SelectArea">
<arg type="i" direction="out" name="x"/>
<arg type="i" direction="out" name="y"/>
<arg type="i" direction="out" name="width"/>
<arg type="i" direction="out" name="height"/>
</method>
</interface>
</node>

View File

@@ -65,6 +65,11 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
menuitem in single-user, single-session situations.
</_description>
</key>
<key name="show-full-name" type="b">
<default>true</default>
<_summary>Show full name in the user menu</_summary>
<_description>Whether the users full name is shown in the user menu or not.</_description>
</key>
<key name="remember-mount-password" type="b">
<default>false</default>
<_summary>Whether to remember password for mounting encrypted or remote filesystems</_summary>

View File

@@ -206,12 +206,11 @@ StScrollBar StButton#vhandle:active {
}
.popup-separator-menu-item {
-gradient-height: 1px;
-gradient-start: rgba(255,255,255,0.0);
-gradient-end: rgba(255,255,255,0.3);
-margin-horizontal: 24px;
height: 1px;
padding: 8px 0px;
-gradient-height: 2px;
-gradient-start: rgba(8,8,8,0);
-gradient-end: #333333;
-margin-horizontal: 1.5em;
height: 1em;
}
.popup-alternating-menu-item:alternate {
@@ -252,10 +251,6 @@ StScrollBar StButton#vhandle:active {
icon-size: 1.09em;
}
.popup-battery-percentage {
padding-left: 24px;
}
/* Switches */
.toggle-switch {
width: 65px;
@@ -292,17 +287,13 @@ StScrollBar StButton#vhandle:active {
.hotplug-notification-item,
.hotplug-resident-eject-button,
.modal-dialog-button {
font-weight: bold;
border: 1px solid #8b8b8b;
background-gradient-direction: vertical;
background-gradient-start: rgba(255, 255, 255, 0.2);
background-gradient-end: rgba(255, 255, 255, 0);
}
.dash-search-button,
.modal-dialog-button {
font-weight: bold;
}
.dash-search-button:hover,
.notification-button:hover,
.notification-icon-button:hover,
@@ -363,7 +354,7 @@ StScrollBar StButton#vhandle:active {
background-gradient-start: rgba(5,5,6,0.1);
background-gradient-end: rgba(254,254,254,0.1);
background-gradient-direction: vertical;
transition-duration: 300ms;
transition-duration: 300;
box-shadow: inset 0px 2px 4px rgba(0,0,0,0.6);
}
@@ -398,7 +389,7 @@ StScrollBar StButton#vhandle:active {
color: rgb(64, 64, 64);
caret-color: rgb(64, 64, 64);
font-weight: bold;
transition-duration: 0ms;
transition-duration: 0;
}
.notification StEntry,
@@ -429,7 +420,7 @@ StScrollBar StButton#vhandle:active {
background-color: black;
font-weight: bold;
height: 1.86em;
transition-duration: 250ms;
transition-duration: 250;
}
#panel.lock-screen {
@@ -512,7 +503,7 @@ StScrollBar StButton#vhandle:active {
-minimum-hpadding: 6px;
font-weight: bold;
color: #ccc;
transition-duration: 100ms;
transition-duration: 100;
}
#panel.unlock-screen .panel-button,
@@ -567,10 +558,6 @@ StScrollBar StButton#vhandle:active {
spacing: 4px;
}
.lock-screen-status-button-box {
spacing: 8px;
}
/* User Menu */
#panelUserMenu {
@@ -626,25 +613,17 @@ StScrollBar StButton#vhandle:active {
/* Overview */
#overview {
spacing: 24px;
spacing: 40px;
}
#overview-group {
spacing: 32px;
}
.workspaces-display {
spacing: 32px; /* needs to be the same value as #overview-group */
}
.window-caption {
spacing: 25px;
}
.workspace-controls {
visible-width: 32px; /* Amount visible before hovering */
}
.workspace-thumbnails-background {
border: 1px solid rgba(128, 128, 128, 0.4);
border-right: 0px;
@@ -662,6 +641,7 @@ StScrollBar StButton#vhandle:active {
.workspace-thumbnails {
spacing: 11px;
visible-width: 96px;
}
.workspace-thumbnail-indicator {
@@ -723,12 +703,8 @@ StScrollBar StButton#vhandle:active {
}
.window-picker {
-horizontal-spacing: 32px;
-vertical-spacing: 32px;
}
.window-picker.external-monitor {
padding: 32px;
-horizontal-spacing: 40px;
-vertical-spacing: 40px;
}
/* Dash */
@@ -755,6 +731,10 @@ StScrollBar StButton#vhandle:active {
height: 24px;
}
#viewSelector {
spacing: 1em;
}
/* Search Box */
#searchEntry {
@@ -775,22 +755,15 @@ StScrollBar StButton#vhandle:active {
/* Search Results */
#searchResults {
padding: 20px 10px 10px 10px;
spacing: 18px;
}
#searchResultsBin {
max-width: 1000px;
}
#searchResultsContent {
padding-right: 20px;
spacing: 16px;
}
#searchResultsContent:rtl {
padding-right: 0px;
/* for scrollbars */
padding-left: 20px;
padding-right: 20px;
}
.search-section {
@@ -860,15 +833,10 @@ StScrollBar StButton#vhandle:active {
}
.all-app {
padding: 16px 25px 16px 16px;
padding: 16px;
spacing: 20px;
}
.all-app:rtl {
padding-right: 16px;
padding-left: 25px;
}
.app-filter {
font-weight: bold;
height: 2.85em;
@@ -930,7 +898,7 @@ StScrollBar StButton#vhandle:active {
border-radius: 4px;
padding: 3px;
border: 1px rgba(0,0,0,0);
transition-duration: 100ms;
transition-duration: 100;
text-align: center;
}
@@ -947,7 +915,7 @@ StScrollBar StButton#vhandle:active {
.grid-search-result:hover .overview-icon {
background-color: rgba(255,255,255,0.1);
text-shadow: black 0px 2px 2px;
transition-duration: 100ms;
transition-duration: 100;
color:white;
}
@@ -963,20 +931,19 @@ StScrollBar StButton#vhandle:active {
color: white;
}
.show-apps:checked > .overview-icon,
.show-apps:active > .overview-icon {
.show-apps:checked > .overview-icon {
background-gradient-start: rgba(255, 255, 255, .05);
background-gradient-end: rgba(255, 255, 255, .15);
background-gradient-direction: vertical;
border-radius: 4px;
box-shadow: inset 0px 1px 2px 0px rgba(0, 0, 0, 1);
transition-duration: 100ms;
transition-duration: 100;
}
.show-apps:checked .show-apps-icon,
.show-apps:focus .show-apps-icon {
color: white;
transition-duration: 100ms;
transition-duration: 100;
}
.app-well-app:focus > .overview-icon,
@@ -1015,7 +982,7 @@ StScrollBar StButton#vhandle:active {
-minimum-hpadding: 6px;
font-weight: bold;
color: #ccc;
transition-duration: 100ms;
transition-duration: 100;
padding-left: .3em;
padding-right: .3em;
}
@@ -1318,7 +1285,7 @@ StScrollBar StButton#vhandle:active {
#message-tray {
background: #2e3436 url(message-tray-background.png);
background-repeat: repeat;
height: 72px;
transition-duration: 250;
}
#message-tray:keyboard {
@@ -1331,10 +1298,6 @@ StScrollBar StButton#vhandle:active {
outline: 1px solid rgba(128, 128, 128, 0.3);
}
.message-tray-summary {
height: 72px;
}
.no-messages-label {
font-family: cantarell, sans-serif;
font-size: 11pt;
@@ -1343,9 +1306,9 @@ StScrollBar StButton#vhandle:active {
.notification {
border-radius: 10px 10px 0px 0px;
background: rgba(0,0,0,0.9);
background: rgba(0,0,0,0.8);
padding: 8px 8px 4px 8px;
spacing-rows: 4px;
spacing-rows: 10px;
spacing-columns: 10px;
}
@@ -1375,7 +1338,7 @@ StScrollBar StButton#vhandle:active {
.summary-boxpointer {
-arrow-border-radius: 15px;
-arrow-background-color: rgba(0,0,0,0.9);
-arrow-background-color: rgba(0,0,0,0.8);
-arrow-base: 36px;
-arrow-rise: 18px;
color: white;
@@ -1425,7 +1388,6 @@ StScrollBar StButton#vhandle:active {
}
.notification-actions {
padding-top: 18px;
spacing: 10px;
}
@@ -1564,6 +1526,10 @@ StScrollBar StButton#vhandle:active {
border-radius: 4px;
}
#summary-mode {
height: 72px;
}
.summary-source-button {
padding: 6px 3px 6px 3px;
}
@@ -1588,7 +1554,7 @@ StScrollBar StButton#vhandle:active {
.summary-source {
border-radius: 4px;
padding: 0 6px 0 6px;
transition-duration: 100ms;
transition-duration: 100;
}
.summary-source-counter {
@@ -2417,7 +2383,6 @@ StScrollBar StButton#vhandle:active {
.screen-shield-notifications-box {
spacing: 18px;
max-width: 34em;
}
.screen-shield-notification-source {
@@ -2451,8 +2416,3 @@ StScrollBar StButton#vhandle:active {
padding-bottom: 0px;
}
.input-source-switcher-symbol {
font-size: 42pt;
width: 96px;
height: 96px;
}

View File

@@ -14,7 +14,7 @@
height="16"
id="svg12430"
version="1.1"
inkscape:version="0.48.3.1 r9886"
inkscape:version="0.48+devel r11908 custom"
sodipodi:docname="more-results.svg">
<defs
id="defs12432" />
@@ -26,8 +26,8 @@
inkscape:pageopacity="1"
inkscape:pageshadow="2"
inkscape:zoom="1"
inkscape:cx="8.3155237"
inkscape:cy="0.89548874"
inkscape:cx="7.4498765"
inkscape:cy="9.9072581"
inkscape:document-units="px"
inkscape:current-layer="g14642-3-0"
showgrid="false"
@@ -35,8 +35,8 @@
inkscape:showpageshadow="false"
inkscape:window-width="2560"
inkscape:window-height="1376"
inkscape:window-x="1200"
inkscape:window-y="187"
inkscape:window-x="1600"
inkscape:window-y="27"
inkscape:window-maximized="1">
<inkscape:grid
type="xygrid"
@@ -50,7 +50,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>
@@ -69,27 +69,11 @@
inkscape:radius="0"
inkscape:original="M 145.1875 400 C 144.5248 400 144 400.54899 144 401.21875 L 144 410.78125 C 144 411.45101 144.5248 412 145.1875 412 L 154.8125 412 C 155.4752 412 156 411.45101 156 410.78125 L 156 401.21875 C 156 400.54899 155.4752 400 154.8125 400 L 145.1875 400 z M 149 403 L 151 403 L 151 405 L 153 405 L 153 407 L 151 407 L 151 409 L 149 409 L 149 407 L 147 407 L 147 405 L 149 405 L 149 403 z "
xlink:href="#rect11749-5-0-1-8"
style="color:#bebebe;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;opacity:0.8"
style="color:#bebebe;fill:#000000;fill-opacity:0.38207546;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible"
id="path13004"
inkscape:href="#rect11749-5-0-1-8"
d="M 145.1875,400 C 144.5248,400 144,400.54899 144,401.21875 l 0,9.5625 c 0,0.66976 0.5248,1.21875 1.1875,1.21875 l 9.625,0 c 0.6627,0 1.1875,-0.54899 1.1875,-1.21875 l 0,-9.5625 C 156,400.54899 155.4752,400 154.8125,400 L 145.1875,400 z m 3.8125,3 2,0 0,2 2,0 0,2 -2,0 0,2 -2,0 0,-2 -2,0 0,-2 2,0 L 149,403 Z"
transform="translate(0,1)" />
<use
x="0"
y="0"
xlink:href="#path13004"
id="use11960"
transform="translate(1,-1)"
width="16"
height="16" />
<use
x="0"
y="0"
xlink:href="#use11960"
id="use11962"
transform="translate(-2,0)"
width="16"
height="16" />
<path
inkscape:connector-curvature="0"
style="color:#bebebe;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible"

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

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

View File

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

View File

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

View File

@@ -28,7 +28,6 @@ nobase_dist_js_DATA = \
misc/extensionUtils.js \
misc/fileUtils.js \
misc/gnomeSession.js \
misc/hash.js \
misc/history.js \
misc/jsParse.js \
misc/loginManager.js \
@@ -42,6 +41,7 @@ nobase_dist_js_DATA = \
ui/boxpointer.js \
ui/calendar.js \
ui/checkBox.js \
ui/centerLayout.js \
ui/ctrlAltTab.js \
ui/dash.js \
ui/dateMenu.js \
@@ -50,6 +50,7 @@ nobase_dist_js_DATA = \
ui/extensionSystem.js \
ui/extensionDownloader.js \
ui/environment.js \
ui/flashspot.js \
ui/ibusCandidatePopup.js\
ui/grabHelper.js \
ui/iconGrid.js \
@@ -74,7 +75,6 @@ nobase_dist_js_DATA = \
ui/popupMenu.js \
ui/remoteSearch.js \
ui/runDialog.js \
ui/screenshot.js \
ui/screenShield.js \
ui/scripting.js \
ui/search.js \

View File

@@ -128,7 +128,6 @@ const UserListItem = new Lang.Class({
let layout = new St.BoxLayout({ vertical: false });
this.actor = new St.Button({ style_class: 'login-dialog-user-list-item',
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
can_focus: true,
child: layout,
reactive: true,
@@ -493,7 +492,6 @@ const SessionListItem = new Lang.Class({
this.id = id;
this.actor = new St.Button({ style_class: 'login-dialog-session-list-item',
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
can_focus: true,
reactive: true,
x_fill: true,
@@ -534,7 +532,6 @@ const SessionListItem = new Lang.Class({
color.alpha / 255);
cr.arc(width / 2, height / 2, width / 3, 0, 2 * Math.PI);
cr.fill();
cr.$dispose();
},
_onClicked: function() {
@@ -554,7 +551,6 @@ const SessionList = new Lang.Class({
this.actor.child = this._box;
this._button = new St.Button({ style_class: 'login-dialog-session-list-button',
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
can_focus: true,
x_fill: true,
y_fill: true });
@@ -677,7 +673,7 @@ const LoginDialog = new Lang.Class({
this.parent({ shellReactive: true,
styleClass: 'login-dialog',
parentActor: parentActor,
keybindingMode: Shell.KeyBindingMode.LOGIN_SCREEN,
keybindingMode: Main.KeybindingMode.LOGIN_SCREEN,
shouldFadeIn: false });
this.connect('destroy',
Lang.bind(this, this._onDestroy));
@@ -790,7 +786,6 @@ const LoginDialog = new Lang.Class({
let notListedLabel = new St.Label({ text: _("Not listed?"),
style_class: 'login-dialog-not-listed-label' });
this._notListedButton = new St.Button({ style_class: 'login-dialog-not-listed-button',
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
can_focus: true,
child: notListedLabel,
reactive: true,

View File

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

View File

@@ -1,141 +0,0 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Lang = imports.lang;
const System = imports.system;
const Params = imports.misc.params;
// This is an implementation of EcmaScript SameValue algorithm,
// which returns true if two values are not observably distinguishable.
// It was taken from http://wiki.ecmascript.org/doku.php?id=harmony:egal
//
// In the future, we may want to use the 'is' operator instead.
function _sameValue(x, y) {
if (x === y) {
// 0 === -0, but they are not identical
return x !== 0 || 1 / x === 1 / y;
}
// NaN !== NaN, but they are identical.
// NaNs are the only non-reflexive value, i.e., if x !== x,
// then x is a NaN.
// isNaN is broken: it converts its argument to number, so
// isNaN("foo") => true
return x !== x && y !== y;
}
const _hashers = {
object: function(o) { return o ? System.addressOf(o) : 'null'; },
function: function(f) { return System.addressOf(f); },
string: function(s) { return s; },
number: function(n) { return String(n); },
undefined: function() { return 'undefined'; },
};
/* Map is meant to be similar in usage to ES6 Map, which is
described at http://wiki.ecmascript.org/doku.php?id=harmony:simple_maps_and_sets,
without requiring more than ES5 + Gjs extensions.
Known differences from other implementations:
Polyfills around the web usually implement HashMaps for
primitive values and reversed WeakMaps for object keys,
but we want real maps with real O(1) semantics in all cases,
and the easiest way is to have different hashers for different
types.
Known differences from the ES6 specification:
- Map is a Lang.Class, not a ES6 class, so inheritance,
prototype, sealing, etc. work differently.
- items(), keys() and values() don't return iterators,
they return actual arrays, so they incur a full copy everytime
they're called, and they don't see changes if you mutate
the table while iterating
(admittedly, the ES6 spec is a bit unclear on this, and
the reference code would just blow up)
*/
const Map = new Lang.Class({
Name: 'Map',
_init: function(iterable) {
this._pool = { };
if (iterable) {
for (let i = 0; i < iterable.length; i++) {
let [key, value] = iterable[i];
this.set(key, value);
}
}
},
_hashKey: function(key) {
let type = typeof(key);
return type + ':' + _hashers[type](key);
},
_internalGet: function(key) {
let hash = this._hashKey(key);
let node = this._pool[hash];
if (node && _sameValue(node.key, key))
return [true, node.value];
else
return [false, null];
},
get: function(key) {
return this._internalGet(key)[1];
},
has: function(key) {
return this._internalGet(key)[0];
},
set: function(key, value) {
let hash = this._hashKey(key);
let node = this._pool[hash];
if (node) {
node.key = key;
node.value = value;
} else {
this._pool[hash] = { key: key, value: value };
}
},
delete: function(key) {
let hash = this._hashKey(key);
let node = this._pool[hash];
if (node && _sameValue(node.key, key)) {
delete this._pool[hash];
return [node.key, node.value];
} else {
return [null, null];
}
},
keys: function() {
let pool = this._pool;
return Object.getOwnPropertyNames(pool).map(function(hash) {
return pool[hash].key;
});
},
values: function() {
let pool = this._pool;
return Object.getOwnPropertyNames(pool).map(function(hash) {
return pool[hash].value;
});
},
items: function() {
let pool = this._pool;
return Object.getOwnPropertyNames(pool).map(function(hash) {
return [pool[hash].key, pool[hash].value];
});
},
size: function() {
return Object.getOwnPropertyNames(this._pool).length;
},
});

View File

@@ -5,7 +5,7 @@ const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const UPowerGlib = imports.gi.UPowerGlib;
const SystemdLoginManagerIface = <interface name='org.freedesktop.login1.Manager'>
<method name='PowerOff'>
@@ -26,16 +26,6 @@ const SystemdLoginManagerIface = <interface name='org.freedesktop.login1.Manager
<method name='CanSuspend'>
<arg type='s' direction='out'/>
</method>
<method name='Inhibit'>
<arg type='s' direction='in'/>
<arg type='s' direction='in'/>
<arg type='s' direction='in'/>
<arg type='s' direction='in'/>
<arg type='h' direction='out'/>
</method>
<signal name='PrepareForSleep'>
<arg type='b' direction='out'/>
</signal>
</interface>;
const SystemdLoginSessionIface = <interface name='org.freedesktop.login1.Session'>
@@ -61,6 +51,12 @@ const ConsoleKitManagerIface = <interface name='org.freedesktop.ConsoleKit.Manag
</interface>;
const ConsoleKitSessionIface = <interface name='org.freedesktop.ConsoleKit.Session'>
<method name='IsActive'>
<arg type='b' direction='out' />
</method>
<signal name='ActiveChanged'>
<arg type='b' direction='out' />
</signal>
<signal name='Lock' />
<signal name='Unlock' />
</interface>;
@@ -97,8 +93,6 @@ const LoginManagerSystemd = new Lang.Class({
this._proxy = new SystemdLoginManager(Gio.DBus.system,
'org.freedesktop.login1',
'/org/freedesktop/login1');
this._proxy.connectSignal('PrepareForSleep',
Lang.bind(this, this._prepareForSleep));
},
// Having this function is a bit of a hack since the Systemd and ConsoleKit
@@ -115,6 +109,10 @@ const LoginManagerSystemd = new Lang.Class({
return this._currentSession;
},
get sessionActive() {
return Shell.session_is_active_for_systemd();
},
canPowerOff: function(asyncCallback) {
this._proxy.CanPowerOffRemote(function(result, error) {
if (error)
@@ -152,33 +150,8 @@ const LoginManagerSystemd = new Lang.Class({
suspend: function() {
this._proxy.SuspendRemote(true);
},
inhibit: function(reason, callback) {
let inVariant = GLib.Variant.new('(ssss)',
['sleep',
'GNOME Shell',
reason,
'delay']);
this._proxy.call_with_unix_fd_list('Inhibit', inVariant, 0, -1, null, null,
Lang.bind(this, function(proxy, result) {
let fd = -1;
try {
let [outVariant, fdList] = proxy.call_with_unix_fd_list_finish(result);
fd = fdList.steal_fds(outVariant.deep_unpack())[0];
callback(new Gio.UnixInputStream({ fd: fd }));
} catch(e) {
logError(e, "Error getting systemd inhibitor");
callback(null);
}
}));
},
_prepareForSleep: function(proxy, sender, [aboutToSuspend]) {
this.emit('prepare-for-sleep', aboutToSuspend);
}
});
Signals.addSignalMethods(LoginManagerSystemd.prototype);
const LoginManagerConsoleKit = new Lang.Class({
Name: 'LoginManagerConsoleKit',
@@ -187,6 +160,7 @@ const LoginManagerConsoleKit = new Lang.Class({
this._proxy = new ConsoleKitManager(Gio.DBus.system,
'org.freedesktop.ConsoleKit',
'/org/freedesktop/ConsoleKit/Manager');
this._upClient = new UPowerGlib.Client();
},
// Having this function is a bit of a hack since the Systemd and ConsoleKit
@@ -203,6 +177,19 @@ const LoginManagerConsoleKit = new Lang.Class({
return this._currentSession;
},
get sessionActive() {
if (this._sessionActive !== undefined)
return this._sessionActive;
let session = this.getCurrentSessionProxy();
session.connectSignal('ActiveChanged', Lang.bind(this, function(object, senderName, [isActive]) {
this._sessionActive = isActive;
}));
[this._sessionActive] = session.IsActiveSync();
return this._sessionActive;
},
canPowerOff: function(asyncCallback) {
this._proxy.CanStopRemote(function(result, error) {
if (error)
@@ -222,7 +209,10 @@ const LoginManagerConsoleKit = new Lang.Class({
},
canSuspend: function(asyncCallback) {
return false;
Mainloop.idle_add(Lang.bind(this, function() {
asyncCallback(this._upClient.get_can_suspend());
return false;
}));
},
powerOff: function() {
@@ -234,12 +224,6 @@ const LoginManagerConsoleKit = new Lang.Class({
},
suspend: function() {
this.emit('prepare-for-sleep', true);
this.emit('prepare-for-sleep', false);
},
inhibit: function(reason, callback) {
callback(null);
this._upClient.suspend_sync(null);
}
});
Signals.addSignalMethods(LoginManagerConsoleKit.prototype);

View File

@@ -2,93 +2,9 @@
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const NMGtk = imports.gi.NMGtk;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
// _getMobileProvidersDatabase:
//
// Gets the database of mobile providers, with references between MCCMNC/SID and
// operator name
//
let _mpd;
function _getMobileProvidersDatabase() {
if (_mpd == null) {
try {
_mpd = new NMGtk.MobileProvidersDatabase();
_mpd.init(null);
} catch (e) {
log(e.message);
_mpd = null;
}
}
return _mpd;
}
// _findProviderForMccMnc:
// @operator_name: operator name
// @operator_code: operator code
//
// Given an operator name string (which may not be a real operator name) and an
// operator code string, tries to find a proper operator name to display.
//
function _findProviderForMccMnc(operator_name, operator_code) {
if (operator_name) {
if (operator_name.length != 0 &&
(operator_name.length > 6 || operator_name.length < 5)) {
// this looks like a valid name, i.e. not an MCCMNC (that some
// devices return when not yet connected
return operator_name;
}
if (isNaN(parseInt(operator_name))) {
// name is definitely not a MCCMNC, so it may be a name
// after all; return that
return operator_name;
}
}
let needle;
if ((!operator_name || operator_name.length == 0) && operator_code)
needle = operator_code;
else if (operator_name && (operator_name.length == 6 || operator_name.length == 5))
needle = operator_name;
else // nothing to search
return null;
let mpd = _getMobileProvidersDatabase();
if (mpd) {
let provider = mpd.lookup_3gpp_mcc_mnc(needle);
if (provider)
return provider.get_name();
}
return null;
}
// _findProviderForSid:
// @sid: System Identifier of the serving CDMA network
//
// Tries to find the operator name corresponding to the given SID
//
function _findProviderForSid(sid) {
if (sid == 0)
return null;
let mpd = _getMobileProvidersDatabase();
if (mpd) {
let provider = mpd.lookup_cdma_sid(sid);
if (provider)
return provider.get_name();
}
return null;
}
//------------------------------------------------------------------------------
// Support for the old ModemManager interface (MM < 0.7)
//------------------------------------------------------------------------------
// The following are not the complete interfaces, just the methods we need
// (or may need in the future)
@@ -126,6 +42,76 @@ const ModemCdmaInterface = <interface name="org.freedesktop.ModemManager.Modem.C
const ModemCdmaProxy = Gio.DBusProxy.makeProxyWrapper(ModemCdmaInterface);
let _providersTable;
function _getProvidersTable() {
if (_providersTable)
return _providersTable;
return _providersTable = Shell.mobile_providers_parse(null,null);
}
function findProviderForMCCMNC(table, needle) {
let needlemcc = needle.substring(0, 3);
let needlemnc = needle.substring(3, needle.length);
let name2, name3;
for (let iter in table) {
let country = table[iter];
let providers = country.get_providers();
// Search through each country's providers
for (let i = 0; i < providers.length; i++) {
let provider = providers[i];
// Search through MCC/MNC list
let list = provider.get_gsm_mcc_mnc();
for (let j = 0; j < list.length; j++) {
let mccmnc = list[j];
// Match both 2-digit and 3-digit MNC; prefer a
// 3-digit match if found, otherwise a 2-digit one.
if (mccmnc.mcc != needlemcc)
continue; // MCC was wrong
if (!name3 && needle.length == 6 && needlemnc == mccmnc.mnc)
name3 = provider.name;
if (!name2 && needlemnc.substring(0, 2) == mccmnc.mnc.substring(0, 2))
name2 = provider.name;
if (name2 && name3)
break;
}
}
}
return name3 || name2 || null;
}
function findProviderForSid(table, sid) {
if (sid == 0)
return null;
// Search through each country
for (let iter in table) {
let country = table[iter];
let providers = country.get_providers();
// Search through each country's providers
for (let i = 0; i < providers.length; i++) {
let provider = providers[i];
let cdma_sid = provider.get_cdma_sid();
// Search through CDMA SID list
for (let j = 0; j < cdma_sid.length; j++) {
if (cdma_sid[j] == sid)
return provider.name;
}
}
}
return null;
}
const ModemGsm = new Lang.Class({
Name: 'ModemGsm',
@@ -141,7 +127,7 @@ const ModemGsm = new Lang.Class({
this.emit('notify::signal-quality');
}));
this._proxy.connectSignal('RegistrationInfo', Lang.bind(this, function(proxy, sender, [status, code, name]) {
this.operator_name = _findProviderForMccMnc(name, code);
this.operator_name = this._findOperatorName(name, code);
this.emit('notify::operator-name');
}));
this._proxy.GetRegistrationInfoRemote(Lang.bind(this, function([result], err) {
@@ -151,7 +137,7 @@ const ModemGsm = new Lang.Class({
}
let [status, code, name] = result;
this.operator_name = _findProviderForMccMnc(name, code);
this.operator_name = this._findOperatorName(name, code);
this.emit('notify::operator-name');
}));
this._proxy.GetSignalQualityRemote(Lang.bind(this, function(result, err) {
@@ -164,6 +150,32 @@ const ModemGsm = new Lang.Class({
}
this.emit('notify::signal-quality');
}));
},
_findOperatorName: function(name, opCode) {
if (name) {
if (name && name.length != 0 && (name.length > 6 || name.length < 5)) {
// this looks like a valid name, i.e. not an MCCMNC (that some
// devices return when not yet connected
return name;
}
if (isNaN(parseInt(name))) {
// name is definitely not a MCCMNC, so it may be a name
// after all; return that
return name;
}
}
let needle;
if ((name == null || name.length == 0) && opCode)
needle = opCode;
else if (name.length == 6 || name.length == 5)
needle = name;
else // nothing to search
return null;
let table = _getProvidersTable();
return findProviderForMCCMNC(table, needle);
}
});
Signals.addSignalMethods(ModemGsm.prototype);
@@ -203,99 +215,15 @@ const ModemCdma = new Lang.Class({
// it will return an error if the device is not connected
this.operator_name = null;
} else {
let [bandClass, band, sid] = result;
this.operator_name = _findProviderForSid(sid)
let [bandClass, band, id] = result;
if (name.length > 0) {
let table = _getProvidersTable();
this.operator_name = findProviderForSid(table, id);
} else
this.operator_name = null;
}
this.emit('notify::operator-name');
}));
}
});
Signals.addSignalMethods(ModemCdma.prototype);
//------------------------------------------------------------------------------
// Support for the new ModemManager1 interface (MM >= 0.7)
//------------------------------------------------------------------------------
const BroadbandModemInterface = <interface name="org.freedesktop.ModemManager1.Modem">
<property name="SignalQuality" type="(ub)" access="read" />
</interface>;
const BroadbandModemProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModemInterface);
const BroadbandModem3gppInterface = <interface name="org.freedesktop.ModemManager1.Modem.Modem3gpp">
<property name="OperatorCode" type="s" access="read" />
<property name="OperatorName" type="s" access="read" />
</interface>;
const BroadbandModem3gppProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModem3gppInterface);
const BroadbandModemCdmaInterface = <interface name="org.freedesktop.ModemManager1.Modem.ModemCdma">
<property name="Sid" type="u" access="read" />
</interface>;
const BroadbandModemCdmaProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModemCdmaInterface);
const BroadbandModem = new Lang.Class({
Name: 'BroadbandModem',
_init: function(path, capabilities) {
this._proxy = new BroadbandModemProxy(Gio.DBus.system, 'org.freedesktop.ModemManager1', path);
this._proxy_3gpp = new BroadbandModem3gppProxy(Gio.DBus.system, 'org.freedesktop.ModemManager1', path);
this._proxy_cdma = new BroadbandModemCdmaProxy(Gio.DBus.system, 'org.freedesktop.ModemManager1', path);
this._capabilities = capabilities;
this._proxy.connect('g-properties-changed', Lang.bind(this, function(proxy, properties) {
if ('SignalQuality' in properties.deep_unpack())
this._reloadSignalQuality();
}));
this._reloadSignalQuality();
this._proxy_3gpp.connect('g-properties-changed', Lang.bind(this, function(proxy, properties) {
let unpacked = properties.deep_unpack();
if ('OperatorName' in unpacked || 'OperatorCode' in unpacked)
this._reload3gppOperatorName();
}));
this._reload3gppOperatorName();
this._proxy_cdma.connect('g-properties-changed', Lang.bind(this, function(proxy, properties) {
let unpacked = properties.deep_unpack();
if ('Nid' in unpacked || 'Sid' in unpacked)
this._reloadCdmaOperatorName();
}));
this._reloadCdmaOperatorName();
},
_reloadSignalQuality: function() {
let [quality, recent] = this._proxy.SignalQuality;
this.signal_quality = quality;
this.emit('notify::signal-quality');
},
_reloadOperatorName: function() {
let new_name = "";
if (this.operator_name_3gpp && this.operator_name_3gpp.length > 0)
new_name += this.operator_name_3gpp;
if (this.operator_name_cdma && this.operator_name_cdma.length > 0) {
if (new_name != "")
new_name += ", ";
new_name += this.operator_name_cdma;
}
this.operator_name = new_name;
this.emit('notify::operator-name');
},
_reload3gppOperatorName: function() {
let name = this._proxy_3gpp.OperatorName;
let code = this._proxy_3gpp.OperatorCode;
this.operator_name_3gpp = _findProviderForMccMnc(name, code);
this._reloadOperatorName();
},
_reloadCdmaOperatorName: function() {
let sid = this._proxy_cdma.Sid;
this.operator_name_cdma = _findProviderForSid(sid);
this._reloadOperatorName();
}
});
Signals.addSignalMethods(BroadbandModem.prototype);

View File

@@ -153,7 +153,6 @@ const ViewByCategories = new Lang.Class({
y_fill: false,
style_class: 'vfade' });
this._categoryScroll.add_actor(this._categoryBox);
this._categoryScroll.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
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 });

View File

@@ -220,8 +220,25 @@ const BoxPointer = new Lang.Class({
this.bin.allocate(childBox, flags);
if (this._sourceActor && this._sourceActor.mapped) {
this._reposition();
this._updateFlip();
this._reposition(this._sourceActor, this._arrowAlignment);
if (this._shouldFlip()) {
switch (this._arrowSide) {
case St.Side.TOP:
this._arrowSide = St.Side.BOTTOM;
break;
case St.Side.BOTTOM:
this._arrowSide = St.Side.TOP;
break;
case St.Side.LEFT:
this._arrowSide = St.Side.RIGHT;
break;
case St.Side.RIGHT:
this._arrowSide = St.Side.LEFT;
break;
}
this._reposition(this._sourceActor, this._arrowAlignment);
}
}
},
@@ -388,11 +405,11 @@ const BoxPointer = new Lang.Class({
cr.setLineWidth(borderWidth);
cr.stroke();
}
cr.$dispose();
},
setPosition: function(sourceActor, alignment) {
this._arrowSide = this._userArrowSide;
// We need to show it now to force an allocation,
// so that we can query the correct size.
this.actor.show();
@@ -400,8 +417,7 @@ const BoxPointer = new Lang.Class({
this._sourceActor = sourceActor;
this._arrowAlignment = alignment;
this._reposition();
this._updateFlip();
this._reposition(sourceActor, alignment);
},
setSourceAlignment: function(alignment) {
@@ -413,10 +429,7 @@ const BoxPointer = new Lang.Class({
this.setPosition(this._sourceActor, this._arrowAlignment);
},
_reposition: function() {
let sourceActor = this._sourceActor;
let alignment = this._arrowAlignment;
_reposition: function(sourceActor, alignment) {
// Position correctly relative to the sourceActor
let sourceNode = sourceActor.get_theme_node();
let sourceContentBox = sourceNode.get_content_box(sourceActor.get_allocation_box());
@@ -540,7 +553,7 @@ const BoxPointer = new Lang.Class({
_shiftActor : function() {
// Since the position of the BoxPointer depends on the allocated size
// of the BoxPointer and the position of the source actor, trying
// to position the BoxPointer via the x/y properties will result in
// to position the BoxPoiner via the x/y properties will result in
// allocation loops and warnings. Instead we do the positioning via
// the anchor point, which is independent of allocation, and leave
// x == y == 0.
@@ -548,47 +561,37 @@ const BoxPointer = new Lang.Class({
-(this._yPosition + this._yOffset));
},
_calculateArrowSide: function(arrowSide) {
_shouldFlip: function() {
let sourceAllocation = Shell.util_get_transformed_allocation(this._sourceActor);
let [minWidth, minHeight, boxWidth, boxHeight] = this._container.get_preferred_size();
let boxAllocation = Shell.util_get_transformed_allocation(this.actor);
let boxWidth = boxAllocation.x2 - boxAllocation.x1;
let boxHeight = boxAllocation.y2 - boxAllocation.y1;
let monitor = Main.layoutManager.findMonitorForActor(this.actor);
switch (arrowSide) {
switch (this._arrowSide) {
case St.Side.TOP:
if (sourceAllocation.y2 + boxHeight > monitor.y + monitor.height &&
if (boxAllocation.y2 > monitor.y + monitor.height &&
boxHeight < sourceAllocation.y1 - monitor.y)
return St.Side.BOTTOM;
return true;
break;
case St.Side.BOTTOM:
if (sourceAllocation.y1 - boxHeight < monitor.y &&
if (boxAllocation.y1 < monitor.y &&
boxHeight < monitor.y + monitor.height - sourceAllocation.y2)
return St.Side.TOP;
return true;
break;
case St.Side.LEFT:
if (sourceAllocation.y2 + boxWidth > monitor.x + monitor.width &&
if (boxAllocation.x2 > monitor.x + monitor.width &&
boxWidth < sourceAllocation.x1 - monitor.x)
return St.Side.RIGHT;
return true;
break;
case St.Side.RIGHT:
if (sourceAllocation.y1 - boxWidth < monitor.x &&
if (boxAllocation.x1 < monitor.x &&
boxWidth < monitor.x + monitor.width - sourceAllocation.x2)
return St.Side.LEFT;
return true;
break;
}
return arrowSide;
},
_updateFlip: function() {
let arrowSide = this._calculateArrowSide(this._userArrowSide);
if (this._arrowSide != arrowSide) {
this._arrowSide = arrowSide;
this._reposition();
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
this._container.queue_relayout();
return false;
}));
}
return false;
},
set xOffset(offset) {

View File

@@ -11,6 +11,7 @@ const Mainloop = imports.mainloop;
const Shell = imports.gi.Shell;
const MSECS_IN_DAY = 24 * 60 * 60 * 1000;
const WEEKDATE_HEADER_WIDTH_DIGITS = 3;
const SHOW_WEEKDATE_KEY = 'show-weekdate';
// in org.gnome.desktop.interface
@@ -94,6 +95,15 @@ function _getCalendarWeekForDate(date) {
return weekNumber;
}
function _getDigitWidth(actor){
let context = actor.get_pango_context();
let themeNode = actor.get_theme_node();
let font = themeNode.get_font();
let metrics = context.get_metrics(font, context.get_language());
let width = metrics.get_approximate_digit_width();
return width;
}
function _getCalendarDayAbbreviation(dayNumber) {
let abbreviations = [
/* Translators: Calendar grid abbreviation for Sunday.
@@ -335,6 +345,8 @@ const Calendar = new Lang.Class({
_init: function() {
this._weekStart = Shell.util_get_week_start();
this._weekdate = NaN;
this._digitWidth = NaN;
this._settings = new Gio.Settings({ schema: 'org.gnome.shell.calendar' });
this._settings.connect('changed::' + SHOW_WEEKDATE_KEY, Lang.bind(this, this._onSettingsChange));
@@ -407,6 +419,8 @@ const Calendar = new Lang.Class({
this.actor.add(this._topBox,
{ row: 0, col: 0, col_span: offsetCols + 7 });
this.actor.connect('style-changed', Lang.bind(this, this._onStyleChange));
let back = new St.Button({ style_class: 'calendar-change-month-back' });
this._topBox.add(back);
back.connect('clicked', Lang.bind(this, this._onPrevMonthButtonClicked));
@@ -443,6 +457,18 @@ const Calendar = new Lang.Class({
this._firstDayIndex = this.actor.get_n_children();
},
_onStyleChange: function(actor, event) {
// width of a digit in pango units
this._digitWidth = _getDigitWidth(this.actor) / Pango.SCALE;
this._setWeekdateHeaderWidth();
},
_setWeekdateHeaderWidth: function() {
if (this.digitWidth != NaN && this._useWeekdate && this._weekdateHeader) {
this._weekdateHeader.set_width (this._digitWidth * WEEKDATE_HEADER_WIDTH_DIGITS);
}
},
_onScroll : function(actor, event) {
switch (event.get_scroll_direction()) {
case Clutter.ScrollDirection.UP:

60
js/ui/centerLayout.js Normal file
View File

@@ -0,0 +1,60 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Lang = imports.lang;
const Clutter = imports.gi.Clutter;
const CenterLayout = new Lang.Class({
Name: 'CenterLayout',
Extends: Clutter.BoxLayout,
vfunc_allocate: function(container, box, flags) {
let rtl = container.get_text_direction() == Clutter.TextDirection.RTL;
let availWidth = box.x2 - box.x1;
let availHeight = box.y2 - box.y1;
// Assume that these are the first three widgets and they are all visible.
let [left, center, right] = container.get_children();
// Only support horizontal layouts for now.
let [leftMinWidth, leftNaturalWidth] = left.get_preferred_width(availHeight);
let [centerMinWidth, centerNaturalWidth] = center.get_preferred_width(availHeight);
let [rightMinWidth, rightNaturalWidth] = right.get_preferred_width(availHeight);
let sideWidth = (availWidth - centerMinWidth) / 2;
let childBox = new Clutter.ActorBox();
childBox.y1 = box.y1;
childBox.y2 = box.y1 + availHeight;
let leftSide = Math.min(Math.floor(sideWidth), leftNaturalWidth);
let rightSide = Math.min(Math.floor(sideWidth), rightNaturalWidth);
if (rtl) {
childBox.x1 = availWidth - leftSide;
childBox.x2 = availWidth;
} else {
childBox.x1 = 0;
childBox.x2 = leftSide;
}
childBox.x1 += box.x1;
left.allocate(childBox, flags);
let maxSide = Math.max(leftSide, rightSide);
let sideWidth = Math.max((availWidth - centerNaturalWidth) / 2, maxSide);
childBox.x1 = box.x1 + Math.ceil(sideWidth);
childBox.x2 = box.x2 - Math.ceil(sideWidth);
center.allocate(childBox, flags);
if (rtl) {
childBox.x1 = 0;
childBox.x2 = rightSide;
} else {
childBox.x1 = availWidth - rightSide;
childBox.x2 = availWidth;
}
childBox.x1 += box.x1;
right.allocate(childBox, flags);
}
});

View File

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

View File

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

View File

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

View File

@@ -613,8 +613,7 @@ const NetworkAgent = new Lang.Class({
this._vpnRequests = { };
this._native.auto_register = false;
if (this._native.registered)
this._native.unregister();
this._native.unregister();
},
_newRequest: function(agent, requestId, connection, settingName, hints, flags) {

View File

@@ -159,13 +159,14 @@ const AuthenticationDialog = new Lang.Class({
messageBox.add(this._nullMessageLabel);
this._nullMessageLabel.show();
this._cancelButton = this.addButton({ label: _("Cancel"),
action: Lang.bind(this, this.cancel),
key: Clutter.Escape });
this._okButton = this.addButton({ label: _("Authenticate"),
action: Lang.bind(this, this._onAuthenticateButtonPressed),
default: true },
{ expand: true, x_fill: false, x_align: St.Align.END });
this.setButtons([{ label: _("Cancel"),
action: Lang.bind(this, this.cancel),
key: Clutter.Escape
},
{ label: _("Authenticate"),
action: Lang.bind(this, this._onAuthenticateButtonPressed),
default: true
}]);
this._doneEmitted = false;
@@ -213,17 +214,8 @@ const AuthenticationDialog = new Lang.Class({
}
},
_updateSensitivity: function(sensitive) {
this._passwordEntry.reactive = sensitive;
this._passwordEntry.clutter_text.editable = sensitive;
this._okButton.can_focus = sensitive;
this._okButton.reactive = sensitive;
},
_onEntryActivate: function() {
let response = this._passwordEntry.get_text();
this._updateSensitivity(false);
this._session.response(response);
// When the user responds, dismiss already shown info and
// error texts (if any)
@@ -277,7 +269,6 @@ const AuthenticationDialog = new Lang.Class({
this._passwordBox.show();
this._passwordEntry.set_text('');
this._passwordEntry.grab_key_focus();
this._updateSensitivity(true);
this._ensureOpen();
},

View File

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

View File

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

View File

@@ -102,7 +102,8 @@ const CtrlAltTabManager = new Lang.Class({
icon = app.create_icon_texture(POPUP_APPICON_SIZE);
else
icon = textureCache.bind_pixbuf_property(windows[i], 'icon');
items.push({ name: windows[i].title,
items.push({ window: windows[i],
name: windows[i].title,
iconActor: icon,
sortGroup: SortGroup.MIDDLE });
}

View File

@@ -371,6 +371,7 @@ const Dash = new Lang.Class({
this._maxHeight = -1;
this.iconSize = 64;
this._shownInitially = false;
this._ignoreHeight = false;
this._dragPlaceholder = null;
this._dragPlaceholderPos = -1;
@@ -396,7 +397,10 @@ const Dash = new Lang.Class({
this.actor = new St.Bin({ child: this._container });
this.actor.connect('notify::height', Lang.bind(this,
function() {
if (this._maxHeight != this.actor.height)
if (this._ignoreHeight)
return;
if (this._maxHeight != this.actor.height);
this._queueRedisplay();
this._maxHeight = this.actor.height;
}));
@@ -421,6 +425,8 @@ const Dash = new Lang.Class({
Lang.bind(this, this._onDragCancelled));
Main.overview.connect('window-drag-end',
Lang.bind(this, this._onDragEnd));
Main.overview.connect('showing',
Lang.bind(this, this._onOverviewShowing));
},
_onDragBegin: function() {
@@ -923,6 +929,65 @@ const Dash = new Lang.Class({
}));
return true;
},
_computeTranslation: function() {
let rtl = (this.actor.get_text_direction() == Clutter.TextDirection.RTL);
if (rtl)
return this.actor.width;
else
return - this.actor.width;
},
_onOverviewShowing: function() {
// reset any translation and make sure the actor is visible when
// entering the overview
this.slideX = 0;
this.actor.show();
},
get slideX() {
return this._slideX;
},
set slideX(value) {
this._slideX = value;
this.actor.translation_x = this._slideX;
if (this._slideX > 0) {
let rect = new Clutter.Rect();
rect.size.width = this.actor.width - this._slideX;
rect.size.height = this.actor.height;
this.actor.clip_rect = rect;
} else {
this.actor.clip_rect = null;
}
},
show: function() {
this.actor.show();
Tweener.addTween(this, { slideX: 0,
transition: 'easeOutQuad',
time: DASH_ANIMATION_TIME,
onComplete: Lang.bind(this,
function() {
this._ignoreHeight = false;
})
});
},
hide: function() {
this._ignoreHeight = true;
let hiddenX = this._computeTranslation();
Tweener.addTween(this, { slideX: hiddenX,
transition: 'easeOutQuad',
time: DASH_ANIMATION_TIME,
onComplete: Lang.bind(this,
function() {
this.actor.hide();
})
});
}
});

View File

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

View File

@@ -106,15 +106,11 @@ function enableExtension(uuid) {
extensionOrder.push(uuid);
let stylesheetNames = [global.session_mode + '.css', 'stylesheet.css'];
for (let i = 0; i < stylesheetNames.length; i++) {
let stylesheetFile = extension.dir.get_child(stylesheetNames[i]);
if (stylesheetFile.query_exists(null)) {
let theme = St.ThemeContext.get_for_stage(global.stage).get_theme();
theme.load_stylesheet(stylesheetFile.get_path());
extension.stylesheet = stylesheetFile;
break;
}
let stylesheetFile = extension.dir.get_child('stylesheet.css');
if (stylesheetFile.query_exists(null)) {
let theme = St.ThemeContext.get_for_stage(global.stage).get_theme();
theme.load_stylesheet(stylesheetFile.get_path());
extension.stylesheet = stylesheetFile;
}
extension.stateObj.enable();
@@ -133,7 +129,6 @@ function logExtensionError(uuid, error) {
extension.state = ExtensionState.ERROR;
if (!extension.errors)
extension.errors = [];
extension.errors.push(message);
log('Extension "%s" had error: %s'.format(uuid, message));
_signals.emit('extension-state-changed', { uuid: uuid,

45
js/ui/flashspot.js Normal file
View File

@@ -0,0 +1,45 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Lang = imports.lang;
const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main;
const Tweener = imports.ui.tweener;
const FLASHSPOT_ANIMATION_TIME = 0.25; // seconds
const Flashspot = new Lang.Class({
Name: 'Flashspot',
Extends: Lightbox.Lightbox,
_init: function(area) {
this.parent(Main.uiGroup, { inhibitEvents: true,
width: area.width,
height: area.height });
this.actor.style_class = 'flashspot';
this.actor.set_position(area.x, area.y);
},
fire: function() {
this.actor.opacity = 0;
Tweener.addTween(this.actor,
{ opacity: 255,
time: FLASHSPOT_ANIMATION_TIME,
transition: 'linear',
onComplete: Lang.bind(this, this._onFireShowComplete)
});
this.actor.show();
},
_onFireShowComplete: function() {
Tweener.addTween(this.actor,
{ opacity: 0,
time: FLASHSPOT_ANIMATION_TIME,
transition: 'linear',
onComplete: Lang.bind(this, function() {
this.destroy();
})
});
}
});

View File

@@ -183,9 +183,6 @@ const GrabHelper = new Lang.Class({
else if (hadFocus || params.grabFocus)
_navigateActor(newFocus);
if ((params.grabFocus || params.modal) && !this._capturedEventId)
this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
this._grabStack.push(params);
return true;
},
@@ -195,6 +192,8 @@ const GrabHelper = new Lang.Class({
if (firstGrab) {
if (!Main.pushModal(this._owner, this._modalParams))
return false;
this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
}
this._modalCount++;
@@ -206,6 +205,11 @@ const GrabHelper = new Lang.Class({
if (this._modalCount > 0)
return;
if (this._capturedEventId > 0) {
global.stage.disconnect(this._capturedEventId);
this._capturedEventId = 0;
}
Main.popModal(this._owner);
global.sync_pointer();
},
@@ -304,11 +308,6 @@ const GrabHelper = new Lang.Class({
this._releaseFocusGrab();
}
if (!this.grabbed && this._capturedEventId > 0) {
global.stage.disconnect(this._capturedEventId);
this._capturedEventId = 0;
}
if (hadFocus) {
let poppedGrab = poppedGrabs[0];
_navigateActor(poppedGrab.savedFocus);
@@ -317,13 +316,6 @@ const GrabHelper = new Lang.Class({
_onCapturedEvent: function(actor, event) {
let type = event.type();
if (type == Clutter.EventType.KEY_PRESS &&
event.get_key_symbol() == Clutter.KEY_Escape) {
this.ungrab({ isUser: true });
return true;
}
let press = type == Clutter.EventType.BUTTON_PRESS;
let release = type == Clutter.EventType.BUTTON_RELEASE;
let button = press || release;
@@ -336,6 +328,12 @@ const GrabHelper = new Lang.Class({
if (!button && this._modalCount == 0)
return false;
if (type == Clutter.EventType.KEY_PRESS &&
event.get_key_symbol() == Clutter.KEY_Escape) {
this.ungrab({ isUser: true });
return true;
}
if (this._isWithinGrabbedActor(event.get_source()))
return false;

View File

@@ -23,8 +23,6 @@ const BaseIcon = new Lang.Class({
this.actor._delegate = this;
this.actor.connect('style-changed',
Lang.bind(this, this._onStyleChanged));
this.actor.connect('destroy',
Lang.bind(this, this._onDestroy));
this._spacing = 0;
@@ -54,9 +52,6 @@ const BaseIcon = new Lang.Class({
this._setSizeManually = params.setSizeManually;
this.icon = null;
let cache = St.TextureCache.get_default();
this._iconThemeChangedId = cache.connect('icon-theme-changed', Lang.bind(this, this._onIconThemeChanged));
},
_allocate: function(actor, box, flags) {
@@ -151,22 +146,7 @@ const BaseIcon = new Lang.Class({
size = found ? len : ICON_SIZE;
}
if (this.iconSize == size && this._iconBin.child)
return;
this._createIconTexture(size);
},
_onDestroy: function() {
if (this._iconThemeChangedId > 0) {
let cache = St.TextureCache.get_default();
cache.disconnect(this._iconThemeChangedId);
this._iconThemeChangedId = 0;
}
},
_onIconThemeChanged: function() {
this._createIconTexture(this.iconSize);
}
});

View File

@@ -2,21 +2,18 @@
const Caribou = imports.gi.Caribou;
const Clutter = imports.gi.Clutter;
const DBus = imports.dbus;
const Gdk = imports.gi.Gdk;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const St = imports.gi.St;
const BoxPointer = imports.ui.boxpointer;
const Layout = imports.ui.layout;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const KEYBOARD_REST_TIME = Layout.KEYBOARD_ANIMATION_TIME * 2 * 1000;
const KEYBOARD_SCHEMA = 'org.gnome.shell.keyboard';
const KEYBOARD_TYPE = 'keyboard-type';
@@ -59,7 +56,14 @@ const Key = new Lang.Class({
if (this._key.name == 'Control_L' || this._key.name == 'Alt_L')
this._key.latch = true;
this._key.connect('key-pressed', Lang.bind(this, function ()
{ this.actor.checked = true }));
this._key.connect('key-released', Lang.bind(this, function ()
{ this.actor.checked = false; }));
if (this._extended_keys.length > 0) {
this._grabbed = false;
this._eventCaptureId = 0;
this._key.connect('notify::show-subkeys', Lang.bind(this, this._onShowSubkeysChanged));
this._boxPointer = new BoxPointer.BoxPointer(St.Side.BOTTOM,
{ x_fill: true,
@@ -111,23 +115,52 @@ const Key = new Lang.Class({
this._boxPointer.bin.add_actor(this._extended_keyboard);
},
get subkeys() {
return this._boxPointer;
_onEventCapture: function (actor, event) {
let source = event.get_source();
let type = event.type();
if ((type == Clutter.EventType.BUTTON_PRESS ||
type == Clutter.EventType.BUTTON_RELEASE) &&
this._extended_keyboard.contains(source)) {
source.extended_key.press();
source.extended_key.release();
return false;
}
if (type == Clutter.EventType.BUTTON_PRESS) {
this._boxPointer.actor.hide();
this._ungrab();
return true;
}
return false;
},
_ungrab: function () {
global.stage.disconnect(this._eventCaptureId);
this._eventCaptureId = 0;
this._grabbed = false;
Main.popModal(this.actor);
},
_onShowSubkeysChanged: function () {
if (this._key.show_subkeys) {
this.actor.fake_release();
this._boxPointer.actor.raise_top();
this._boxPointer.setPosition(this.actor, 0.5);
this.emit('show-subkeys');
this.actor.fake_release();
this._boxPointer.show(BoxPointer.PopupAnimation.FULL);
this.actor.set_hover(false);
if (!this._grabbed) {
Main.pushModal(this.actor);
this._eventCaptureId = global.stage.connect('captured-event', Lang.bind(this, this._onEventCapture));
this._grabbed = true;
}
this._key.release();
} else {
this.emit('hide-subkeys');
if (this._grabbed)
this._ungrab();
this._boxPointer.hide(BoxPointer.PopupAnimation.FULL);
}
}
});
Signals.addSignalMethods(Key.prototype);
const Keyboard = new Lang.Class({
// HACK: we can't set Name, because it collides with Name dbus property
@@ -142,26 +175,16 @@ const Keyboard = new Lang.Class({
this._focusInExtendedKeys = false;
this._timestamp = global.display.get_current_time_roundtrip();
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._redraw));
this._keyboardSettings = new Gio.Settings({ schema: KEYBOARD_SCHEMA });
this._keyboardSettings.connect('changed', Lang.bind(this, this._settingsChanged));
this._a11yApplicationsSettings = new Gio.Settings({ schema: A11Y_APPLICATIONS_SCHEMA });
this._a11yApplicationsSettings.connect('changed', Lang.bind(this, this._settingsChanged));
this._settingsChanged();
},
this._showIdleId = 0;
this._subkeysBoxPointer = null;
this._capturedEventId = 0;
this._capturedPress = false;
this._keyboardVisible = false;
Main.layoutManager.connect('keyboard-visible-changed', Lang.bind(this, function(o, visible) {
this._keyboardVisible = visible;
}));
this._keyboardRequested = false;
this._keyboardRestingId = 0;
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._redraw));
init: function () {
this._redraw();
},
@@ -250,14 +273,10 @@ const Keyboard = new Lang.Class({
return;
let time = global.get_current_time();
if (!(focus instanceof Clutter.Text)) {
if (focus instanceof Clutter.Text)
this.Show(time);
else
this.Hide(time);
return;
}
if (!this._showIdleId)
this._showIdleId = GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE,
Lang.bind(this, function() { this.Show(time); }));
},
_addKeys: function () {
@@ -312,19 +331,6 @@ const Keyboard = new Lang.Class({
return trayButton;
},
_onCapturedEvent: function(actor, event) {
let type = event.type();
let press = type == Clutter.EventType.BUTTON_PRESS;
let release = type == Clutter.EventType.BUTTON_RELEASE;
if (press)
this._capturedPress = true;
else if (release && this._capturedPress)
this._hideSubkeys();
return true;
},
_addRows : function (keys, layout) {
let keyboard_row = new St.BoxLayout();
for (let i = 0; i < keys.length; ++i) {
@@ -347,19 +353,6 @@ const Keyboard = new Lang.Class({
// Add new key for hiding message tray
right_box.add(this._getTrayIcon());
}
button.connect('show-subkeys', Lang.bind(this, function() {
if (this._subkeysBoxPointer)
this._subkeysBoxPointer.hide(BoxPointer.PopupAnimation.FULL);
this._subkeysBoxPointer = button.subkeys;
this._subkeysBoxPointer.show(BoxPointer.PopupAnimation.FULL);
if (!this._capturedEventId)
this._capturedEventId = this.actor.connect('captured-event',
Lang.bind(this, this._onCapturedEvent));
}));
button.connect('hide-subkeys', Lang.bind(this, function() {
this._hideSubkeys();
}));
}
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 });
@@ -469,37 +462,7 @@ const Keyboard = new Lang.Class({
actor._extended_keys || actor.extended_key;
},
_clearKeyboardRestTimer: function() {
if (!this._keyboardRestingId)
return;
GLib.source_remove(this._keyboardRestingId);
this._keyboardRestingId = 0;
},
show: function (monitor) {
this._keyboardRequested = true;
if (this._keyboardVisible) {
if (monitor != Main.layoutManager.keyboardIndex) {
Main.layoutManager.keyboardIndex = monitor;
this._redraw();
}
return;
}
this._clearKeyboardRestTimer();
this._keyboardRestingId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
KEYBOARD_REST_TIME,
Lang.bind(this, function() {
this._clearKeyboardRestTimer();
this._show(monitor);
}));
},
_show: function(monitor) {
if (!this._keyboardRequested)
return;
Main.layoutManager.keyboardIndex = monitor;
this._redraw();
Main.layoutManager.showKeyboard();
@@ -507,41 +470,10 @@ const Keyboard = new Lang.Class({
},
hide: function () {
this._keyboardRequested = false;
if (!this._keyboardVisible)
return;
this._clearKeyboardRestTimer();
this._keyboardRestingId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
KEYBOARD_REST_TIME,
Lang.bind(this, function() {
this._clearKeyboardRestTimer();
this._hide();
}));
},
_hide: function() {
if (this._keyboardRequested)
return;
this._hideSubkeys();
Main.layoutManager.hideKeyboard();
this._createSource();
},
_hideSubkeys: function() {
if (this._subkeysBoxPointer) {
this._subkeysBoxPointer.hide(BoxPointer.PopupAnimation.FULL);
this._subkeysBoxPointer = null;
}
if (this._capturedEventId) {
this.actor.disconnect(this._capturedEventId);
this._capturedEventId = 0;
}
this._capturedPress = false;
},
_moveTemporarily: function () {
let currentWindow = global.screen.get_display().focus_window;
let rect = currentWindow.get_outer_rect();
@@ -570,13 +502,6 @@ const Keyboard = new Lang.Class({
return one - two;
},
_clearShowIdle: function() {
if (!this._showIdleId)
return;
GLib.source_remove(this._showIdleId);
this._showIdleId = 0;
},
// D-Bus methods
Show: function(timestamp) {
if (!this._enableKeyboard)
@@ -585,8 +510,6 @@ const Keyboard = new Lang.Class({
if (this._compareTimestamp(timestamp, this._timestamp) < 0)
return;
this._clearShowIdle();
if (timestamp != Clutter.CURRENT_TIME)
this._timestamp = timestamp;
this.show(Main.layoutManager.focusIndex);
@@ -599,8 +522,6 @@ const Keyboard = new Lang.Class({
if (this._compareTimestamp(timestamp, this._timestamp) < 0)
return;
this._clearShowIdle();
if (timestamp != Clutter.CURRENT_TIME)
this._timestamp = timestamp;
this.hide();
@@ -635,7 +556,11 @@ const KeyboardSource = new Lang.Class({
this.keepTrayOnSummaryClick = true;
},
handleSummaryClick: function(button) {
handleSummaryClick: function() {
let event = Clutter.get_current_event();
if (event.type() != Clutter.EventType.BUTTON_RELEASE)
return false;
this.open();
return true;
},

View File

@@ -16,7 +16,7 @@ const Tweener = imports.ui.tweener;
const HOT_CORNER_ACTIVATION_TIMEOUT = 0.5;
const STARTUP_ANIMATION_TIME = 0.2;
const KEYBOARD_ANIMATION_TIME = 0.15;
const KEYBOARD_ANIMATION_TIME = 0.5;
const PLYMOUTH_TRANSITION_TIME = 1;
const MonitorConstraint = new Lang.Class({
@@ -95,12 +95,6 @@ const MonitorConstraint = new Lang.Class({
}
});
const defaultParams = {
trackFullscreen: false,
affectsStruts: false,
affectsInputRegion: true
};
const LayoutManager = new Lang.Class({
Name: 'LayoutManager',
@@ -114,11 +108,9 @@ const LayoutManager = new Lang.Class({
this._background = null;
this._leftPanelBarrier = 0;
this._rightPanelBarrier = 0;
this._trayBarrier = 0;
this._inOverview = false;
this._updateRegionIdle = 0;
this._trackedActors = [];
this._chrome = new Chrome(this);
this.screenShieldGroup = new St.Widget({ name: 'screenShieldGroup',
visible: false,
@@ -144,43 +136,24 @@ const LayoutManager = new Lang.Class({
this.addChrome(this.keyboardBox);
this._keyboardHeightNotifyId = 0;
// Need to update struts on new workspaces when they are added
global.screen.connect('notify::n-workspaces',
Lang.bind(this, this._queueUpdateRegions));
global.screen.connect('restacked',
Lang.bind(this, this._windowsRestacked));
global.screen.connect('monitors-changed',
Lang.bind(this, this._monitorsChanged));
this._monitorsChanged();
this._chrome.connect('primary-fullscreen-changed', Lang.bind(this, function(chrome, state) {
this.emit('primary-fullscreen-changed', state);
}));
},
// This is called by Main after everything else is constructed;
// it needs access to Main.overview, which didn't exist
// Chrome.init() needs access to Main.overview, which didn't exist
// yet when the LayoutManager was constructed.
init: function() {
Main.overview.connect('showing', Lang.bind(this, this._overviewShowing));
Main.overview.connect('hidden', Lang.bind(this, this._overviewHidden));
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
this._chrome.init();
this._startupAnimation();
},
_overviewShowing: function() {
this._inOverview = true;
this._updateVisibility();
this._queueUpdateRegions();
},
_overviewHidden: function() {
this._inOverview = false;
this._updateVisibility();
this._queueUpdateRegions();
},
_sessionUpdated: function() {
this._updateVisibility();
this._queueUpdateRegions();
},
_updateMonitors: function() {
let screen = global.screen;
@@ -257,7 +230,7 @@ const LayoutManager = new Lang.Class({
let corner = new HotCorner();
this._hotCorners.push(corner);
corner.actor.set_position(cornerX, cornerY);
this.addChrome(corner.actor);
this._chrome.addActor(corner.actor);
}
},
@@ -314,9 +287,6 @@ const LayoutManager = new Lang.Class({
this._updateMonitors();
this._updateBoxes();
this._updateHotCorners();
this._updateFullscreen();
this._updateVisibility();
this._queueUpdateRegions();
this.emit('monitors-changed');
},
@@ -351,11 +321,11 @@ const LayoutManager = new Lang.Class({
global.stage_input_mode == Shell.StageInputMode.FULLSCREEN) {
let focusActor = global.stage.key_focus;
if (focusActor)
i = this.findIndexForActor(focusActor);
i = this._chrome.findIndexForActor(focusActor);
} else {
let focusWindow = global.display.focus_window;
if (focusWindow)
i = this.findIndexForWindow(focusWindow);
i = this._chrome.findIndexForWindow(focusWindow.get_compositor_private());
}
return i;
@@ -404,7 +374,8 @@ const LayoutManager = new Lang.Class({
},
_fadeBackgroundComplete: function() {
this._freezeUpdateRegions();
// Don't animate the strut
this._chrome.freezeUpdateRegions();
if (this._background != null) {
this._background.destroy();
@@ -422,7 +393,7 @@ const LayoutManager = new Lang.Class({
_startupAnimationComplete: function() {
this.emit('panel-box-changed');
this._thawUpdateRegions();
this._chrome.thawUpdateRegions();
},
showKeyboard: function () {
@@ -449,7 +420,7 @@ const LayoutManager = new Lang.Class({
_showKeyboardComplete: function() {
// Poke Chrome to update the input shape; it doesn't notice
// anchor point changes
this._updateRegions();
this._chrome.updateRegions();
this._keyboardHeightNotifyId = this.keyboardBox.connect('notify::height', Lang.bind(this, function () {
this.keyboardBox.anchor_y = this.keyboardBox.height;
@@ -466,7 +437,7 @@ const LayoutManager = new Lang.Class({
Tweener.addTween(this.keyboardBox,
{ anchor_y: 0,
time: immediate ? 0 : KEYBOARD_ANIMATION_TIME,
transition: 'easeInQuad',
transition: 'easeOutQuad',
onComplete: this._hideKeyboardComplete,
onCompleteScope: this
});
@@ -483,7 +454,7 @@ const LayoutManager = new Lang.Class({
},
_hideKeyboardComplete: function() {
this._updateRegions();
this._chrome.updateRegions();
},
// addChrome:
@@ -506,8 +477,7 @@ const LayoutManager = new Lang.Class({
// monitor (it will be hidden whenever a fullscreen window is visible,
// and shown otherwise)
addChrome: function(actor, params) {
Main.uiGroup.add_actor(actor);
this._trackActor(actor, params);
this._chrome.addActor(actor, params);
},
// trackChrome:
@@ -522,26 +492,7 @@ const LayoutManager = new Lang.Class({
// though some possibilities don't make sense. By default, @actor has
// the same params as its chrome ancestor.
trackChrome: 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 a chrome actor');
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.hasOwnProperty(prop))
params[prop] = ancestorData[prop];
}
this._trackActor(actor, params);
this._chrome.trackActor(actor, params);
},
// untrackChrome:
@@ -549,7 +500,7 @@ const LayoutManager = new Lang.Class({
//
// Undoes the effect of trackChrome()
untrackChrome: function(actor) {
this._untrackActor(actor);
this._chrome.untrackActor(actor);
},
// removeChrome:
@@ -557,348 +508,11 @@ const LayoutManager = new Lang.Class({
//
// Removes @actor from the chrome
removeChrome: function(actor) {
Main.uiGroup.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.isToplevel = actor.get_parent() == Main.uiGroup;
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) {
let newParent = actor.get_parent();
if (!newParent) {
this._untrackActor(actor);
} else {
let i = this._findActor(actor);
let actorData = this._trackedActors[i];
actorData.isToplevel = (newParent == Main.uiGroup);
}
},
_updateVisibility: function() {
for (let i = 0; i < this._trackedActors.length; i++) {
let actorData = this._trackedActors[i], visible;
if (!actorData.trackFullscreen)
continue;
if (!actorData.isToplevel)
continue;
if (this._inOverview || !Main.sessionMode.hasWindows)
visible = true;
else if (this.findMonitorForActor(actorData.actor).inFullscreen)
visible = false;
else
visible = true;
actorData.actor.visible = visible;
}
},
_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 i;
}
// 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 i;
}
// otherwise on no monitor
return -1;
},
findIndexForWindow: function(window) {
let rect = window.get_input_rect();
let i = this._findMonitorForRect(rect.x, rect.y, rect.width, rect.height);
if (i >= 0)
return i;
return this.primaryIndex; // Not on any monitor, pretend its on the primary
},
// 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
findIndexForActor: function(actor) {
let [x, y] = actor.get_transformed_position();
let [w, h] = actor.get_transformed_size();
let i = this._findMonitorForRect(x, y, w, h);
if (i >= 0)
return i;
return this.primaryIndex; // Not on any monitor, pretend its on the primary
},
findMonitorForWindow: function(window) {
let rect = window.get_input_rect();
let i = this._findMonitorForRect(rect.x, rect.y, rect.width, rect.height);
if (i >= 0)
return this.monitors[i];
else
return null;
this._chrome.removeActor(actor);
},
findMonitorForActor: function(actor) {
return this.monitors[this.findIndexForActor(actor)];
},
_queueUpdateRegions: function() {
if (!this._updateRegionIdle && !this._freezeUpdateCount)
this._updateRegionIdle = Mainloop.idle_add(Lang.bind(this, this._updateRegions),
Meta.PRIORITY_BEFORE_REDRAW);
},
_freezeUpdateRegions: function() {
if (this._updateRegionIdle)
this._updateRegions();
this._freezeUpdateCount++;
},
_thawUpdateRegions: function() {
this._freezeUpdateCount--;
this._queueUpdateRegions();
},
_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;
// Ordinary chrome 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 metaWindow = window.meta_window;
let layer = metaWindow.get_layer();
// Skip minimized windows
if (!window.showing_on_its_workspace())
continue;
if (layer == Meta.StackLayer.FULLSCREEN) {
let monitor = this.findMonitorForWindow(metaWindow);
if (monitor)
monitor.inFullscreen = true;
}
if (layer == Meta.StackLayer.OVERRIDE_REDIRECT) {
// Check whether the window is screen sized
let isScreenSized =
(window.x == 0 && window.y == 0 &&
window.width == global.screen_width &&
window.height == global.screen_height);
if (isScreenSized) {
for (let i = 0; i < this.monitors.length; i++)
this.monitors[i].inFullscreen = true;
}
// Or whether it is monitor sized
let monitor = this.findMonitorForWindow(metaWindow);
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;
let primaryWasInFullscreen = this.primaryMonitor.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();
}
if (primaryWasInFullscreen != this.primaryMonitor.inFullscreen) {
this.emit('primary-fullscreen-changed', this.primaryMonitor.inFullscreen);
}
},
_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() &&
!Main.uiGroup.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 >= primary.x + primary.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;
return this.monitors[this._chrome.findIndexForActor(actor)];
}
});
Signals.addSignalMethods(LayoutManager.prototype);
@@ -1075,3 +689,449 @@ const HotCorner = new Lang.Class({
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 = {
trackFullscreen: false,
affectsStruts: false,
affectsInputRegion: true
};
const Chrome = new Lang.Class({
Name: 'Chrome',
_init: function(layoutManager) {
this._layoutManager = layoutManager;
this._monitors = [];
this._inOverview = false;
this._updateRegionIdle = 0;
this._freezeUpdateCount = 0;
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._relayout();
},
init: function() {
Main.overview.connect('showing', Lang.bind(this, this._overviewShowing));
Main.overview.connect('hidden', Lang.bind(this, this._overviewHidden));
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
},
addActor: function(actor, params) {
Main.uiGroup.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 a chrome actor');
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.hasOwnProperty(prop))
params[prop] = ancestorData[prop];
}
this._trackActor(actor, params);
},
untrackActor: function(actor) {
this._untrackActor(actor);
},
removeActor: function(actor) {
Main.uiGroup.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.isToplevel = actor.get_parent() == Main.uiGroup;
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) {
let newParent = actor.get_parent();
if (!newParent) {
this._untrackActor(actor);
} else {
let i = this._findActor(actor);
let actorData = this._trackedActors[i];
actorData.isToplevel = (newParent == Main.uiGroup);
}
},
_updateVisibility: function() {
for (let i = 0; i < this._trackedActors.length; i++) {
let actorData = this._trackedActors[i], visible;
if (!actorData.trackFullscreen)
continue;
if (!actorData.isToplevel)
continue;
if (this._inOverview || !Main.sessionMode.hasWindows)
visible = true;
else if (this.findMonitorForActor(actorData.actor).inFullscreen)
visible = false;
else
visible = true;
actorData.actor.visible = visible;
}
},
_overviewShowing: function() {
this._inOverview = true;
this._updateVisibility();
this._queueUpdateRegions();
},
_overviewHidden: function() {
this._inOverview = false;
this._updateVisibility();
this._queueUpdateRegions();
},
_sessionUpdated: function() {
this._updateVisibility();
this._queueUpdateRegions();
},
_relayout: function() {
this._monitors = this._layoutManager.monitors;
this._primaryIndex = this._layoutManager.primaryIndex;
this._primaryMonitor = this._layoutManager.primaryMonitor;
this._updateFullscreen();
this._updateVisibility();
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 i;
}
// 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 i;
}
// otherwise on no monitor
return -1;
},
findIndexForWindow: function(window) {
let i = this._findMonitorForRect(window.x, window.y, window.width, window.height);
if (i >= 0)
return i;
return this._primaryIndex; // Not on any monitor, pretend its on the primary
},
// 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
findIndexForActor: function(actor) {
let [x, y] = actor.get_transformed_position();
let [w, h] = actor.get_transformed_size();
let i = this._findMonitorForRect(x, y, w, h);
if (i >= 0)
return i;
return this._primaryIndex; // Not on any monitor, pretend its on the primary
},
findMonitorForWindow: function(window) {
let i = this._findMonitorForRect(window.x, window.y, window.width, window.height);
if (i >= 0)
return this._monitors[i];
else
return null;
},
findMonitorForActor: function(actor) {
return this._monitors[this.findIndexForActor(actor)];
},
_queueUpdateRegions: function() {
if (!this._updateRegionIdle && !this._freezeUpdateCount)
this._updateRegionIdle = Mainloop.idle_add(Lang.bind(this, this.updateRegions),
Meta.PRIORITY_BEFORE_REDRAW);
},
freezeUpdateRegions: function() {
if (this._updateRegionIdle)
this.updateRegions();
this._freezeUpdateCount++;
},
thawUpdateRegions: function() {
this._freezeUpdateCount--;
this._queueUpdateRegions();
},
_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;
// Ordinary chrome 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) {
// Check whether the window is screen sized
let isScreenSized =
(window.x == 0 && window.y == 0 &&
window.width == global.screen_width &&
window.height == global.screen_height);
if (isScreenSized) {
for (let i = 0; i < this._monitors.length; i++)
this._monitors[i].inFullscreen = true;
}
// Or whether it is monitor sized
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;
let primaryWasInFullscreen = this._primaryMonitor.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();
}
if (primaryWasInFullscreen != this._primaryMonitor.inFullscreen) {
this.emit('primary-fullscreen-changed', this._primaryMonitor.inFullscreen);
}
},
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() &&
!Main.uiGroup.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

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

View File

@@ -39,7 +39,6 @@ var commandHeader = 'const Clutter = imports.gi.Clutter; ' +
* in the shell core code too. */
'const stage = global.stage; ' +
/* Special lookingGlass functions */
'const inspect = Lang.bind(Main.lookingGlass, Main.lookingGlass.inspect); ' +
'const it = Main.lookingGlass.getIt(); ' +
'const r = Lang.bind(Main.lookingGlass, Main.lookingGlass.getResult); ';
@@ -872,7 +871,8 @@ const LookingGlass = new Lang.Class({
inspectIcon.connect('button-press-event', Lang.bind(this, function () {
let inspector = new Inspector(this);
inspector.connect('target', Lang.bind(this, function(i, target, stageX, stageY) {
this._pushResult('inspect(' + Math.round(stageX) + ', ' + Math.round(stageY) + ')', target);
this._pushResult('<inspect x:' + stageX + ' y:' + stageY + '>',
target);
}));
inspector.connect('closed', Lang.bind(this, function() {
this.actor.show();
@@ -1057,10 +1057,6 @@ const LookingGlass = new Lang.Class({
this._entry.text = '';
},
inspect: function(x, y) {
return global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y);
},
getIt: function () {
return this._it;
},
@@ -1133,7 +1129,7 @@ const LookingGlass = new Lang.Class({
if (this._open)
return;
if (!Main.pushModal(this._entry, { keybindingMode: Shell.KeyBindingMode.LOOKING_GLASS }))
if (!Main.pushModal(this._entry, { keybindingMode: Main.KeybindingMode.LOOKING_GLASS }))
return;
this._notebook.selectIndex(0);

View File

@@ -40,6 +40,18 @@ const Util = imports.misc.util;
const OVERRIDES_SCHEMA = 'org.gnome.shell.overrides';
const DEFAULT_BACKGROUND_COLOR = Clutter.Color.from_pixel(0x2e3436ff);
const KeybindingMode = {
NONE: 0, // block all keybindings
NORMAL: 1 << 0, // window mode
OVERVIEW: 1 << 1,
LOCK_SCREEN: 1 << 2,
UNLOCK_SCREEN: 1 << 3,
LOGIN_SCREEN: 1 << 4,
MESSAGE_TRAY: 1 << 5,
SYSTEM_MODAL: 1 << 6,
LOOKING_GLASS: 1 << 7
};
let componentManager = null;
let panel = null;
let overview = null;
@@ -56,7 +68,7 @@ let shellDBusService = null;
let shellMountOpDBusService = null;
let screenSaverDBus = null;
let modalCount = 0;
let keybindingMode = Shell.KeyBindingMode.NORMAL;
let keybindingMode = KeybindingMode.NORMAL;
let modalActorFocusStack = [];
let uiGroup = null;
let magnifier = null;
@@ -68,17 +80,19 @@ let _defaultCssStylesheet = null;
let _cssStylesheet = null;
let _overridesSettings = null;
let background = null;
function _sessionUpdated() {
wm.setCustomKeybindingHandler('panel-main-menu',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
KeybindingMode.NORMAL |
KeybindingMode.OVERVIEW,
sessionMode.hasOverview ? Lang.bind(overview, overview.toggle) : null);
wm.allowKeybinding('overlay-key', Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW);
wm.allowKeybinding('overlay-key', KeybindingMode.NORMAL |
KeybindingMode.OVERVIEW);
wm.setCustomKeybindingHandler('panel-run-dialog',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
KeybindingMode.NORMAL |
KeybindingMode.OVERVIEW,
sessionMode.hasRunDialog ? openRunDialog : null);
if (sessionMode.isGreeter)
screenShield.showDialog();
@@ -95,8 +109,6 @@ function start() {
Gio.DesktopAppInfo.set_desktop_env('GNOME');
sessionMode = new SessionMode.SessionMode();
sessionMode.connect('updated', _loadDefaultStylesheet);
shellDBusService = new ShellDBus.GnomeShell();
shellMountOpDBusService = new ShellMountOperation.GnomeShellMountOpHandler();
@@ -119,7 +131,8 @@ function start() {
global.stage.color = DEFAULT_BACKGROUND_COLOR;
global.stage.no_clear_hint = true;
_loadDefaultStylesheet();
_defaultCssStylesheet = global.datadir + '/theme/gnome-shell.css';
loadTheme();
// Set up stage hierarchy to group all UI actors under one container.
uiGroup = new Shell.GenericContainer({ name: 'uiGroup' });
@@ -147,13 +160,13 @@ function start() {
xdndHandler = new XdndHandler.XdndHandler();
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
overview = new Overview.Overview();
wm = new WindowManager.WindowManager();
magnifier = new Magnifier.Magnifier();
if (UnlockDialog.isSupported())
screenShield = new ScreenShield.ScreenShield();
else
screenShield = new ScreenShield.ScreenShieldFallback();
panel = new Panel.Panel();
wm = new WindowManager.WindowManager();
messageTray = new MessageTray.MessageTray();
keyboard = new Keyboard.Keyboard();
notificationDaemon = new NotificationDaemon.NotificationDaemon();
@@ -161,6 +174,7 @@ function start() {
componentManager = new Components.ComponentManager();
layoutManager.init();
keyboard.init();
overview.init();
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT,
@@ -380,18 +394,6 @@ function _nWorkspacesChanged() {
return false;
}
function _loadDefaultStylesheet() {
if (!sessionMode.isPrimary)
return;
let stylesheet = global.datadir + '/theme/' + sessionMode.stylesheetName;
if (_defaultCssStylesheet == stylesheet)
return;
_defaultCssStylesheet = stylesheet;
loadTheme();
}
/**
* getThemeStylesheet:
*
@@ -517,7 +519,7 @@ function isInModalStack(actor) {
* - options: Meta.ModalOptions flags to indicate that the pointer is
* already grabbed
*
* - keybindingMode: used to set the current Shell.KeyBindingMode to filter
* - keybindingMode: used to set the current Main.KeybindingMode to filter
* global keybindings; the default of NONE will filter
* out all keybindings
*
@@ -526,7 +528,7 @@ function isInModalStack(actor) {
function pushModal(actor, params) {
params = Params.parse(params, { timestamp: global.get_current_time(),
options: 0,
keybindingMode: Shell.KeyBindingMode.NONE });
keybindingMode: KeybindingMode.NONE });
if (modalCount == 0) {
if (!global.begin_modal(params.timestamp, params.options)) {
@@ -544,20 +546,19 @@ function pushModal(actor, params) {
if (index >= 0)
popModal(actor);
});
let prevFocus = global.stage.get_key_focus();
let prevFocusDestroyId;
if (prevFocus != null) {
prevFocusDestroyId = prevFocus.connect('destroy', function() {
let curFocus = global.stage.get_key_focus();
let curFocusDestroyId;
if (curFocus != null) {
curFocusDestroyId = curFocus.connect('destroy', function() {
let index = _findModal(actor);
if (index >= 0)
modalActorFocusStack[index].prevFocus = null;
modalActorFocusStack[index].actor = null;
});
}
modalActorFocusStack.push({ actor: actor,
focus: curFocus,
destroyId: actorDestroyId,
prevFocus: prevFocus,
prevFocusDestroyId: prevFocusDestroyId,
focusDestroyId: curFocusDestroyId,
keybindingMode: keybindingMode });
keybindingMode = params.keybindingMode;
@@ -587,7 +588,7 @@ function popModal(actor, timestamp) {
global.stage.set_key_focus(null);
global.end_modal(timestamp);
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
keybindingMode = Shell.KeyBindingMode.NORMAL;
keybindingMode = KeybindingMode.NORMAL;
throw new Error('incorrect pop');
}
@@ -598,33 +599,18 @@ function popModal(actor, timestamp) {
record.actor.disconnect(record.destroyId);
if (focusIndex == modalActorFocusStack.length - 1) {
if (record.prevFocus)
record.prevFocus.disconnect(record.prevFocusDestroyId);
if (record.focus)
record.focus.disconnect(record.focusDestroyId);
keybindingMode = record.keybindingMode;
global.stage.set_key_focus(record.prevFocus);
global.stage.set_key_focus(record.focus);
} else {
// If we have:
// global.stage.set_focus(a);
// Main.pushModal(b);
// Main.pushModal(c);
// Main.pushModal(d);
//
// then we have the stack:
// [{ prevFocus: a, actor: b },
// { prevFocus: b, actor: c },
// { prevFocus: c, actor: d }]
//
// When actor c is destroyed/popped, if we only simply remove the
// record, then the focus stack will be [a, c], rather than the correct
// [a, b]. Shift the focus stack up before removing the record to ensure
// that we get the correct result.
let t = modalActorFocusStack[modalActorFocusStack.length - 1];
if (t.prevFocus)
t.prevFocus.disconnect(t.prevFocusDestroyId);
if (t.focus)
t.focus.disconnect(t.focusDestroyId);
// Remove from the middle, shift the focus chain up
for (let i = modalActorFocusStack.length - 1; i > focusIndex; i--) {
modalActorFocusStack[i].prevFocus = modalActorFocusStack[i - 1].prevFocus;
modalActorFocusStack[i].prevFocusDestroyId = modalActorFocusStack[i - 1].prevFocusDestroyId;
modalActorFocusStack[i].focus = modalActorFocusStack[i - 1].focus;
modalActorFocusStack[i].focusDestroyId = modalActorFocusStack[i - 1].focusDestroyId;
modalActorFocusStack[i].keybindingMode = modalActorFocusStack[i - 1].keybindingMode;
}
}
@@ -636,7 +622,7 @@ function popModal(actor, timestamp) {
global.end_modal(timestamp);
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
Meta.enable_unredirect_for_screen(global.screen);
keybindingMode = Shell.KeyBindingMode.NORMAL;
keybindingMode = KeybindingMode.NORMAL;
}
function createLookingGlass() {

View File

@@ -18,7 +18,6 @@ const BoxPointer = imports.ui.boxpointer;
const CtrlAltTab = imports.ui.ctrlAltTab;
const GnomeSession = imports.misc.gnomeSession;
const GrabHelper = imports.ui.grabHelper;
const Hash = imports.misc.hash;
const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main;
const PointerWatcher = imports.ui.pointerWatcher;
@@ -244,36 +243,6 @@ function makeCloseButton() {
return closeButton;
}
function strHasSuffix(string, suffix) {
return string.substr(-suffix.length) == suffix;
}
// NotificationPolicy:
// An object that holds all bits of configurable policy related to a notification
// source, such as whether to play sound or honour the critical bit.
//
// A notification without a policy object will inherit the default one.
const NotificationPolicy = new Lang.Class({
Name: 'NotificationPolicy',
_init: function(params) {
params = Params.parse(params, { enable: true,
enableSound: true,
showBanners: true,
forceExpanded: false,
showInLockScreen: true,
detailsInLockScreen: false
});
Lang.copyProperties(params, this);
},
// Do nothing for the default policy. These methods are only useful for the
// GSettings policy.
store: function() { },
destroy: function() { }
});
Signals.addSignalMethods(NotificationPolicy.prototype);
// Notification:
// @source: the notification's Source
// @title: the title
@@ -345,7 +314,6 @@ const Notification = new Lang.Class({
this.resident = false;
// 'transient' is a reserved keyword in JS, so we have to use an alternate variable name
this.isTransient = false;
this.isMusic = false;
this.forFeedback = false;
this.expanded = false;
this.focused = false;
@@ -353,8 +321,8 @@ const Notification = new Lang.Class({
this._destroyed = false;
this._useActionIcons = false;
this._customContent = false;
this.bannerBodyText = null;
this.bannerBodyMarkup = false;
this._bannerBodyText = null;
this._bannerBodyMarkup = false;
this._titleFitsInBannerMode = true;
this._titleDirection = Clutter.TextDirection.DEFAULT;
this._spacing = 0;
@@ -461,7 +429,7 @@ const Notification = new Lang.Class({
this._actionArea = null;
this._buttonBox = null;
}
if (params.clear)
if (this._imageBin && params.clear)
this.unsetImage();
if (!this._scrollArea && !this._actionArea && !this._imageBin)
@@ -506,12 +474,12 @@ const Notification = new Lang.Class({
// is done correctly automatically.
this._table.set_text_direction(this._titleDirection);
// Unless the notification has custom content, we save this.bannerBodyText
// Unless the notification has custom content, we save this._bannerBodyText
// to add it to the content of the notification if the notification is
// expandable due to other elements in its content area or due to the banner
// not fitting fully in the single-line mode.
this.bannerBodyText = this._customContent ? null : banner;
this.bannerBodyMarkup = params.bannerMarkup;
this._bannerBodyText = this._customContent ? null : banner;
this._bannerBodyMarkup = params.bannerMarkup;
banner = banner ? banner.replace(/\n/g, ' ') : '';
@@ -519,7 +487,7 @@ const Notification = new Lang.Class({
this._bannerLabel.queue_relayout();
// Add the bannerBody now if we know for sure we'll need it
if (this.bannerBodyText && this.bannerBodyText.indexOf('\n') > -1)
if (this._bannerBodyText && this._bannerBodyText.indexOf('\n') > -1)
this._addBannerBody();
if (params.body)
@@ -543,8 +511,7 @@ const Notification = new Lang.Class({
this._table.add_style_class_name('multi-line-notification');
this._scrollArea = new St.ScrollView({ style_class: 'notification-scrollview',
vscrollbar_policy: this._scrollPolicy,
hscrollbar_policy: Gtk.PolicyType.NEVER,
visible: this.expanded });
hscrollbar_policy: Gtk.PolicyType.NEVER });
this._table.add(this._scrollArea, { row: 1,
col: 2 });
this._updateLastColumnSettings();
@@ -585,10 +552,10 @@ const Notification = new Lang.Class({
},
_addBannerBody: function() {
if (this.bannerBodyText) {
let text = this.bannerBodyText;
this.bannerBodyText = null;
this.addBody(text, this.bannerBodyMarkup);
if (this._bannerBodyText) {
let text = this._bannerBodyText;
this._bannerBodyText = null;
this.addBody(text, this._bannerBodyMarkup);
}
},
@@ -620,7 +587,6 @@ const Notification = new Lang.Class({
this._addBannerBody();
}
this._actionArea = actor;
this._actionArea.visible = this.expanded;
if (!props)
props = {};
@@ -643,15 +609,11 @@ const Notification = new Lang.Class({
},
setImage: function(image) {
this.unsetImage();
if (!image)
return;
this._imageBin = new St.Bin({ opacity: 230,
child: image,
visible: this.expanded });
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._addBannerBody();
@@ -701,10 +663,9 @@ const Notification = new Lang.Class({
let button = new St.Button({ can_focus: true });
button._actionId = id;
let iconName = strHasSuffix(id, '-symbolic') ? id : id + '-symbolic';
if (this._useActionIcons && Gtk.IconTheme.get_default().has_icon(iconName)) {
if (this._useActionIcons && Gtk.IconTheme.get_default().has_icon(id)) {
button.add_style_class_name('notification-icon-button');
button.child = new St.Icon({ icon_name: iconName });
button.child = new St.Icon({ icon_name: id });
} else {
button.add_style_class_name('notification-button');
button.label = label;
@@ -880,7 +841,7 @@ const Notification = new Lang.Class({
},
_canExpandContent: function() {
return this.bannerBodyText ||
return this._bannerBodyText ||
(!this._titleFitsInBannerMode && !this._table.has_style_class_name('multi-line-notification'));
},
@@ -893,14 +854,6 @@ const Notification = new Lang.Class({
this.expanded = true;
this.actor.remove_style_class_name('notification-unexpanded');
// Show additional content that we keep hidden in banner mode
if (this._imageBin)
this._imageBin.show();
if (this._actionArea)
this._actionArea.show();
if (this._scrollArea)
this._scrollArea.show();
// The banner is never shown when the title did not fit, so this
// can be an if-else statement.
if (!this._titleFitsInBannerMode) {
@@ -932,23 +885,12 @@ const Notification = new Lang.Class({
if (this._destroyed)
return;
this.expanded = false;
// Hide additional content that we keep hidden in banner mode
if (this._imageBin)
this._imageBin.hide();
if (this._actionArea)
this._actionArea.hide();
if (this._scrollArea)
this._scrollArea.hide();
// Make sure we don't line wrap the title, and ellipsize it instead.
this._titleLabel.clutter_text.line_wrap = false;
this._titleLabel.clutter_text.ellipsize = Pango.EllipsizeMode.END;
// Restore banner opacity in case the notification is shown in the
// banner mode again on update.
this._bannerLabel.opacity = 255;
// Restore height requisition
this.actor.add_style_class_name('notification-unexpanded');
this.emit('collapsed');
@@ -1116,11 +1058,10 @@ const Source = new Lang.Class({
this.isTransient = false;
this.isChat = false;
this.isMuted = false;
this.showInLockScreen = true;
this.keepTrayOnSummaryClick = false;
this.notifications = [];
this.policy = this._createPolicy();
},
get count() {
@@ -1139,10 +1080,6 @@ const Source = new Lang.Class({
this.emit('count-updated');
},
_createPolicy: function() {
return new NotificationPolicy();
},
buildRightClickMenu: function() {
let item;
let rightClickMenu = new St.BoxLayout({ name: 'summary-right-click-menu',
@@ -1232,13 +1169,11 @@ const Source = new Lang.Class({
notify: function(notification) {
notification.acknowledged = false;
this.pushNotification(notification);
if (!this.isMuted && this.policy.showBanners)
this.emit('notify', notification);
if (!this.isMuted)
this.emit('notify', notification);
},
destroy: function(reason) {
this.policy.destroy();
this.emit('destroy', reason);
},
@@ -1280,16 +1215,7 @@ const Source = new Lang.Class({
hasResidentNotification: function() {
return this.notifications.some(function(n) { return n.resident; });
},
getMusicNotification: function() {
for (let i = 0; i < this.notifications.length; i++) {
if (this.notifications[i].isMusic)
return this.notifications[i];
}
return null;
},
}
});
Signals.addSignalMethods(Source.prototype);
@@ -1343,14 +1269,6 @@ const SummaryItem = new Lang.Class({
global.focus_manager.add_group(this.rightClickMenu);
},
destroy: function() {
// remove the actor from the summary item so it doesn't get destroyed
// with us
this._sourceBox.remove_actor(this._sourceIcon);
this.actor.destroy();
},
_onKeyPress: function(actor, event) {
if (event.get_key_symbol() == Clutter.KEY_Up) {
actor.emit('clicked', 1);
@@ -1494,7 +1412,7 @@ const MessageTray = new Lang.Class({
actor.grab_key_focus();
}));
global.focus_manager.add_group(this.actor);
this._summary = new St.BoxLayout({ style_class: 'message-tray-summary',
this._summary = new St.BoxLayout({ name: 'summary-mode',
reactive: true,
track_hover: true,
x_align: Clutter.ActorAlign.END,
@@ -1535,7 +1453,7 @@ const MessageTray = new Lang.Class({
this.idleMonitor = new GnomeDesktop.IdleMonitor();
this._grabHelper = new GrabHelper.GrabHelper(this.actor,
{ keybindingMode: Shell.KeyBindingMode.MESSAGE_TRAY });
{ keybindingMode: Main.KeybindingMode.MESSAGE_TRAY });
this._grabHelper.addActor(this._summaryBoxPointer.actor);
this._grabHelper.addActor(this.actor);
if (Main.panel.statusArea.activities)
@@ -1589,12 +1507,13 @@ const MessageTray = new Lang.Class({
this._overviewVisible = true;
this._grabHelper.ungrab(); // drop modal grab if necessary
this.actor.add_style_pseudo_class('overview');
this._updateState();
this.show();
}));
Main.overview.connect('hiding', Lang.bind(this,
function() {
this._overviewVisible = false;
this._escapeTray();
this.actor.remove_style_pseudo_class('overview');
this._updateState();
}));
@@ -1603,19 +1522,19 @@ const MessageTray = new Lang.Class({
Main.wm.addKeybinding('toggle-message-tray',
new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }),
Meta.KeyBindingFlags.NONE,
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.MESSAGE_TRAY |
Shell.KeyBindingMode.OVERVIEW,
Main.KeybindingMode.NORMAL |
Main.KeybindingMode.MESSAGE_TRAY |
Main.KeybindingMode.OVERVIEW,
Lang.bind(this, this.toggleAndNavigate));
Main.wm.addKeybinding('focus-active-notification',
new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }),
Meta.KeyBindingFlags.NONE,
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.MESSAGE_TRAY |
Shell.KeyBindingMode.OVERVIEW,
Main.KeybindingMode.NORMAL |
Main.KeybindingMode.MESSAGE_TRAY |
Main.KeybindingMode.OVERVIEW,
Lang.bind(this, this._expandActiveNotification));
this._sources = new Hash.Map();
this._summaryItems = [];
this._chatSummaryItemsCount = 0;
let pointerWatcher = PointerWatcher.getPointerWatcher();
@@ -1625,19 +1544,22 @@ const MessageTray = new Lang.Class({
this._trayDwellUserTime = 0;
this._sessionUpdated();
this._noMessages = new St.Label({ text: _("No Messages"),
style_class: 'no-messages-label',
x_align: Clutter.ActorAlign.CENTER,
x_expand: true,
y_align: Clutter.ActorAlign.CENTER,
y_expand: true });
this.actor.add_actor(this._noMessages);
this._updateNoMessagesLabel();
},
_updateNoMessagesLabel: function() {
this._noMessages.visible = this._sources.size() == 0;
if (this._summaryItems.length == 0 && !this._noMessages) {
this._noMessages = new St.Label({ text: _("No Messages"),
style_class: 'no-messages-label',
x_align: Clutter.ActorAlign.CENTER,
x_expand: true,
y_align: Clutter.ActorAlign.CENTER,
y_expand: true });
this.actor.add_actor(this._noMessages);
} else if (this._summaryItems.length > 0 && this._noMessages) {
this._noMessages.destroy();
this._noMessages = null;
}
},
_sessionUpdated: function() {
@@ -1706,7 +1628,10 @@ const MessageTray = new Lang.Class({
},
_onNotificationKeyRelease: function(actor, event) {
if (event.get_key_symbol() == Clutter.KEY_Escape && event.get_state() == 0) {
let ignoredModifiers = global.display.get_ignored_modifier_mask();
let modifierState = event.get_state() & ~ignoredModifiers;
if (event.get_key_symbol() == Clutter.KEY_Escape && modifierState == 0) {
this._closeNotification();
return true;
}
@@ -1724,7 +1649,15 @@ const MessageTray = new Lang.Class({
},
contains: function(source) {
return this._sources.has(source);
return this._getIndexOfSummaryItemForSource(source) >= 0;
},
_getIndexOfSummaryItemForSource: function(source) {
for (let i = 0; i < this._summaryItems.length; i++) {
if (this._summaryItems[i].source == source)
return i;
}
return -1;
},
add: function(source) {
@@ -1733,23 +1666,7 @@ const MessageTray = new Lang.Class({
return;
}
// Register that we got a notification for this source
source.policy.store();
source.policy.connect('enable-changed', Lang.bind(this, this._onSourceEnableChanged, source));
source.policy.connect('policy-changed', Lang.bind(this, this._updateState));
this._onSourceEnableChanged(source.policy, source);
},
_addSource: function(source) {
let obj = {
source: source,
summaryItem: new SummaryItem(source),
notifyId: 0,
destroyId: 0,
mutedChangedId: 0
};
let summaryItem = obj.summaryItem;
let summaryItem = new SummaryItem(source);
if (source.isChat) {
this._summary.insert_child_at_index(summaryItem.actor, 0);
@@ -1758,11 +1675,11 @@ const MessageTray = new Lang.Class({
this._summary.insert_child_at_index(summaryItem.actor, this._chatSummaryItemsCount);
}
this._sources.set(source, obj);
this._summaryItems.push(summaryItem);
obj.notifyId = source.connect('notify', Lang.bind(this, this._onNotify));
obj.destroyId = source.connect('destroy', Lang.bind(this, this._onSourceDestroy));
obj.mutedChangedId = source.connect('muted-changed', Lang.bind(this,
source.connect('notify', Lang.bind(this, this._onNotify));
source.connect('muted-changed', Lang.bind(this,
function () {
if (source.isMuted)
this._notificationQueue = this._notificationQueue.filter(function(notification) {
@@ -1781,28 +1698,35 @@ const MessageTray = new Lang.Class({
this._onSummaryItemClicked(summaryItem, 3);
}));
source.connect('destroy', Lang.bind(this, this._onSourceDestroy));
// We need to display the newly-added summary item, but if the
// caller is about to post a notification, we want to show that
// *first* and not show the summary item until after it hides.
// So postpone calling _updateState() a tiny bit.
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() { this._updateState(); return false; }));
this.emit('source-added', source);
this.emit('summary-item-added', summaryItem);
this._updateNoMessagesLabel();
},
_removeSource: function(source) {
let [, obj] = this._sources.delete(source);
let summaryItem = obj.summaryItem;
getSummaryItems: function() {
return this._summaryItems;
},
_onSourceDestroy: function(source) {
let index = this._getIndexOfSummaryItemForSource(source);
if (index == -1)
return;
let summaryItemToRemove = this._summaryItems[index];
this._summaryItems.splice(index, 1);
if (source.isChat)
this._chatSummaryItemsCount--;
source.disconnect(obj.notifyId);
source.disconnect(obj.destroyId);
source.disconnect(obj.mutedChangedId);
let needUpdate = false;
if (this._notification && this._notification.source == source) {
@@ -1810,14 +1734,12 @@ const MessageTray = new Lang.Class({
this._notificationRemoved = true;
needUpdate = true;
}
if (this._clickedSummaryItem == summaryItem) {
if (this._clickedSummaryItem == summaryItemToRemove) {
this._setClickedSummaryItem(null);
needUpdate = true;
}
summaryItem.destroy();
this.emit('source-removed', source);
summaryItemToRemove.actor.destroy();
this._updateNoMessagesLabel();
@@ -1825,26 +1747,6 @@ const MessageTray = new Lang.Class({
this._updateState();
},
getSources: function() {
return this._sources.keys();
},
_onSourceEnableChanged: function(policy, source) {
let wasEnabled = this.contains(source);
let shouldBeEnabled = policy.enable;
if (wasEnabled != shouldBeEnabled) {
if (shouldBeEnabled)
this._addSource(source);
else
this._removeSource(source);
}
},
_onSourceDestroy: function(source) {
this._removeSource(source);
},
_onNotificationDestroy: function(notification) {
if (this._notification == notification && (this._notificationState == State.SHOWN || this._notificationState == State.SHOWING)) {
this._updateNotificationTimeout(0);
@@ -1888,6 +1790,11 @@ const MessageTray = new Lang.Class({
this._updateState();
},
show: function() {
this._traySummoned = true;
this._updateState();
},
_onNotify: function(source, notification) {
if (this._summaryBoxPointerItem && this._summaryBoxPointerItem.source == source) {
if (this._summaryBoxPointerState == State.HIDING) {
@@ -1930,7 +1837,7 @@ const MessageTray = new Lang.Class({
},
_onSummaryItemClicked: function(summaryItem, button) {
if (summaryItem.source.handleSummaryClick(button)) {
if (summaryItem.source.handleSummaryClick()) {
if (summaryItem.source.keepTrayOnSummaryClick)
this._setClickedSummaryItem(null);
else
@@ -2134,14 +2041,14 @@ const MessageTray = new Lang.Class({
}
// Summary
let summarySummoned = this._pointerInSummary || this._overviewVisible || this._traySummoned;
let summarySummoned = this._pointerInSummary || this._traySummoned;
let summaryPinned = this._pointerInTray || summarySummoned || this._locked;
let summaryHovered = this._pointerInTray || this._pointerInSummary;
let notificationsVisible = this._notificationState != State.HIDDEN;
let notificationsDone = !notificationsVisible && !notificationsPending;
let summaryOptionalInOverview = this._overviewVisible && !this._locked && !summaryHovered;
let summaryOptionalInOverview = !this._locked && !summaryHovered;
let mustHideSummary = (notificationsPending && (notificationUrgent || summaryOptionalInOverview))
|| notificationsVisible || !Main.sessionMode.hasNotifications;
@@ -2289,9 +2196,7 @@ const MessageTray = new Lang.Class({
this._tween(this.actor, '_trayState', State.HIDDEN,
{ y: 0,
time: ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: this._onTrayHidden,
onCompleteScope: this
transition: 'easeOutQuad'
});
// Note that we might have entered here without a grab,
@@ -2301,11 +2206,6 @@ const MessageTray = new Lang.Class({
this._lightbox.hide();
},
_onTrayHidden: function() {
if (!this._overviewVisible)
this.actor.remove_style_pseudo_class('overview');
},
_hideDesktopClone: function(now) {
this._tween(this._desktopClone, '_desktopCloneState', State.HIDDEN,
{ y: 0,
@@ -2342,7 +2242,7 @@ const MessageTray = new Lang.Class({
this._notificationClickedId = this._notification.connect('done-displaying',
Lang.bind(this, this._escapeTray));
this._notificationUnfocusedId = this._notification.connect('unfocused', Lang.bind(this, function() {
this._notification.connect('unfocused', Lang.bind(this, function() {
this._updateState();
}));
this._notificationBin.child = this._notification.actor;
@@ -2371,10 +2271,8 @@ const MessageTray = new Lang.Class({
_updateShowingNotification: function() {
this._notification.acknowledged = true;
// We auto-expand notifications with CRITICAL urgency, or for which the relevant setting
// is on in the control center.
if (this._notification.urgency == Urgency.CRITICAL ||
this._notification.source.policy.forceExpanded)
// We auto-expand notifications with CRITICAL urgency.
if (this._notification.urgency == Urgency.CRITICAL)
this._expandNotification(true);
// We tween all notifications to full opacity. This ensures that both new notifications and
@@ -2458,18 +2356,11 @@ const MessageTray = new Lang.Class({
this.idleMonitor.disconnect(this._idleMonitorBecameActiveId);
this._idleMonitorBecameActiveId = 0;
}
if (this._notificationExpandedId) {
this._notification.disconnect(this._notificationExpandedId);
this._notificationExpandedId = 0;
}
if (this._notificationClickedId) {
this._notification.disconnect(this._notificationClickedId);
this._notificationClickedId = 0;
}
if (this._notificationUnfocusedId) {
this._notification.disconnect(this._notificationUnfocusedId);
this._notificationUnfocusedId = 0;
}
if (this._notificationRemoved) {
Tweener.removeTweens(this._notificationWidget);
@@ -2491,19 +2382,19 @@ const MessageTray = new Lang.Class({
},
_hideNotificationCompleted: function() {
this._notification.collapseCompleted();
let notification = this._notification;
this._notification = null;
if (notification.isTransient)
notification.destroy(NotificationDestroyedReason.EXPIRED);
this._notificationRemoved = false;
this._notificationWidget.hide();
this._closeButton.hide();
this._pointerInTray = false;
this.actor.hover = false; // Clutter doesn't emit notify::hover when actors move
this._notificationBin.child = null;
this._notificationWidget.hide();
this._notification.collapseCompleted();
this._notification.disconnect(this._notificationClickedId);
this._notificationClickedId = 0;
let notification = this._notification;
this._notification = null;
if (notification.isTransient)
notification.destroy(NotificationDestroyedReason.EXPIRED);
},
_expandActiveNotification: function() {
@@ -2691,20 +2582,6 @@ const MessageTray = new Lang.Class({
_onSummaryBoxPointerUngrabbed: function() {
this._summaryBoxPointerState = State.HIDING;
if (this._summaryBoxPointerContentUpdatedId) {
this._summaryBoxPointerItem.disconnect(this._summaryBoxPointerContentUpdatedId);
this._summaryBoxPointerContentUpdatedId = 0;
}
if (this._summaryBoxPointerCloseClickedId != 0) {
this._summaryBoxPointerItem.closeButton.disconnect(this._summaryBoxPointerCloseClickedId);
this._summaryBoxPointerCloseClickedId = 0;
}
if (this._sourceDoneDisplayingId) {
this._summaryBoxPointerItem.source.disconnect(this._sourceDoneDisplayingId);
this._sourceDoneDisplayingId = 0;
}
this._unlock();
if (this._summaryBoxPointerItem.source.notifications.length == 0) {
@@ -2727,6 +2604,14 @@ const MessageTray = new Lang.Class({
this._summaryBoxPointerState = State.HIDDEN;
this._summaryBoxPointer.bin.child = null;
this._summaryBoxPointerItem.disconnect(this._summaryBoxPointerContentUpdatedId);
this._summaryBoxPointerContentUpdatedId = 0;
if (this._summaryBoxPointerCloseClickedId != 0) {
this._summaryBoxPointerItem.closeButton.disconnect(this._summaryBoxPointerCloseClickedId);
this._summaryBoxPointerCloseClickedId = 0;
}
this._summaryBoxPointerItem.source.disconnect(this._sourceDoneDisplayingId);
this._summaryBoxPointerDoneDisplayingId = 0;
let sourceNotificationStackDoneShowing = null;
if (doneShowingNotificationStack) {

View File

@@ -37,7 +37,7 @@ const ModalDialog = new Lang.Class({
params = Params.parse(params, { shellReactive: false,
styleClass: null,
parentActor: Main.uiGroup,
keybindingMode: Shell.KeyBindingMode.SYSTEM_MODAL,
keybindingMode: Main.KeybindingMode.SYSTEM_MODAL,
shouldFadeIn: true });
this.state = State.CLOSED;
@@ -96,7 +96,8 @@ const ModalDialog = new Lang.Class({
y_align: St.Align.START });
this.buttonLayout = new St.BoxLayout({ style_class: 'modal-dialog-button-box',
vertical: false });
visible: false,
vertical: false });
this.dialogLayout.add(this.buttonLayout,
{ expand: true,
x_align: St.Align.MIDDLE,
@@ -119,6 +120,7 @@ const ModalDialog = new Lang.Class({
setButtons: function(buttons) {
this.clearButtons();
this.buttonLayout.visible = (buttons.length > 0);
for (let i = 0; i < buttons.length; i++) {
let buttonInfo = buttons[i];
@@ -157,7 +159,6 @@ const ModalDialog = new Lang.Class({
keys = [];
let button = new St.Button({ style_class: 'modal-dialog-button',
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
reactive: true,
can_focus: true,
label: label });

View File

@@ -103,126 +103,6 @@ const STANDARD_TRAY_ICON_IMPLEMENTATIONS = {
'ibus-ui-gtk': 'keyboard'
};
const NotificationGenericPolicy = new Lang.Class({
Name: 'NotificationGenericPolicy',
Extends: MessageTray.NotificationPolicy,
_init: function() {
// Don't chain to parent, it would try setting
// our properties to the defaults
this.id = 'generic';
this._masterSettings = new Gio.Settings({ schema: 'org.gnome.desktop.notifications' });
this._masterSettings.connect('changed', Lang.bind(this, this._changed));
},
store: function() { },
destroy: function() {
this._masterSettings.run_dispose();
},
_changed: function(settings, key) {
this.emit('policy-changed', key);
},
get enable() {
return true;
},
get enableSound() {
return true;
},
get showBanners() {
return this._masterSettings.get_boolean('show-banners');
},
get forceExpanded() {
return false;
},
get showInLockScreen() {
return this._masterSettings.get_boolean('show-in-lock-screen');
},
get detailsInLockScreen() {
return false;
}
});
const NotificationApplicationPolicy = new Lang.Class({
Name: 'NotificationApplicationPolicy',
Extends: MessageTray.NotificationPolicy,
_init: function(id) {
// Don't chain to parent, it would try setting
// our properties to the defaults
this.id = id;
this._canonicalId = this._canonicalizeId(id)
this._masterSettings = new Gio.Settings({ schema: 'org.gnome.desktop.notifications' });
this._settings = new Gio.Settings({ schema: 'org.gnome.desktop.notifications.application',
path: '/org/gnome/desktop/notifications/application/' + this._canonicalId + '/' });
this._masterSettings.connect('changed', Lang.bind(this, this._changed));
this._settings.connect('changed', Lang.bind(this, this._changed));
},
store: function() {
this._settings.set_string('application-id', this.id + '.desktop');
let apps = this._masterSettings.get_strv('application-children');
if (apps.indexOf(this._canonicalId) < 0) {
apps.push(this._canonicalId);
this._masterSettings.set_strv('application-children', apps);
}
},
destroy: function() {
this._masterSettings.run_dispose();
this._settings.run_dispose();
},
_changed: function(settings, key) {
this.emit('policy-changed', key);
},
_canonicalizeId: function(id) {
// Keys are restricted to lowercase alphanumeric characters and dash,
// and two dashes cannot be in succession
return id.toLowerCase().replace(/[^a-z0-9\-]/g, '-').replace(/--+/g, '-');
},
get enable() {
return this._settings.get_boolean('enable');
},
get enableSound() {
return this._settings.get_boolean('enable-sound-alerts');
},
get showBanners() {
return this._masterSettings.get_boolean('show-banners') &&
this._settings.get_boolean('show-banners');
},
get forceExpanded() {
return this._settings.get_boolean('force-expanded');
},
get showInLockScreen() {
return this._masterSettings.get_boolean('show-in-lock-screen') &&
this._settings.get_boolean('show-in-lock-screen');
},
get detailsInLockScreen() {
return this._settings.get_boolean('details-in-lock-screen');
}
});
const NotificationDaemon = new Lang.Class({
Name: 'NotificationDaemon',
@@ -247,42 +127,42 @@ const NotificationDaemon = new Lang.Class({
this._trayManager.manage_stage(global.stage, Main.messageTray.actor);
},
_imageForNotificationData: function(hints) {
if (hints['image-data']) {
_iconForNotificationData: function(icon, hints) {
// If an icon is not specified, we use 'image-data' or 'image-path' hint for an icon
// and don't show a large image. There are currently many applications that use
// notify_notification_set_icon_from_pixbuf() from libnotify, which in turn sets
// the 'image-data' hint. These applications don't typically pass in 'app_icon'
// argument to Notify() and actually expect the pixbuf to be shown as an icon.
// So the logic here does the right thing for this case. If both an icon and either
// one of 'image-data' or 'image-path' are specified, we show both an icon and
// a large image.
if (icon) {
if (icon.substr(0, 7) == 'file://')
return new Gio.FileIcon({ file: Gio.File.new_for_uri(icon) });
else if (icon[0] == '/') {
return new Gio.FileIcon({ file: Gio.File.new_for_path(icon) });
} else
return new Gio.ThemedIcon({ name: icon });
} else if (hints['image-data']) {
let [width, height, rowStride, hasAlpha,
bitsPerSample, nChannels, data] = hints['image-data'];
return Shell.util_create_pixbuf_from_data(data, GdkPixbuf.Colorspace.RGB, hasAlpha,
bitsPerSample, width, height, rowStride);
} else if (hints['image-path']) {
return new Gio.FileIcon({ file: Gio.File.new_for_path(hints['image-path']) });
} else {
let stockIcon;
switch (hints.urgency) {
case Urgency.LOW:
case Urgency.NORMAL:
stockIcon = 'gtk-dialog-info';
break;
case Urgency.CRITICAL:
stockIcon = 'gtk-dialog-error';
break;
}
return new Gio.ThemedIcon({ name: stockIcon });
}
return null;
},
_fallbackIconForNotificationData: function(hints) {
let stockIcon;
switch (hints.urgency) {
case Urgency.LOW:
case Urgency.NORMAL:
stockIcon = 'gtk-dialog-info';
break;
case Urgency.CRITICAL:
stockIcon = 'gtk-dialog-error';
break;
}
return new Gio.ThemedIcon({ name: stockIcon });
},
_iconForNotificationData: function(icon) {
if (icon) {
if (icon.substr(0, 7) == 'file://')
return new Gio.FileIcon({ file: Gio.File.new_for_uri(icon) });
else if (icon[0] == '/')
return new Gio.FileIcon({ file: Gio.File.new_for_path(icon) });
else
return new Gio.ThemedIcon({ name: icon });
}
return null;
},
_lookupSource: function(title, pid, trayIcon) {
@@ -333,7 +213,7 @@ const NotificationDaemon = new Lang.Class({
}
}
let source = new Source(title, pid, sender, trayIcon, ndata ? ndata.hints['desktop-entry'] : null);
let source = new Source(title, pid, sender, trayIcon);
source.setTransient(isForTransientNotification);
if (!isForTransientNotification) {
@@ -475,8 +355,12 @@ const NotificationDaemon = new Lang.Class({
[ndata.id, ndata.icon, ndata.summary, ndata.body,
ndata.actions, ndata.hints, ndata.notification];
let gicon = this._iconForNotificationData(icon, hints);
if (notification == null) {
notification = new MessageTray.Notification(source);
notification = new MessageTray.Notification(source, summary, body,
{ gicon: gicon,
bannerMarkup: true });
ndata.notification = notification;
notification.connect('destroy', Lang.bind(this,
function(n, reason) {
@@ -499,36 +383,29 @@ const NotificationDaemon = new Lang.Class({
function(n, actionId) {
this._emitActionInvoked(ndata.id, actionId);
}));
} else {
notification.update(summary, body, { gicon: gicon,
bannerMarkup: true,
clear: true });
}
// Mark music notifications so they can be shown in the screen shield
notification.isMusic = (ndata.hints['category'] == 'x-gnome.music');
let gicon = this._iconForNotificationData(icon, hints);
let gimage = this._imageForNotificationData(hints);
let image = null;
// If an icon is not specified, we use 'image-data' or 'image-path' hint for an icon
// and don't show a large image. There are currently many applications that use
// notify_notification_set_icon_from_pixbuf() from libnotify, which in turn sets
// the 'image-data' hint. These applications don't typically pass in 'app_icon'
// argument to Notify() and actually expect the pixbuf to be shown as an icon.
// So the logic here does the right thing for this case. If both an icon and either
// one of 'image-data' or 'image-path' are specified, we show both an icon and
// a large image.
if (gicon && gimage)
image = new St.Icon({ gicon: gimage,
icon_size: notification.IMAGE_SIZE });
else if (!gicon && gimage)
gicon = gimage;
else if (!gicon)
gicon = this._fallbackIconForNotificationData(hints);
notification.update(summary, body, { gicon: gicon,
bannerMarkup: true,
clear: true });
notification.setImage(image);
// We only display a large image if an icon is also specified.
if (icon && (hints['image-data'] || hints['image-path'])) {
let image = null;
if (hints['image-data']) {
let [width, height, rowStride, hasAlpha,
bitsPerSample, nChannels, data] = hints['image-data'];
image = St.TextureCache.get_default().load_from_raw(data, hasAlpha,
width, height, rowStride, notification.IMAGE_SIZE);
} else if (hints['image-path']) {
image = St.TextureCache.get_default().load_uri_async(GLib.filename_to_uri(hints['image-path'], null),
notification.IMAGE_SIZE,
notification.IMAGE_SIZE);
}
notification.setImage(image);
} else {
notification.unsetImage();
}
if (actions.length) {
notification.setUseActionIcons(hints['action-icons'] == true);
@@ -558,7 +435,7 @@ const NotificationDaemon = new Lang.Class({
// of the 'transient' hint with hints['transient'] rather than hints.transient
notification.setTransient(hints['transient'] == true);
let sourceGIcon = source.useNotificationIcon ? gicon : null;
let sourceGIcon = source.useNotificationIcon ? this._iconForNotificationData(icon, hints) : null;
source.processNotification(notification, sourceGIcon);
},
@@ -638,22 +515,12 @@ const Source = new Lang.Class({
Name: 'NotificationDaemonSource',
Extends: MessageTray.Source,
_init: function(title, pid, sender, trayIcon, appId) {
// Need to set the app before chaining up, so
// methods called from the parent constructor can find it
this.trayIcon = trayIcon;
this.pid = pid;
this.app = this._getApp(appId);
_init: function(title, pid, sender, trayIcon) {
this.parent(title);
this.initialTitle = title;
if (this.app)
this.title = this.app.get_name();
else
this.useNotificationIcon = true;
this.pid = pid;
if (sender)
this._nameWatcherId = Gio.DBus.session.watch_name(sender,
Gio.BusNameWatcherFlags.NONE,
@@ -662,19 +529,16 @@ const Source = new Lang.Class({
else
this._nameWatcherId = 0;
if (this.trayIcon) {
// Try again finding the app, using the WM_CLASS from the tray icon
this._setSummaryIcon(this.trayIcon);
this.useNotificationIcon = false;
}
},
this._setApp();
if (this.app)
this.title = this.app.get_name();
else
this.useNotificationIcon = true;
_createPolicy: function() {
if (this.app) {
let id = this.app.get_id().replace(/\.desktop$/,'');
return new NotificationApplicationPolicy(id);
} else {
return new NotificationGenericPolicy();
this.trayIcon = trayIcon;
if (this.trayIcon) {
this._setSummaryIcon(this.trayIcon);
this.useNotificationIcon = false;
}
},
@@ -701,17 +565,19 @@ const Source = new Lang.Class({
this.notify(notification);
},
handleSummaryClick: function(button) {
handleSummaryClick: function() {
if (!this.trayIcon)
return false;
let event = Clutter.get_current_event();
if (event.type() != Clutter.EventType.BUTTON_RELEASE)
return false;
// Left clicks are passed through only where there aren't unacknowledged
// notifications, so it possible to open them in summary mode; right
// clicks are always forwarded, as the right click menu is not useful for
// tray icons
if (button == 1 &&
if (event.get_button() == 1 &&
this.notifications.length > 0)
return false;
@@ -724,7 +590,7 @@ const Source = new Lang.Class({
return true;
},
_getApp: function(appId) {
_getApp: function() {
let app;
app = Shell.WindowTracker.get_default().get_app_from_pid(this.pid);
@@ -732,13 +598,7 @@ const Source = new Lang.Class({
return app;
if (this.trayIcon) {
app = Shell.AppSystem.get_default().lookup_wmclass(this.trayIcon.wm_class);
if (app != null)
return app;
}
if (appId) {
app = Shell.AppSystem.get_default().lookup_app(appId + '.desktop');
app = Shell.AppSystem.get_default().lookup_wmclass(this.trayIcon.wmclass);
if (app != null)
return app;
}
@@ -746,11 +606,11 @@ const Source = new Lang.Class({
return null;
},
_setApp: function(appId) {
_setApp: function() {
if (this.app)
return;
this.app = this._getApp(appId);
this.app = this._getApp();
if (!this.app)
return;

View File

@@ -10,6 +10,7 @@ const St = imports.gi.St;
const Shell = imports.gi.Shell;
const Gdk = imports.gi.Gdk;
const CenterLayout = imports.ui.centerLayout;
const Dash = imports.ui.dash;
const DND = imports.ui.dnd;
const Main = imports.ui.main;
@@ -30,9 +31,9 @@ const GLSL_DIM_EFFECT_CODE = '\
vec2 dist = cogl_tex_coord_in[0].xy - vec2(0.5, 0.5); \
float elipse_radius = 0.5; \
/* from https://bugzilla.gnome.org/show_bug.cgi?id=669798: \
the alpha on the gradient goes from 250 at its darkest to 180 at its most transparent. */ \
float y = 250.0 / 255.0; \
float x = 180.0 / 255.0; \
the alpha on the gradient goes from 165 at its darkest to 98 at its most transparent. */ \
float y = 165.0 / 255.0; \
float x = 98.0 / 255.0; \
/* interpolate darkening value, based on distance from screen center */ \
float val = min(length(dist), elipse_radius); \
float a = mix(x, y, val / elipse_radius); \
@@ -97,6 +98,13 @@ const ShellInfo = new Lang.Class({
}
});
const ControlsChange = {
BEFORE_PAGE: 1,
AFTER_PAGE: 2,
DND_START: 3,
DND_END: 4
};
const Overview = new Lang.Class({
Name: 'Overview',
@@ -141,7 +149,19 @@ const Overview = new Lang.Class({
vertical: true });
this._overview._delegate = this;
this._group = new St.BoxLayout({ name: 'overview-group' });
let layout = new CenterLayout.CenterLayout();
this._group = new St.Widget({ name: 'overview-group',
layout_manager: layout });
this._spacing = 0;
this._group.connect('style-changed',
Lang.bind(this, function() {
let node = this._group.get_theme_node();
let spacing = node.get_length('spacing');
if (spacing != this._spacing) {
this._spacing = spacing;
}
}));
this._capturedEventId = 0;
this._buttonPressId = 0;
@@ -237,25 +257,97 @@ const Overview = new Lang.Class({
this._viewSelector = new ViewSelector.ViewSelector(this._searchEntry,
this._dash.showAppsButton);
this._group.add(this._viewSelector.actor, { x_fill: true,
expand: true });
this._group.add_actor(this._viewSelector.actor);
this._thumbnailsBox = new WorkspaceThumbnail.ThumbnailsBox();
this._group.add_actor(this._thumbnailsBox.actor);
Main.ctrlAltTabManager.addGroup(this._thumbnailsBox.actor, _("Workspaces"), 'view-list-symbolic');
// Add our same-line elements after the search entry
this._overview.add(this._group, { y_fill: true,
expand: true });
this._overview.add_actor(this._group);
// Then account for message tray
this._messageTrayGhost = new St.Bin({ style_class: 'message-tray-summary',
this._messageTrayGhost = new St.Bin({ child: new Clutter.Clone({ source: Main.messageTray.actor }),
reactive: false,
opacity: 0,
x_fill: true,
y_fill: true });
this._overview.add_actor(this._messageTrayGhost);
this._viewSelector.connect('after-page-change', Lang.bind(this,
function() {
this._setSideControlsVisibility(ControlsChange.AFTER_PAGE);
}));
this._viewSelector.connect('before-page-change', Lang.bind(this,
function() {
this._setSideControlsVisibility(ControlsChange.BEFORE_PAGE);
}));
this.connect('item-drag-begin', Lang.bind(this,
function() {
this._setSideControlsVisibility(ControlsChange.DND_START);
}));
this.connect('item-drag-cancelled', Lang.bind(this,
function() {
this._setSideControlsVisibility(ControlsChange.DND_END);
}));
this.connect('item-drag-end', Lang.bind(this,
function() {
this._setSideControlsVisibility(ControlsChange.DND_END);
}));
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._relayout));
this._relayout();
},
_setSideControlsVisibility: function(changeType) {
// Ignore the case when we're leaving the overview, since
// actors will be made visible again when entering the overview
// next time, and animating them while doing so is just
// unnecesary noise
if (!this.visible || this.animationInProgress)
return;
let appsActive = this._viewSelector.getAppsActive();
let searchActive = this._viewSelector.getSearchActive();
let dashVisible = !searchActive || (changeType == ControlsChange.DND_START);
let thumbnailsVisible = (!searchActive && !appsActive) || (changeType == ControlsChange.DND_START);
let trayVisible = !searchActive;
let trayGhostVisible = trayVisible || dashVisible;
if ((changeType == ControlsChange.BEFORE_PAGE) ||
(changeType == ControlsChange.DND_START)) {
if (dashVisible)
this._dash.show();
if (thumbnailsVisible)
this._thumbnailsBox.show();
}
if ((changeType == ControlsChange.BEFORE_PAGE) ||
(changeType == ControlsChange.DND_END)) {
if (!dashVisible) {
this._dash.hide();
}
if (!thumbnailsVisible)
this._thumbnailsBox.hide();
}
if (changeType == ControlsChange.BEFORE_PAGE ||
changeType == ControlsChange.DND_START) {
if (trayGhostVisible)
this._messageTrayGhost.show();
if (trayVisible)
Main.messageTray.show();
else
Main.messageTray.hide();
} else if (changeType == ControlsChange.AFTER_PAGE ||
changeType == ControlsChange.DND_END) {
if (!trayGhostVisible)
this._messageTrayGhost.hide();
}
},
addSearchProvider: function(provider) {
this._viewSelector.addSearchProvider(provider);
},
@@ -394,7 +486,7 @@ const Overview = new Lang.Class({
stackIndices[stack[i].get_meta_window().get_stable_sequence()] = i;
}
this.emit('windows-restacked', stackIndices);
this.emit('sync-window-stacking', stackIndices);
},
//// Public methods ////
@@ -478,6 +570,7 @@ const Overview = new Lang.Class({
// Disable unredirection while in the overview
Meta.disable_unredirect_for_screen(global.screen);
global.window_group.hide();
this._messageTrayGhost.show();
this._overview.show();
this._background.show();
this._viewSelector.show();
@@ -576,7 +669,7 @@ const Overview = new Lang.Class({
if (this._shown) {
if (!this._modal) {
if (Main.pushModal(this._overview,
{ keybindingMode: Shell.KeyBindingMode.OVERVIEW }))
{ keybindingMode: Main.KeybindingMode.OVERVIEW }))
this._modal = true;
else
this.hide();

View File

@@ -15,6 +15,7 @@ const Signals = imports.signals;
const Atk = imports.gi.Atk;
const CenterLayout = imports.ui.centerLayout;
const Config = imports.misc.config;
const CtrlAltTab = imports.ui.ctrlAltTab;
const DND = imports.ui.dnd;
@@ -315,7 +316,7 @@ const AppMenuButton = new Lang.Class({
},
show: function() {
if (this._visible)
if (this._visible || Main.screenShield.locked)
return;
this._visible = true;
@@ -598,11 +599,6 @@ const AppMenuButton = new Lang.Class({
return;
menu = new PopupMenu.RemoteMenu(this.actor, this._targetApp.menu, this._targetApp.action_group);
menu.connect('activate', Lang.bind(this, function() {
let win = this._targetApp.get_windows()[0];
win.check_alive(global.get_current_time());
}));
} else {
if (this.menu.isDummyQuitMenu)
return;
@@ -751,9 +747,12 @@ const ActivitiesButton = new Lang.Class({
const PanelCorner = new Lang.Class({
Name: 'PanelCorner',
_init: function(side) {
_init: function(box, side) {
this._side = side;
this._box = box;
this._box.connect('style-changed', Lang.bind(this, this._boxStyleChanged));
this.actor = new St.DrawingArea({ style_class: 'panel-corner' });
this.actor.connect('style-changed', Lang.bind(this, this._styleChanged));
this.actor.connect('repaint', Lang.bind(this, this._repaint));
@@ -809,12 +808,12 @@ const PanelCorner = new Lang.Class({
return children[index];
},
setStyleParent: function(box) {
_boxStyleChanged: function() {
let side = this._side;
let rtlAwareContainer = box instanceof St.BoxLayout;
let rtlAwareContainer = this._box instanceof St.BoxLayout;
if (rtlAwareContainer &&
box.get_text_direction() == Clutter.TextDirection.RTL) {
this._box.get_text_direction() == Clutter.TextDirection.RTL) {
if (this._side == St.Side.LEFT)
side = St.Side.RIGHT;
else if (this._side == St.Side.RIGHT)
@@ -823,9 +822,9 @@ const PanelCorner = new Lang.Class({
let button;
if (side == St.Side.LEFT)
button = this._findLeftmostButton(box);
button = this._findLeftmostButton(this._box);
else if (side == St.Side.RIGHT)
button = this._findRightmostButton(box);
button = this._findRightmostButton(this._box);
if (button) {
if (this._button && this._buttonStyleChangedSignalId) {
@@ -852,7 +851,7 @@ const PanelCorner = new Lang.Class({
// The corner doesn't support theme transitions, so override
// the .panel-button default
button.style = 'transition-duration: 0ms';
button.style = 'transition-duration: 0';
}
},
@@ -865,8 +864,8 @@ const PanelCorner = new Lang.Class({
let backgroundColor = node.get_color('-panel-corner-background-color');
let borderColor = node.get_color('-panel-corner-border-color');
let overlap = borderColor.alpha != 0;
let offsetY = overlap ? 0 : borderWidth;
let noOverlap = borderColor.alpha == 0;
let offsetY = noOverlap ? borderWidth : 0;
let cr = this.actor.get_context();
cr.setOperator(Cairo.Operator.SOURCE);
@@ -890,18 +889,17 @@ const PanelCorner = new Lang.Class({
Clutter.cairo_set_source_color(cr, over);
cr.fill();
if (overlap) {
let offset = borderWidth;
Clutter.cairo_set_source_color(cr, backgroundColor);
if (noOverlap)
return;
cr.save();
cr.translate(xOffsetDirection * offset, - offset);
cr.appendPath(savedPath);
cr.fill();
cr.restore();
}
let offset = borderWidth;
Clutter.cairo_set_source_color(cr, backgroundColor);
cr.$dispose();
cr.save();
cr.translate(xOffsetDirection * offset, - offset);
cr.appendPath(savedPath);
cr.fill();
cr.restore();
},
_styleChanged: function() {
@@ -940,12 +938,47 @@ try {
log('NMApplet is not supported. It is possible that your NetworkManager version is too old');
}
const PanelLayout = new Lang.Class({
Name: 'PanelLayout',
Extends: CenterLayout.CenterLayout,
vfunc_allocate: function(container, box, flags) {
this.parent(container, box, flags);
let availWidth = box.x2 - box.x1;
let availHeight = box.y2 - box.y1;
let [left, center, right, leftCorner, rightCorner] = container.get_children();
let childBox = new Clutter.ActorBox();
let cornerMinWidth, cornerMinHeight;
let cornerWidth, cornerHeight;
[cornerMinWidth, cornerWidth] = leftCorner.get_preferred_width(-1);
[cornerMinHeight, cornerHeight] = leftCorner.get_preferred_height(-1);
childBox.x1 = 0;
childBox.x2 = cornerWidth;
childBox.y1 = availHeight;
childBox.y2 = availHeight + cornerHeight;
leftCorner.allocate(childBox, flags);
[cornerMinWidth, cornerWidth] = rightCorner.get_preferred_width(-1);
[cornerMinHeight, cornerHeight] = rightCorner.get_preferred_height(-1);
childBox.x1 = availWidth - cornerWidth;
childBox.x2 = availWidth;
childBox.y1 = availHeight;
childBox.y2 = availHeight + cornerHeight;
rightCorner.allocate(childBox, flags);
}
});
const Panel = new Lang.Class({
Name: 'Panel',
_init : function() {
this.actor = new Shell.GenericContainer({ name: 'panel',
reactive: true });
this.actor = new St.Widget({ name: 'panel',
reactive: true,
layoutManager: new PanelLayout() });
this.actor._delegate = this;
this._sessionStyle = null;
@@ -961,24 +994,20 @@ const Panel = new Lang.Class({
this._rightBox = new St.BoxLayout({ name: 'panelRight' });
this.actor.add_actor(this._rightBox);
this._leftCorner = new PanelCorner(St.Side.LEFT);
if (this.actor.get_text_direction() == Clutter.TextDirection.RTL)
this._leftCorner = new PanelCorner(this._rightBox, St.Side.LEFT);
else
this._leftCorner = new PanelCorner(this._leftBox, St.Side.LEFT);
this.actor.add_actor(this._leftCorner.actor);
this._rightCorner = new PanelCorner(St.Side.RIGHT);
if (this.actor.get_text_direction() == Clutter.TextDirection.RTL)
this._rightCorner = new PanelCorner(this._leftBox, St.Side.RIGHT);
else
this._rightCorner = new PanelCorner(this._rightBox, St.Side.RIGHT);
this.actor.add_actor(this._rightCorner.actor);
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
this.actor.connect('allocate', Lang.bind(this, this._allocate));
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
Main.overview.connect('showing', Lang.bind(this, function () {
this.actor.add_style_pseudo_class('overview');
}));
Main.overview.connect('hiding', Lang.bind(this, function () {
this.actor.remove_style_pseudo_class('overview');
}));
Main.layoutManager.panelBox.add(this.actor);
Main.ctrlAltTabManager.addGroup(this.actor, _("Top Bar"), 'emblem-system-symbolic',
{ sortGroup: CtrlAltTab.SortGroup.TOP });
@@ -987,83 +1016,6 @@ const Panel = new Lang.Class({
this._updatePanel();
},
_getPreferredWidth: function(actor, forHeight, alloc) {
alloc.min_size = -1;
alloc.natural_size = Main.layoutManager.primaryMonitor.width;
},
_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_text_direction() == Clutter.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_text_direction() == Clutter.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, cornerMinHeight;
let cornerWidth, cornerHeight;
[cornerMinWidth, cornerWidth] = this._leftCorner.actor.get_preferred_width(-1);
[cornerMinHeight, cornerHeight] = this._leftCorner.actor.get_preferred_height(-1);
childBox.x1 = 0;
childBox.x2 = cornerWidth;
childBox.y1 = allocHeight;
childBox.y2 = allocHeight + cornerHeight;
this._leftCorner.actor.allocate(childBox, flags);
[cornerMinWidth, cornerWidth] = this._rightCorner.actor.get_preferred_width(-1);
[cornerMinHeight, cornerHeight] = this._rightCorner.actor.get_preferred_height(-1);
childBox.x1 = allocWidth - cornerWidth;
childBox.x2 = allocWidth;
childBox.y1 = allocHeight;
childBox.y2 = allocHeight + cornerHeight;
this._rightCorner.actor.allocate(childBox, flags);
},
_onButtonPress: function(actor, event) {
if (event.get_source() != actor)
return false;
@@ -1144,13 +1096,17 @@ const Panel = new Lang.Class({
this._sessionStyle = Main.sessionMode.panelStyle;
if (this._sessionStyle)
this._addStyleClassName(this._sessionStyle);
},
if (this.actor.get_text_direction() == Clutter.TextDirection.RTL) {
this._leftCorner.setStyleParent(this._rightBox);
this._rightCorner.setStyleParent(this._leftBox);
} else {
this._leftCorner.setStyleParent(this._leftBox);
this._rightCorner.setStyleParent(this._rightBox);
_initBox: function(elements, box) {
for (let i = 0; i < elements.length; i++) {
let role = elements[i];
let constructor = PANEL_ITEM_IMPLEMENTATIONS[role];
if (!constructor) {
// panel icon is not supported (can happen for
// bluetooth or network)
continue;
}
}
},

View File

@@ -244,21 +244,18 @@ const SystemStatusButton = new Lang.Class({
this.setIcon(iconName);
},
get icons() {
return this._box.get_children();
},
addIcon: function(gicon) {
let icon = new St.Icon({ gicon: gicon,
style_class: 'system-status-icon' });
this._box.add_actor(icon);
this.emit('icons-changed');
return icon;
},
setIcon: function(iconName) {
// Need to first add a NULL GIcon and then set icon_name, to ensure
// compatibility with -symbolic fallbacks
if (!this.mainIcon)
this.mainIcon = this.addIcon(null);
this.mainIcon.icon_name = iconName;

View File

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

View File

@@ -207,7 +207,6 @@ const PopupBaseMenuItem = new Lang.Class({
color.alpha / 255);
cr.arc(width / 2, height / 2, width / 3, 0, 2 * Math.PI);
cr.fill();
cr.$dispose();
},
// This returns column widths in logical order (i.e. from the dot
@@ -605,7 +604,6 @@ const PopupSliderMenuItem = new Lang.Class({
color.alpha / 255);
cr.arc(handleX, handleY, handleRadius, 0, 2 * Math.PI);
cr.fill();
cr.$dispose();
},
_startDragging: function(actor, event) {
@@ -638,34 +636,20 @@ const PopupSliderMenuItem = new Lang.Class({
return true;
},
scroll: function(event) {
_onScrollEvent: function (actor, event) {
let direction = event.get_scroll_direction();
let delta;
if (event.is_pointer_emulated())
return;
if (direction == Clutter.ScrollDirection.DOWN) {
delta = -SLIDER_SCROLL_STEP;
} else if (direction == Clutter.ScrollDirection.UP) {
delta = +SLIDER_SCROLL_STEP;
} else if (direction == Clutter.ScrollDirection.SMOOTH) {
let [dx, dy] = event.get_scroll_delta();
// Even though the slider is horizontal, use dy to match
// the UP/DOWN above.
delta = -dy / 10;
this._value = Math.max(0, this._value - SLIDER_SCROLL_STEP);
}
else if (direction == Clutter.ScrollDirection.UP) {
this._value = Math.min(1, this._value + SLIDER_SCROLL_STEP);
}
this._value = Math.min(Math.max(0, this._value + delta), 1);
this._slider.queue_repaint();
this.emit('value-changed', this._value);
},
_onScrollEvent: function(actor, event) {
this.scroll(event);
},
_motionEvent: function(actor, event) {
let absX, absY;
[absX, absY] = event.get_coords();
@@ -889,7 +873,7 @@ const PopupMenuBase = new Lang.Class({
addSettingsAction: function(title, desktopFile) {
let menuItem = this.addAction(title, function() {
let app = Shell.AppSystem.get_default().lookup_app(desktopFile);
let app = Shell.AppSystem.get_default().lookup_setting(desktopFile);
if (!app) {
log('Settings panel for desktop file ' + desktopFile + ' could not be loaded!');

View File

@@ -1,11 +1,9 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const GdkPixbuf = imports.gi.GdkPixbuf;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
const FileUtils = imports.misc.fileUtils;
const Search = imports.ui.search;
@@ -137,12 +135,8 @@ function remoteProvidersLoaded(loadState) {
idxB = sortOrder.indexOf(appIdB);
// if no provider is found in the order, use alphabetical order
if ((idxA == -1) && (idxB == -1)) {
let nameA = providerA.appInfo.get_name();
let nameB = providerB.appInfo.get_name();
return GLib.utf8_collate(nameA, nameB);
}
if ((idxA == -1) && (idxB == -1))
return GLib.utf8_collate(providerA.title, providerB.title);
if (numSorted > 1) {
// if providerA is the last, it goes after everything
@@ -193,18 +187,18 @@ const RemoteSearchProvider = new Lang.Class({
},
createIcon: function(size, meta) {
let gicon;
if (meta['gicon']) {
gicon = Gio.icon_new_for_string(meta['gicon']);
return new St.Icon({ gicon: Gio.icon_new_for_string(meta['gicon']),
icon_size: size });
} else if (meta['icon-data']) {
let [width, height, rowStride, hasAlpha,
bitsPerSample, nChannels, data] = meta['icon-data'];
gicon = Shell.util_create_pixbuf_from_data(data, GdkPixbuf.Colorspace.RGB, hasAlpha,
bitsPerSample, width, height, rowStride);
let textureCache = St.TextureCache.get_default();
return textureCache.load_from_raw(data, hasAlpha,
width, height, rowStride, size);
}
return new St.Icon({ gicon: gicon,
icon_size: size });
return null;
},
_getResultsFinished: function(results, error) {
@@ -221,7 +215,7 @@ const RemoteSearchProvider = new Lang.Class({
Lang.bind(this, this._getResultsFinished),
this._cancellable);
} catch(e) {
log('Error calling GetInitialResultSet for provider %s: %s'.format(this.id, e.toString()));
log('Error calling GetInitialResultSet for provider %s: %s'.format( this.title, e.toString()));
this.searchSystem.pushResults(this, []);
}
},
@@ -234,7 +228,7 @@ const RemoteSearchProvider = new Lang.Class({
Lang.bind(this, this._getResultsFinished),
this._cancellable);
} catch(e) {
log('Error calling GetSubsearchResultSet for provider %s: %s'.format(this.id, e.toString()));
log('Error calling GetSubsearchResultSet for provider %s: %s'.format(this.title, e.toString()));
this.searchSystem.pushResults(this, []);
}
},
@@ -265,7 +259,7 @@ const RemoteSearchProvider = new Lang.Class({
Lang.bind(this, this._getResultMetasFinished, callback),
this._cancellable);
} catch(e) {
log('Error calling GetResultMetas for provider %s: %s'.format(this.id, e.toString()));
log('Error calling GetResultMetas for provider %s: %s'.format(this.title, e.toString()));
callback([]);
}
},

View File

@@ -8,13 +8,11 @@ const GnomeDesktop = imports.gi.GnomeDesktop;
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 TweenerEquations = imports.tweener.equations;
const GnomeSession = imports.misc.gnomeSession;
const Hash = imports.misc.hash;
const Layout = imports.ui.layout;
const LoginManager = imports.misc.loginManager;
const Lightbox = imports.ui.lightbox;
@@ -27,7 +25,6 @@ const Util = imports.misc.util;
const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver';
const LOCK_ENABLED_KEY = 'lock-enabled';
const LOCK_DELAY_KEY = 'lock-delay';
const CURTAIN_SLIDE_TIME = 0.3;
// fraction of screen height the arrow must reach before completing
@@ -129,48 +126,49 @@ const NotificationsBox = new Lang.Class({
name: 'screenShieldNotifications',
style_class: 'screen-shield-notifications-box' });
this._musicBin = new St.Bin({ style_class: 'screen-shield-notifications-box',
visible: false });
this._residentNotificationBox = new St.BoxLayout({ vertical: true,
style_class: 'screen-shield-notifications-box' });
let scrollView = new St.ScrollView({ x_fill: false, x_align: St.Align.START });
this._notificationBox = new St.BoxLayout({ vertical: true,
style_class: 'screen-shield-notifications-box' });
scrollView.add_actor(this._notificationBox);
this._persistentNotificationBox = new St.BoxLayout({ vertical: true,
style_class: 'screen-shield-notifications-box' });
scrollView.add_actor(this._persistentNotificationBox);
this.actor.add(this._musicBin);
this.actor.add(this._residentNotificationBox, { x_fill: true });
this.actor.add(scrollView, { x_fill: true, x_align: St.Align.START });
this._sources = new Hash.Map();
Main.messageTray.getSources().forEach(Lang.bind(this, function(source) {
this._sourceAdded(Main.messageTray, source, true);
this._items = [];
Main.messageTray.getSummaryItems().forEach(Lang.bind(this, function(item) {
this._summaryItemAdded(Main.messageTray, item, true);
}));
this._updateVisibility();
this._sourceAddedId = Main.messageTray.connect('source-added', Lang.bind(this, this._sourceAdded));
this._summaryAddedId = Main.messageTray.connect('summary-item-added', Lang.bind(this, this._summaryItemAdded));
},
destroy: function() {
if (this._sourceAddedId) {
Main.messageTray.disconnect(this._sourceAddedId);
this._sourceAddedId = 0;
if (this._summaryAddedId) {
Main.messageTray.disconnect(this._summaryAddedId);
this._summaryAddedId = 0;
}
let items = this._sources.items();
for (let i = 0; i < items.length; i++) {
let [source, obj] = items[i];
this._removeSource(source, obj);
}
for (let i = 0; i < this._items.length; i++)
this._removeItem(this._items[i]);
this._items = [];
this.actor.destroy();
},
_updateVisibility: function() {
this._musicBin.visible = this._musicBin.child != null && this._musicBin.child.visible;
this._notificationBox.visible = this._notificationBox.get_children().some(function(a) {
this._residentNotificationBox.visible = this._residentNotificationBox.get_n_children() > 0;
this._persistentNotificationBox.visible = this._persistentNotificationBox.get_children().some(function(a) {
return a.visible;
});
this.actor.visible = this._musicBin.visible || this._notificationBox.visible;
this.actor.visible = this._residentNotificationBox.visible || this._persistentNotificationBox.visible;
},
_sourceIsResident: function(source) {
return source.hasResidentNotification() && !source.isChat;
},
_makeNotificationCountText: function(count, isChat) {
@@ -180,16 +178,18 @@ const NotificationsBox = new Lang.Class({
return ngettext("%d new notification", "%d new notifications", count).format(count);
},
_makeNotificationSource: function(source, box) {
_makeNotificationSource: function(source) {
let box = new St.BoxLayout({ style_class: 'screen-shield-notification-source' });
let sourceActor = new MessageTray.SourceActor(source, SUMMARY_ICON_SIZE);
box.add(sourceActor.actor, { y_fill: true });
let textBox = new St.BoxLayout({ vertical: true });
box.add(textBox, { y_fill: false, y_align: St.Align.START });
let title = new St.Label({ text: source.title,
let label = new St.Label({ text: source.title,
style_class: 'screen-shield-notification-label' });
textBox.add(title);
textBox.add(label);
let count = source.unseenCount;
let countLabel = new St.Label({ text: this._makeNotificationCountText(count, source.isChat),
@@ -197,179 +197,118 @@ const NotificationsBox = new Lang.Class({
textBox.add(countLabel);
box.visible = count != 0;
return [title, countLabel];
return [box, countLabel];
},
_makeNotificationDetailedSource: function(source, box) {
let sourceActor = new MessageTray.SourceActor(source, SUMMARY_ICON_SIZE);
box.add(sourceActor.actor, { y_fill: true });
let textBox = new St.BoxLayout({ vertical: true });
box.add(textBox, { y_fill: false, y_align: St.Align.START });
let title = new St.Label({ text: source.title,
style_class: 'screen-shield-notification-label' });
textBox.add(title);
let visible = false;
for (let i = 0; i < source.notifications.length; i++) {
let n = source.notifications[i];
if (n.acknowledged || n.isMusic)
continue;
let body = '';
if (n.bannerBodyText) {
body = n.bannerBodyMarkup ? n.bannerBodyText :
GLib.markup_escape_text(n.bannerBodyMarkup, -1);
}
let label = new St.Label({ style_class: 'screen-shield-notification-count-text' });
label.clutter_text.set_markup('<b>' + n.title + '</b> ' + body);
textBox.add(label);
visible = true;
}
box.visible = visible;
return [title, null];
},
_showSource: function(source, obj, box) {
let musicNotification = source.getMusicNotification();
if (musicNotification != null &&
this._musicBin.child == null) {
if (musicNotification.actor.get_parent() != null)
musicNotification.actor.get_parent().remove_actor(musicNotification.actor);
this._musicBin.child = musicNotification.actor;
this._musicBin.child.visible = obj.visible;
musicNotification.expand(false /* animate */);
obj.musicNotification = musicNotification;
}
if (obj.detailed) {
[obj.titleLabel, obj.countLabel] = this._makeNotificationDetailedSource(source, box);
} else {
[obj.titleLabel, obj.countLabel] = this._makeNotificationSource(source, box);
}
box.visible = obj.visible &&
(source.unseenCount > (musicNotification ? 1 : 0));
},
_sourceAdded: function(tray, source, dontUpdateVisibility) {
// Ignore transient sources
if (source.isTransient)
_summaryItemAdded: function(tray, item, dontUpdateVisibility) {
// Ignore transient sources, or sources explicitly marked not to show
// in the lock screen
if (item.source.isTransient || !item.source.showInLockScreen)
return;
let obj = {
visible: source.policy.showInLockScreen,
detailed: source.policy.detailsInLockScreen,
item: item,
source: item.source,
resident: this._sourceIsResident(item.source),
contentUpdatedId: 0,
sourceDestroyId: 0,
sourceCountChangedId: 0,
sourceTitleChangedId: 0,
sourceUpdatedId: 0,
musicNotification: null,
sourceBox: null,
titleLabel: null,
countLabel: null,
};
obj.sourceBox = new St.BoxLayout({ style_class: 'screen-shield-notification-source' });
this._showSource(source, obj, obj.sourceBox);
this._notificationBox.add(obj.sourceBox, { x_fill: false, x_align: St.Align.START });
if (obj.resident) {
this._residentNotificationBox.add(item.notificationStackWidget);
item.closeButton.hide();
item.prepareNotificationStackForShowing();
} else {
[obj.sourceBox, obj.countLabel] = this._makeNotificationSource(item.source);
this._persistentNotificationBox.add(obj.sourceBox, { x_fill: false, x_align: St.Align.START });
}
obj.sourceCountChangedId = source.connect('count-updated', Lang.bind(this, function(source) {
this._countChanged(source, obj);
}));
obj.sourceTitleChangedId = source.connect('title-changed', Lang.bind(this, function(source) {
this._titleChanged(source, obj);
}));
obj.policyChangedId = source.policy.connect('policy-changed', Lang.bind(this, function(policy, key) {
if (key == 'show-in-lock-screen')
this._visibleChanged(source, obj);
else
this._detailedChanged(source, obj);
}));
obj.sourceDestroyId = source.connect('destroy', Lang.bind(this, function(source) {
this._onSourceDestroy(source, obj);
}));
this._sources.set(source, obj);
obj.contentUpdatedId = item.connect('content-updated', Lang.bind(this, this._onItemContentUpdated));
obj.sourceCountChangedId = item.source.connect('count-updated', Lang.bind(this, this._onSourceChanged));
obj.sourceTitleChangedId = item.source.connect('title-changed', Lang.bind(this, this._onSourceChanged));
obj.sourceDestroyId = item.source.connect('destroy', Lang.bind(this, this._onSourceDestroy));
this._items.push(obj);
if (!dontUpdateVisibility)
this._updateVisibility();
},
_titleChanged: function(source, obj) {
obj.titleLabel.text = source.title;
_findSource: function(source) {
for (let i = 0; i < this._items.length; i++) {
if (this._items[i].source == source)
return i;
}
return -1;
},
_countChanged: function(source, obj) {
if (obj.detailed) {
// A new notification was pushed, or a previous notification was destroyed.
// Give up, and build the list again.
_onItemContentUpdated: function(item) {
let obj = this._items[this._findSource(item.source)];
this._updateItem(obj);
},
obj.sourceBox.destroy_all_children();
obj.titleLabel = obj.countLabel = null;
this._showSource(source, obj, obj.sourceBox);
_onSourceChanged: function(source) {
let obj = this._items[this._findSource(source)];
this._updateItem(obj);
},
_updateItem: function(obj) {
let itemShouldBeResident = this._sourceIsResident(obj.source);
if (itemShouldBeResident && obj.resident) {
// Nothing to do here, the actor is already updated
return;
}
if (obj.resident && !itemShouldBeResident) {
// make into a regular item
obj.item.doneShowingNotificationStack();
this._residentNotificationBox.remove_actor(obj.item.notificationStackWidget);
[obj.sourceBox, obj.countLabel] = this._makeNotificationSource(obj.source);
this._persistentNotificationBox.add(obj.sourceBox, { x_fill: false, x_align: St.Align.START });
} else if (itemShouldBeResident && !obj.resident) {
// make into a resident item
obj.sourceBox.destroy();
obj.sourceBox = obj.countLabel = null;
obj.resident = true;
this._residentNotificationBox.add(obj.item.notificationStackWidget);
obj.item.closeButton.hide();
obj.item.prepareNotificationStackForShowing();
} else {
let count = source.unseenCount;
obj.countLabel.text = this._makeNotificationCountText(count, source.isChat);
// just update the counter
let count = obj.source.unseenCount;
obj.countLabel.text = this._makeNotificationCountText(count, obj.source.isChat);
obj.sourceBox.visible = count != 0;
}
obj.sourceBox.visible = obj.visible &&
(source.unseenCount > (obj.musicNotification ? 1 : 0));
this._updateVisibility();
},
_visibleChanged: function(source, obj) {
if (obj.visible == source.policy.showInLockScreen)
return;
_onSourceDestroy: function(source) {
let idx = this._findSource(source);
obj.visible = source.policy.showInLockScreen;
if (obj.musicNotification)
obj.musicNotification.actor.visible = obj.visible;
obj.sourceBox.visible = obj.visible &&
source.unseenCount > (obj.musicNotification ? 1 : 0);
this._removeItem(this._items[idx]);
this._items.splice(idx, 1);
this._updateVisibility();
},
_detailedChanged: function(source, obj) {
if (obj.detailed == source.policy.detailsInLockScreen)
return;
obj.detailed = source.policy.detailsInLockScreen;
obj.sourceBox.destroy_all_children();
obj.titleLabel = obj.countLabel = null;
this._showSource(source, obj, obj.sourceBox);
},
_onSourceDestroy: function(source, obj) {
this._removeSource(source, obj);
this._updateVisibility();
},
_removeSource: function(source, obj) {
obj.sourceBox.destroy();
obj.sourceBox = obj.titleLabel = obj.countLabel = null;
if (obj.musicNotification) {
this._musicBin.child = null;
obj.musicNotification = null;
_removeItem: function(obj) {
if (obj.resident) {
obj.item.doneShowingNotificationStack();
this._residentNotificationBox.remove_actor(obj.item.notificationStackWidget);
} else {
obj.sourceBox.destroy();
}
source.disconnect(obj.sourceDestroyId);
source.disconnect(obj.sourceCountChangedId);
source.disconnect(obj.sourceTitleChangedId);
source.policy.disconnect(obj.policyChangedId);
this._sources.delete(source);
obj.item.disconnect(obj.contentUpdatedId);
obj.source.disconnect(obj.sourceDestroyId);
obj.source.disconnect(obj.sourceCountChangedId);
obj.source.disconnect(obj.sourceTitleChangedId);
},
});
@@ -455,7 +394,6 @@ const ScreenShield = new Lang.Class({
reactive: true,
can_focus: true,
name: 'lockScreenGroup',
visible: false,
});
this._lockScreenGroup.connect('key-release-event',
Lang.bind(this, this._onLockScreenKeyRelease));
@@ -526,15 +464,10 @@ const ScreenShield = new Lang.Class({
this._screenSaverDBus = new ShellDBus.ScreenSaverDBus(this);
this._inhibitor = null;
this._loginManager = LoginManager.getLoginManager();
this._loginManager.connect('prepare-for-sleep',
Lang.bind(this, this._prepareForSleep));
this._inhibitSuspend();
this._loginSession = this._loginManager.getCurrentSessionProxy();
this._loginSession.connectSignal('Lock', Lang.bind(this, function() { this.lock(false); }));
this._loginSession.connectSignal('Unlock', Lang.bind(this, function() { this.deactivate(false); }));
this._loginSession.connectSignal('Unlock', Lang.bind(this, function() { this.unlock(); }));
this._settings = new Gio.Settings({ schema: SCREENSAVER_SCHEMA });
@@ -542,30 +475,17 @@ const ScreenShield = new Lang.Class({
this._hasLockScreen = false;
this._isGreeter = false;
this._isActive = false;
this._isLocked = false;
this._inUnlockAnimation = false;
this._activationTime = 0;
this._becameActiveId = 0;
this._lockTimeoutId = 0;
this._lightbox = new Lightbox.Lightbox(Main.uiGroup,
{ inhibitEvents: true,
fadeInTime: STANDARD_FADE_TIME,
fadeFactor: 1 });
this._lightbox.connect('shown', Lang.bind(this, this._onLightboxShown));
this.idleMonitor = new GnomeDesktop.IdleMonitor();
},
_liftShield: function(onPrimary, velocity) {
if (this._isLocked) {
this._ensureUnlockDialog(onPrimary, true /* allowCancel */);
this._hideLockScreen(true /* animate */, velocity);
} else {
this.deactivate(true /* animate */);
}
},
_onLockScreenKeyRelease: function(actor, event) {
let symbol = event.get_key_symbol();
@@ -583,7 +503,8 @@ const ScreenShield = new Lang.Class({
if (symbol == Clutter.KEY_Escape ||
symbol == Clutter.KEY_Return ||
symbol == Clutter.KEY_KP_Enter) {
this._liftShield(false, 0);
this._ensureUnlockDialog(true, true);
this._hideLockScreen(true, 0);
return true;
}
@@ -605,37 +526,13 @@ const ScreenShield = new Lang.Class({
// 7 standard scrolls to lift up
if (this._lockScreenScrollCounter > 35) {
this._liftShield(true, 0);
this._ensureUnlockDialog(false, true);
this._hideLockScreen(true, 0);
}
return true;
},
_inhibitSuspend: function() {
this._loginManager.inhibit(_("GNOME needs to lock the screen"),
Lang.bind(this, function(inhibitor) {
this._inhibitor = inhibitor;
}));
},
_uninhibitSuspend: function() {
if (this._inhibitor)
this._inhibitor.close(null);
this._inhibitor = null;
},
_prepareForSleep: function(loginManager, aboutToSuspend) {
if (aboutToSuspend) {
if (!this._settings.get_boolean(LOCK_ENABLED_KEY)) {
this._uninhibitSuspend();
return;
}
this.lock(true);
} else {
this._inhibitSuspend();
}
},
_animateArrows: function() {
let arrows = this._arrowContainer.get_children();
let unitaryDelay = ARROW_ANIMATION_TIME / (arrows.length + 1);
@@ -661,9 +558,7 @@ const ScreenShield = new Lang.Class({
_onDragBegin: function() {
Tweener.removeTweens(this._lockScreenGroup);
this._lockScreenState = MessageTray.State.HIDING;
if (this._isLocked)
this._ensureUnlockDialog(false, false);
this._ensureUnlockDialog(false, false);
return true;
},
@@ -684,7 +579,8 @@ const ScreenShield = new Lang.Class({
if (this._lockScreenGroup.y < -(ARROW_DRAG_THRESHOLD * global.stage.height)) {
// Complete motion automatically
let [velocity, velocityX, velocityY] = this._dragAction.get_velocity(0);
this._liftShield(true, -velocityY);
this._hideLockScreen(true, -velocityY);
this._ensureUnlockDialog(false, true);
} else {
// restore the lock screen to its original place
// try to use the same speed as the normal animation
@@ -724,81 +620,35 @@ const ScreenShield = new Lang.Class({
}
if (!this._isModal) {
Main.pushModal(this.actor, { keybindingMode: Shell.KeyBindingMode.LOCK_SCREEN });
Main.pushModal(this.actor, { keybindingMode: Main.KeybindingMode.LOCK_SCREEN });
this._isModal = true;
}
if (this._lightbox.actor.visible ||
this._isActive) {
// We're either shown and active, or in the process of
// showing.
// The latter is a very unlikely condition (it requires
// idle-delay < 20), but in any case we have nothing
// to do at this point: either isActive is true, or
// it will soon be.
// isActive can also be true if the lightbox is hidden,
// in case the shield is down and the user hasn't unlocked yet
return;
}
this._lightbox.show();
if (!this._isActive) {
this._lightbox.show();
if (this._activationTime == 0)
this._activationTime = GLib.get_monotonic_time();
if (this._activationTime == 0)
this._activationTime = GLib.get_monotonic_time();
if (this._becameActiveId == 0)
this._becameActiveId = this.idleMonitor.connect('became-active',
Lang.bind(this, this._onUserBecameActive));
this._becameActiveId = this.idleMonitor.connect('became-active', Lang.bind(this, function() {
this.idleMonitor.disconnect(this._becameActiveId);
let shouldLock = this._settings.get_boolean(LOCK_ENABLED_KEY) && !this._isLocked;
let lightboxWasShown = this._lightbox.shown;
this._lightbox.hide();
if (shouldLock) {
let lockTimeout = Math.max(STANDARD_FADE_TIME, this._settings.get_uint(LOCK_DELAY_KEY));
this._lockTimeoutId = Mainloop.timeout_add(lockTimeout * 1000,
Lang.bind(this, function() {
this._lockTimeoutId = 0;
this.lock(true);
return false;
}));
let shouldLock = lightboxWasShown && this._settings.get_boolean(LOCK_ENABLED_KEY);
if (shouldLock || this._isLocked) {
this.lock(false);
} else if (this._isActive) {
this.unlock();
}
}));
this._isActive = true;
this.emit('lock-status-changed');
}
},
_onUserBecameActive: function() {
// This function gets called here when the user becomes active
// after gnome-session changed the status to IDLE
// There are four possibilities here:
// - we're called when already locked; isActive and isLocked are true,
// we just go back to the lock screen curtain
// - we're called before the lightbox is fully shown; at this point
// isActive is false, so we just hide the ligthbox, reset the activationTime
// and go back to the unlocked desktop
// - we're called after showing the lightbox, but before the lock
// delay; this is mostly like the case above, but isActive is true now
// so we need to notify gnome-settings-daemon to go back to the normal
// policies for blanking
// (they're handled by the same code, and we emit one extra ActiveChanged
// signal in the case above)
// - we're called after showing the lightbox and after lock-delay; the
// session is effectivelly locked now, it's time to build and show
// the lock screen
this.idleMonitor.disconnect(this._becameActiveId);
this._becameActiveId = 0;
let lightboxWasShown = this._lightbox.shown;
this._lightbox.hide();
// Shortcircuit in case the mouse was moved before the fade completed
if (!lightboxWasShown) {
this.deactivate(false);
return;
}
},
_onLightboxShown: function() {
this.activate(false);
},
showDialog: function() {
// Ensure that the stage window is mapped, before taking a grab
// otherwise X errors out
@@ -813,7 +663,6 @@ const ScreenShield = new Lang.Class({
this.actor.show();
this._isGreeter = Main.sessionMode.isGreeter;
this._isLocked = true;
this._ensureUnlockDialog(true, true);
this._hideLockScreen(false, 0);
},
@@ -834,9 +683,6 @@ const ScreenShield = new Lang.Class({
},
_hideLockScreen: function(animate, velocity) {
if (this._lockScreenState == MessageTray.State.HIDDEN)
return;
this._lockScreenState = MessageTray.State.HIDING;
if (animate) {
@@ -878,7 +724,7 @@ const ScreenShield = new Lang.Class({
let constructor = Main.sessionMode.unlockDialog;
if (!constructor) {
// This session mode has no locking capabilities
this.deactivate(true);
this.unlock();
return;
}
@@ -888,10 +734,8 @@ const ScreenShield = new Lang.Class({
let time = global.get_current_time();
this._dialog.connect('loaded', Lang.bind(this, function() {
if (!this._dialog.open(time, onPrimary)) {
// This is kind of an impossible error: we're already modal
// by the time we reach this...
log('Could not open login dialog: failed to acquire grab');
this.deactivate(true);
this.unlock();
}
}));
@@ -907,17 +751,10 @@ const ScreenShield = new Lang.Class({
},
_onUnlockSucceded: function() {
this.deactivate(true);
this._tweenUnlocked();
},
_resetLockScreen: function(animateLockScreen, animateLockDialog) {
// Don't reset the lock screen unless it is completely hidden
// This prevents the shield going down if the lock-delay timeout
// fires while the user is dragging (which has the potential
// to confuse our state)
if (this._lockScreenState != MessageTray.State.HIDDEN)
return;
this._ensureLockScreen();
this._lockDialogGroup.scale_x = 1;
this._lockDialogGroup.scale_y = 1;
@@ -984,14 +821,6 @@ const ScreenShield = new Lang.Class({
this._lockScreenGroup.fixed_position_set = false;
this._lockScreenScrollCounter = 0;
let prevIsActive = this._isActive;
this._isActive = true;
if (prevIsActive != this._isActive)
this.emit('active-changed');
this._uninhibitSuspend();
this.emit('lock-screen-shown');
},
@@ -1013,10 +842,12 @@ const ScreenShield = new Lang.Class({
this._lockScreenContents.add_actor(this._lockScreenContentsBox);
this._notificationsBox = new NotificationsBox();
this._lockScreenContentsBox.add(this._notificationsBox.actor, { x_fill: true,
y_fill: true,
expand: true });
if (this._settings.get_boolean('show-notifications')) {
this._notificationsBox = new NotificationsBox();
this._lockScreenContentsBox.add(this._notificationsBox.actor, { x_fill: true,
y_fill: true,
expand: true });
}
this._hasLockScreen = true;
},
@@ -1041,10 +872,6 @@ const ScreenShield = new Lang.Class({
},
get locked() {
return this._isLocked;
},
get active() {
return this._isActive;
},
@@ -1052,20 +879,27 @@ const ScreenShield = new Lang.Class({
return this._activationTime;
},
deactivate: function(animate) {
this._hideLockScreen(animate, 0);
_tweenUnlocked: function() {
this._inUnlockAnimation = true;
this.unlock();
Tweener.addTween(this._lockDialogGroup, {
scale_x: 0,
scale_y: 0,
time: animate ? Overview.ANIMATION_TIME : 0,
time: Overview.ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this, this._completeDeactivate),
onComplete: function() {
if (this._dialog) {
this._dialog.destroy();
this._dialog = null;
}
this.actor.hide();
this._inUnlockAnimation = false;
},
onCompleteScope: this
});
},
_completeDeactivate: function() {
unlock: function() {
if (this._hasLockScreen)
this._clearLockScreen();
@@ -1081,31 +915,26 @@ const ScreenShield = new Lang.Class({
this._isModal = false;
}
this.actor.hide();
if (!this._inUnlockAnimation)
this.actor.hide();
if (Main.sessionMode.currentMode == 'lock-screen')
Main.sessionMode.popMode('lock-screen');
if (Main.sessionMode.currentMode == 'unlock-dialog')
Main.sessionMode.popMode('unlock-dialog');
if (this._becameActiveId != 0) {
this.idleMonitor.disconnect(this._becameActiveId);
this._becameActiveId = 0;
}
if (this._lockTimeoutId != 0) {
Mainloop.source_remove(this._lockTimeoutId);
this._lockTimeoutId = 0;
}
this._activationTime = 0;
this._isActive = false;
this._isLocked = false;
this.emit('active-changed');
this.emit('locked-changed');
this.emit('lock-status-changed');
},
activate: function(animate) {
lock: function(animate) {
if (!this._isModal) {
Main.pushModal(this.actor, { keybindingMode: Main.KeybindingMode.LOCK_SCREEN });
this._isModal = true;
}
if (this._activationTime == 0)
this._activationTime = GLib.get_monotonic_time();
@@ -1120,28 +949,9 @@ const ScreenShield = new Lang.Class({
this._resetLockScreen(animate, animate);
// We used to set isActive and emit active-changed here,
// but now we do that from lockScreenShown, which means
// there is a 0.3 seconds window during which the lock
// screen is effectively visible and the screen is locked, but
// the DBus interface reports the screensaver is off.
// This is because when we emit ActiveChanged(true),
// gnome-settings-daemon blanks the screen, and we don't want
// blank during the animation.
// This is not a problem for the idle fade case, because we
// activate without animation in that case.
},
lock: function(animate) {
if (!this._isModal) {
Main.pushModal(this.actor, { keybindingMode: Shell.KeyBindingMode.LOCK_SCREEN });
this._isModal = true;
}
this._isActive = true;
this._isLocked = true;
this.activate(animate);
this.emit('locked-changed');
this.emit('lock-status-changed');
},
});
Signals.addSignalMethods(ScreenShield.prototype);
@@ -1175,13 +985,13 @@ const ScreenShieldFallback = new Lang.Class({
else
this._locked = false;
this.emit('active-changed', this._locked);
this.emit('lock-status-changed', this._locked);
},
_onSignal: function(proxy, senderName, signalName, params) {
if (signalName == 'ActiveChanged') {
[this._locked] = params.deep_unpack();
this.emit('active-changed', this._locked);
this.emit('lock-status-changed', this._locked);
}
},

View File

@@ -1,272 +0,0 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Gdk = imports.gi.Gdk;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const St = imports.gi.St;
const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main;
const Tweener = imports.ui.tweener;
const ScreenshotIface = <interface name="org.gnome.Shell.Screenshot">
<method name="ScreenshotArea">
<arg type="i" direction="in" name="x"/>
<arg type="i" direction="in" name="y"/>
<arg type="i" direction="in" name="width"/>
<arg type="i" direction="in" name="height"/>
<arg type="b" direction="in" name="flash"/>
<arg type="s" direction="in" name="filename"/>
<arg type="b" direction="out" name="success"/>
<arg type="s" direction="out" name="filename_used"/>
</method>
<method name="ScreenshotWindow">
<arg type="b" direction="in" name="include_frame"/>
<arg type="b" direction="in" name="include_cursor"/>
<arg type="b" direction="in" name="flash"/>
<arg type="s" direction="in" name="filename"/>
<arg type="b" direction="out" name="success"/>
<arg type="s" direction="out" name="filename_used"/>
</method>
<method name="Screenshot">
<arg type="b" direction="in" name="include_cursor"/>
<arg type="b" direction="in" name="flash"/>
<arg type="s" direction="in" name="filename"/>
<arg type="b" direction="out" name="success"/>
<arg type="s" direction="out" name="filename_used"/>
</method>
<method name="SelectArea">
<arg type="i" direction="out" name="x"/>
<arg type="i" direction="out" name="y"/>
<arg type="i" direction="out" name="width"/>
<arg type="i" direction="out" name="height"/>
</method>
<method name="FlashArea">
<arg type="i" direction="in" name="x"/>
<arg type="i" direction="in" name="y"/>
<arg type="i" direction="in" name="width"/>
<arg type="i" direction="in" name="height"/>
</method>
</interface>;
const ScreenshotService = new Lang.Class({
Name: 'ScreenshotService',
_init: function() {
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(ScreenshotIface, this);
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell/Screenshot');
Gio.DBus.session.own_name('org.gnome.Shell.Screenshot', Gio.BusNameOwnerFlags.REPLACE, null, null);
},
_onScreenshotComplete: function(obj, result, area, filenameUsed, flash, invocation) {
if (flash && result) {
let flashspot = new Flashspot(area);
flashspot.fire();
}
let retval = GLib.Variant.new('(bs)', [result, filenameUsed]);
invocation.return_value(retval);
},
ScreenshotAreaAsync : function (params, invocation) {
let [x, y, width, height, flash, filename, callback] = params;
let screenshot = new Shell.Screenshot();
screenshot.screenshot_area (x, y, width, height, filename,
Lang.bind(this, this._onScreenshotComplete,
flash, invocation));
},
ScreenshotWindowAsync : function (params, invocation) {
let [include_frame, include_cursor, flash, filename] = params;
let screenshot = new Shell.Screenshot();
screenshot.screenshot_window (include_frame, include_cursor, filename,
Lang.bind(this, this._onScreenshotComplete,
flash, invocation));
},
ScreenshotAsync : function (params, invocation) {
let [include_cursor, flash, filename] = params;
let screenshot = new Shell.Screenshot();
screenshot.screenshot(include_cursor, filename,
Lang.bind(this, this._onScreenshotComplete,
flash, invocation));
},
SelectAreaAsync: function (params, invocation) {
let selectArea = new SelectArea();
selectArea.show();
selectArea.connect('finished', Lang.bind(this,
function(selectArea, areaRectangle) {
if (areaRectangle) {
let retval = GLib.Variant.new('(iiii)',
[areaRectangle.x, areaRectangle.y,
areaRectangle.width, areaRectangle.height]);
invocation.return_value(retval);
} else {
invocation.return_error_literal(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED,
"Operation was cancelled");
}
}));
},
FlashArea: function(x, y, width, height) {
let flashspot = new Flashspot({ x : x, y : y, width: width, height: height});
flashspot.fire();
}
});
const SelectArea = new Lang.Class({
Name: 'SelectArea',
_init: function() {
this._startX = -1;
this._startY = -1;
this._lastX = 0;
this._lastY = 0;
this._initRubberbandColors();
this._group = new St.Widget({ visible: false,
reactive: true,
x: 0,
y: 0 });
Main.uiGroup.add_actor(this._group);
this._group.connect('button-press-event',
Lang.bind(this, this._onButtonPress));
this._group.connect('button-release-event',
Lang.bind(this, this._onButtonRelease));
this._group.connect('key-press-event',
Lang.bind(this, this._onKeyPress));
this._group.connect('motion-event',
Lang.bind(this, this._onMotionEvent));
let constraint = new Clutter.BindConstraint({ source: global.stage,
coordinate: Clutter.BindCoordinate.ALL });
this._group.add_constraint(constraint);
this._rubberband = new Clutter.Rectangle({ color: this._background,
has_border: true,
border_width: 1,
border_color: this._border });
this._group.add_actor(this._rubberband);
},
show: function() {
if (!Main.pushModal(this._group) || this._group.visible)
return;
global.set_cursor(Shell.Cursor.CROSSHAIR);
this._group.visible = true;
},
_initRubberbandColors: function() {
function colorFromRGBA(rgba) {
return new Clutter.Color({ red: rgba.red * 255,
green: rgba.green * 255,
blue: rgba.blue * 255,
alpha: rgba.alpha * 255 });
}
let path = new Gtk.WidgetPath();
path.append_type(Gtk.IconView);
let context = new Gtk.StyleContext();
context.set_path(path);
context.add_class('rubberband');
this._background = colorFromRGBA(context.get_background_color(Gtk.StateFlags.NORMAL));
this._border = colorFromRGBA(context.get_border_color(Gtk.StateFlags.NORMAL));
},
_getGeometry: function() {
return { x: Math.min(this._startX, this._lastX),
y: Math.min(this._startY, this._lastY),
width: Math.abs(this._startX - this._lastX),
height: Math.abs(this._startY - this._lastY) };
},
_onKeyPress: function(actor, event) {
if (event.get_key_symbol() == Clutter.Escape)
this._destroy(null, false);
return;
},
_onMotionEvent: function(actor, event) {
if (this._startX == -1 || this._startY == -1)
return false;
[this._lastX, this._lastY] = event.get_coords();
let geometry = this._getGeometry();
this._rubberband.set_position(geometry.x, geometry.y);
this._rubberband.set_size(geometry.width, geometry.height);
return false;
},
_onButtonPress: function(actor, event) {
[this._startX, this._startY] = event.get_coords();
this._rubberband.set_position(this._startX, this._startY);
return false;
},
_onButtonRelease: function(actor, event) {
this._destroy(this._getGeometry(), true);
return false;
},
_destroy: function(geometry, fade) {
Tweener.addTween(this._group,
{ opacity: 0,
time: fade ? 0.2 : 0,
transition: 'easeOutQuad',
onComplete: Lang.bind(this,
function() {
Main.popModal(this._group);
this._group.destroy();
global.unset_cursor();
this.emit('finished', geometry);
})
});
}
});
Signals.addSignalMethods(SelectArea.prototype);
const FLASHSPOT_ANIMATION_OUT_TIME = 0.5; // seconds
const Flashspot = new Lang.Class({
Name: 'Flashspot',
Extends: Lightbox.Lightbox,
_init: function(area) {
this.parent(Main.uiGroup, { inhibitEvents: true,
width: area.width,
height: area.height });
this.actor.style_class = 'flashspot';
this.actor.set_position(area.x, area.y);
},
fire: function() {
this.actor.show();
this.actor.opacity = 255;
Tweener.addTween(this.actor,
{ opacity: 0,
time: FLASHSPOT_ANIMATION_OUT_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() {
this.destroy();
})
});
}
});

View File

@@ -17,26 +17,6 @@ const Search = imports.ui.search;
const MAX_LIST_SEARCH_RESULTS_ROWS = 3;
const MAX_GRID_SEARCH_RESULTS_ROWS = 1;
const MaxWidthBin = new Lang.Class({
Name: 'MaxWidthBin',
Extends: St.Bin,
vfunc_allocate: function(box, flags) {
let themeNode = this.get_theme_node();
let maxWidth = themeNode.get_max_width();
let availWidth = box.x2 - box.x1;
let adjustedBox = box;
if (availWidth > maxWidth) {
let excessWidth = availWidth - maxWidth;
adjustedBox.x1 += Math.floor(excessWidth / 2);
adjustedBox.x2 -= Math.floor(excessWidth / 2);
}
this.parent(adjustedBox, flags);
}
});
const SearchResult = new Lang.Class({
Name: 'SearchResult',
@@ -198,7 +178,7 @@ const ListSearchResults = new Lang.Class({
this._content = new St.BoxLayout({ style_class: 'list-search-results',
vertical: true });
this.actor.add(this._content, { expand: true });
this.actor.add_actor(this._content);
this._notDisplayedResult = [];
this._terms = [];
@@ -321,19 +301,12 @@ const SearchResults = new Lang.Class({
this._content = new St.BoxLayout({ name: 'searchResultsContent',
vertical: true });
this._contentBin = new MaxWidthBin({ name: 'searchResultsBin',
x_fill: true,
y_fill: true,
child: this._content });
let scrollChild = new St.BoxLayout();
scrollChild.add(this._contentBin, { expand: true });
this._scrollView = new St.ScrollView({ x_fill: true,
y_fill: false,
style_class: 'vfade' });
this._scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
this._scrollView.add_actor(scrollChild);
this._scrollView.add_actor(this._content);
let action = new Clutter.PanAction({ interpolate: true });
action.connect('pan', Lang.bind(this, this._onPan));
this._scrollView.add_action(action);

View File

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

View File

@@ -15,7 +15,6 @@ const DEFAULT_MODE = 'restrictive';
const _modes = {
'restrictive': {
parentMode: null,
stylesheetName: 'gnome-shell.css',
hasOverview: false,
showCalendarEvents: false,
allowSettings: false,

View File

@@ -9,8 +9,8 @@ const Config = imports.misc.config;
const ExtensionSystem = imports.ui.extensionSystem;
const ExtensionDownloader = imports.ui.extensionDownloader;
const ExtensionUtils = imports.misc.extensionUtils;
const Flashspot = imports.ui.flashspot;
const Main = imports.ui.main;
const Screenshot = imports.ui.screenshot;
const GnomeShellIface = <interface name="org.gnome.Shell">
<method name="Eval">
@@ -18,6 +18,34 @@ const GnomeShellIface = <interface name="org.gnome.Shell">
<arg type="b" direction="out" name="success" />
<arg type="s" direction="out" name="result" />
</method>
<method name="ScreenshotArea">
<arg type="i" direction="in" name="x"/>
<arg type="i" direction="in" name="y"/>
<arg type="i" direction="in" name="width"/>
<arg type="i" direction="in" name="height"/>
<arg type="b" direction="in" name="flash"/>
<arg type="s" direction="in" name="filename"/>
<arg type="b" direction="out" name="success"/>
</method>
<method name="ScreenshotWindow">
<arg type="b" direction="in" name="include_frame"/>
<arg type="b" direction="in" name="include_cursor"/>
<arg type="b" direction="in" name="flash"/>
<arg type="s" direction="in" name="filename"/>
<arg type="b" direction="out" name="success"/>
</method>
<method name="Screenshot">
<arg type="b" direction="in" name="include_cursor"/>
<arg type="b" direction="in" name="flash"/>
<arg type="s" direction="in" name="filename"/>
<arg type="b" direction="out" name="success"/>
</method>
<method name="FlashArea">
<arg type="i" direction="in" name="x"/>
<arg type="i" direction="in" name="y"/>
<arg type="i" direction="in" name="width"/>
<arg type="i" direction="in" name="height"/>
</method>
<property name="Mode" type="s" access="read" />
<property name="OverviewActive" type="b" access="readwrite" />
<property name="ShellVersion" type="s" access="read" />
@@ -48,7 +76,6 @@ const GnomeShell = new Lang.Class({
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell');
this._extensionsSerivce = new GnomeShellExtensions();
this._screenshotService = new Screenshot.ScreenshotService();
},
/**
@@ -84,6 +111,82 @@ const GnomeShell = new Lang.Class({
return [success, returnValue];
},
_onScreenshotComplete: function(obj, result, area, flash, invocation) {
if (flash && result) {
let flashspot = new Flashspot.Flashspot(area);
flashspot.fire();
}
let retval = GLib.Variant.new('(b)', [result]);
invocation.return_value(retval);
},
/**
* ScreenshotArea:
* @x: The X coordinate of the area
* @y: The Y coordinate of the area
* @width: The width of the area
* @height: The height of the area
* @flash: Whether to flash the area or not
* @filename: The filename for the screenshot
*
* Takes a screenshot of the passed in area and saves it
* in @filename as png image, it returns a boolean
* indicating whether the operation was successful or not.
*
*/
ScreenshotAreaAsync : function (params, invocation) {
let [x, y, width, height, flash, filename, callback] = params;
let screenshot = new Shell.Screenshot();
screenshot.screenshot_area (x, y, width, height, filename,
Lang.bind(this, this._onScreenshotComplete,
flash, invocation));
},
/**
* ScreenshotWindow:
* @include_frame: Whether to include the frame or not
* @include_cursor: Whether to include the cursor image or not
* @flash: Whether to flash the window area or not
* @filename: The filename for the screenshot
*
* Takes a screenshot of the focused window (optionally omitting the frame)
* and saves it in @filename as png image, it returns a boolean
* indicating whether the operation was successful or not.
*
*/
ScreenshotWindowAsync : function (params, invocation) {
let [include_frame, include_cursor, flash, filename] = params;
let screenshot = new Shell.Screenshot();
screenshot.screenshot_window (include_frame, include_cursor, filename,
Lang.bind(this, this._onScreenshotComplete,
flash, invocation));
},
/**
* Screenshot:
* @filename: The filename for the screenshot
* @include_cursor: Whether to include the cursor image or not
* @flash: Whether to flash the screen or not
*
* Takes a screenshot of the whole screen and saves it
* in @filename as png image, it returns a boolean
* indicating whether the operation was successful or not.
*
*/
ScreenshotAsync : function (params, invocation) {
let [include_cursor, flash, filename] = params;
let screenshot = new Shell.Screenshot();
screenshot.screenshot(include_cursor, filename,
Lang.bind(this, this._onScreenshotComplete,
flash, invocation));
},
FlashArea: function(x, y, width, height) {
let flashspot = new Flashspot.Flashspot({ x : x, y : y, width: width, height: height});
flashspot.fire();
},
Mode: global.session_mode,
get OverviewActive() {
@@ -247,8 +350,8 @@ const ScreenSaverDBus = new Lang.Class({
this.parent();
this._screenShield = screenShield;
screenShield.connect('active-changed', Lang.bind(this, function(shield) {
this._dbusImpl.emit_signal('ActiveChanged', GLib.Variant.new('(b)', [shield.active]));
screenShield.connect('lock-status-changed', Lang.bind(this, function(shield) {
this._dbusImpl.emit_signal('ActiveChanged', GLib.Variant.new('(b)', [shield.locked]));
}));
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(ScreenSaverIface, this);
@@ -269,13 +372,13 @@ const ScreenSaverDBus = new Lang.Class({
SetActive: function(active) {
if (active)
this._screenShield.activate(true);
this._screenShield.lock(true);
else
this._screenShield.unlock(false);
this._screenShield.unlock();
},
GetActive: function() {
return this._screenShield.active;
return this._screenShield.locked;
},
GetActiveTime: function() {

View File

@@ -9,7 +9,6 @@ const St = imports.gi.St;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const NotificationDaemon = imports.ui.notificationDaemon;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
@@ -57,7 +56,7 @@ const Indicator = new Lang.Class({
this._fullMenuItems = [new PopupMenu.PopupSeparatorMenuItem(),
new PopupMenu.PopupMenuItem(_("Send Files to Device...")),
new PopupMenu.PopupMenuItem(_("Set Up a New Device...")),
new PopupMenu.PopupMenuItem(_("Set up a New Device...")),
new PopupMenu.PopupSeparatorMenuItem()];
this._hasDevices = false;
@@ -81,7 +80,7 @@ const Indicator = new Lang.Class({
this._applet.connect('notify::show-full-menu', Lang.bind(this, this._updateFullMenu));
this._updateFullMenu();
this.menu.addSettingsAction(_("Bluetooth Settings"), 'gnome-bluetooth-panel.desktop');
this.menu.addSettingsAction(_("Bluetooth Settings"), 'bluetooth-properties.desktop');
this._applet.connect('pincode-request', Lang.bind(this, this._pinRequest));
this._applet.connect('confirm-request', Lang.bind(this, this._confirmRequest));
@@ -287,7 +286,6 @@ const Indicator = new Lang.Class({
_ensureSource: function() {
if (!this._source) {
this._source = new MessageTray.Source(_("Bluetooth"), 'bluetooth-active');
this._source.policy = new NotificationDaemon.NotificationApplicationPolicy('gnome-bluetooth-panel');
Main.messageTray.add(this._source);
}
},
@@ -448,5 +446,10 @@ const PinNotification = new Lang.Class({
return this._entry.clutter_text.text.length == 6;
else
return true;
},
grabFocus: function(lockTray) {
this.parent(lockTray);
global.stage.set_key_focus(this._entry);
}
});

View File

@@ -1,11 +1,9 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const GnomeDesktop = imports.gi.GnomeDesktop;
const Lang = imports.lang;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const St = imports.gi.St;
@@ -23,7 +21,6 @@ try {
const Main = imports.ui.main;
const PopupMenu = imports.ui.popupMenu;
const PanelMenu = imports.ui.panelMenu;
const SwitcherPopup = imports.ui.switcherPopup;
const Util = imports.misc.util;
const DESKTOP_INPUT_SOURCES_SCHEMA = 'org.gnome.desktop.input-sources';
@@ -50,7 +47,6 @@ const IBusManager = new Lang.Class({
this._engines = {};
this._ready = false;
this._registerPropertiesId = 0;
this._currentEngineName = null;
this._nameWatcherId = Gio.DBus.session.watch_name(IBus.SERVICE_IBUS,
Gio.BusNameWatcherFlags.NONE,
@@ -70,10 +66,6 @@ const IBusManager = new Lang.Class({
this._engines = {};
this._ready = false;
this._registerPropertiesId = 0;
this._currentEngineName = null;
if (this._readyCallback)
this._readyCallback(false);
},
_onNameAppeared: function() {
@@ -97,10 +89,12 @@ const IBusManager = new Lang.Class({
let name = enginesList[i].get_name();
this._engines[name] = enginesList[i];
}
this._updateReadiness();
} else {
this._clear();
return;
}
this._updateReadiness();
},
_initPanelService: function(ibus, result) {
@@ -111,36 +105,27 @@ const IBusManager = new Lang.Class({
this._candidatePopup.setPanelService(this._panelService);
// Need to set this to get 'global-engine-changed' emitions
this._ibus.set_watch_ibus_signal(true);
this._ibus.connect('global-engine-changed', Lang.bind(this, this._engineChanged));
this._ibus.connect('global-engine-changed', Lang.bind(this, this._resetProperties));
this._panelService.connect('update-property', Lang.bind(this, this._updateProperty));
// If an engine is already active we need to get its properties
this._ibus.get_global_engine_async(-1, null, Lang.bind(this, function(i, result) {
let engine;
try {
engine = this._ibus.get_global_engine_async_finish(result);
if (!engine)
return;
} catch(e) {
return;
}
this._engineChanged(this._ibus, engine.get_name());
}));
this._updateReadiness();
this._resetProperties();
} else {
this._clear();
return;
}
this._updateReadiness();
},
_updateReadiness: function() {
this._ready = (Object.keys(this._engines).length > 0 &&
this._panelService != null);
if (this._readyCallback)
this._readyCallback(this._ready);
if (this._ready && this._readyCallback)
this._readyCallback();
},
_engineChanged: function(bus, engineName) {
this._currentEngineName = engineName;
_resetProperties: function() {
this.emit('properties-registered', null);
if (this._registerPropertiesId != 0)
return;
@@ -153,18 +138,25 @@ const IBusManager = new Lang.Class({
this._panelService.disconnect(this._registerPropertiesId);
this._registerPropertiesId = 0;
this.emit('properties-registered', this._currentEngineName, props);
this.emit('properties-registered', props);
}));
},
_updateProperty: function(panel, prop) {
this.emit('property-updated', this._currentEngineName, prop);
this.emit('property-updated', prop);
},
activateProperty: function(key, state) {
this._panelService.property_activate(key, state);
},
hasProperties: function(id) {
if (id == 'anthy')
return true;
return false;
},
getEngineDesc: function(id) {
if (!IBus || !this._ready)
return null;
@@ -189,127 +181,16 @@ const LayoutMenuItem = new Lang.Class({
}
});
const InputSource = new Lang.Class({
Name: 'InputSource',
_init: function(type, id, displayName, shortName, index) {
this.type = type;
this.id = id;
this.displayName = displayName;
this._shortName = shortName;
this.index = index;
this._menuItem = new LayoutMenuItem(this.displayName, this._shortName);
this._menuItem.connect('activate', Lang.bind(this, this.activate));
this._indicatorLabel = new St.Label({ text: this._shortName });
this.properties = null;
},
destroy: function() {
this._menuItem.destroy();
this._indicatorLabel.destroy();
},
get shortName() {
return this._shortName;
},
set shortName(v) {
this._shortName = v;
this._menuItem.indicator.set_text(v);
this._indicatorLabel.set_text(v);
},
get menuItem() {
return this._menuItem;
},
get indicatorLabel() {
return this._indicatorLabel;
},
activate: function() {
this.emit('activate');
},
});
Signals.addSignalMethods(InputSource.prototype);
const InputSourcePopup = new Lang.Class({
Name: 'InputSourcePopup',
Extends: SwitcherPopup.SwitcherPopup,
_init: function(items, action, actionBackward) {
this.parent(items);
this._action = action;
this._actionBackward = actionBackward;
},
_createSwitcher: function() {
this._switcherList = new InputSourceSwitcher(this._items);
return true;
},
_initialSelection: function(backward, binding) {
if (binding == 'switch-input-source') {
if (backward)
this._selectedIndex = this._items.length - 1;
} else if (binding == 'switch-input-source-backward') {
if (!backward)
this._selectedIndex = this._items.length - 1;
}
this._select(this._selectedIndex);
},
_keyPressHandler: function(keysym, backwards, action) {
if (action == this._action)
this._select(backwards ? this._previous() : this._next());
else if (action == this._actionBackward)
this._select(backwards ? this._next() : this._previous());
else if (keysym == Clutter.Left)
this._select(this._previous());
else if (keysym == Clutter.Right)
this._select(this._next());
},
_finish : function() {
this.parent();
this._items[this._selectedIndex].activate();
},
});
const InputSourceSwitcher = new Lang.Class({
Name: 'InputSourceSwitcher',
Extends: SwitcherPopup.SwitcherList,
_init: function(items) {
this.parent(true);
for (let i = 0; i < items.length; i++)
this._addIcon(items[i]);
},
_addIcon: function(item) {
let box = new St.BoxLayout({ vertical: true });
let bin = new St.Bin({ style_class: 'input-source-switcher-symbol' });
let symbol = new St.Label({ text: item.shortName });
bin.set_child(symbol);
box.add(bin, { x_fill: false, y_fill: false } );
let text = new St.Label({ text: item.displayName });
box.add(text, { x_fill: false });
this.addItem(box, text);
}
});
const InputSourceIndicator = new Lang.Class({
Name: 'InputSourceIndicator',
Extends: PanelMenu.Button,
_propertiesWhitelist: [
'InputMode',
'TypingMode',
'DictMode'
],
_init: function() {
this.parent(0.0, _("Keyboard"));
@@ -320,36 +201,14 @@ const InputSourceIndicator = new Lang.Class({
this.actor.add_actor(this._container);
this.actor.add_style_class_name('panel-status-button');
// All valid input sources currently in the gsettings
// KEY_INPUT_SOURCES list indexed by their index there
this._inputSources = {};
// All valid input sources currently in the gsettings
// KEY_INPUT_SOURCES list of type INPUT_SOURCE_TYPE_IBUS
// indexed by the IBus ID
this._ibusSources = {};
this._labelActors = {};
this._layoutItems = {};
this._currentSource = null;
// All valid input sources currently in the gsettings
// KEY_INPUT_SOURCES list ordered by most recently used
this._mruSources = [];
this._keybindingAction =
Main.wm.addKeybinding('switch-input-source',
new Gio.Settings({ schema: "org.gnome.desktop.wm.keybindings" }),
Meta.KeyBindingFlags.REVERSES,
Shell.KeyBindingMode.ALL,
Lang.bind(this, this._switchInputSource));
this._keybindingActionBackward =
Main.wm.addKeybinding('switch-input-source-backward',
new Gio.Settings({ schema: "org.gnome.desktop.wm.keybindings" }),
Meta.KeyBindingFlags.REVERSES |
Meta.KeyBindingFlags.REVERSED,
Shell.KeyBindingMode.ALL,
Lang.bind(this, this._switchInputSource));
this._settings = new Gio.Settings({ schema: DESKTOP_INPUT_SOURCES_SCHEMA });
this._settings.connect('changed::' + KEY_CURRENT_INPUT_SOURCE, Lang.bind(this, this._currentInputSourceChanged));
this._settings.connect('changed::' + KEY_INPUT_SOURCES, Lang.bind(this, this._inputSourcesChanged));
this._currentSourceIndex = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE);
this._xkbInfo = new GnomeDesktop.XkbInfo();
this._propSeparator = new PopupMenu.PopupSeparatorMenuItem();
@@ -358,8 +217,9 @@ const InputSourceIndicator = new Lang.Class({
this.menu.addMenuItem(this._propSection);
this._propSection.actor.hide();
this._ibusReady = false;
this._ibusManager = new IBusManager(Lang.bind(this, this._ibusReadyCallback));
this._properties = null;
this._ibusManager = new IBusManager(Lang.bind(this, this._inputSourcesChanged));
this._ibusManager.connect('properties-registered', Lang.bind(this, this._ibusPropertiesRegistered));
this._ibusManager.connect('property-updated', Lang.bind(this, this._ibusPropertyUpdated));
this._inputSourcesChanged();
@@ -370,14 +230,7 @@ const InputSourceIndicator = new Lang.Class({
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
this._sessionUpdated();
this.menu.addSettingsAction(_("Region & Language Settings"), 'gnome-region-panel.desktop');
this._sourcesPerWindow = false;
this._focusWindowNotifyId = 0;
this._overviewShowingId = 0;
this._overviewHiddenId = 0;
this._settings.connect('changed::per-window', Lang.bind(this, this._sourcesPerWindowChanged));
this._sourcesPerWindowChanged();
this.menu.addSettingsAction(_("Region and Language Settings"), 'gnome-region-panel.desktop');
},
_sessionUpdated: function() {
@@ -388,29 +241,18 @@ const InputSourceIndicator = new Lang.Class({
this._showLayoutItem.actor.visible = Main.sessionMode.allowSettings;
},
_ibusReadyCallback: function(ready) {
if (this._ibusReady == ready)
return;
this._ibusReady = ready;
this._mruSources = [];
this._inputSourcesChanged();
},
_switchInputSource: function(display, screen, window, binding) {
let popup = new InputSourcePopup(this._mruSources, this._keybindingAction, this._keybindingActionBackward);
let modifiers = binding.get_modifiers();
let backwards = modifiers & Meta.VirtualModifier.SHIFT_MASK;
if (!popup.show(backwards, binding.get_name(), binding.get_mask()))
popup.destroy();
},
_currentInputSourceChanged: function() {
let nVisibleSources = Object.keys(this._inputSources).length;
let newSourceIndex = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE);
let newSource = this._inputSources[newSourceIndex];
let nVisibleSources = Object.keys(this._layoutItems).length;
let newCurrentSourceIndex = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE);
let newLayoutItem = this._layoutItems[newCurrentSourceIndex];
let hasProperties;
if (!newSource || (nVisibleSources < 2 && !newSource.properties)) {
if (newLayoutItem)
hasProperties = this._ibusManager.hasProperties(newLayoutItem.ibusEngineId);
else
hasProperties = false;
if (!newLayoutItem || (nVisibleSources < 2 && !hasProperties)) {
// This source index might be invalid if we weren't able
// to build a menu item for it, so we hide ourselves since
// we can't fix it here. *shrug*
@@ -424,125 +266,106 @@ const InputSourceIndicator = new Lang.Class({
this.actor.show();
let oldSource;
[oldSource, this._currentSource] = [this._currentSource, newSource];
if (oldSource) {
oldSource.menuItem.setShowDot(false);
this._container.set_skip_paint(oldSource.indicatorLabel, true);
if (this._layoutItems[this._currentSourceIndex]) {
this._layoutItems[this._currentSourceIndex].setShowDot(false);
this._container.set_skip_paint(this._labelActors[this._currentSourceIndex], true);
}
newSource.menuItem.setShowDot(true);
this._container.set_skip_paint(newSource.indicatorLabel, false);
newLayoutItem.setShowDot(true);
this._buildPropSection(newSource.properties);
let newLabelActor = this._labelActors[newCurrentSourceIndex];
this._container.set_skip_paint(newLabelActor, false);
for (let i = 1; i < this._mruSources.length; ++i)
if (this._mruSources[i] == newSource) {
let currentSource = this._mruSources.splice(i, 1);
this._mruSources = currentSource.concat(this._mruSources);
break;
}
if (hasProperties)
newLabelActor.set_text(newLayoutItem.indicator.get_text());
this._changePerWindowSource();
this._currentSourceIndex = newCurrentSourceIndex;
},
_inputSourcesChanged: function() {
let sources = this._settings.get_value(KEY_INPUT_SOURCES);
let nSources = sources.n_children();
for (let i in this._inputSources)
this._inputSources[i].destroy();
for (let i in this._layoutItems)
this._layoutItems[i].destroy();
this._inputSources = {};
this._ibusSources = {};
for (let i in this._labelActors)
this._labelActors[i].destroy();
let inputSourcesByShortName = {};
this._layoutItems = {};
this._labelActors = {};
let infos = [];
let infosByShortName = {};
for (let i = 0; i < nSources; i++) {
let displayName;
let shortName;
let info = { exists: false };
let [type, id] = sources.get_child_value(i).deep_unpack();
let exists = false;
if (type == INPUT_SOURCE_TYPE_XKB) {
[exists, displayName, shortName, , ] =
[info.exists, info.displayName, info.shortName, , ] =
this._xkbInfo.get_layout_info(id);
} else if (type == INPUT_SOURCE_TYPE_IBUS) {
let engineDesc = this._ibusManager.getEngineDesc(id);
if (engineDesc) {
let language = IBus.get_language_name(engineDesc.get_language());
exists = true;
displayName = language + ' (' + engineDesc.get_longname() + ')';
shortName = this._makeEngineShortName(engineDesc);
info.exists = true;
info.displayName = language + ' (' + engineDesc.get_longname() + ')';
info.shortName = this._makeEngineShortName(engineDesc);
info.ibusEngineId = id;
}
}
if (!exists)
if (!info.exists)
continue;
let is = new InputSource(type, id, displayName, shortName, i);
info.sourceIndex = i;
is.connect('activate', Lang.bind(this, function() {
if (this._currentSource.index == is.index)
return;
this._settings.set_value(KEY_CURRENT_INPUT_SOURCE,
GLib.Variant.new_uint32(is.index));
}));
if (!(is.shortName in inputSourcesByShortName))
inputSourcesByShortName[is.shortName] = [];
inputSourcesByShortName[is.shortName].push(is);
this._inputSources[is.index] = is;
if (is.type == INPUT_SOURCE_TYPE_IBUS)
this._ibusSources[is.id] = is;
if (!(info.shortName in infosByShortName))
infosByShortName[info.shortName] = [];
infosByShortName[info.shortName].push(info);
infos.push(info);
}
let menuIndex = 0;
for (let i in this._inputSources) {
let is = this._inputSources[i];
if (inputSourcesByShortName[is.shortName].length > 1) {
let sub = inputSourcesByShortName[is.shortName].indexOf(is) + 1;
is.shortName += String.fromCharCode(0x2080 + sub);
for (let i = 0; i < infos.length; i++) {
let info = infos[i];
if (infosByShortName[info.shortName].length > 1) {
let sub = infosByShortName[info.shortName].indexOf(info) + 1;
info.shortName += String.fromCharCode(0x2080 + sub);
}
this.menu.addMenuItem(is.menuItem, menuIndex++);
let item = new LayoutMenuItem(info.displayName, info.shortName);
item.ibusEngineId = info.ibusEngineId;
this._layoutItems[info.sourceIndex] = item;
this.menu.addMenuItem(item, i);
item.connect('activate', Lang.bind(this, function() {
this._settings.set_value(KEY_CURRENT_INPUT_SOURCE,
GLib.Variant.new_uint32(info.sourceIndex));
}));
this._container.add_actor(is.indicatorLabel);
this._container.set_skip_paint(is.indicatorLabel, true);
let shortLabel = new St.Label({ text: info.shortName });
this._labelActors[info.sourceIndex] = shortLabel;
this._container.add_actor(shortLabel);
this._container.set_skip_paint(shortLabel, true);
}
let sourcesList = [];
for (let i in this._inputSources)
sourcesList.push(this._inputSources[i]);
let mruSources = [];
for (let i = 0; i < this._mruSources.length; i++) {
for (let j = 0; j < sourcesList.length; j++)
if (this._mruSources[i].type == sourcesList[j].type &&
this._mruSources[i].id == sourcesList[j].id) {
mruSources = mruSources.concat(sourcesList.splice(j, 1));
break;
}
}
this._mruSources = mruSources.concat(sourcesList);
this._currentInputSourceChanged();
},
_showLayout: function() {
Main.overview.hide();
let source = this._currentSource;
let sources = this._settings.get_value(KEY_INPUT_SOURCES);
let current = this._settings.get_uint(KEY_CURRENT_INPUT_SOURCE);
let [type, id] = sources.get_child_value(current).deep_unpack();
let xkbLayout = '';
let xkbVariant = '';
if (source.type == INPUT_SOURCE_TYPE_XKB) {
[, , , xkbLayout, xkbVariant] = this._xkbInfo.get_layout_info(source.id);
} else if (source.type == INPUT_SOURCE_TYPE_IBUS) {
let engineDesc = this._ibusManager.getEngineDesc(source.id);
if (type == INPUT_SOURCE_TYPE_XKB) {
[, , , xkbLayout, xkbVariant] = this._xkbInfo.get_layout_info(id);
} else if (type == INPUT_SOURCE_TYPE_IBUS) {
let engineDesc = this._ibusManager.getEngineDesc(id);
if (engineDesc) {
xkbLayout = engineDesc.get_layout();
xkbVariant = '';
@@ -571,25 +394,26 @@ const InputSourceIndicator = new Lang.Class({
return String.fromCharCode(0x2328); // keyboard glyph
},
_ibusPropertiesRegistered: function(im, engineName, props) {
let source = this._ibusSources[engineName];
if (!source)
return;
source.properties = props;
if (source == this._currentSource)
this._currentInputSourceChanged();
_propertyWhitelisted: function(prop) {
for (let i = 0; i < this._propertiesWhitelist.length; ++i) {
let key = prop.get_key();
if (key.substr(0, this._propertiesWhitelist[i].length) == this._propertiesWhitelist[i])
return true;
}
return false;
},
_ibusPropertyUpdated: function(im, engineName, prop) {
let source = this._ibusSources[engineName];
if (!source)
_ibusPropertiesRegistered: function(im, props) {
this._properties = props;
this._buildPropSection();
},
_ibusPropertyUpdated: function(im, prop) {
if (!this._propertyWhitelisted(prop))
return;
if (this._updateSubProperty(source.properties, prop) &&
source == this._currentSource)
this._currentInputSourceChanged();
if (this._updateSubProperty(this._properties, prop))
this._buildPropSection();
},
_updateSubProperty: function(props, prop) {
@@ -609,12 +433,25 @@ const InputSourceIndicator = new Lang.Class({
return false;
},
_buildPropSection: function(properties) {
_updateIndicatorLabel: function(text) {
let layoutItem = this._layoutItems[this._currentSourceIndex];
let hasProperties;
if (layoutItem)
hasProperties = this._ibusManager.hasProperties(layoutItem.ibusEngineId);
else
hasProperties = false;
if (hasProperties)
this._labelActors[this._currentSourceIndex].set_text(text);
},
_buildPropSection: function() {
this._propSeparator.actor.hide();
this._propSection.actor.hide();
this._propSection.removeAll();
this._buildPropSubMenu(this._propSection, properties);
this._buildPropSubMenu(this._propSection, this._properties);
if (!this._propSection.isEmpty()) {
this._propSection.actor.show();
@@ -631,7 +468,8 @@ const InputSourceIndicator = new Lang.Class({
for (let i = 0; (p = props.get(i)) != null; ++i) {
let prop = p;
if (!prop.get_visible())
if (!this._propertyWhitelisted(prop) ||
!prop.get_visible())
continue;
if (prop.get_key() == 'InputMode') {
@@ -642,17 +480,15 @@ const InputSourceIndicator = new Lang.Class({
text = prop.get_label().get_text();
if (text && text.length > 0 && text.length < 3)
this._currentSource.indicatorLabel.set_text(text);
this._updateIndicatorLabel(text);
}
let item;
switch (prop.get_prop_type()) {
case IBus.PropType.MENU:
let type = prop.get_prop_type();
if (type == IBus.PropType.MENU) {
item = new PopupMenu.PopupSubMenuMenuItem(prop.get_label().get_text());
this._buildPropSubMenu(item.menu, prop.get_sub_props());
break;
case IBus.PropType.RADIO:
} else if (type == IBus.PropType.RADIO) {
item = new PopupMenu.PopupMenuItem(prop.get_label().get_text());
item.prop = prop;
radioGroup.push(item);
@@ -677,39 +513,7 @@ const InputSourceIndicator = new Lang.Class({
}
}
}));
break;
case IBus.PropType.TOGGLE:
item = new PopupMenu.PopupSwitchMenuItem(prop.get_label().get_text(), prop.get_state() == IBus.PropState.CHECKED);
item.prop = prop;
item.connect('toggled', Lang.bind(this, function() {
if (item.state) {
item.prop.set_state(IBus.PropState.CHECKED);
this._ibusManager.activateProperty(item.prop.get_key(),
IBus.PropState.CHECKED);
} else {
item.prop.set_state(IBus.PropState.UNCHECKED);
this._ibusManager.activateProperty(item.prop.get_key(),
IBus.PropState.UNCHECKED);
}
}));
break;
case IBus.PropType.NORMAL:
item = new PopupMenu.PopupMenuItem(prop.get_label().get_text());
item.prop = prop;
item.connect('activate', Lang.bind(this, function() {
this._ibusManager.activateProperty(item.prop.get_key(),
IBus.PropState.CHECKED);
}));
break;
case IBus.PropType.SEPARATOR:
item = new PopupMenu.PopupSeparatorMenuItem();
break;
default:
log ('IBus property %s has invalid type %d'.format(prop.get_key(), type));
} else {
continue;
}
@@ -718,91 +522,14 @@ const InputSourceIndicator = new Lang.Class({
}
},
_getNewInputSource: function(current) {
for (let i in this._inputSources) {
let is = this._inputSources[i];
if (is.type == current.type &&
is.id == current.id)
return is;
}
return this._currentSource;
},
_getCurrentWindow: function() {
if (Main.overview.visible)
return Main.overview;
else
return global.display.focus_window;
},
_setPerWindowInputSource: function() {
let window = this._getCurrentWindow();
if (!window)
return;
if (!window._inputSources) {
window._inputSources = this._inputSources;
window._currentSource = this._currentSource;
} else if (window._inputSources == this._inputSources) {
window._currentSource.activate();
} else {
window._inputSources = this._inputSources;
window._currentSource = this._getNewInputSource(window._currentSource);
window._currentSource.activate();
}
},
_sourcesPerWindowChanged: function() {
this._sourcesPerWindow = this._settings.get_boolean('per-window');
if (this._sourcesPerWindow && this._focusWindowNotifyId == 0) {
this._focusWindowNotifyId = global.display.connect('notify::focus-window',
Lang.bind(this, this._setPerWindowInputSource));
this._overviewShowingId = Main.overview.connect('showing',
Lang.bind(this, this._setPerWindowInputSource));
this._overviewHiddenId = Main.overview.connect('hidden',
Lang.bind(this, this._setPerWindowInputSource));
} else if (!this._sourcesPerWindow && this._focusWindowNotifyId != 0) {
global.display.disconnect(this._focusWindowNotifyId);
this._focusWindowNotifyId = 0;
Main.overview.disconnect(this._overviewShowingId);
this._overviewShowingId = 0;
Main.overview.disconnect(this._overviewHiddenId);
this._overviewHiddenId = 0;
let windows = global.get_window_actors().map(function(w) {
return w.meta_window;
});
for (let i = 0; i < windows.length; ++i) {
delete windows[i]._inputSources;
delete windows[i]._currentSource;
}
delete Main.overview._inputSources;
delete Main.overview._currentSource;
}
},
_changePerWindowSource: function() {
if (!this._sourcesPerWindow)
return;
let window = this._getCurrentWindow();
if (!window)
return;
window._inputSources = this._inputSources;
window._currentSource = this._currentSource;
},
_containerGetPreferredWidth: function(container, for_height, alloc) {
// Here, and in _containerGetPreferredHeight, we need to query
// for the height of all children, but we ignore the results
// for those we don't actually display.
let max_min_width = 0, max_natural_width = 0;
for (let i in this._inputSources) {
let is = this._inputSources[i];
let [min_width, natural_width] = is.indicatorLabel.get_preferred_width(for_height);
for (let i in this._labelActors) {
let [min_width, natural_width] = this._labelActors[i].get_preferred_width(for_height);
max_min_width = Math.max(max_min_width, min_width);
max_natural_width = Math.max(max_natural_width, natural_width);
}
@@ -814,9 +541,8 @@ const InputSourceIndicator = new Lang.Class({
_containerGetPreferredHeight: function(container, for_width, alloc) {
let max_min_height = 0, max_natural_height = 0;
for (let i in this._inputSources) {
let is = this._inputSources[i];
let [min_height, natural_height] = is.indicatorLabel.get_preferred_height(for_width);
for (let i in this._labelActors) {
let [min_height, natural_height] = this._labelActors[i].get_preferred_height(for_width);
max_min_height = Math.max(max_min_height, min_height);
max_natural_height = Math.max(max_natural_height, natural_height);
}
@@ -832,9 +558,7 @@ const InputSourceIndicator = new Lang.Class({
box.y2 -= box.y1;
box.y1 = 0;
for (let i in this._inputSources) {
let is = this._inputSources[i];
is.indicatorLabel.allocate_align_fill(box, 0.5, 0, false, false, flags);
}
for (let i in this._labelActors)
this._labelActors[i].allocate_align_fill(box, 0.5, 0, false, false, flags);
}
});

View File

@@ -10,53 +10,48 @@ const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const VolumeMenu = imports.ui.status.volume;
const FakeStatusIcon = new Lang.Class({
Name: 'FakeStatusIcon',
_init: function(button) {
this.actor = new St.BoxLayout({ style_class: 'panel-status-button-box' });
this._button = button;
this._button.connect('icons-updated', Lang.bind(this, this._reconstructIcons));
this._button.actor.bind_property('visible', this.actor, 'visible',
GObject.BindingFlags.SYNC_CREATE);
this._reconstructIcons();
},
_reconstructIcons: function() {
this.actor.destroy_all_children();
this._button.icons.forEach(Lang.bind(this, function(icon) {
let newIcon = new St.Icon({ style_class: 'system-status-icon' });
icon.bind_property('gicon', newIcon, 'gicon',
GObject.BindingFlags.SYNC_CREATE);
icon.bind_property('visible', newIcon, 'visible',
GObject.BindingFlags.SYNC_CREATE);
this.actor.add_actor(newIcon);
}));
}
});
const Indicator = new Lang.Class({
Name: 'LockScreenMenuIndicator',
Extends: PanelMenu.SystemStatusButton,
_init: function() {
this.parent(null, _("Volume, network, battery"));
this._box.style_class = 'lock-screen-status-button-box';
this._volumeControl = VolumeMenu.getMixerControl();
this._volumeMenu = new VolumeMenu.VolumeMenu(this._volumeControl);
this.menu.addMenuItem(this._volumeMenu);
this._volume = Main.panel.statusArea.volume;
if (this._volume) {
this._volumeIcon = this.addIcon(null);
this._volume.mainIcon.bind_property('gicon', this._volumeIcon, 'gicon',
GObject.BindingFlags.SYNC_CREATE);
this._volume.mainIcon.bind_property('visible', this._volumeIcon, 'visible',
GObject.BindingFlags.SYNC_CREATE);
this._volume = new FakeStatusIcon(Main.panel.statusArea.volume);
this._box.add_child(this._volume.actor);
// Network may not exist if the user doesn't have NetworkManager
if (Main.panel.statusArea.network) {
this._network = new FakeStatusIcon(Main.panel.statusArea.network);
this._box.add_child(this._network.actor);
this._volumeControl = VolumeMenu.getMixerControl();
this._volumeMenu = new VolumeMenu.VolumeMenu(this._volumeControl);
this.menu.addMenuItem(this._volumeMenu);
}
this._battery = new FakeStatusIcon(Main.panel.statusArea.battery);
this._box.add_child(this._battery.actor);
this._network = Main.panel.statusArea.network;
if (this._network) {
this._networkIcon = this.addIcon(null);
this._network.mainIcon.bind_property('gicon', this._networkIcon, 'gicon',
GObject.BindingFlags.SYNC_CREATE);
this._network.mainIcon.bind_property('visible', this._networkIcon, 'visible',
GObject.BindingFlags.SYNC_CREATE);
this._networkSecondaryIcon = this.addIcon(null);
this._network.secondaryIcon.bind_property('gicon', this._networkSecondaryIcon, 'gicon',
GObject.BindingFlags.SYNC_CREATE);
this._network.secondaryIcon.bind_property('visible', this._networkSecondaryIcon, 'visible',
GObject.BindingFlags.SYNC_CREATE);
}
this._battery = Main.panel.statusArea.battery;
if (this._battery) {
this._batteryIcon = this.addIcon(null);
this._battery.mainIcon.bind_property('gicon', this._batteryIcon, 'gicon',
GObject.BindingFlags.SYNC_CREATE);
this._battery.mainIcon.bind_property('visible', this._batteryIcon, 'visible',
GObject.BindingFlags.SYNC_CREATE);
}
}
});

View File

@@ -5,29 +5,20 @@ const Gio = imports.gi.Gio;
const Lang = imports.lang;
const NetworkManager = imports.gi.NetworkManager;
const NMClient = imports.gi.NMClient;
const NMGtk = imports.gi.NMGtk;
const Signals = imports.signals;
const St = imports.gi.St;
// Some of the new code depends on as-yet-unreleased NM
var NMGtk;
try {
NMGtk = imports.gi.NMGtk;
} catch(e) {
NMGtk = null;
}
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const MessageTray = imports.ui.messageTray;
const NotificationDaemon = imports.ui.notificationDaemon;
const ModemManager = imports.misc.modemManager;
const Util = imports.misc.util;
const NMConnectionCategory = {
INVALID: 'invalid',
WIRED: 'wired',
VIRTUAL: 'virtual',
WIRELESS: 'wireless',
WWAN: 'wwan',
VPN: 'vpn'
@@ -309,10 +300,13 @@ const NMDevice = new Lang.Class({
Extends: NMConnectionBased,
_init: function(client, device, connections) {
this._client = client;
this._setDevice(device);
this.parent(connections);
this.device = device;
this.device._delegate = this;
this._stateChangedId = this.device.connect('state-changed', Lang.bind(this, this._deviceStateChanged));
// protected
this._client = client;
this.parent(connections);
this._activeConnection = null;
this._activeConnectionItem = null;
this._autoConnectionItem = null;
@@ -337,12 +331,23 @@ const NMDevice = new Lang.Class({
},
destroy: function() {
this._setDevice(null);
if (this.device)
this.device._delegate = null;
if (this._deferredWorkId) {
// Just clear out, the actual removal is handled when the
// actor is destroyed
this._deferredWorkId = 0;
if (this._stateChangedId) {
// Need to go through GObject.Object.prototype because
// nm_device_disconnect conflicts with g_signal_disconnect
GObject.Object.prototype.disconnect.call(this.device, this._stateChangedId);
this._stateChangedId = 0;
}
if (this._carrierChangedId) {
// see above for why this is needed
GObject.Object.prototype.disconnect.call(this.device, this._carrierChangedId);
this._carrierChangedId = 0;
}
if (this._firmwareChangedId) {
GObject.Object.prototype.disconnect.call(this.device, this._firmwareChangedId);
this._firmwareChangedId = 0;
}
this._clearSection();
@@ -351,33 +356,6 @@ const NMDevice = new Lang.Class({
this.section.destroy();
},
_setDevice: function(device) {
if (device) {
this.device = device;
this.device._delegate = this;
this._stateChangedId = this.device.connect('state-changed', Lang.bind(this, this._deviceStateChanged));
} else if (this.device) {
this.device._delegate = null;
if (this._stateChangedId) {
// Need to go through GObject.Object.prototype because
// nm_device_disconnect conflicts with g_signal_disconnect
GObject.Object.prototype.disconnect.call(this.device, this._stateChangedId);
this._stateChangedId = 0;
}
if (this._carrierChangedId) {
GObject.Object.prototype.disconnect.call(this.device, this._carrierChangedId);
this._carrierChangedId = 0;
}
if (this._firmwareChangedId) {
GObject.Object.prototype.disconnect.call(this.device, this._firmwareChangedId);
this._firmwareChangedId = 0;
}
this.device = null;
}
},
deactivate: function() {
this.device.disconnect(null);
return true;
@@ -392,7 +370,7 @@ const NMDevice = new Lang.Class({
// Otherwise, if no connection is currently configured,
// try automatic configuration (or summon the config dialog)
if (this._connections.length == 1) {
this._client.activate_connection(this._connections[0].connection, this.device || null, null, null);
this._client.activate_connection(this._connections[0].connection, this.device, null, null);
return true;
} else if (this._connections.length == 0) {
return this._activateAutomaticConnection();
@@ -412,7 +390,7 @@ const NMDevice = new Lang.Class({
},
get connected() {
return this.device && this.device.state == NetworkManager.DeviceState.ACTIVATED;
return this.device.state == NetworkManager.DeviceState.ACTIVATED;
},
clearActiveConnection: function(activeConnection) {
@@ -432,6 +410,7 @@ const NMDevice = new Lang.Class({
this._activeConnection = activeConnection;
this._clearSection();
this._queueCreateSection();
},
@@ -445,9 +424,6 @@ const NMDevice = new Lang.Class({
},
getStatusLabel: function() {
if (!this.device)
return null;
switch(this.device.state) {
case NetworkManager.DeviceState.DISCONNECTED:
case NetworkManager.DeviceState.ACTIVATED:
@@ -498,8 +474,7 @@ const NMDevice = new Lang.Class({
},
syncDescription: function() {
if (this.device && this.device._description)
this.statusItem.label.text = this.device._description;
this.statusItem.label.text = this.device._description;
},
// protected
@@ -508,10 +483,8 @@ const NMDevice = new Lang.Class({
},
_queueCreateSection: function() {
if (this._deferredWorkId) {
this._clearSection();
Main.queueDeferredWork(this._deferredWorkId);
}
this._clearSection();
Main.queueDeferredWork(this._deferredWorkId);
},
_clearSection: function() {
@@ -611,6 +584,7 @@ const NMDevice = new Lang.Class({
this._updateStatusItem();
this._clearSection();
this._queueCreateSection();
this.emit('state-changed');
},
@@ -637,11 +611,12 @@ const NMDevice = new Lang.Class({
}
});
const NMDeviceSimple = new Lang.Class({
Name: 'NMDeviceSimple',
const NMDeviceWired = new Lang.Class({
Name: 'NMDeviceWired',
Extends: NMDevice,
_init: function(client, device, connections) {
this._autoConnectionName = _("Auto Ethernet");
this.category = NMConnectionCategory.WIRED;
this.parent(client, device, connections);
@@ -656,19 +631,6 @@ const NMDeviceSimple = new Lang.Class({
// we can do it here because addConnection and removeConnection
// both call _createSection at some point
this.section.actor.visible = this._connections.length > 1;
}
});
const NMDeviceWired = new Lang.Class({
Name: 'NMDeviceWired',
Extends: NMDeviceSimple,
_init: function(client, device, connections) {
device._description = _("Wired");
this._autoConnectionName = _("Auto Ethernet");
this.category = NMConnectionCategory.WIRED;
this.parent(client, device, connections);
},
_createAutomaticConnection: function() {
@@ -692,24 +654,12 @@ const NMDeviceModem = new Lang.Class({
_init: function(client, device, connections) {
let is_wwan = false;
device._description = _("Mobile broadband");
this._enabled = true;
this.mobileDevice = null;
this._connectionType = 'ppp';
this._capabilities = device.current_capabilities;
// Support new ModemManager1 devices
if (device.udi.indexOf('/org/freedesktop/ModemManager1/Modem') == 0) {
is_wwan = true;
this.mobileDevice = new ModemManager.BroadbandModem(device.udi, device.current_capabilities);
if (this._capabilities & NetworkManager.DeviceModemCapabilities.GSM_UMTS) {
this._connectionType = NetworkManager.SETTING_GSM_SETTING_NAME;
} else if (this._capabilities & NetworkManager.DeviceModemCapabilities.LTE) {
this._connectionType = NetworkManager.SETTING_GSM_SETTING_NAME;
} else if (this._capabilities & NetworkManager.DeviceModemCapabilities.CDMA_EVDO) {
this._connectionType = NetworkManager.SETTING_CDMA_SETTING_NAME;
}
} else if (this._capabilities & NetworkManager.DeviceModemCapabilities.GSM_UMTS) {
if (this._capabilities & NetworkManager.DeviceModemCapabilities.GSM_UMTS) {
is_wwan = true;
this.mobileDevice = new ModemManager.ModemGsm(device.udi);
this._connectionType = NetworkManager.SETTING_GSM_SETTING_NAME;
@@ -719,8 +669,7 @@ const NMDeviceModem = new Lang.Class({
this._connectionType = NetworkManager.SETTING_CDMA_SETTING_NAME;
} else if (this._capabilities & NetworkManager.DeviceModemCapabilities.LTE) {
is_wwan = true;
this.mobileDevice = new ModemManager.ModemGsm(device.udi);
this._connectionType = NetworkManager.SETTING_GSM_SETTING_NAME;
// FIXME: support signal quality
}
if (is_wwan) {
@@ -823,7 +772,6 @@ const NMDeviceBluetooth = new Lang.Class({
Extends: NMDevice,
_init: function(client, device, connections) {
device._description = _("Bluetooth");
this._autoConnectionName = this._makeConnectionName(device);
device.connect('notify::name', Lang.bind(this, this._updateAutoConnectionName));
@@ -866,6 +814,7 @@ const NMDeviceBluetooth = new Lang.Class({
_updateAutoConnectionName: function() {
this._autoConnectionName = this._makeConnectionName(this.device);
this._clearSection();
this._queueCreateSection();
this._updateStatusItem();
}
@@ -1127,8 +1076,10 @@ const NMDeviceWireless = new Lang.Class({
this._networks.splice(res.network, 1);
let newPos = Util.insertSorted(this._networks, network, Lang.bind(this, this._networkSortFunction));
if (newPos != res.network)
if (newPos != res.network) {
this._clearSection();
this._queueCreateSection();
}
},
_accessPointAdded: function(device, accessPoint) {
@@ -1181,8 +1132,10 @@ const NMDeviceWireless = new Lang.Class({
let newPos = Util.insertSorted(this._networks, apObj, this._networkSortFunction);
// Queue an update of the UI if we changed the order
if (newPos != pos)
if (newPos != pos) {
this._clearSection();
this._queueCreateSection();
}
},
_accessPointRemoved: function(device, accessPoint) {
@@ -1242,10 +1195,12 @@ const NMDeviceWireless = new Lang.Class({
if (res.network < this._networks.length-1)
okNext = this._networkSortFunction(this._networks[res.network + 1], apObj) <= 0;
if (!okPrev || !okNext)
if (!okPrev || !okNext) {
this._clearSection();
this._queueCreateSection();
else if (apObj.item)
} else if (apObj.item) {
apObj.item.updateBestAP(apObj.accessPoints[0]);
}
}
},
@@ -1321,6 +1276,7 @@ const NMDeviceWireless = new Lang.Class({
if (forceupdate) {
this._networks.sort(this._networkSortFunction);
this._clearSection();
this._queueCreateSection();
}
},
@@ -1353,6 +1309,7 @@ const NMDeviceWireless = new Lang.Class({
if (forceupdate) {
this._networks.sort(this._networkSortFunction);
this._clearSection();
this._queueCreateSection();
}
},
@@ -1463,56 +1420,6 @@ const NMDeviceWireless = new Lang.Class({
},
});
const NMDeviceVirtual = new Lang.Class({
Name: 'NMDeviceVirtual',
Extends: NMDeviceSimple,
_init: function(client, iface, connections) {
this.iface = iface;
this.parent(client, null, connections);
this.category = NMConnectionCategory.VIRTUAL;
},
_shouldShowConnectionList: function() {
return this.hasConnections();
},
connectionValid: function(connection) {
return connection.get_virtual_iface_name() == this.iface;
},
addConnection: function(connection) {
if (!this.device && !this.hasConnections())
this.statusItem.label.text = NMGtk.utils_get_connection_device_name(connection);
this.parent(connection);
},
adoptDevice: function(device) {
if (device.get_iface() == this.iface) {
this._setDevice(device);
if (device._description)
this.syncDescription();
this._updateStatusItem();
this.emit('state-changed');
return true;
} else
return false;
},
removeDevice: function(device) {
if (device == this.device) {
this._setDevice(null);
this._updateStatusItem();
this.emit('state-changed');
}
},
hasConnections: function() {
return this._connections.length != 0;
}
});
const NMVPNSection = new Lang.Class({
Name: 'NMVPNSection',
Extends: NMConnectionBased,
@@ -1693,7 +1600,6 @@ const NMApplet = new Lang.Class({
this._nmDevices = [];
this._devices = { };
this._virtualDevices = [ ];
this._devices.wired = {
section: new PopupMenu.PopupMenuSection(),
@@ -1704,15 +1610,6 @@ const NMApplet = new Lang.Class({
this.menu.addMenuItem(this._devices.wired.section);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._devices.virtual = {
section: new PopupMenu.PopupMenuSection(),
devices: [ ],
};
this._devices.virtual.section.actor.hide();
this.menu.addMenuItem(this._devices.virtual.section);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._devices.wireless = {
section: new PopupMenu.PopupMenuSection(),
devices: [ ],
@@ -1743,17 +1640,8 @@ const NMApplet = new Lang.Class({
this._dtypes[NetworkManager.DeviceType.WIFI] = NMDeviceWireless;
this._dtypes[NetworkManager.DeviceType.MODEM] = NMDeviceModem;
this._dtypes[NetworkManager.DeviceType.BT] = NMDeviceBluetooth;
this._dtypes[NetworkManager.DeviceType.INFINIBAND] = NMDeviceSimple;
// TODO: WiMax support
// Virtual device types
this._vtypes = { };
if (NMGtk) {
this._vtypes[NetworkManager.SETTING_VLAN_SETTING_NAME] = NMDeviceVirtual;
this._vtypes[NetworkManager.SETTING_BOND_SETTING_NAME] = NMDeviceVirtual;
this._vtypes[NetworkManager.SETTING_BRIDGE_SETTING_NAME] = NMDeviceVirtual;
}
// Connection types
this._ctypes = { };
this._ctypes[NetworkManager.SETTING_WIRELESS_SETTING_NAME] = NMConnectionCategory.WIRELESS;
@@ -1763,12 +1651,6 @@ const NMApplet = new Lang.Class({
this._ctypes[NetworkManager.SETTING_BLUETOOTH_SETTING_NAME] = NMConnectionCategory.WWAN;
this._ctypes[NetworkManager.SETTING_CDMA_SETTING_NAME] = NMConnectionCategory.WWAN;
this._ctypes[NetworkManager.SETTING_GSM_SETTING_NAME] = NMConnectionCategory.WWAN;
this._ctypes[NetworkManager.SETTING_INFINIBAND_SETTING_NAME] = NMConnectionCategory.WIRED;
if (NMGtk) {
this._ctypes[NetworkManager.SETTING_VLAN_SETTING_NAME] = NMConnectionCategory.VIRTUAL;
this._ctypes[NetworkManager.SETTING_BOND_SETTING_NAME] = NMConnectionCategory.VIRTUAL;
this._ctypes[NetworkManager.SETTING_BRIDGE_SETTING_NAME] = NMConnectionCategory.VIRTUAL;
}
this._ctypes[NetworkManager.SETTING_VPN_SETTING_NAME] = NMConnectionCategory.VPN;
this._settings = NMClient.RemoteSettings.new(null);
@@ -1796,7 +1678,6 @@ const NMApplet = new Lang.Class({
if (!this._source) {
this._source = new MessageTray.Source(_("Network Manager"),
'network-transmit-receive');
this._source.policy = new NotificationDaemon.NotificationApplicationPolicy('gnome-network-panel');
this._source.connect('destroy', Lang.bind(this, function() {
this._source = null;
@@ -1821,14 +1702,7 @@ const NMApplet = new Lang.Class({
let devices = this._devices[category].devices;
let item = this._devices[category].item;
let section = this._devices[category].section;
let visible;
if (category == NMConnectionCategory.VIRTUAL)
visible = !section.isEmpty();
else
visible = devices.length > 0;
if (!visible)
if (devices.length == 0)
section.actor.hide();
else {
section.actor.show();
@@ -1889,50 +1763,9 @@ const NMApplet = new Lang.Class({
MessageTray.Urgency.HIGH);
},
_syncDeviceNames: function() {
if (NMGtk) {
let names = NMGtk.utils_disambiguate_device_names(this._nmDevices);
for (let i = 0; i < this._nmDevices.length; i++) {
let device = this._nmDevices[i];
device._description = names[i];
if (device._delegate)
device._delegate.syncDescription();
}
} else {
for (let i = 0; i < this._nmDevices.length; i++) {
let device = this._nmDevices[i];
device._delegate.syncDescription();
}
}
},
_makeWrapperDevice: function(wrapperClass, device) {
let wrapper = new wrapperClass(this._client, device, this._connections);
_deviceAdded: function(client, device, skipSyncDeviceNames) {
if (device._delegate) {
// already seen, not adding again
return;
}
for (let i = 0; i < this._virtualDevices.length; i++) {
if (this._virtualDevices[i].adoptDevice(device)) {
this._nmDevices.push(device);
if (!skipSyncDeviceNames)
this._syncDeviceNames();
return;
}
}
let wrapperClass = this._dtypes[device.get_device_type()];
if (wrapperClass) {
let wrapper = new wrapperClass(this._client, device, this._connections);
this._addDeviceWrapper(wrapper);
this._nmDevices.push(device);
if (!skipSyncDeviceNames)
this._syncDeviceNames();
}
},
_addDeviceWrapper: function(wrapper) {
wrapper._activationFailedId = wrapper.connect('activation-failed',
Lang.bind(this, this._onActivationFailed));
wrapper._deviceStateChangedId = wrapper.connect('state-changed', Lang.bind(this, function(dev) {
@@ -1944,42 +1777,60 @@ const NMApplet = new Lang.Class({
wrapper.disconnect(wrapper._destroyId);
});
let section = this._devices[wrapper.category].section;
section.addMenuItem(wrapper.statusItem);
section.addMenuItem(wrapper.section);
return wrapper;
},
let devices = this._devices[wrapper.category].devices;
devices.push(wrapper);
_syncDeviceNames: function() {
let names = NMGtk.utils_disambiguate_device_names(this._nmDevices);
for (let i = 0; i < this._nmDevices.length; i++) {
let device = this._nmDevices[i];
if (device._description != names[i]) {
device._description = names[i];
device._delegate.syncDescription();
}
}
},
this._syncSectionTitle(wrapper.category);
_deviceAdded: function(client, device, skipSyncDeviceNames) {
if (device._delegate) {
// already seen, not adding again
return;
}
let wrapperClass = this._dtypes[device.get_device_type()];
if (wrapperClass) {
let wrapper = this._makeWrapperDevice(wrapperClass, device);
let section = this._devices[wrapper.category].section;
let devices = this._devices[wrapper.category].devices;
section.addMenuItem(wrapper.statusItem);
section.addMenuItem(wrapper.section);
devices.push(wrapper);
this._nmDevices.push(device);
if (!skipSyncDeviceNames)
this._syncDeviceNames();
this._syncSectionTitle(wrapper.category);
}
},
_deviceRemoved: function(client, device) {
let pos = this._nmDevices.indexOf(device);
if (pos != -1) {
this._nmDevices.splice(pos, 1);
this._syncDeviceNames();
}
let wrapper = device._delegate;
if (!wrapper) {
if (!device._delegate) {
log('Removing a network device that was not added');
return;
}
if (wrapper instanceof NMDeviceVirtual)
wrapper.removeDevice(device);
else
this._removeDeviceWrapper(wrapper);
},
_removeDeviceWrapper: function(wrapper) {
let wrapper = device._delegate;
wrapper.destroy();
let devices = this._devices[wrapper.category].devices;
let pos = devices.indexOf(wrapper);
devices.splice(pos, 1);
pos = this._nmDevices.indexOf(device);
this._nmDevices.splice(pos, 1);
this._syncDeviceNames();
this._syncSectionTitle(wrapper.category)
},
@@ -2174,13 +2025,6 @@ const NMApplet = new Lang.Class({
devices[i].removeConnection(connection);
}
if (section == NMConnectionCategory.VIRTUAL) {
let iface = connection.get_virtual_iface_name();
let wrapper = this._findVirtualDevice(iface);
if (wrapper && !wrapper.hasConnections())
this._removeDeviceWrapper(wrapper);
}
connection.disconnect(connection._removedId);
connection.disconnect(connection._updatedId);
connection._removedId = connection._updatedId = 0;
@@ -2194,27 +2038,6 @@ const NMApplet = new Lang.Class({
let section = connection._section;
if (section == NMConnectionCategory.VIRTUAL) {
let wrapperClass = this._vtypes[connection._type];
if (!wrapperClass)
return;
let iface = connection.get_virtual_iface_name();
let wrapper = this._findVirtualDevice(iface);
if (!wrapper) {
wrapper = new wrapperClass(this._client, iface, this._connections);
this._addDeviceWrapper(wrapper);
this._virtualDevices.push(wrapper);
// We might already have a device for this connection
for (let i = 0; i < this._nmDevices.length; i++) {
let device = this._nmDevices[i];
if (wrapper.adoptDevice(device))
break;
}
}
}
if (section == NMConnectionCategory.INVALID)
return;
if (section == NMConnectionCategory.VPN) {
@@ -2227,15 +2050,6 @@ const NMApplet = new Lang.Class({
}
},
_findVirtualDevice: function(iface) {
for (let i = 0; i < this._virtualDevices.length; i++) {
if (this._virtualDevices[i].iface == iface)
return this._virtualDevices[i];
}
return null;
},
_hideDevices: function() {
this._devicesHidden = true;
@@ -2251,7 +2065,6 @@ const NMApplet = new Lang.Class({
this._statusSection.actor.hide();
this._syncSectionTitle(NMConnectionCategory.WIRED);
this._syncSectionTitle(NMConnectionCategory.VIRTUAL);
this._syncSectionTitle(NMConnectionCategory.WIRELESS);
this._syncSectionTitle(NMConnectionCategory.WWAN);
},
@@ -2289,7 +2102,6 @@ const NMApplet = new Lang.Class({
this.setIcon('network-wireless-acquiring-symbolic');
break;
case NMConnectionCategory.WIRED:
case NMConnectionCategory.VIRTUAL:
this.setIcon('network-wired-acquiring-symbolic');
break;
default:
@@ -2329,7 +2141,6 @@ const NMApplet = new Lang.Class({
break;
}
case NMConnectionCategory.WIRED:
case NMConnectionCategory.VIRTUAL:
this.setIcon('network-wired-symbolic');
break;
case NMConnectionCategory.WWAN:

View File

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

View File

@@ -5,7 +5,6 @@ const Lang = imports.lang;
const Gio = imports.gi.Gio;
const Gvc = imports.gi.Gvc;
const St = imports.gi.St;
const Signals = imports.signals;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
@@ -27,210 +26,6 @@ function getMixerControl() {
return _mixerControl;
}
const StreamSlider = new Lang.Class({
Name: 'StreamSlider',
_init: function(control, title) {
this._control = control;
this.item = new PopupMenu.PopupMenuSection();
this._title = new PopupMenu.PopupMenuItem(title, { reactive: false });
this._slider = new PopupMenu.PopupSliderMenuItem(0);
this._slider.connect('value-changed', Lang.bind(this, this._sliderChanged));
this._slider.connect('drag-end', Lang.bind(this, this._notifyVolumeChange));
this.item.addMenuItem(this._title);
this.item.addMenuItem(this._slider);
this._stream = null;
this._shouldShow = true;
},
get stream() {
return this._stream;
},
set stream(stream) {
if (this._stream) {
this._disconnectStream(this._stream);
}
this._stream = stream;
if (this._stream) {
this._connectStream(this._stream);
this._updateVolume();
} else {
this.emit('stream-updated');
}
this._updateVisibility();
},
_disconnectStream: function(stream) {
stream.disconnect(this._mutedChangedId);
this._mutedChangedId = 0;
stream.disconnect(this._volumeChangedId);
this._volumeChangedId = 0;
},
_connectStream: function(stream) {
this._mutedChangedId = stream.connect('notify::is-muted', Lang.bind(this, this._updateVolume));
this._volumeChangedId = stream.connect('notify::volume', Lang.bind(this, this._updateVolume));
},
_shouldBeVisible: function() {
return this._stream != null;
},
_updateVisibility: function() {
let visible = this._shouldBeVisible();
this._title.actor.visible = visible;
this._slider.actor.visible = visible;
},
scroll: function(event) {
this._slider.scroll(event);
},
setValue: function(value) {
// piggy-back off of sliderChanged
this._slider.setValue(value);
},
_sliderChanged: function(slider, value, property) {
if (!this._stream)
return;
let volume = value * this._control.get_vol_max_norm();
let prevMuted = this._stream.is_muted;
if (volume < 1) {
this._stream.volume = 0;
if (!prevMuted)
this._stream.change_is_muted(true);
} else {
this._stream.volume = volume;
if (prevMuted)
this._stream.change_is_muted(false);
}
this._stream.push_volume();
},
_notifyVolumeChange: function() {
global.cancel_theme_sound(VOLUME_NOTIFY_ID);
global.play_theme_sound(VOLUME_NOTIFY_ID,
'audio-volume-change',
_("Volume changed"),
Clutter.get_current_event ());
},
_updateVolume: function() {
let muted = this._stream.is_muted;
this._slider.setValue(muted ? 0 : (this._stream.volume / this._control.get_vol_max_norm()));
this.emit('stream-updated');
},
getIcon: function() {
if (!this._stream)
return null;
let volume = this._stream.volume;
if (this._stream.is_muted || volume <= 0) {
return 'audio-volume-muted-symbolic';
} else {
let n = Math.floor(3 * volume / this._control.get_vol_max_norm()) + 1;
if (n < 2)
return 'audio-volume-low-symbolic';
if (n >= 3)
return 'audio-volume-high-symbolic';
return 'audio-volume-medium-symbolic';
}
}
});
Signals.addSignalMethods(StreamSlider.prototype);
const OutputStreamSlider = new Lang.Class({
Name: 'OutputStreamSlider',
Extends: StreamSlider,
_connectStream: function(stream) {
this.parent(stream);
this._portChangedId = stream.connect('notify::port', Lang.bind(this, this._portChanged));
this._portChanged();
},
_findHeadphones: function(sink) {
// This only works for external headphones (e.g. bluetooth)
if (sink.get_form_factor() == 'headset' ||
sink.get_form_factor() == 'headphone')
return true;
// a bit hackish, but ALSA/PulseAudio have a number
// of different identifiers for headphones, and I could
// not find the complete list
if (sink.get_ports().length > 0)
return sink.get_port().port.indexOf('headphone') >= 0;
return false;
},
_disconnectStream: function(stream) {
this.parent(stream);
stream.disconnect(this._portChangedId);
this._portChangedId = 0;
},
_portChanged: function() {
let hasHeadphones = this._findHeadphones(this._stream);
if (hasHeadphones != this._hasHeadphones) {
this._hasHeadphones = hasHeadphones;
this.emit('headphones-changed', this._hasHeadphones);
}
}
});
const InputStreamSlider = new Lang.Class({
Name: 'InputStreamSlider',
Extends: StreamSlider,
_init: function(control, title) {
this.parent(control, title);
this._control.connect('stream-added', Lang.bind(this, this._maybeShowInput));
this._control.connect('stream-removed', Lang.bind(this, this._maybeShowInput));
},
_connectStream: function(stream) {
this.parent(stream);
this._maybeShowInput();
},
_maybeShowInput: function() {
// only show input widgets if any application is recording audio
let showInput = false;
let recordingApps = this._control.get_source_outputs();
if (this._stream && recordingApps) {
for (let i = 0; i < recordingApps.length; i++) {
let outputStream = recordingApps[i];
let id = outputStream.get_application_id();
// but skip gnome-volume-control and pavucontrol
// (that appear as recording because they show the input level)
if (!id || (id != 'org.gnome.VolumeControl' && id != 'org.PulseAudio.pavucontrol')) {
showInput = true;
break;
}
}
}
this._showInput = showInput;
this._updateVisibility();
},
_shouldBeVisible: function() {
return this.parent() && this._showInput;
}
});
const VolumeMenu = new Lang.Class({
Name: 'VolumeMenu',
Extends: PopupMenu.PopupMenuSection,
@@ -244,48 +39,207 @@ const VolumeMenu = new Lang.Class({
this._control.connect('state-changed', Lang.bind(this, this._onControlStateChanged));
this._control.connect('default-sink-changed', Lang.bind(this, this._readOutput));
this._control.connect('default-source-changed', Lang.bind(this, this._readInput));
this._control.connect('stream-added', Lang.bind(this, this._maybeShowInput));
this._control.connect('stream-removed', Lang.bind(this, this._maybeShowInput));
this._volumeMax = this._control.get_vol_max_norm();
this._output = null;
this._outputVolumeId = 0;
this._outputMutedId = 0;
/* Translators: This is the label for audio volume */
this._output = new OutputStreamSlider(this._control, _("Volume"));
this._output.connect('stream-updated', Lang.bind(this, function() {
this.emit('icon-changed');
}));
this._output.connect('headphones-changed', Lang.bind(this, function(stream, value) {
this.emit('headphones-changed', value);
}));
this.addMenuItem(this._output.item);
this._input = new InputStreamSlider(this._control, _("Microphone"));
this.addMenuItem(this._input.item);
this._outputTitle = new PopupMenu.PopupMenuItem(_("Volume"), { reactive: false });
this._outputSlider = new PopupMenu.PopupSliderMenuItem(0);
this._outputSlider.connect('value-changed', Lang.bind(this, this._sliderChanged, '_output'));
this._outputSlider.connect('drag-end', Lang.bind(this, this._notifyVolumeChange));
this.addMenuItem(this._outputTitle);
this.addMenuItem(this._outputSlider);
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._input = null;
this._inputVolumeId = 0;
this._inputMutedId = 0;
this._inputTitle = new PopupMenu.PopupMenuItem(_("Microphone"), { reactive: false });
this._inputSlider = new PopupMenu.PopupSliderMenuItem(0);
this._inputSlider.connect('value-changed', Lang.bind(this, this._sliderChanged, '_input'));
this._inputSlider.connect('drag-end', Lang.bind(this, this._notifyVolumeChange));
this.addMenuItem(this._inputTitle);
this.addMenuItem(this._inputSlider);
this._onControlStateChanged();
},
scroll: function(event) {
this._output.scroll(event);
scroll: function(direction) {
let currentVolume = this._output.volume;
if (direction == Clutter.ScrollDirection.DOWN) {
let prev_muted = this._output.is_muted;
this._output.volume = Math.max(0, currentVolume - this._volumeMax * VOLUME_ADJUSTMENT_STEP);
if (this._output.volume < 1) {
this._output.volume = 0;
if (!prev_muted)
this._output.change_is_muted(true);
}
this._output.push_volume();
}
else if (direction == Clutter.ScrollDirection.UP) {
this._output.volume = Math.min(this._volumeMax, currentVolume + this._volumeMax * VOLUME_ADJUSTMENT_STEP);
this._output.change_is_muted(false);
this._output.push_volume();
}
this._notifyVolumeChange();
},
_onControlStateChanged: function() {
if (this._control.get_state() == Gvc.MixerControlState.READY) {
this._readInput();
this._readOutput();
this._readInput();
this._maybeShowInput();
} else {
this.emit('icon-changed');
this.emit('icon-changed', null);
}
},
_findHeadphones: function(sink) {
// This only works for external headphones (e.g. bluetooth)
if (sink.get_form_factor() == 'headset' ||
sink.get_form_factor() == 'headphone')
return true;
// a bit hackish, but ALSA/PulseAudio have a number
// of different identifiers for headphones, and I could
// not find the complete list
let port = sink.get_port();
if (port)
return port.port.indexOf('headphone') >= 0;
return false;
},
_portChanged: function() {
this.hasHeadphones = this._findHeadphones(this._output);
this.emit('headphones-changed');
},
_readOutput: function() {
this._output.stream = this._control.get_default_sink();
if (this._outputVolumeId) {
this._output.disconnect(this._outputVolumeId);
this._output.disconnect(this._outputMutedId);
this._output.disconnect(this._outputPortId);
this._outputVolumeId = 0;
this._outputMutedId = 0;
this._outputPortId = 0;
}
this._output = this._control.get_default_sink();
if (this._output) {
this._outputMutedId = this._output.connect('notify::is-muted', Lang.bind(this, this._mutedChanged, '_output'));
this._outputVolumeId = this._output.connect('notify::volume', Lang.bind(this, this._volumeChanged, '_output'));
this._outputPortId = this._output.connect('notify::port', Lang.bind(this, this._portChanged));
this._mutedChanged(null, null, '_output');
this._volumeChanged(null, null, '_output');
this._portChanged();
} else {
this.hasHeadphones = false;
this._outputSlider.setValue(0);
this.emit('icon-changed', 'audio-volume-muted-symbolic');
}
},
_readInput: function() {
this._input.stream = this._control.get_default_source();
if (this._inputVolumeId) {
this._input.disconnect(this._inputVolumeId);
this._input.disconnect(this._inputMutedId);
this._inputVolumeId = 0;
this._inputMutedId = 0;
}
this._input = this._control.get_default_source();
if (this._input) {
this._inputMutedId = this._input.connect('notify::is-muted', Lang.bind(this, this._mutedChanged, '_input'));
this._inputVolumeId = this._input.connect('notify::volume', Lang.bind(this, this._volumeChanged, '_input'));
this._mutedChanged (null, null, '_input');
this._volumeChanged (null, null, '_input');
} else {
this._inputTitle.actor.hide();
this._inputSlider.actor.hide();
}
},
getIcon: function() {
return this._output.getIcon();
_maybeShowInput: function() {
// only show input widgets if any application is recording audio
let showInput = false;
let recordingApps = this._control.get_source_outputs();
if (this._input && recordingApps) {
for (let i = 0; i < recordingApps.length; i++) {
let outputStream = recordingApps[i];
let id = outputStream.get_application_id();
// but skip gnome-volume-control and pavucontrol
// (that appear as recording because they show the input level)
if (!id || (id != 'org.gnome.VolumeControl' && id != 'org.PulseAudio.pavucontrol')) {
showInput = true;
break;
}
}
}
this._inputTitle.actor.visible = showInput;
this._inputSlider.actor.visible = showInput;
},
_volumeToIcon: function(volume) {
if (volume <= 0) {
return 'audio-volume-muted-symbolic';
} else {
let n = Math.floor(3 * volume / this._volumeMax) + 1;
if (n < 2)
return 'audio-volume-low-symbolic';
if (n >= 3)
return 'audio-volume-high-symbolic';
return 'audio-volume-medium-symbolic';
}
},
_sliderChanged: function(slider, value, property) {
if (this[property] == null) {
log ('Volume slider changed for %s, but %s does not exist'.format(property, property));
return;
}
let volume = value * this._volumeMax;
let prev_muted = this[property].is_muted;
if (volume < 1) {
this[property].volume = 0;
if (!prev_muted)
this[property].change_is_muted(true);
} else {
this[property].volume = volume;
if (prev_muted)
this[property].change_is_muted(false);
}
this[property].push_volume();
},
_notifyVolumeChange: function() {
global.cancel_theme_sound(VOLUME_NOTIFY_ID);
global.play_theme_sound(VOLUME_NOTIFY_ID, 'audio-volume-change');
},
_mutedChanged: function(object, param_spec, property) {
let muted = this[property].is_muted;
let slider = this[property+'Slider'];
slider.setValue(muted ? 0 : (this[property].volume / this._volumeMax));
if (property == '_output') {
if (muted)
this.emit('icon-changed', 'audio-volume-muted-symbolic');
else
this.emit('icon-changed', this._volumeToIcon(this._output.volume));
}
},
_volumeChanged: function(object, param_spec, property) {
this[property+'Slider'].setValue(this[property].volume / this._volumeMax);
if (property == '_output' && !this._output.is_muted)
this.emit('icon-changed', this._volumeToIcon(this._output.volume));
}
});
@@ -298,13 +252,13 @@ const Indicator = new Lang.Class({
this._control = getMixerControl();
this._volumeMenu = new VolumeMenu(this._control);
this._volumeMenu.connect('icon-changed', Lang.bind(this, function(menu) {
let icon = this._volumeMenu.getIcon();
this.actor.visible = (icon != null);
this._volumeMenu.connect('icon-changed', Lang.bind(this, function(menu, icon) {
this._hasPulseAudio = (icon != null);
this.setIcon(icon);
this._syncVisibility();
}));
this._volumeMenu.connect('headphones-changed', Lang.bind(this, function(menu, value) {
this._headphoneIcon.visible = value;
this._volumeMenu.connect('headphones-changed', Lang.bind(this, function() {
this._syncVisibility();
}));
this._headphoneIcon = this.addIcon(new Gio.ThemedIcon({ name: 'headphones-symbolic' }));
@@ -318,7 +272,13 @@ const Indicator = new Lang.Class({
this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
},
_syncVisibility: function() {
this.actor.visible = this._hasPulseAudio;
this.mainIcon.visible = this._hasPulseAudio;
this._headphoneIcon.visible = this._hasPulseAudio && this._volumeMenu.hasHeadphones;
},
_onScrollEvent: function(actor, event) {
this._volumeMenu.scroll(event);
this._volumeMenu.scroll(event.get_scroll_direction());
}
});

View File

@@ -46,8 +46,7 @@ const SwitcherPopup = new Lang.Class({
this._selectedIndex = 0;
this.actor = new Shell.GenericContainer({ style_class: 'switcher-popup',
reactive: true,
visible: false });
reactive: true });
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
this.actor.connect('allocate', Lang.bind(this, this._allocate));
@@ -638,6 +637,5 @@ function drawArrow(area, side) {
Clutter.cairo_set_source_color(cr, bodyColor);
cr.fill();
cr.$dispose();
}

View File

@@ -115,7 +115,7 @@ const UnlockDialog = new Lang.Class({
_init: function(parentActor) {
this.parent({ shellReactive: true,
styleClass: 'login-dialog',
keybindingMode: Shell.KeyBindingMode.UNLOCK_SCREEN,
keybindingMode: Main.KeybindingMode.UNLOCK_SCREEN,
parentActor: parentActor
});
@@ -198,23 +198,18 @@ const UnlockDialog = new Lang.Class({
x_align: St.Align.END,
y_align: St.Align.MIDDLE });
let screenSaverSettings = new Gio.Settings({ schema: 'org.gnome.desktop.screensaver' });
if (screenSaverSettings.get_boolean('user-switch-enabled')) {
let otherUserLabel = new St.Label({ text: _("Log in as another user"),
style_class: 'login-dialog-not-listed-label' });
this._otherUserButton = new St.Button({ style_class: 'login-dialog-not-listed-button',
can_focus: true,
child: otherUserLabel,
reactive: true,
x_align: St.Align.START,
x_fill: true });
this._otherUserButton.connect('clicked', Lang.bind(this, this._otherUserClicked));
this.dialogLayout.add(this._otherUserButton,
{ x_align: St.Align.START,
x_fill: false });
} else {
this._otherUserButton = null;
}
let otherUserLabel = new St.Label({ text: _("Log in as another user"),
style_class: 'login-dialog-not-listed-label' });
this._otherUserButton = new St.Button({ style_class: 'login-dialog-not-listed-button',
can_focus: true,
child: otherUserLabel,
reactive: true,
x_align: St.Align.START,
x_fill: true });
this._otherUserButton.connect('clicked', Lang.bind(this, this._otherUserClicked));
this.dialogLayout.add(this._otherUserButton,
{ x_align: St.Align.START,
x_fill: false });
this._updateSensitivity(true);
@@ -236,10 +231,8 @@ const UnlockDialog = new Lang.Class({
this._promptEntry.reactive = sensitive;
this._promptEntry.clutter_text.editable = sensitive;
this._updateOkButtonSensitivity(sensitive && this._promptEntry.text.length > 0);
if (this._otherUserButton) {
this._otherUserButton.reactive = sensitive;
this._otherUserButton.can_focus = sensitive;
}
this._otherUserButton.reactive = sensitive;
this._otherUserButton.can_focus = sensitive;
},
_updateOkButtonSensitivity: function(sensitive) {

View File

@@ -22,12 +22,12 @@ const Util = imports.misc.util;
const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown';
const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver';
const PRIVACY_SCHEMA = 'org.gnome.desktop.privacy'
const DISABLE_USER_SWITCH_KEY = 'disable-user-switching';
const DISABLE_LOCK_SCREEN_KEY = 'disable-lock-screen';
const DISABLE_LOG_OUT_KEY = 'disable-log-out';
const LOCK_ENABLED_KEY = 'lock-enabled';
const ALWAYS_SHOW_LOG_OUT_KEY = 'always-show-log-out';
const SHOW_FULL_NAME_IN_TOP_BAR_KEY = 'show-full-name-in-top-bar';
const SHOW_FULL_NAME_KEY = 'show-full-name';
const DIALOG_ICON_SIZE = 64;
@@ -477,7 +477,6 @@ const UserMenuButton = new Lang.Class({
this._screenSaverSettings = new Gio.Settings({ schema: SCREENSAVER_SCHEMA });
this._lockdownSettings = new Gio.Settings({ schema: LOCKDOWN_SCHEMA });
this._privacySettings = new Gio.Settings({ schema: PRIVACY_SCHEMA });
this._userManager = AccountsService.UserManager.get_default();
@@ -554,12 +553,10 @@ const UserMenuButton = new Lang.Class({
Lang.bind(this, this._updateLogout));
this._lockdownSettings.connect('changed::' + DISABLE_LOCK_SCREEN_KEY,
Lang.bind(this, this._updateLockScreen));
global.settings.connect('changed::' + ALWAYS_SHOW_LOG_OUT_KEY,
Lang.bind(this, this._updateLogout));
this._screenSaverSettings.connect('changed::' + SHOW_FULL_NAME_IN_TOP_BAR_KEY,
this._screenSaverSettings.connect('changed::' + SHOW_FULL_NAME_KEY,
Lang.bind(this, this._updateUserName));
this._privacySettings.connect('changed::' + SHOW_FULL_NAME_IN_TOP_BAR_KEY,
Lang.bind(this, this._updateUserName));
global.settings.connect('changed::' + SHOW_FULL_NAME_KEY,
Lang.bind(this, this._updateUserName));
this._updateSwitchUser();
this._updateLogout();
this._updateLockScreen();
@@ -584,7 +581,6 @@ const UserMenuButton = new Lang.Class({
Lang.bind(this, this._updateHaveShutdown));
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
Main.screenShield.connect('locked-changed', Lang.bind(this, this._updatePresenceIcon));
this._sessionUpdated();
},
@@ -597,7 +593,6 @@ const UserMenuButton = new Lang.Class({
this.setSensitive(!Main.sessionMode.isLocked);
this._updatePresenceIcon();
this._updateUserName();
},
_onDestroy: function() {
@@ -606,10 +601,10 @@ const UserMenuButton = new Lang.Class({
},
_updateUserName: function() {
let settings = this._privacySettings;
let settings = global.settings;
if (Main.sessionMode.isLocked)
settings = this._screenSaverSettings;
if (this._user.is_loaded && settings.get_boolean(SHOW_FULL_NAME_IN_TOP_BAR_KEY))
if (this._user.is_loaded && settings.get_boolean(SHOW_FULL_NAME_KEY))
this._name.set_text(this._user.get_real_name());
else
this._name.set_text("");
@@ -704,11 +699,6 @@ const UserMenuButton = new Lang.Class({
this._iconBox.child = this._idleIcon;
else
this._iconBox.child = this._offlineIcon;
if (Main.sessionMode.isLocked)
this._iconBox.visible = Main.screenShield.locked;
else
this._iconBox.visible = true;
},
_setupAccounts: function() {
@@ -829,7 +819,7 @@ const UserMenuButton = new Lang.Class({
_onMyAccountActivate: function() {
Main.overview.hide();
let app = Shell.AppSystem.get_default().lookup_app('gnome-user-accounts-panel.desktop');
let app = Shell.AppSystem.get_default().lookup_setting('gnome-user-accounts-panel.desktop');
app.activate();
},
@@ -871,8 +861,18 @@ const UserMenuButton = new Lang.Class({
this._suspendOrPowerOffItem.state == PopupMenu.PopupAlternatingMenuItemState.DEFAULT) {
this._session.ShutdownRemote();
} else {
this.menu.close(BoxPointer.PopupAnimation.NONE);
this._loginManager.suspend();
if (this._screenSaverSettings.get_boolean(LOCK_ENABLED_KEY)) {
let tmpId = Main.screenShield.connect('lock-screen-shown', Lang.bind(this, function() {
Main.screenShield.disconnect(tmpId);
this._loginManager.suspend();
}));
this.menu.close(BoxPointer.PopupAnimation.NONE);
Main.screenShield.lock(true);
} else {
this._loginManager.suspend();
}
}
}
});

View File

@@ -12,7 +12,6 @@ const St = imports.gi.St;
const AppDisplay = imports.ui.appDisplay;
const Main = imports.ui.main;
const Params = imports.misc.params;
const RemoteSearch = imports.ui.remoteSearch;
const Search = imports.ui.search;
const SearchDisplay = imports.ui.searchDisplay;
@@ -40,11 +39,17 @@ const ViewSelector = new Lang.Class({
Name: 'ViewSelector',
_init : function(searchEntry, showAppsButton) {
this.actor = new Shell.Stack({ name: 'viewSelector' });
this.actor = new St.BoxLayout({ name: 'viewSelector',
vertical: true });
this._showAppsButton = showAppsButton;
this._showAppsButton.connect('notify::checked', Lang.bind(this, this._onShowAppsButtonToggled));
this._pageArea = new Shell.Stack();
this.actor.add(this._pageArea, { x_fill: true,
y_fill: true,
expand: true });
this._activePage = null;
this._searchActive = false;
@@ -77,17 +82,16 @@ const ViewSelector = new Lang.Class({
this._capturedEventId = 0;
this._workspacesDisplay = new WorkspacesView.WorkspacesDisplay();
this._workspacesPage = this._addPage(this._workspacesDisplay.actor,
this._workspacesPage = this._addPage(this._workspacesDisplay.actor, null,
_("Windows"), 'emblem-documents-symbolic');
this._appDisplay = new AppDisplay.AllAppDisplay();
this._appsPage = this._addPage(this._appDisplay.actor,
this._appsPage = this._addPage(this._appDisplay.actor, null,
_("Applications"), 'view-grid-symbolic');
this._searchResults = new SearchDisplay.SearchResults(this._searchSystem);
this._searchPage = this._addPage(this._searchResults.actor,
_("Search"), 'edit-find-symbolic',
{ a11yFocus: this._entry });
this._searchPage = this._addPage(this._searchResults.actor, this._entry,
_("Search"), 'edit-find-symbolic');
this._searchSettings = new Gio.Settings({ schema: Search.SEARCH_PROVIDERS_SCHEMA });
this._searchSettings.connect('changed::disabled', Lang.bind(this, this._reloadRemoteProviders));
@@ -114,9 +118,6 @@ const ViewSelector = new Lang.Class({
global.focus_manager.add_group(this._searchResults.actor);
Main.overview.connect('item-drag-begin',
Lang.bind(this, this._resetShowAppsButton));
this._stageKeyPressId = 0;
Main.overview.connect('showing', Lang.bind(this,
function () {
@@ -136,8 +137,8 @@ const ViewSelector = new Lang.Class({
Main.wm.addKeybinding('toggle-application-view',
new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }),
Meta.KeyBindingFlags.NONE,
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Main.KeybindingMode.NORMAL |
Main.KeybindingMode.OVERVIEW,
Lang.bind(this, this._toggleAppsPage));
},
@@ -170,16 +171,14 @@ const ViewSelector = new Lang.Class({
this._workspacesDisplay.hide();
},
_addPage: function(actor, name, a11yIcon, params) {
params = Params.parse(params, { a11yFocus: null });
_addPage: function(actor, a11yFocus, name, a11yIcon) {
let page = new St.Bin({ child: actor,
x_align: St.Align.START,
y_align: St.Align.START,
x_fill: true,
y_fill: true });
if (params.a11yFocus)
Main.ctrlAltTabManager.addGroup(params.a11yFocus, name, a11yIcon);
if (a11yFocus)
Main.ctrlAltTabManager.addGroup(a11yFocus, name, a11yIcon);
else
Main.ctrlAltTabManager.addGroup(actor, name, a11yIcon,
{ proxy: this.actor,
@@ -188,7 +187,7 @@ const ViewSelector = new Lang.Class({
this._a11yFocusPage(page);
})
});;
this.actor.add_actor(page);
this._pageArea.add_actor(page);
return page;
},
@@ -209,11 +208,16 @@ const ViewSelector = new Lang.Class({
});
}
this.emit('before-page-change');
page.show();
Tweener.addTween(page,
{ opacity: 255,
time: 0.1,
transition: 'easeOutQuad'
transition: 'easeOutQuad',
onComplete: Lang.bind(this,
function() {
this.emit('after-page-change');
})
});
},
@@ -332,33 +336,37 @@ const ViewSelector = new Lang.Class({
this._searchActive = this._entry.get_text() != '';
let startSearch = this._searchActive && !searchPreviouslyActive;
if (startSearch)
if (startSearch) {
this._searchResults.startingSearch();
}
if (this._searchActive) {
this._entry.set_secondary_icon(this._activeIcon);
if (this._iconClickedId == 0)
if (this._iconClickedId == 0) {
this._iconClickedId = this._entry.connect('secondary-icon-clicked',
Lang.bind(this, this.reset));
if (this._searchTimeoutId == 0)
this._searchTimeoutId = Mainloop.timeout_add(150,
Lang.bind(this, this._doSearch));
} else {
if (this._iconClickedId > 0) {
this._entry.disconnect(this._iconClickedId);
this._iconClickedId = 0;
Lang.bind(this, function() {
this.reset();
}));
}
} else {
if (this._iconClickedId > 0)
this._entry.disconnect(this._iconClickedId);
this._iconClickedId = 0;
this._entry.set_secondary_icon(this._inactiveIcon);
this._searchCancelled();
if (this._searchTimeoutId > 0) {
Mainloop.source_remove(this._searchTimeoutId);
this._searchTimeoutId = 0;
}
this._entry.set_secondary_icon(this._inactiveIcon);
this._searchCancelled();
return;
}
if (this._searchTimeoutId > 0)
return;
this._searchTimeoutId = Mainloop.timeout_add(150, Lang.bind(this, this._doSearch));
},
_onKeyPress: function(entry, event) {
@@ -368,6 +376,15 @@ const ViewSelector = new Lang.Class({
this.reset();
return true;
}
} else if (symbol == Clutter.Return || symbol == Clutter.KP_Enter) {
// We can't connect to 'activate' here because search providers
// might want to do something with the modifiers in activateDefault.
if (this._searchTimeoutId > 0) {
Mainloop.source_remove(this._searchTimeoutId);
this._doSearch();
}
this._searchResults.activateDefault();
return true;
} else if (this._searchActive) {
let arrowNext, nextDirection;
if (entry.get_text_direction() == Clutter.TextDirection.RTL) {
@@ -392,15 +409,6 @@ const ViewSelector = new Lang.Class({
} else if (symbol == arrowNext && this._text.position == -1) {
this._searchResults.navigateFocus(nextDirection);
return true;
} else if (symbol == Clutter.Return || symbol == Clutter.KP_Enter) {
// We can't connect to 'activate' here because search providers
// might want to do something with the modifiers in activateDefault.
if (this._searchTimeoutId > 0) {
Mainloop.source_remove(this._searchTimeoutId);
this._doSearch();
}
this._searchResults.activateDefault();
return true;
}
}
return false;
@@ -451,6 +459,10 @@ const ViewSelector = new Lang.Class({
RemoteSearch.loadRemoteSearchProviders(Lang.bind(this, this.addSearchProvider));
},
getAppsActive: function() {
return this._showAppsButton.checked;
},
addSearchProvider: function(provider) {
if (!this._shouldUseSearchProvider(provider))
return;
@@ -462,6 +474,10 @@ const ViewSelector = new Lang.Class({
removeSearchProvider: function(provider) {
this._searchSystem.unregisterProvider(provider);
this._searchResults.destroyProviderMeta(provider);
},
getSearchActive: function() {
return this._searchActive;
}
});
Signals.addSignalMethods(ViewSelector.prototype);

View File

@@ -105,74 +105,74 @@ const WindowManager = new Lang.Class({
this._workspaceSwitcherPopup = null;
this.setCustomKeybindingHandler('switch-to-workspace-left',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Main.KeybindingMode.NORMAL |
Main.KeybindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('switch-to-workspace-right',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Main.KeybindingMode.NORMAL |
Main.KeybindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('switch-to-workspace-up',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Main.KeybindingMode.NORMAL |
Main.KeybindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('switch-to-workspace-down',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Main.KeybindingMode.NORMAL |
Main.KeybindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('move-to-workspace-left',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Main.KeybindingMode.NORMAL |
Main.KeybindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('move-to-workspace-right',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Main.KeybindingMode.NORMAL |
Main.KeybindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('move-to-workspace-up',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Main.KeybindingMode.NORMAL |
Main.KeybindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('move-to-workspace-down',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Main.KeybindingMode.NORMAL |
Main.KeybindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('switch-applications',
Shell.KeyBindingMode.NORMAL,
Main.KeybindingMode.NORMAL,
Lang.bind(this, this._startAppSwitcher));
this.setCustomKeybindingHandler('switch-group',
Shell.KeyBindingMode.NORMAL,
Main.KeybindingMode.NORMAL,
Lang.bind(this, this._startAppSwitcher));
this.setCustomKeybindingHandler('switch-applications-backward',
Shell.KeyBindingMode.NORMAL,
Main.KeybindingMode.NORMAL,
Lang.bind(this, this._startAppSwitcher));
this.setCustomKeybindingHandler('switch-group-backward',
Shell.KeyBindingMode.NORMAL,
Main.KeybindingMode.NORMAL,
Lang.bind(this, this._startAppSwitcher));
this.setCustomKeybindingHandler('switch-windows',
Shell.KeyBindingMode.NORMAL,
Main.KeybindingMode.NORMAL,
Lang.bind(this, this._startWindowSwitcher));
this.setCustomKeybindingHandler('switch-windows-backward',
Shell.KeyBindingMode.NORMAL,
Main.KeybindingMode.NORMAL,
Lang.bind(this, this._startWindowSwitcher));
this.setCustomKeybindingHandler('switch-panels',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW |
Shell.KeyBindingMode.LOCK_SCREEN |
Shell.KeyBindingMode.UNLOCK_SCREEN |
Shell.KeyBindingMode.LOGIN_SCREEN,
Main.KeybindingMode.NORMAL |
Main.KeybindingMode.OVERVIEW |
Main.KeybindingMode.LOCK_SCREEN |
Main.KeybindingMode.UNLOCK_SCREEN |
Main.KeybindingMode.LOGIN_SCREEN,
Lang.bind(this, this._startA11ySwitcher));
this.setCustomKeybindingHandler('switch-panels-backward',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW |
Shell.KeyBindingMode.LOCK_SCREEN |
Shell.KeyBindingMode.UNLOCK_SCREEN |
Shell.KeyBindingMode.LOGIN_SCREEN,
Main.KeybindingMode.NORMAL |
Main.KeybindingMode.OVERVIEW |
Main.KeybindingMode.LOCK_SCREEN |
Main.KeybindingMode.UNLOCK_SCREEN |
Main.KeybindingMode.LOGIN_SCREEN,
Lang.bind(this, this._startA11ySwitcher));
this.addKeybinding('open-application-menu',
new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }),
Meta.KeyBindingFlags.NONE,
Shell.KeyBindingMode.NORMAL,
Main.KeybindingMode.NORMAL,
Lang.bind(this, this._openAppMenu));
Main.overview.connect('showing', Lang.bind(this, function() {
@@ -191,15 +191,13 @@ const WindowManager = new Lang.Class({
},
addKeybinding: function(name, settings, flags, modes, handler) {
let action = global.display.add_keybinding(name, settings, flags, handler);
if (action != Meta.KeyBindingAction.NONE)
if (global.display.add_keybinding(name, settings, flags, handler))
this.allowKeybinding(name, modes);
return action;
},
removeKeybinding: function(name) {
if (global.display.remove_keybinding(name))
this.allowKeybinding(name, Shell.KeyBindingMode.NONE);
this.allowKeybinding(name, Main.KeybindingMode.NONE);
},
allowKeybinding: function(name, modes) {
@@ -221,9 +219,7 @@ const WindowManager = new Lang.Class({
_shouldAnimateActor: function(actor) {
if (!this._shouldAnimate())
return false;
let windowType = actor.meta_window.get_window_type();
return windowType == Meta.WindowType.NORMAL ||
windowType == Meta.WindowType.MODAL_DIALOG;
return actor.meta_window.get_window_type() == Meta.WindowType.NORMAL;
},
_removeEffect : function(list, actor) {
@@ -242,32 +238,23 @@ const WindowManager = new Lang.Class({
}
actor.set_scale(1.0, 1.0);
actor.move_anchor_point_from_gravity(Clutter.Gravity.CENTER);
/* scale window down to 0x0.
* maybe TODO: get icon geometry passed through and move the window towards it?
*/
this._minimizing.push(actor);
let xDest, yDest, xScale, yScale;
let [success, geom] = actor.meta_window.get_icon_geometry();
if (success) {
xDest = geom.x;
yDest = geom.y;
xScale = geom.width / actor.width;
yScale = geom.height / actor.height;
} else {
/* scale window down to 0x0. */
let monitor = Main.layoutManager.findMonitorForWindow(actor.meta_window);
xDest = monitor.x;
yDest = monitor.y;
xScale = 0.0;
yScale = 0.0;
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
xDest += monitor.width;
}
let primary = Main.layoutManager.primaryMonitor;
let xDest = primary.x;
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
xDest += primary.width;
Tweener.addTween(actor,
{ scale_x: xScale,
scale_y: yScale,
{ scale_x: 0.0,
scale_y: 0.0,
x: xDest,
y: yDest,
y: 0,
time: WINDOW_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: this._minimizeWindowDone,
@@ -382,56 +369,57 @@ const WindowManager = new Lang.Class({
actor._windowType = type;
}));
if (actor.meta_window.is_attached_dialog()) {
this._checkDimming(actor.get_meta_window().get_transient_for());
if (this._shouldAnimate()) {
actor.set_scale(1.0, 0.0);
actor.scale_gravity = Clutter.Gravity.CENTER;
actor.show();
this._mapping.push(actor);
Tweener.addTween(actor,
{ scale_y: 1,
time: WINDOW_ANIMATION_TIME,
transition: "easeOutQuad",
onComplete: this._mapWindowDone,
onCompleteScope: this,
onCompleteParams: [shellwm, actor],
onOverwrite: this._mapWindowOverwrite,
onOverwriteScope: this,
onOverwriteParams: [shellwm, actor]
});
return;
}
shellwm.completed_map(actor);
return;
}
if (!this._shouldAnimateActor(actor)) {
shellwm.completed_map(actor);
return;
}
if (actor.meta_window.is_attached_dialog()) {
/* Scale the window from the center of the parent */
this._checkDimming(actor.get_meta_window().get_transient_for());
actor.set_scale(1.0, 0.0);
actor.scale_gravity = Clutter.Gravity.CENTER;
actor.show();
this._mapping.push(actor);
actor.opacity = 0;
actor.show();
Tweener.addTween(actor,
{ scale_y: 1,
time: WINDOW_ANIMATION_TIME,
transition: "easeOutQuad",
onComplete: this._mapWindowDone,
onCompleteScope: this,
onCompleteParams: [shellwm, actor],
onOverwrite: this._mapWindowOverwrite,
onOverwriteScope: this,
onOverwriteParams: [shellwm, actor]
});
} else {
/* Fade window in */
actor.opacity = 0;
actor.show();
this._mapping.push(actor);
Tweener.addTween(actor,
{ opacity: 255,
time: WINDOW_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: this._mapWindowDone,
onCompleteScope: this,
onCompleteParams: [shellwm, actor],
onOverwrite: this._mapWindowOverwrite,
onOverwriteScope: this,
onOverwriteParams: [shellwm, actor]
});
}
/* Fade window in */
this._mapping.push(actor);
Tweener.addTween(actor,
{ opacity: 255,
time: WINDOW_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: this._mapWindowDone,
onCompleteScope: this,
onCompleteParams: [shellwm, actor],
onOverwrite: this._mapWindowOverwrite,
onOverwriteScope: this,
onOverwriteParams: [shellwm, actor]
});
},
_mapWindowDone : function(shellwm, actor) {
if (this._removeEffect(this._mapping, actor)) {
Tweener.removeTweens(actor);
actor.opacity = 255;
actor.scale_y = 1;
shellwm.completed_map(actor);
}
},
@@ -453,21 +441,18 @@ const WindowManager = new Lang.Class({
return win != window;
});
}
if (!this._shouldAnimateActor(actor)) {
shellwm.completed_destroy(actor);
return;
}
this._destroying.push(actor);
if (window.is_attached_dialog()) {
let parent = window.get_transient_for();
this._checkDimming(parent, window);
if (!this._shouldAnimate()) {
shellwm.completed_destroy(actor);
return;
}
actor.set_scale(1.0, 1.0);
actor.scale_gravity = Clutter.Gravity.CENTER;
actor.show();
this._destroying.push(actor);
actor._parentDestroyId = parent.connect('unmanaged', Lang.bind(this, function () {
Tweener.removeTweens(actor);
@@ -502,14 +487,14 @@ const WindowManager = new Lang.Class({
},
_filterKeybinding: function(shellwm, binding) {
if (Main.keybindingMode == Shell.KeyBindingMode.NONE)
if (Main.keybindingMode == Main.KeybindingMode.NONE)
return true;
// There's little sense in implementing a keybinding in mutter and
// not having it work in NORMAL mode; handle this case generically
// so we don't have to explicitly allow all builtin keybindings in
// NORMAL mode.
if (Main.keybindingMode == Shell.KeyBindingMode.NORMAL &&
if (Main.keybindingMode == Main.KeybindingMode.NORMAL &&
binding.is_builtin())
return false;

View File

@@ -1,6 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
@@ -29,6 +30,9 @@ const CLOSE_BUTTON_FADE_TIME = 0.1;
const DRAGGING_WINDOW_OPACITY = 100;
const BUTTON_LAYOUT_SCHEMA = 'org.gnome.shell.overrides';
const BUTTON_LAYOUT_KEY = 'button-layout';
// When calculating a layout, we calculate the scale of windows and the percent
// of the available area the new layout uses. If the values for the new layout,
// when weighted with the values as below, are worse than the previous layout's,
@@ -120,8 +124,6 @@ const WindowClone = new Lang.Class({
this.actor._delegate = this;
this._slot = [0, 0, 0, 0];
this._dragSlot = [0, 0, 0, 0];
this._stackAbove = null;
this._sizeChangedId = this.realWindow.connect('size-changed',
@@ -157,15 +159,22 @@ const WindowClone = new Lang.Class({
this._selected = false;
},
set slot(slot) {
this._slot = slot;
},
get slot() {
if (this.inDrag)
return this._dragSlot;
else
return this._slot;
let x, y, w, h;
if (this.inDrag) {
x = this.dragOrigX;
y = this.dragOrigY;
w = this.actor.width * this.dragOrigScale;
h = this.actor.height * this.dragOrigScale;
} else {
x = this.actor.x;
y = this.actor.y;
w = this.actor.width * this.actor.scale_x;
h = this.actor.height * this.actor.scale_y;
}
return [x, y, w, h];
},
setStackAbove: function (actor) {
@@ -256,37 +265,23 @@ const WindowClone = new Lang.Class({
_onScroll : function (actor, event) {
let direction = event.get_scroll_direction();
let delta;
if (event.is_pointer_emulated())
return;
if (direction == Clutter.ScrollDirection.DOWN) {
delta = -SCROLL_SCALE_AMOUNT;
} else if (direction == Clutter.ScrollDirection.UP) {
delta = +SCROLL_SCALE_AMOUNT;
} else if (direction == Clutter.ScrollDirection.SMOOTH) {
let [dx, dy] = event.get_scroll_delta();
delta = -dy * 10;
}
if (delta > 0) {
if (direction == Clutter.ScrollDirection.UP) {
if (this._zoomStep == undefined)
this._zoomStart();
if (this._zoomStep < 100) {
this._zoomStep += delta;
this._zoomStep = Math.min(100, this._zoomStep);
this._zoomStep += SCROLL_SCALE_AMOUNT;
this._zoomUpdate();
}
} else if (delta < 0) {
} else if (direction == Clutter.ScrollDirection.DOWN) {
if (this._zoomStep > 0) {
this._zoomStep += delta;
this._zoomStep -= SCROLL_SCALE_AMOUNT;
this._zoomStep = Math.max(0, this._zoomStep);
this._zoomUpdate();
}
if (this._zoomStep <= 0.0)
this._zoomEnd();
}
},
_zoomUpdate : function () {
@@ -397,7 +392,6 @@ const WindowClone = new Lang.Class({
if (this._zooming)
this._zoomEnd();
this._dragSlot = this._slot;
[this.dragOrigX, this.dragOrigY] = this.actor.get_position();
this.dragOrigScale = this.actor.scale_x;
this.inDrag = true;
@@ -462,7 +456,11 @@ const WindowOverlay = new Lang.Class({
this._updateCaptionId = metaWindow.connect('notify::title',
Lang.bind(this, function(w) {
this.title.text = w.title;
this.relayout(false);
// we need this for the next call to get_preferred_width
// to return useful results
this.title.set_size(-1, -1);
this._repositionSelf();
}));
let button = new St.Button({ style_class: 'window-close' });
@@ -537,14 +535,35 @@ const WindowOverlay = new Lang.Class({
return [this.borderSize, this.borderSize];
},
relayout: function(animate) {
_repositionSelf: function() {
let [cloneX, cloneY, cloneWidth, cloneHeight] = this._windowClone.slot;
this.updatePositions(cloneX, cloneY, cloneWidth, cloneHeight, false);
},
/**
* @cloneX: x position of windowClone
* @cloneY: y position of windowClone
* @cloneWidth: width of windowClone
* @cloneHeight height of windowClone
*/
// These parameters are not the values retrieved with
// get_transformed_position() and get_transformed_size(),
// as windowClone might be moving.
// See Workspace._showWindowOverlay
updatePositions: function(cloneX, cloneY, cloneWidth, cloneHeight, animate) {
let button = this.closeButton;
let title = this.title;
let layout = Meta.prefs_get_button_layout();
let side = layout.left_buttons.indexOf(Meta.ButtonFunction.CLOSE) > -1 ? St.Side.LEFT : St.Side.RIGHT;
let settings = new Gio.Settings({ schema: BUTTON_LAYOUT_SCHEMA });
let layout = settings.get_string(BUTTON_LAYOUT_KEY);
let rtl = Clutter.get_default_text_direction() == Clutter.TextDirection.RTL;
let split = layout.split(":");
let side;
if (split[0].indexOf("close") > -1)
side = rtl ? St.Side.RIGHT : St.Side.LEFT;
else
side = rtl ? St.Side.LEFT : St.Side.RIGHT;
let buttonX;
let buttonY = cloneY - (button.height - button._overlap);
@@ -558,16 +577,8 @@ const WindowOverlay = new Lang.Class({
else
button.set_position(Math.floor(buttonX), Math.floor(buttonY));
// Clutter.Actor.get_preferred_width() will return the fixed width if one
// is set, so we need to reset the width by calling set_width(-1), to forward
// the call down to StLabel.
// We also need to save and restore the current width, otherwise the animation
// starts from the wrong point.
let prevTitleWidth = title.width;
title.set_width(-1);
let [titleMinWidth, titleNatWidth] = title.get_preferred_width(-1);
let titleWidth = Math.max(titleMinWidth, Math.min(titleNatWidth, cloneWidth));
title.width = prevTitleWidth;
let titleX = cloneX + (cloneWidth - titleWidth) / 2;
let titleY = cloneY + cloneHeight + title._spacing;
@@ -746,10 +757,11 @@ const LayoutStrategy = new Lang.Class({
Name: 'LayoutStrategy',
Abstract: true,
_init: function(monitor, rowSpacing, columnSpacing) {
_init: function(monitor, rowSpacing, columnSpacing, bottomPadding) {
this._monitor = monitor;
this._rowSpacing = rowSpacing;
this._columnSpacing = columnSpacing;
this._bottomPadding = bottomPadding;
},
_newRow: function() {
@@ -774,23 +786,24 @@ const LayoutStrategy = new Lang.Class({
windows: [] };
},
// Computes and returns a fancy scale for @window using the
// Compute the size and fancy scale for @window using the
// base scale, @scale.
_computeWindowScale: function(window, scale) {
// Since we align windows next to each other, the height of the
// thumbnails is much more important to preserve than the width of
// them, so two windows with equal height, but maybe differering
// widths line up.
let ratio = window.actor.height / this._monitor.height;
//
// Returns a list structure: [ scaledWidth, scaledHeight, fancyScale ]
// where scaledWidth and scaledHeight are the window's
// width and height, scaled by fancyScale for convenience.
_computeWindowSizeAndScale: function(window, scale) {
let width = window.actor.width;
let height = window.actor.height;
let ratio;
// The purpose of this manipulation here is to prevent windows
// from getting too small. For something like a calculator window,
// we need to bump up the size just a bit to make sure it looks
// good. We'll use a multiplier of 1.5 for this.
if (width > height)
ratio = width / this._monitor.width;
else
ratio = height / this._monitor.height;
// Map from [0, 1] to [1.5, 1]
let fancyScale = _interpolate(1.5, 1, ratio) * scale;
return fancyScale;
let fancyScale = (2 / (1 + ratio)) * scale;
return [width * fancyScale, height * fancyScale, fancyScale];
},
// Compute the size of each row, by assigning to the properties
@@ -828,7 +841,7 @@ const LayoutStrategy = new Lang.Class({
let area = layout.area;
let hspacing = (layout.maxColumns - 1) * this._columnSpacing;
let vspacing = (layout.numRows - 1) * this._rowSpacing;
let vspacing = (layout.numRows - 1) * this._rowSpacing + this._bottomPadding;
let spacedWidth = area.width - hspacing;
let spacedHeight = area.height - vspacing;
@@ -862,7 +875,7 @@ const LayoutStrategy = new Lang.Class({
y += row.height + this._rowSpacing;
}
let height = y - this._rowSpacing;
let height = y - this._rowSpacing + this._bottomPadding;
let baseY = (area.height - height) / 2;
for (let i = 0; i < rows.length; i++) {
@@ -872,10 +885,7 @@ const LayoutStrategy = new Lang.Class({
for (let j = 0; j < row.windows.length; j++) {
let window = row.windows[j];
let s = this._computeWindowScale(window, scale);
s = Math.min(s, WINDOW_CLONE_MAXIMUM_SCALE);
let width = window.actor.width * s;
let height = window.actor.height * s;
let [width, height, s] = this._computeWindowSizeAndScale(window, scale);
let y = row.y + row.height - height;
let x = baseX;
@@ -936,9 +946,7 @@ const UnalignedLayoutStrategy = new Lang.Class({
for (; windowIdx < windows.length; windowIdx++) {
let window = windows[windowIdx];
let s = this._computeWindowScale(window, 1);
let width = window.actor.width * s;
let height = window.actor.height * s;
let [width, height] = this._computeWindowSizeAndScale(window, 1);
row.fullHeight = Math.max(row.fullHeight, height);
// either new width is < idealWidth or new width is nearer from idealWidth then oldWidth
@@ -1004,9 +1012,9 @@ const GridLayoutStrategy = new Lang.Class({
let window = windows[windowIdx];
row.windows.push(window);
let s = this._computeWindowScale(window, 1);
maxWindowWidth = Math.max(maxWindowWidth, window.actor.width * s);
maxWindowHeight = Math.max(maxWindowHeight, window.actor.height * s);
let [width, height] = this._computeWindowSizeAndScale(window, 1);
maxWindowWidth = Math.max(maxWindowWidth, width);
maxWindowHeight = Math.max(maxWindowHeight, height);
}
}
@@ -1041,8 +1049,6 @@ const Workspace = new Lang.Class({
this._windowOverlaysGroup.set_size(0, 0);
this.actor = new St.Widget({ style_class: 'window-picker' });
if (monitorIndex != Main.layoutManager.primaryIndex)
this.actor.add_style_class_name('external-monitor');
this.actor.set_size(0, 0);
this._dropRect = new Clutter.Rectangle({ opacity: 0 });
@@ -1178,8 +1184,8 @@ const Workspace = new Lang.Class({
let slot = slots[i];
let clone = clones[i];
let metaWindow = clone.metaWindow;
let overlay = clone.overlay;
clone.slotId = i;
let mainIndex = this._lookupIndex(metaWindow);
let overlay = this._windowOverlays[mainIndex];
// Positioning a window currently being dragged must be avoided;
// we'll just leave a blank spot in the layout for it.
@@ -1187,7 +1193,6 @@ const Workspace = new Lang.Class({
continue;
let [x, y, scale] = slot;
clone.slot = [x, y, clone.actor.width * scale, clone.actor.height * scale];
if (overlay && initialPositioning)
overlay.hide();
@@ -1207,7 +1212,7 @@ const Workspace = new Lang.Class({
// Make the window slightly transparent to indicate it's hidden
Tweener.addTween(clone.actor,
{ opacity: 128,
{ opacity: 255,
time: Overview.ANIMATION_TIME,
transition: 'easeInQuad'
});
@@ -1219,7 +1224,7 @@ const Workspace = new Lang.Class({
Tweener.removeTweens(clone.actor);
clone.actor.set_position(x, y);
clone.actor.set_scale(scale, scale);
clone.overlay.relayout(false);
this._updateWindowOverlayPositions(clone, overlay, x, y, scale, false);
this._showWindowOverlay(clone, overlay, isOnCurrentWorkspace);
}
}
@@ -1254,7 +1259,15 @@ const Workspace = new Lang.Class({
})
});
clone.overlay.relayout(true);
this._updateWindowOverlayPositions(clone, overlay, x, y, scale, true);
},
_updateWindowOverlayPositions: function(clone, overlay, x, y, scale, animate) {
if (!overlay)
return;
let [cloneWidth, cloneHeight] = clone.actor.get_size();
overlay.updatePositions(x, y, cloneWidth * scale, cloneHeight * scale, animate);
},
_showWindowOverlay: function(clone, overlay, fade) {
@@ -1389,10 +1402,9 @@ const Workspace = new Lang.Class({
let scale = win._overviewHint.scale;
delete win._overviewHint;
clone.slot = [x, y, clone.actor.width * scale, clone.actor.height * scale];
clone.actor.set_position (x, y);
clone.actor.set_scale (scale, scale);
clone.overlay.relayout(false);
this._updateWindowOverlayPositions(clone, overlay, x, y, scale, false);
} else {
// Position new windows at the top corner of the workspace rather
// than where they were placed for real to avoid the window
@@ -1557,7 +1569,6 @@ const Workspace = new Lang.Class({
_addWindowClone : function(win) {
let clone = new WindowClone(win, this);
let overlay = new WindowOverlay(clone, this._windowOverlaysGroup);
clone.overlay = overlay;
clone.connect('selected',
Lang.bind(this, this._onCloneSelected));
@@ -1629,7 +1640,7 @@ const Workspace = new Lang.Class({
}
},
_computeLayout: function(windows, area, rowSpacing, columnSpacing) {
_computeLayout: function(windows, area, rowSpacing, columnSpacing, bottomPadding) {
// We look for the largest scale that allows us to fit the
// largest row/tallest column on the workspace.
@@ -1645,7 +1656,7 @@ const Workspace = new Lang.Class({
break;
let strategyClass = numRows > 2 ? GridLayoutStrategy : UnalignedLayoutStrategy;
let strategy = new strategyClass(this._monitor, rowSpacing, columnSpacing);
let strategy = new strategyClass(this._monitor, rowSpacing, columnSpacing, bottomPadding);
let layout = { area: area, strategy: strategy, numRows: numRows, numColumns: numColumns };
strategy.computeLayout(windows, layout);
@@ -1677,12 +1688,6 @@ const Workspace = new Lang.Class({
// Window grid spacing
let columnSpacing = node.get_length('-horizontal-spacing');
let rowSpacing = node.get_length('-vertical-spacing');
let padding = {
left: node.get_padding(St.Side.LEFT),
top: node.get_padding(St.Side.TOP),
bottom: node.get_padding(St.Side.BOTTOM),
right: node.get_padding(St.Side.RIGHT),
};
if (!totalWindows)
return [];
@@ -1697,25 +1702,19 @@ const Workspace = new Lang.Class({
[leftBorder, rightBorder] = overlay.chromeWidths();
} else {
[closeButtonHeight, captionHeight] = [0, 0];
[leftBorder, rightBorder] = [0, 0];
}
rowSpacing += captionHeight;
columnSpacing += (rightBorder + leftBorder) / 2;
padding.top += closeButtonHeight;
padding.bottom += captionHeight;
padding.left += leftBorder;
padding.right += rightBorder;
columnSpacing += rightBorder;
let area = {
x: this._x + padding.left,
y: this._y + padding.top,
width: this._width - padding.left - padding.right,
height: this._height - padding.top - padding.bottom,
};
let area = { x: this._x, y: this._y, width: this._width, height: this._height };
area.y += closeButtonHeight;
area.height -= closeButtonHeight;
area.x += leftBorder;
area.width -= leftBorder;
if (!this._currentLayout)
this._currentLayout = this._computeLayout(windows, area, rowSpacing, columnSpacing);
this._currentLayout = this._computeLayout(windows, area, rowSpacing, columnSpacing, captionHeight);
let layout = this._currentLayout;
let strategy = layout.strategy;

View File

@@ -496,6 +496,8 @@ const ThumbnailsBox = new Lang.Class({
_init: function() {
this.actor = new Shell.GenericContainer({ reactive: true,
style_class: 'workspace-thumbnails',
can_focus: true,
track_hover: true,
request_mode: Clutter.RequestMode.WIDTH_FOR_HEIGHT });
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
@@ -524,6 +526,7 @@ const ThumbnailsBox = new Lang.Class({
this._indicator = indicator;
this.actor.add_actor(indicator);
this._inDrag = false;
this._dropWorkspace = -1;
this._dropPlaceholderPos = -1;
this._dropPlaceholder = new St.Bin({ style_class: 'placeholder' });
@@ -544,7 +547,13 @@ const ThumbnailsBox = new Lang.Class({
this.actor.connect('button-press-event', function() { return true; });
this.actor.connect('button-release-event', Lang.bind(this, this._onButtonRelease));
this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
this.actor.connect('scroll-event',
Lang.bind(this, this._onScrollEvent));
this.actor.connect('key-release-event',
Lang.bind(this, this._onKeyRelease));
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._updateTranslation));
this.actor.connect('notify::hover', Lang.bind(this, this._updateTranslation));
Main.overview.connect('showing',
Lang.bind(this, this._createThumbnails));
@@ -569,6 +578,40 @@ const ThumbnailsBox = new Lang.Class({
Lang.bind(this, this._updateSwitcherVisibility));
},
_getInitialTranslation: function() {
// Always show the pager when hover, during a drag, or if workspaces are
// actually used, e.g. there are windows on more than one
let alwaysZoomOut = this.actor.hover || this._inDrag || global.screen.n_workspaces > 2;
if (!alwaysZoomOut) {
let monitors = Main.layoutManager.monitors;
let primary = Main.layoutManager.primaryMonitor;
/* Look for any monitor to the right of the primary, if there is
* one, we always keep zoom out, otherwise its hard to reach
* the thumbnail area without passing into the next monitor. */
for (let i = 0; i < monitors.length; i++) {
if (monitors[i].x >= primary.x + primary.width) {
alwaysZoomOut = true;
break;
}
}
}
if (alwaysZoomOut)
return 0;
let visibleWidth = this.actor.get_theme_node().get_length('visible-width');
let rtl = (this.actor.get_text_direction() == Clutter.TextDirection.RTL);
return rtl ? (visibleWidth - this.actor.width) : (this.actor.width - visibleWidth);
},
_updateTranslation: function() {
Tweener.addTween(this, { slideX: this._getInitialTranslation(),
time: SLIDE_ANIMATION_TIME,
transition: 'easeOutQuad' });
},
_updateSwitcherVisibility: function() {
this.actor.visible =
this._settings.get_boolean('dynamic-workspaces') ||
@@ -592,6 +635,9 @@ const ThumbnailsBox = new Lang.Class({
},
_onDragBegin: function() {
this._inDrag = true;
this._updateTranslation();
this._dragCancelled = false;
this._dragMonitor = {
dragMotion: Lang.bind(this, this._onDragMotion)
@@ -614,6 +660,8 @@ const ThumbnailsBox = new Lang.Class({
_endDrag: function() {
this._clearDragPlaceholder();
DND.removeDragMonitor(this._dragMonitor);
this._inDrag = false;
this._updateTranslation();
},
_onDragMotion: function(dragEvent) {
@@ -746,7 +794,7 @@ const ThumbnailsBox = new Lang.Class({
global.screen.connect('notify::n-workspaces',
Lang.bind(this, this._workspacesChanged));
this._syncStackingId =
Main.overview.connect('windows-restacked',
Main.overview.connect('sync-window-stacking',
Lang.bind(this, this._syncStacking));
this._targetScale = 0;
@@ -770,6 +818,11 @@ const ThumbnailsBox = new Lang.Class({
this.addThumbnails(0, global.screen.n_workspaces);
// reset any translation and make sure the actor is visible when
// entering the overview
this.slideX = this._getInitialTranslation();
this.actor.show();
this._updateSwitcherVisibility();
},
@@ -782,7 +835,6 @@ const ThumbnailsBox = new Lang.Class({
global.screen.disconnect(this._nWorkspacesNotifyId);
this._nWorkspacesNotifyId = 0;
}
if (this._syncStackingId > 0) {
Main.overview.disconnect(this._syncStackingId);
this._syncStackingId = 0;
@@ -793,6 +845,49 @@ const ThumbnailsBox = new Lang.Class({
this._thumbnails = [];
},
_computeTranslation: function() {
let rtl = (this.actor.get_text_direction() == Clutter.TextDirection.RTL);
if (rtl)
return - this.actor.width;
else
return this.actor.width;
},
get slideX() {
return this._slideX;
},
set slideX(value) {
this._slideX = value;
this.actor.translation_x = this._slideX;
if (this._slideX > 0) {
let rect = new Clutter.Rect();
rect.size.width = this._background.width - this._slideX;
rect.size.height = this._background.height;
this.actor.clip_rect = rect;
} else {
this.actor.clip_rect = null;
}
},
show: function() {
this.actor.show();
this._updateTranslation();
},
hide: function() {
let hiddenX = this._computeTranslation();
Tweener.addTween(this, { slideX: hiddenX,
transition: 'easeOutQuad',
time: SLIDE_ANIMATION_TIME,
onComplete: Lang.bind(this, function () {
this.actor.hide();
})
});
},
_workspacesChanged: function() {
let oldNumWorkspaces = this._thumbnails.length;
let newNumWorkspaces = global.screen.n_workspaces;
@@ -862,7 +957,7 @@ const ThumbnailsBox = new Lang.Class({
this._queueUpdateStates();
},
_syncStacking: function(overview, stackIndices) {
_syncStacking: function(actor, stackIndices) {
for (let i = 0; i < this._thumbnails.length; i++)
this._thumbnails[i].syncStacking(stackIndices);
},
@@ -1228,5 +1323,19 @@ const ThumbnailsBox = new Lang.Class({
Main.wm.actionMoveWorkspace(Meta.MotionDirection.DOWN);
break;
}
},
_onKeyRelease: function (actor, event) {
switch (event.get_key_symbol()) {
case Clutter.KEY_Up:
Main.wm.actionMoveWorkspace(Meta.MotionDirection.UP);
break;
case Clutter.KEY_Down:
Main.wm.actionMoveWorkspace(Meta.MotionDirection.DOWN);
break;
case Clutter.KEY_Return:
Main.overview.toggle();
break;
}
}
});

View File

@@ -23,8 +23,6 @@ const MAX_WORKSPACES = 16;
const OVERRIDE_SCHEMA = 'org.gnome.shell.overrides';
const CONTROLS_POP_IN_TIME = 0.1;
const WorkspacesView = new Lang.Class({
Name: 'WorkspacesView',
@@ -69,7 +67,7 @@ const WorkspacesView = new Lang.Class({
// Add workspace actors
for (let w = 0; w < global.screen.n_workspaces; w++)
this.actor.add_actor(this._workspaces[w].actor);
this._workspaces[w].actor.reparent(this.actor);
this._workspaces[activeWorkspaceIndex].actor.raise_top();
this._extraWorkspaces = [];
@@ -129,10 +127,11 @@ const WorkspacesView = new Lang.Class({
continue;
let ws = new Workspace.Workspace(null, i);
ws.setGeometry(monitors[i].x,
monitors[i].y,
monitors[i].width,
monitors[i].height);
let overviewSpacing = Main.overview._spacing;
ws.setGeometry(monitors[i].x + overviewSpacing/2,
monitors[i].y + overviewSpacing/2,
monitors[i].width - overviewSpacing,
monitors[i].height - overviewSpacing);
global.overlay_group.add_actor(ws.actor);
this._extraWorkspaces.push(ws);
}
@@ -437,24 +436,11 @@ const WorkspacesDisplay = new Lang.Class({
Name: 'WorkspacesDisplay',
_init: function() {
this.actor = new Shell.GenericContainer({ style_class: 'workspaces-display' });
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
this.actor.connect('allocate', Lang.bind(this, this._allocate));
this.actor = new Shell.GenericContainer();
this.actor.connect('allocate', Lang.bind(this, this._updateWorkspacesGeometry));
this.actor.connect('parent-set', Lang.bind(this, this._parentSet));
this.actor.set_clip_to_allocation(true);
this._spacing = 0;
this.actor.connect('style-changed', Lang.bind(this,
function() {
let node = this.actor.get_theme_node();
let spacing = node.get_length('spacing');
if (spacing != this._spacing) {
this._spacing = spacing;
this._updateWorkspacesGeometry();
}
}));
let clickAction = new Clutter.ClickAction()
clickAction.connect('clicked', Lang.bind(this, function(action) {
// Only switch to the workspace when there's no application
@@ -482,25 +468,9 @@ const WorkspacesDisplay = new Lang.Class({
Main.overview.addAction(panAction);
this.actor.bind_property('mapped', panAction, 'enabled', GObject.BindingFlags.SYNC_CREATE);
let controls = new St.Bin({ style_class: 'workspace-controls',
request_mode: Clutter.RequestMode.WIDTH_FOR_HEIGHT,
y_align: St.Align.START,
y_fill: true });
this._controls = controls;
this.actor.add_actor(controls);
controls.reactive = true;
controls.track_hover = true;
controls.connect('notify::hover',
Lang.bind(this, this._onControlsHoverChanged));
this._primaryIndex = Main.layoutManager.primaryIndex;
this._thumbnailsBox = new WorkspaceThumbnail.ThumbnailsBox();
controls.add_actor(this._thumbnailsBox.actor);
this._workspacesViews = [];
this._workspaces = [];
this._workspacesViews = null;
this._primaryScrollAdjustment = null;
this._settings = new Gio.Settings({ schema: OVERRIDE_SCHEMA });
@@ -512,26 +482,6 @@ const WorkspacesDisplay = new Lang.Class({
this._inDrag = false;
this._cancelledDrag = false;
this._controlsInitiallyHovered = false;
this._alwaysZoomOut = false;
this._zoomOut = false;
this._zoomFraction = 0;
this._updateAlwaysZoom();
// If we stop hiding the overview on layout changes, we will need to
// update the _workspacesViews here
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._updateAlwaysZoom));
Main.xdndHandler.connect('drag-begin', Lang.bind(this, function(){
this._alwaysZoomOut = true;
}));
Main.xdndHandler.connect('drag-end', Lang.bind(this, function(){
this._alwaysZoomOut = false;
this._updateAlwaysZoom();
}));
global.screen.connect('notify::n-workspaces',
Lang.bind(this, this._workspacesChanged));
@@ -556,29 +506,10 @@ const WorkspacesDisplay = new Lang.Class({
},
show: function() {
if(!this._alwaysZoomOut) {
let [mouseX, mouseY] = global.get_pointer();
let [x, y] = this._controls.get_transformed_position();
let [width, height] = this._controls.get_transformed_size();
let visibleWidth = this._controls.get_theme_node().get_length('visible-width');
let rtl = (Clutter.get_default_text_direction () == Clutter.TextDirection.RTL);
if(rtl)
x = x + width - visibleWidth;
if(mouseX > x - 0.5 && mouseX < x + visibleWidth + 0.5 &&
mouseY > y - 0.5 && mouseY < y + height + 0.5)
this._controlsInitiallyHovered = true;
}
this._zoomOut = this._alwaysZoomOut;
this._zoomFraction = this._alwaysZoomOut ? 1 : 0;
this._updateZoom();
this._controls.show();
this._updateWorkspacesViews();
this._restackedNotifyId =
Main.overview.connect('windows-restacked',
Main.overview.connect('sync-window-stacking',
Lang.bind(this, this._onRestacked));
if (this._itemDragBeginId == 0)
@@ -608,11 +539,6 @@ const WorkspacesDisplay = new Lang.Class({
},
hide: function() {
this._controls.hide();
if (!this._alwaysZoomOut)
this.zoomFraction = 0;
if (this._restackedNotifyId > 0){
Main.overview.disconnect(this._restackedNotifyId);
this._restackedNotifyId = 0;
@@ -644,7 +570,7 @@ const WorkspacesDisplay = new Lang.Class({
for (let i = 0; i < this._workspacesViews.length; i++)
this._workspacesViews[i].destroy();
this._workspacesViews = [];
this._workspacesViews = null;
for (let i = 0; i < this._workspaces.length; i++)
for (let w = 0; w < this._workspaces[i].length; w++) {
@@ -663,12 +589,14 @@ const WorkspacesDisplay = new Lang.Class({
},
_updateWorkspacesViews: function() {
for (let i = 0; i < this._workspacesViews.length; i++)
this._workspacesViews[i].destroy();
if (this._workspacesViews)
for (let i = 0; i < this._workspacesViews.length; i++)
this._workspacesViews[i].destroy();
for (let i = 0; i < this._workspaces.length; i++)
for (let w = 0; w < this._workspaces[i].length; w++)
this._workspaces[i][w].destroy();
if (this._workspaces)
for (let i = 0; i < this._workspaces.length; i++)
for (let w = 0; w < this._workspaces[i].length; w++)
this._workspaces[i][w].destroy();
this._workspacesViews = [];
this._workspaces = [];
@@ -716,7 +644,7 @@ const WorkspacesDisplay = new Lang.Class({
},
_getPrimaryView: function() {
if (!this._workspacesViews.length)
if (!this._workspacesViews)
return null;
if (this._workspacesOnlyOnPrimary)
return this._workspacesViews[0];
@@ -728,76 +656,6 @@ const WorkspacesDisplay = new Lang.Class({
return this._getPrimaryView().getActiveWorkspace().hasMaximizedWindows();
},
// zoomFraction property allows us to tween the controls sliding in and out
set zoomFraction(fraction) {
this._zoomFraction = fraction;
this.actor.queue_relayout();
},
get zoomFraction() {
return this._zoomFraction;
},
_updateAlwaysZoom: function() {
// Always show the pager if workspaces are actually used,
// e.g. there are windows on more than one
this._alwaysZoomOut = global.screen.n_workspaces > 2;
if (this._alwaysZoomOut)
return;
let monitors = Main.layoutManager.monitors;
let primary = Main.layoutManager.primaryMonitor;
/* Look for any monitor to the right of the primary, if there is
* one, we always keep zoom out, otherwise its hard to reach
* the thumbnail area without passing into the next monitor. */
for (let i = 0; i < monitors.length; i++) {
if (monitors[i].x >= primary.x + primary.width) {
this._alwaysZoomOut = true;
break;
}
}
},
_getPreferredWidth: function (actor, forHeight, alloc) {
// pass through the call in case the child needs it, but report 0x0
this._controls.get_preferred_width(forHeight);
},
_getPreferredHeight: function (actor, forWidth, alloc) {
// pass through the call in case the child needs it, but report 0x0
this._controls.get_preferred_height(forWidth);
},
_allocate: function (actor, box, flags) {
let childBox = new Clutter.ActorBox();
let totalWidth = box.x2 - box.x1;
// width of the controls
let [controlsMin, controlsNatural] = this._controls.get_preferred_width(box.y2 - box.y1);
// Amount of space on the screen we reserve for the visible control
let controlsVisible = this._controls.get_theme_node().get_length('visible-width');
let controlsReserved = controlsVisible * (1 - this._zoomFraction) + controlsNatural * this._zoomFraction;
let rtl = (Clutter.get_default_text_direction () == Clutter.TextDirection.RTL);
if (rtl) {
childBox.x2 = controlsReserved;
childBox.x1 = childBox.x2 - controlsNatural;
} else {
childBox.x1 = totalWidth - controlsReserved;
childBox.x2 = childBox.x1 + controlsNatural;
}
childBox.y1 = 0;
childBox.y2 = box.y2- box.y1;
this._controls.allocate(childBox, flags);
this._updateWorkspacesGeometry();
},
_parentSet: function(actor, oldParent) {
if (oldParent && this._notifyOpacityId)
oldParent.disconnect(this._notifyOpacityId);
@@ -825,7 +683,7 @@ const WorkspacesDisplay = new Lang.Class({
},
_updateWorkspacesGeometry: function() {
if (!this._workspacesViews.length)
if (!this._workspacesViews)
return;
let fullWidth = this.actor.allocation.x2 - this.actor.allocation.x1;
@@ -834,23 +692,19 @@ const WorkspacesDisplay = new Lang.Class({
let width = fullWidth;
let height = fullHeight;
let [controlsMin, controlsNatural] = this._controls.get_preferred_width(height);
let controlsVisible = this._controls.get_theme_node().get_length('visible-width');
let [x, y] = this.actor.get_transformed_position();
let rtl = (Clutter.get_default_text_direction () == Clutter.TextDirection.RTL);
let clipWidth = width - controlsVisible;
let clipWidth = width;
let clipHeight = fullHeight;
let clipX = rtl ? x + controlsVisible : x;
let clipX = x;
let clipY = y + (fullHeight - clipHeight) / 2;
let widthAdjust = this._zoomOut ? controlsNatural : controlsVisible;
widthAdjust += this._spacing;
width -= widthAdjust;
let overviewSpacing = Main.overview._spacing;
width -= overviewSpacing;
if (rtl)
x += widthAdjust;
x += overviewSpacing;
let monitors = Main.layoutManager.monitors;
let m = 0;
@@ -865,25 +719,22 @@ const WorkspacesDisplay = new Lang.Class({
monitors[i].y,
monitors[i].width,
monitors[i].height);
this._workspacesViews[m].setGeometry(monitors[i].x,
monitors[i].y,
monitors[i].width,
monitors[i].height);
this._workspacesViews[m].setGeometry(monitors[i].x + overviewSpacing/2,
monitors[i].y + overviewSpacing/2,
monitors[i].width - overviewSpacing,
monitors[i].height - overviewSpacing);
m++;
}
}
},
_onRestacked: function(overview, stackIndices) {
_onRestacked: function(actor, stackIndices) {
for (let i = 0; i < this._workspacesViews.length; i++)
this._workspacesViews[i].syncStacking(stackIndices);
},
_workspacesChanged: function() {
this._updateAlwaysZoom();
this._updateZoom();
if (!this._workspacesViews.length)
if (this._workspacesViews == null)
return;
let oldNumWorkspaces = this._workspaces[0].length;
@@ -936,35 +787,6 @@ const WorkspacesDisplay = new Lang.Class({
newNumWorkspaces);
},
_updateZoom : function() {
if (Main.overview.animationInProgress)
return;
let shouldZoom = this._alwaysZoomOut || this._controls.hover;
if (shouldZoom != this._zoomOut) {
this._zoomOut = shouldZoom;
this._updateWorkspacesGeometry();
if (!this._workspacesViews.length)
return;
Tweener.addTween(this,
{ zoomFraction: this._zoomOut ? 1 : 0,
time: WORKSPACE_SWITCH_TIME,
transition: 'easeOutQuad' });
for (let i = 0; i < this._workspacesViews.length; i++)
this._workspacesViews[i].updateWindowPositions();
}
},
_onControlsHoverChanged: function() {
if(!this._controls.hover)
this._controlsInitiallyHovered = false;
if(!this._controlsInitiallyHovered)
this._updateZoom();
},
_dragBegin: function() {
this._inDrag = true;
this._cancelledDrag = false;
@@ -980,22 +802,11 @@ const WorkspacesDisplay = new Lang.Class({
},
_onDragMotion: function(dragEvent) {
let controlsHovered = this._controls.contains(dragEvent.targetActor);
this._controls.set_hover(controlsHovered);
return DND.DragMotionResult.CONTINUE;
},
_dragEnd: function() {
this._inDrag = false;
// We do this deferred because drag-end is emitted before dnd.js emits
// event/leave events that were suppressed during the drag. If we didn't
// defer this, we'd zoom out then immediately zoom in because of the
// enter event we received. That would normally be invisible but we
// might as well avoid it.
Meta.later_add(Meta.LaterType.BEFORE_REDRAW,
Lang.bind(this, this._updateZoom));
}
});
Signals.addSignalMethods(WorkspacesDisplay.prototype);

View File

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

View File

@@ -11,7 +11,6 @@ js/misc/util.js
js/ui/appDisplay.js
js/ui/appFavorites.js
js/ui/calendar.js
js/ui/components/automountManager.js
js/ui/components/autorunManager.js
js/ui/components/keyring.js
js/ui/components/networkAgent.js
@@ -56,5 +55,6 @@ src/shell-app.c
src/shell-app-system.c
src/shell-global.c
src/shell-keyring-prompt.c
src/shell-mobile-providers.c
src/shell-polkit-authentication-agent.c
src/shell-util.c

237
po/ar.po
View File

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

475
po/as.po

File diff suppressed because it is too large Load Diff

727
po/be.po

File diff suppressed because it is too large Load Diff

988
po/de.po

File diff suppressed because it is too large Load Diff

915
po/es.po

File diff suppressed because it is too large Load Diff

173
po/et.po
View File

@@ -13,8 +13,8 @@ msgstr ""
"Project-Id-Version: gnome-shell MASTER\n"
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
"shell&keywords=I18N+L10N&component=general\n"
"POT-Creation-Date: 2012-12-09 13:03+0000\n"
"PO-Revision-Date: 2012-12-19 16:47+0300\n"
"POT-Creation-Date: 2012-11-16 18:27+0000\n"
"PO-Revision-Date: 2012-11-16 20:40+0300\n"
"Last-Translator: Mattias Põldaru <mahfiaz@gmail.com>\n"
"Language-Team: Estonian <>\n"
"Language: et\n"
@@ -135,28 +135,6 @@ msgstr ""
"See võti keelab automaatse 'Logi välja' menüükirje peitmise, kui arvutis on "
"üks kasutaja ning avatud üks seanss."
msgid "Show full name in the user menu"
msgstr "Kasutajamenüüs näidatakse kasutaja tervet nime"
msgid "Whether the users full name is shown in the user menu or not."
msgstr "Kas kasutajamenüüs näidatakse kasutaja kogu nime või mitte."
msgid ""
"Whether to remember password for mounting encrypted or remote filesystems"
msgstr ""
"Kas pidada meeles krüpteeritud või kaugfailisüsteemide haakimise paroole."
msgid ""
"The shell will request a password when an encrypted device or a remote "
"filesystem is mounted. If the password can be saved for future use a "
"'Remember Password' checkbox will be present. This key sets the default "
"state of the checkbox."
msgstr ""
"Shell küsib parooli, kui haagitakse krüpteeritud seade või kaugfailisüsteem. "
"Kui parooli on võimalik salvestada edaspidiseks kasutuseks, näidatakse "
"'Salvesta parool' märkeruutu. See võti määrab selle märkeruudu vaikimisi "
"oleku."
msgid "Show the week date in the calendar"
msgstr "Kalendris näidatakse kuupäeva nädalavormingus"
@@ -249,18 +227,6 @@ msgstr ""
"konteinervormingusse salvestades tuleks ka sellele vormingule vastav laiend "
"määrata."
msgid "The application icon mode."
msgstr "Rakenduste ikooni režiim."
msgid ""
"Configures how the windows are shown in the switcher. Valid possibilities "
"are 'thumbnail-only' (shows a thumbnail of the window), 'app-icon-"
"only' (shows only the application icon) or 'both'."
msgstr ""
"Seadistab, kuidas aknaid aknavahetajas kuvatakse. Sobivad väärtused on "
"'thumbnail-only' (näidatakse ainult pisipilti aknast), 'app-icon-"
"only' (näidatakse ainult akna ikooni) või 'both' (mõlemad)."
msgid "Attach modal dialog to the parent window"
msgstr "Modaaldialoog kuulub vanemakna juurde"
@@ -622,8 +588,11 @@ msgstr "Helista"
msgid "File Transfer"
msgstr "Failiülekanne"
msgid "Chat"
msgstr "Vestlus"
msgid "Subscription request"
msgstr "Tellimuse päring"
msgid "Connection error"
msgstr "Ühenduse viga"
msgid "Unmute"
msgstr "Heli peale"
@@ -631,25 +600,27 @@ msgstr "Heli peale"
msgid "Mute"
msgstr "Heli maha"
#. Translators: this is the word "Yesterday" followed by a time string. i.e. "Yesterday, 14:30"
#. Translators: this is a time format string followed by the word "Yesterday". i.e. "14:30 on Yesterday"
#, no-c-format
msgid "<b>Yesterday</b>, <b>%H:%M</b>"
msgstr "<b>Eile</b> <b>%H:%M</b>"
msgid "<b>%H:%M</b> on Yesterday"
msgstr "Eile <b>%H:%M</b>"
#. Translators: this is the week day name followed by a time string. i.e. "Monday, 14:30
#. Translators: this is a time format string followed by a week day name. i.e. "14:30 on Monday
#, no-c-format
msgid "<b>%A</b>, <b>%H:%M</b>"
msgstr "<b>%A</b>, <b>%H:%M</b>"
msgid "<b>%H:%M</b> on <b>%A</b>"
msgstr "<b>%H:%M</b>, <b>%A</b>"
#. Translators: this is the month name and day number followed by a time string. i.e. "May 25, 14:30"
#. Translators: this is a time format in the style of "14:30 on Wednesday, May 25",
#. shown when you get a chat message in the same year
#, no-c-format
msgid "<b>%B</b> <b>%d</b>, <b>%H:%M</b>"
msgstr "<b>%d. %B</b>, <b>%H:%M</b>"
msgid "<b>%H:%M</b> on <b>%A</b>, <b>%B</b> <b>%d</b>"
msgstr "<b>%H:%M</b>, <b>%A</b>, <b>%d. %B</b>"
#. Translators: this is the month name, day number, year number followed by a time string. i.e. "May 25 2012, 14:30"
#. Translators: this is a time format in the style of "14:30 on Wednesday, May 25, 2012",
#. shown when you get a chat message in a different year
#, no-c-format
msgid "<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> "
msgstr "<b>%d. %B %Y</b>, <b>%H:%M</b>"
msgid "<b>%H:%M</b> on <b>%A</b>, <b>%B</b> <b>%d</b>, %Y"
msgstr "<b>%H:%M</b>, <b>%A</b>, <b>%d. %B</b> %Y"
#. Translators: this is the other person changing their old IM name to their new
#. IM name.
@@ -786,18 +757,18 @@ msgstr "Sisemine viga"
#. translators: argument is the account name, like
#. * name@jabber.org for example.
#, c-format
msgid "Unable to connect to %s"
msgstr "Pole võimalik ühenduda võrguga %s"
msgid "Connection to %s failed"
msgstr "Ühendus %s nurjus"
msgid "View account"
msgstr "Konto kuvamine"
msgid "Reconnect"
msgstr "Ühendu uuesti"
msgid "Edit account"
msgstr "Konto muutmine"
msgid "Unknown reason"
msgstr "Põhjus teadmata"
msgid "Windows"
msgstr "Aknad"
msgid "Show Applications"
msgstr "Rakenduste kuvamine"
@@ -920,6 +891,8 @@ msgstr "Näita vigu"
msgid "Enabled"
msgstr "Lubatud"
#. translators:
#. * The device has been disabled
msgid "Disabled"
msgstr "Keelatud"
@@ -944,9 +917,6 @@ msgstr "Ava"
msgid "Remove"
msgstr "Eemalda"
msgid "No Messages"
msgstr "Teateid pole"
msgid "Message Tray"
msgstr "Teateala"
@@ -1017,9 +987,6 @@ msgid_plural "%d new notifications"
msgstr[0] "%d uus märguanne"
msgstr[1] "%d uut märguannet"
msgid "Lock"
msgstr "Lukusta"
msgid "Searching..."
msgstr "Otsimine..."
@@ -1140,7 +1107,6 @@ msgstr "Luba ainult seekord"
msgid "Reject"
msgstr "Lükka tagasi"
#. Translators: argument is the device short name
#, c-format
msgid "Pairing confirmation for %s"
msgstr "Paardumise kinnitus seadmele %s"
@@ -1153,7 +1119,6 @@ msgstr "Seade '%s' tahab selle arvutiga paarduda"
msgid "Please confirm whether the PIN '%06d' matches the one on the device."
msgstr "Palun kontrolli, kas PIN-kood '%06d' kattub seadme parooliga."
#. Translators: this is the verb, not the noun
msgid "Matches"
msgstr "Kattub"
@@ -1243,8 +1208,14 @@ msgstr "Automaatne juhtmeta ühendus"
msgid "Enable networking"
msgstr "Luba võrguühendused"
msgid "Wi-Fi"
msgstr "Wi-Fi"
msgid "Wired"
msgstr "Juhtmega"
msgid "Wireless"
msgstr "Juhtmeta"
msgid "Mobile broadband"
msgstr "Mobiiliühendus"
msgid "Network Settings"
msgstr "Võrgusätted"
@@ -1348,9 +1319,6 @@ msgstr "Mikrofon"
msgid "Log in as another user"
msgstr "Logi sisse teise kasutajana"
msgid "Unlock Window"
msgstr "Võta aken lukust lahti"
msgid "Available"
msgstr "Saadaval"
@@ -1381,6 +1349,9 @@ msgstr "Vaheta kasutajat"
msgid "Log Out"
msgstr "Logi välja"
msgid "Lock"
msgstr "Lukusta"
msgid "Install Updates & Restart"
msgstr "Paigalda uuendused ja taaskäivita"
@@ -1394,6 +1365,9 @@ msgstr ""
"Märguanded on nüüd keelatud, sealhulgas vestlusteated. Sinu netiolekut "
"muudeti, et teised teaksid, et sa ei pruugi nende teateid näha."
msgid "Windows"
msgstr "Aknad"
msgid "Applications"
msgstr "Rakendused"
@@ -1422,6 +1396,25 @@ msgstr "'%s' on valmis"
msgid "Evolution Calendar"
msgstr "Evolutioni kalender"
#. translators:
#. * The number of sound outputs on a particular device
#, c-format
msgid "%u Output"
msgid_plural "%u Outputs"
msgstr[0] "%u väljund"
msgstr[1] "%u väljundit"
#. translators:
#. * The number of sound inputs on a particular device
#, c-format
msgid "%u Input"
msgid_plural "%u Inputs"
msgstr[0] "%u sisend"
msgstr[1] "%u sisendit"
msgid "System Sounds"
msgstr "Süsteemi helid"
msgid "Print version"
msgstr "Printimise versioon"
@@ -1453,46 +1446,6 @@ msgstr "Vaikimisi"
msgid "Authentication dialog was dismissed by the user"
msgstr "Kasutaja katkestas autentimisdialoogi"
#~ msgid "Subscription request"
#~ msgstr "Tellimuse päring"
#~ msgid "Connection error"
#~ msgstr "Ühenduse viga"
#~ msgid "<b>%H:%M</b> on Yesterday"
#~ msgstr "Eile <b>%H:%M</b>"
#~ msgid "<b>%H:%M</b> on <b>%A</b>, <b>%B</b> <b>%d</b>, %Y"
#~ msgstr "<b>%H:%M</b>, <b>%A</b>, <b>%d. %B</b> %Y"
#~ msgid "Connection to %s failed"
#~ msgstr "Ühendus %s nurjus"
#~ msgid "Reconnect"
#~ msgstr "Ühendu uuesti"
#~ msgid "Wired"
#~ msgstr "Juhtmega"
#~ msgid "Wireless"
#~ msgstr "Juhtmeta"
#~ msgid "Mobile broadband"
#~ msgstr "Mobiiliühendus"
#~ msgid "%u Output"
#~ msgid_plural "%u Outputs"
#~ msgstr[0] "%u väljund"
#~ msgstr[1] "%u väljundit"
#~ msgid "%u Input"
#~ msgid_plural "%u Inputs"
#~ msgstr[0] "%u sisend"
#~ msgstr[1] "%u sisendit"
#~ msgid "System Sounds"
#~ msgstr "Süsteemi helid"
#~ msgid "disabled OpenSearch providers"
#~ msgstr "keelatud OpenSearch pakkujad"

2083
po/eu.po

File diff suppressed because it is too large Load Diff

1874
po/fur.po

File diff suppressed because it is too large Load Diff

632
po/gl.po

File diff suppressed because it is too large Load Diff

779
po/he.po

File diff suppressed because it is too large Load Diff

873
po/hu.po

File diff suppressed because it is too large Load Diff

888
po/it.po

File diff suppressed because it is too large Load Diff

1832
po/kk.po

File diff suppressed because it is too large Load Diff

962
po/lt.po

File diff suppressed because it is too large Load Diff

471
po/nb.po

File diff suppressed because it is too large Load Diff

705
po/nl.po

File diff suppressed because it is too large Load Diff

477
po/pa.po

File diff suppressed because it is too large Load Diff

839
po/pl.po

File diff suppressed because it is too large Load Diff

944
po/pt.po

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

815
po/sk.po

File diff suppressed because it is too large Load Diff

1073
po/sl.po

File diff suppressed because it is too large Load Diff

846
po/sr.po

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

3525
po/ug.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

File diff suppressed because it is too large Load Diff

View File

@@ -86,6 +86,7 @@ include Makefile-hotplug-sniffer.am
gnome_shell_cflags = \
$(GNOME_SHELL_CFLAGS) \
$(SYSTEMD_CFLAGS) \
-I$(srcdir)/tray \
-DVERSION=\"$(VERSION)\" \
-DLOCALEDIR=\"$(datadir)/locale\" \
@@ -114,7 +115,7 @@ shell_public_headers_h = \
shell-gtk-embed.h \
shell-global.h \
shell-invert-lightness-effect.h \
shell-keybinding-modes.h \
shell-mobile-providers.h \
shell-mount-operation.h \
shell-network-agent.h \
shell-perf-log.h \
@@ -159,6 +160,7 @@ libgnome_shell_la_SOURCES = \
shell-invert-lightness-effect.c \
shell-keyring-prompt.h \
shell-keyring-prompt.c \
shell-mobile-providers.c \
shell-mount-operation.c \
shell-network-agent.c \
shell-perf-log.c \
@@ -279,6 +281,7 @@ libgnome_shell_la_LDFLAGS = -avoid-version
libgnome_shell_la_LIBADD = \
-lm \
$(GNOME_SHELL_LIBS) \
$(SYSTEMD_LIBS) \
$(BLUETOOTH_LIBS) \
libst-1.0.la \
libtray.la \

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