Compare commits

..

4 Commits

Author SHA1 Message Date
11bf65e2b4 status/keyboard: Disregard the backwards flag for the popup switcher
This seems to cause more harm than good. It puzzles users looking at
the shortcuts list in g-c-c and it prevents otherwise valid shortcuts
like Shift+Hangul from working.

https://bugzilla.gnome.org/show_bug.cgi?id=695823
2013-04-05 18:12:48 +02:00
a8b7e559aa status/keyboard: Allow switching input source in the message tray
We still can't show a popup switcher in the message tray but we can at
least degrade gracefully and advance to the next input source.

https://bugzilla.gnome.org/show_bug.cgi?id=697009
2013-04-05 18:12:48 +02:00
b733e30a4a status/keyboard: Switch input source on special modifiers accelerator
This simply mimics the X server's layout switching behavior by
advancing to the next input source and wrapping around.

https://bugzilla.gnome.org/show_bug.cgi?id=697008
2013-04-05 18:12:48 +02:00
b607958134 status/keyboard: Synchronize input source switching with key events
Currently we simply set the gsettings key when activating an input
source. This obviously introduces a time window, between the event that
activates the switch and when the switch is complete, under which key
events are being delivered to applications and interpreted according
to the previous input source.

The patches in bug 696996 introduce a DBus API in g-s-d that allows us
to know when an input source if effectively active. Using that and
freezing keyboard events in the X server until we hear back from g-s-d
we can ensure that events won't be misinterpreted after an input
source switch.

https://bugzilla.gnome.org/show_bug.cgi?id=697007
2013-04-05 18:12:47 +02:00
168 changed files with 16512 additions and 21099 deletions

164
NEWS
View File

@ -1,167 +1,3 @@
3.9.4
=====
* Fix chat entries not being focused when expanded [Jasper; #698778]
* Fix alignment of "Not Listed?" label [Mathieu; #702307]
* Fix alignment of time stamps in chat notifications [Carlos; #687809]
* Round the ends of slider trough [Jasper; #702825]
* Add support for "box-shadow: none" [Cosimo; #702782]
* Keep chrome below popup windows [Florian; #702338]
* Move the session list to a popup menu [Ray; #702818]
* Fix autorun notifications for "non-native" volumes [Matthias; #703418]
* dnd: Speed up by not picking on each motion event [Jasper; #703443]
* Fix management of asynchronous background loading [Lionel; #703001]
* Rework focus handling [Jasper; #700735]
* Optimize box-shadow rendering [Lionel; #689858]
* Remove support for fixed positioning in BoxLayouts [Florian; #703808]
* Misc bug fixes and cleanups [Adel, Jasper, Florian, Ray, Lionel, Emilio;
#702849, #610279, #703132, #703105, #703160, #703126, #703304, #703403,
#698593, #703442, #703565, #700901, #703874, #703807, #703893, #703909]
Contributors:
Mathieu Bridon, Giovanni Campagna, Cosimo Cecchi, Matthias Clasen,
Fran Diéguez, Adel Gadllah, Lionel Landwerlin, Florian Müllner,
Emilio Pozuelo Monfort, Carlos Soriano, Jasper St. Pierre, Ray Strode
Translations:
Baurzhan Muftakhidinov [kk], Marek Černocký [cs], Daniel Mustieles [es],
Fran Diéguez [gl], Kjartan Maraas [nb], Andika Triwidada [id],
Benjamin Steinwender [de], Nguyễn Thái Ngọc Duy [vi], Trần Ngọc Quân [vi]
3.9.3
=====
* Don't push window thumbs when workspace switcher is hidden [Jasper; #701167]
* Tweak timeout for activating windows during XDND [Adel; #700150]
* Fix ellipsization in control buttons in app picker [Carlos; #696307]
* Fix DND to empty dash [Florian; #684618]
* Fix OSD window appearing below system modal dialogs [Rui; #701269]
* Clear clipboard on screen lock to prevent information leak [Florian; #698922]
* Allow session mode specific overrides schema [Florian; #701717]
* window-switcher: Only show windows from current workspace by default
[Florian; #701214]
* logout dialog: Show the correct text right away [Matthias; #702056]
* bluetooth: Port to bluez 5 [Emilio; #700891]
* dateMenu: Allow events to span multiple lines [Giovanni; #701231]
* gdm: Clear message queue when no more messages are pending [Jonh; #702458]
* Misc bug fixes and cleanups [Jasper, Florian, Adel, Giovanni; #693836,
#700972, #701386, #700877, #701755, #698918, #701224, #702125, #701954,
#701849, #702121]
Contributors:
Giovanni Campagna, Matthias Clasen, Fran Diéguez, Adel Gadllah, Rui Matos,
Florian Müllner, Emilio Pozuelo Monfort, Carlos Soriano, Jasper St. Pierre,
Jonh Wendell
Translations:
Marek Černocký [cs], Victor Ibragimov [tg], Fran Diéguez [gl],
Benjamin Steinwender [de], Cheng-Chia Tseng [zh_HK, zh_TW],
eternalhui [zh_CN], Ivaylo Valkov [bg], Kjartan Maraas [nb],
Daniel Mustieles [es]
3.9.2
=====
* Use a symbolic icon for DESKTOP windows [Matthias; #697914]
* Move paint state cache into StWidget [Jasper; #697274]
* gdm: Fix regression where domain login hint not shown [Stef; #698200]
* Make calendar keyboard navigable [Tanner; #667434]
* Hide "Open Calendar" item when no calendar app is installed [Lionel; #697725]
* Update how branding appears on login screen [Florian; #694912, #699877]
* Allow OSD popups to grow if necessary [Marta; #696523]
* Fix offset of shadow offscreen rendering [Lionel; #698301]
* Fix insensitive button preventing empty keyring password [Stef; #696304]
* Allow cancelling keyring dialog between prompts [Stef; #682830]
* modalDialog: Show spinner while working [Stef; #684438]
* overview: Only show close buttons for windows that may close [Jasper; #699269]
* Add input purpose and hints to StEntry and StIMText [Daiki; #691392]
* Set input-purpose property for password entries [Rui; #700043]
* Provide a DBus API for screencasting [Florian; #696247]
* overview: Disable hotcorner during DND [Jasper; #698484]
* polkitAgent: Allow retrying after mistyped passwords [Stef; #684431]
* Add a way to get backtraces from criticals and warnings [Giovanni; #700262]
* Allow switch-to-workspace-n keybindings in overview [Florian; #649977]
* Update man page [Matthias; #700339]
* Add FocusSearch DBus method [Florian; #700536]
* Hide frequent view when app monitoring is disabled [Florian; #699714]
* Show switcher popup for switch-to-workspace-n keybindings [Elad; #659288]
* gdm: Update the session chooser style [Allan; #695742]
* Fix some app folders getting truncated at the top [Florian; #694371]
* Don't block the message tray while a notification is showing [Jasper; #700639]
* popupMenu: Allow for an optional border for slider handle [Florian; #697917]
* Re-lock screen when restarted after a crash [Colin; #691987]
* Synchronize input source switching with key events [Rui; #697007]
* Switch input source on modifiers-only accelerator [Rui; #697008]
* Allow input source switching in message tray [Rui; #697009]
* Misc bug fixes and cleanups [Alban, Jasper, Giovanni, Florian, Rui, Tomeu,
Stef, Gustavo; #698863, #699799, #699800, #676285, #699975, #700097, #698812,
#698486, #700194, #695314, #700257, #699678, #700356, #700322, #700394,
#700409, #700595, #700625, #691746, #700620, #700807, #659288, #700784,
#700842, #700847, #700488, #700735, #696159, #700900, #700853, #700923,
#700944, #697661, #700854, #700190, #699189, #701097]
Contributors:
Elad Alfassa, Alban Browaeys, Giovanni Campagna, Matthias Clasen, Allan Day,
Tanner Doshier, Lionel Landwerlin, Rui Matos, Simon McVittie,
Marta Milakovic, Florian Müllner, Gustavo Padovan, Jasper St. Pierre,
Daiki Ueno, Tomeu Vizoso, Stef Walter, Colin Walters
Translations:
Matej Urbančič [sl], Kjartan Maraas [nb], Victor Ibragimov [tg],
Dušan Kazik [sk], Gil Forcada [ca], Daniel Mustieles [es]
3.9.1
=====
* Add additional toggle-overview keybinding [Matthias; #698251]
* Disable <super> shortcut when sticky keys are enabled [Matthias; #685974]
* Disable tray context menu while a notification displays [Jasper; #695800]
* Watch GApplication busy state [Cosimo; #697207]
* Disable style transitions if animations are disabled [Jasper; #698391]
* Filter out hidden applications from "Frequent" view [Giovanni; #696949]
* Fix window previews swapping place randomly [Jasper; #694469, #698776]
* Add support for serialized GIcons in remote search providers [Cosimo; #698761]
* Fix hotcorner regression in RTL locales [Jasper; #698884]
* Allow some keybindings to work while a top bar menu is open [Florian; #698938]
* Make open-app-menu keybinding a toggle action [Florian; #686756]
* Only recognize common URL schemes in notification messages [Monica; #661225]
* Misc fixes and cleanups [Tim, Jasper, Florian, Giovanni, Owen; #698531,
#698622, #698427, #698483, #698513, #697203, #698959, #698918, #699029,
#699075, #696720, #649748]
Contributors:
Giovanni Campagna, Cosimo Cecchi, Monica Chelliah, Matthias Clasen, Tim Lunn,
Florian Müllner, Jasper St. Pierre, Michael Wood, Owen W. Taylor
Translations:
Fran Diéguez [gl], Muhammet Kara [tr], Daniel Mustieles [es],
Gil Forcada [ia], Anish A [ml], Dimitris Spingos [el], Marek Černocký [cs],
Žygimantas Beručka [lt]
3.8.1
=====
* Clip window group during startup animation [Jasper; #696323]
* Check for logind rather than systemd [Martin; #696252]
* Don't special-case last remote search provider position [Giovanni; #694974]
* Fix memory leaks [Ray, Jasper; ##697119, #697295, #697300, #697395]
* AppSwitcherPopup: Activate only the selected window if any [Rui; #697480]
* Enable screen recorder keybinding in all modes [Florian; #696200]
* Remove box-shadow from screen shield for performance reasons [Adel; #697274]
* Add support for -st-natural-width/height CSS properties [Giovanni; #664411]
* Remove excessive padding from notification buttons [Allan; #664411]
* Fix thumbnail dragging in overview [Jasper; #697504]
* theme-node: Add get_url()/lookup_url() methods [Florian; #693688]
* Misc bug fixes and cleanups [Jasper, Rui, Colin, David, Ray, Matthias:
#695859, #696259, #696585, #696436, #697432, #697435, #697560, #697722,
#697709]
Contributors:
Giovanni Campagna, Matthias Clasen, Allan Day, Adel Gadllah, David Gumberg,
Rui Matos, Florian Müllner, Martin Pitt, Jasper St. Pierre, Ray Strode,
Colin Walters
Translations:
Daniel Martinez [an], Bruce Cowan [en_GB], Khaled Hosny [ar],
Ihar Hrachyshka [be], Aron Xu [zh_CN], Wojciech Szczęsny [pl],
Inaki Larranaga Murgoitio [eu], Kjartan Maraas [nb],
Милош Поповић [sr, sr@latin], Trần Ngọc Quân [vi]
3.8.0.1
=======
* Background bug fixes [Ray; #696712]

View File

@ -1,5 +1,5 @@
AC_PREREQ(2.63)
AC_INIT([gnome-shell],[3.9.4],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
AC_INIT([gnome-shell],[3.8.0.1],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_SRCDIR([src/shell-global.c])
@ -63,18 +63,18 @@ AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
CLUTTER_MIN_VERSION=1.13.4
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
GJS_MIN_VERSION=1.35.4
MUTTER_MIN_VERSION=3.9.4
MUTTER_MIN_VERSION=3.8.0
GTK_MIN_VERSION=3.7.9
GIO_MIN_VERSION=2.37.0
GIO_MIN_VERSION=2.35.0
LIBECAL_MIN_VERSION=3.5.3
LIBEDATASERVER_MIN_VERSION=3.5.3
TELEPATHY_GLIB_MIN_VERSION=0.17.5
POLKIT_MIN_VERSION=0.100
STARTUP_NOTIFICATION_MIN_VERSION=0.11
GCR_MIN_VERSION=3.7.5
GCR_MIN_VERSION=3.3.90
GNOME_DESKTOP_REQUIRED_VERSION=3.7.90
GNOME_MENUS_REQUIRED_VERSION=3.5.3
NETWORKMANAGER_MIN_VERSION=0.9.8
NETWORKMANAGER_MIN_VERSION=0.9.6
PULSE_MIN_VERS=2.0
# Collect more than 20 libraries for a prize!
@ -96,7 +96,7 @@ PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes
libnm-glib libnm-util >= $NETWORKMANAGER_MIN_VERSION
libnm-gtk >= $NETWORKMANAGER_MIN_VERSION
libsecret-unstable gcr-base-3 >= $GCR_MIN_VERSION)
libsecret-unstable gcr-3 >= $GCR_MIN_VERSION)
PKG_CHECK_MODULES(GNOME_SHELL_JS, gio-2.0 gjs-internals-1.0 >= $GJS_MIN_VERSION)
PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 >= 0.6.8 x11)
@ -109,7 +109,7 @@ PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.7.4)
PKG_CHECK_MODULES(CARIBOU, caribou-1.0 >= 0.4.8)
AC_MSG_CHECKING([for bluetooth support])
PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.9.0],
PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.1.0],
[BLUETOOTH_DIR=`$PKG_CONFIG --variable=applet_libdir gnome-bluetooth-1.0`
BLUETOOTH_LIBS=`$PKG_CONFIG --variable=applet_libs gnome-bluetooth-1.0`
AC_SUBST([BLUETOOTH_LIBS],["$BLUETOOTH_LIBS"])

View File

@ -11,9 +11,6 @@
<KeyListEntry name="focus-active-notification"
_description="Focus the active notification"/>
<KeyListEntry name="toggle-overview"
_description="Show the overview"/>
<KeyListEntry name="toggle-application-view"
_description="Show all applications"/>

View File

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

View File

@ -1,96 +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.Screencast:
@short_description: Screencast interface
The interface used to record screen contents.
-->
<interface name="org.gnome.Shell.Screencast">
<!--
Screencast:
@file_template: the template for the filename to use
@options: a dictionary of optional parameters
@success: whether the screencast was started successfully
@filename_used: the file where the screencast is being saved
Records a screencast of the whole screen and saves it
(by default) as webm video under a filename derived from
@file_template. The template is either a relative or absolute
filename which may contain some escape sequences - %d and %t
will be replaced by the start date and time of the recording.
If a relative name is used, the screencast will be saved in the
$XDG_VIDEOS_DIR if it exists, or the home directory otherwise.
The actual filename of the saved video is returned in @filename_used.
The set of optional parameters in @options currently consists of:
'draw-cursor'(b): whether the cursor should be included in the
recording (true)
'framerate'(i): the number of frames per second that should be
recorded if possible (30)
'pipeline'(s): the GStreamer pipeline used to encode recordings
in gst-launch format; if not specified, the
recorder will produce vp8 (webm) video (unset)
-->
<method name="Screencast">
<arg type="s" direction="in" name="file_template"/>
<arg type="a{sv}" direction="in" name="options"/>
<arg type="b" direction="in" name="flash"/>
<arg type="b" direction="out" name="success"/>
<arg type="s" direction="out" name="filename_used"/>
</method>
<!--
ScreencastArea:
@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
@file_template: the template for the filename to use
@options: a dictionary of optional parameters
@success: whether the screencast was started successfully
@filename_used: the file where the screencast is being saved
Records a screencast of the passed in area and saves it
(by default) as webm video under a filename derived from
@file_template. The template is either a relative or absolute
filename which may contain some escape sequences - %d and %t
will be replaced by the start date and time of the recording.
If a relative name is used, the screencast will be saved in the
$XDG_VIDEOS_DIR if it exists, or the home directory otherwise.
The actual filename of the saved video is returned in @filename_used.
The set of optional parameters in @options currently consists of:
'draw-cursor'(b): whether the cursor should be included in the
recording (true)
'framerate'(i): the number of frames per second that should be
recorded if possible (30)
'pipeline'(s): the GStreamer pipeline used to encode recordings
in gst-launch format; if not specified, the
recorder will produce vp8 (webm) video (unset)
-->
<method name="ScreencastArea">
<arg type="i" direction="in" name="x"/>
<arg type="i" direction="in" name="y"/>
<arg type="i" direction="in" name="width"/>
<arg type="i" direction="in" name="height"/>
<arg type="s" direction="in" name="file_template"/>
<arg type="a{sv}" direction="in" name="options"/>
<arg type="b" direction="out" name="success"/>
<arg type="s" direction="out" name="filename_used"/>
</method>
<!--
StopScreencast:
@success: whether stopping the recording was successful
Stop the recording started by either Screencast or ScreencastArea.
-->
<method name="StopScreencast">
<arg type="b" direction="out" name="success"/>
</method>
</interface>
</node>

View File

@ -46,7 +46,7 @@
<!--
GetResultMetas:
@identifiers: An array of result identifiers as returned by GetInitialResultSet() or GetSubsearchResultSet()
@metas: A dictionary describing the given search result, containing a human-readable 'name' (string), along with the result identifier this meta is for, 'id' (string). Optionally, 'icon' (a serialized GIcon as obtained by g_icon_serialize) can be specified if the result can be better served with a thumbnail of the content (such as with images). 'gicon' (a serialized GIcon as obtained by g_icon_to_string) or 'icon-data' (raw image data as (iiibiiay) - width, height, rowstride, has-alpha, bits per sample, channels, data) are deprecated values that can also be used for that purpose. A 'description' field (string) may also be specified if more context would help the user find the desired result.
@metas: A dictionary describing the given search result, containing a human-readable 'name' (string), along with the result identifier this meta is for, 'id' (string). Optionally, either 'gicon' (a serialized GIcon) or 'icon-data' (raw image data as (iiibiiay) - width, height, rowstride, has-alpha, bits per sample, channels, data) can be specified if the result can be better served with a thumbnail of the content (such as with images). A 'description' field (string) may also be specified if more context would help the user find the desired result.
Return an array of meta data used to display each given result
-->

View File

@ -21,6 +21,16 @@
EnableExtension and DisableExtension DBus methods on org.gnome.Shell.
</_description>
</key>
<key name="enable-app-monitoring" type="b">
<default>true</default>
<_summary>Whether to collect stats about applications usage</_summary>
<_description>
The shell normally monitors active applications in order to present
the most used ones (e.g. in launchers). While this data will be
kept private, you may want to disable this for privacy reasons.
Please note that doing so won't remove already saved data.
</_description>
</key>
<key name="favorite-apps" type="as">
<default>[ 'epiphany.desktop', 'evolution.desktop', 'empathy.desktop', 'rhythmbox.desktop', 'shotwell.desktop', 'libreoffice-writer.desktop', 'nautilus.desktop', 'gnome-documents.desktop' ]</default>
<_summary>List of desktop file IDs for favorite applications</_summary>
@ -107,13 +117,6 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
Overview.
</_description>
</key>
<key name="toggle-overview" type="as">
<default>["&lt;Super&gt;s"]</default>
<_summary>Keybinding to open the overview</_summary>
<_description>
Keybinding to open the Activities Overview.
</_description>
</key>
<key name="toggle-message-tray" type="as">
<default>["&lt;Super&gt;m"]</default>
<_summary>Keybinding to toggle the visibility of the message tray</_summary>
@ -186,19 +189,6 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
</key>
</schema>
<schema id="org.gnome.shell.app-switcher"
path="/org/gnome/shell/app-switcher/"
gettext-domain="@GETTEXT_PACKAGE@">
<key type="b" name="current-workspace-only">
<default>false</default>
<summary>Limit switcher to current workspace.</summary>
<description>
If true, only applications that have windows on the current workspace are shown in the switcher.
Otherwise, all applications are included.
</description>
</key>
</schema>
<enum id="org.gnome.shell.window-switcher.AppIconMode">
<value value="1" nick="thumbnail-only"/>
<value value="2" nick="app-icon-only"/>
@ -217,7 +207,7 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
</_description>
</key>
<key type="b" name="current-workspace-only">
<default>true</default>
<default>false</default>
<summary>Limit switcher to current workspace.</summary>
<description>
If true, only windows from the current workspace are shown in the switcher.

View File

@ -123,26 +123,8 @@ StScrollBar StButton#vhandle:active {
background-image: url("checkbox-focused.svg");
}
/* Slider */
.slider {
height: 1em;
min-width: 15em;
-slider-height: 0.3em;
-slider-background-color: #333333;
-slider-border-color: #5f5f5f;
-slider-active-background-color: #76b0ec;
-slider-active-border-color: #1f6dbc;
-slider-border-width: 1px;
-slider-handle-radius: 0.5em;
}
/* PopupMenu */
.popup-menu-ornament {
text-align: center;
}
.popup-menu-boxpointer,
.candidate-popup-boxpointer {
-arrow-border-radius: 8px;
@ -189,6 +171,13 @@ StScrollBar StButton#vhandle:active {
border-width: 0px;
}
.popup-combo-menu {
background-color: rgba(0,0,0,0.9);
padding: 1em 0em;
border: 1px solid #5f5f5f;
border-radius: 9px;
}
/* The remaining popup-menu sizing is all done in ems, so that if you
* override .popup-menu.font-size, everything else will scale with it.
*/
@ -212,6 +201,10 @@ StScrollBar StButton#vhandle:active {
.popup-image-menu-item {
}
.popup-combobox-item {
spacing: 1em;
}
.popup-separator-menu-item {
-gradient-height: 1px;
-gradient-start: rgba(255,255,255,0.0);
@ -225,6 +218,22 @@ StScrollBar StButton#vhandle:active {
font-weight: bold;
}
.popup-slider-menu-item {
height: 1em;
min-width: 15em;
-slider-height: 0.3em;
-slider-background-color: #333333;
-slider-border-color: #5f5f5f;
-slider-active-background-color: #76b0ec;
-slider-active-border-color: #1f6dbc;
-slider-border-width: 1px;
-slider-handle-radius: 0.5em;
}
.popup-device-menu-item {
spacing: .5em;
}
.popup-status-menu-item {
font-weight: normal;
color: #999;
@ -234,10 +243,19 @@ StScrollBar StButton#vhandle:active {
color: white;
}
.popup-subtitle-menu-item, .popup-subtitle-menu-item:insensitive {
font-weight: bold;
color: white;
}
.popup-menu-icon {
icon-size: 1.09em;
}
.popup-battery-percentage {
padding-left: 24px;
}
/* Switches */
.toggle-switch {
width: 65px;
@ -262,51 +280,10 @@ StScrollBar StButton#vhandle:active {
background-size: contain;
}
/* Network */
.nm-dialog {
max-height: 400px;
}
.nm-dialog-content {
spacing: 8px;
}
.nm-dialog-header-hbox {
spacing: 4px;
}
.nm-dialog-header-icon {
icon-size: 32px;
}
.nm-dialog-scroll-view {
border: 2px solid #666;
border-radius: 6px;
}
.nm-dialog-header {
font-weight: bold;
}
.nm-dialog-item {
font-size: 12pt;
border-bottom: 1px solid #666;
padding: 12px;
}
.nm-dialog-item:checked {
background-color: #333;
}
.nm-dialog-icons {
.nm-menu-item-icons {
spacing: .5em;
}
.nm-dialog-icon {
icon-size: 16px;
}
/* Buttons */
.candidate-page-button,
@ -344,6 +321,10 @@ StScrollBar StButton#vhandle:active {
border-width: 2px;
}
.app-view-control:focus {
padding: 3px;
}
.app-view-control:first-child:ltr:focus,
.app-view-control:last-child:rtl:focus {
border-right-width: 1px;
@ -378,8 +359,7 @@ StScrollBar StButton#vhandle:active {
.modal-dialog-button,
.notification-button,
.hotplug-notification-item,
.app-view-controls,
#screenShieldNotifications {
.app-view-controls {
border-radius: 18px;
}
@ -397,7 +377,6 @@ StScrollBar StButton#vhandle:active {
/* Entries */
#searchEntry,
.login-dialog StEntry,
.notification StEntry,
.modal-dialog StEntry {
color: rgb(64, 64, 64);
@ -409,7 +388,6 @@ StScrollBar StButton#vhandle:active {
}
#searchEntry,
.login-dialog StEntry,
.run-dialog-entry,
.notification StEntry {
border: 2px solid rgba(245,245,245,0.2);
@ -422,7 +400,6 @@ StScrollBar StButton#vhandle:active {
#searchEntry:focus,
#searchEntry:hover,
.login-dialog StEntry:focus,
.notification StEntry:focus,
.modal-dialog StEntry {
border: 2px solid rgb(136,138,133);
@ -432,7 +409,6 @@ StScrollBar StButton#vhandle:active {
box-shadow: inset 0px 2px 4px rgba(0,0,0,0.6);
}
.login-dialog StEntry:focus,
.notification StEntry:focus,
.modal-dialog StEntry:focus {
border: 2px solid #3465a4;
@ -456,7 +432,6 @@ StScrollBar StButton#vhandle:active {
transition-duration: 0ms;
}
.login-dialog StEntry,
.notification StEntry,
.modal-dialog StEntry {
border-radius: 5px;
@ -470,7 +445,6 @@ StScrollBar StButton#vhandle:active {
padding: 0 4px;
}
.login-dialog StEntry:insensitive,
.modal-dialog StEntry:insensitive {
border-color: #666666;
color: #9f9f9f;
@ -488,6 +462,10 @@ StScrollBar StButton#vhandle:active {
height: 1.86em;
}
#panel.lock-screen {
background-color: rgba(0,0,0,0.3);
}
#panel.unlock-screen,
#panel.login-screen {
background-color: transparent;
@ -534,7 +512,6 @@ StScrollBar StButton#vhandle:active {
}
#appMenu {
spinner-image: url("process-working.svg");
spacing: 4px;
}
@ -622,30 +599,58 @@ StScrollBar StButton#vhandle:active {
spacing: 8px;
}
/* User Menu */
#panelUserMenu {
spacing: 4px;
}
.status-chooser {
spacing: .4em;
}
.status-chooser .popup-menu-item,
.status-chooser-combo .popup-menu-item {
padding: .4em;
}
.status-chooser-user-icon {
border: 2px solid #8b8b8b;
border-radius: 5px;
width: 48pt;
height: 48pt;
background-size: contain;
}
.status-chooser-user-icon:hover {
border: 2px solid #bbbbbb;
}
.status-chooser-user-name {
font-weight: bold;
font-size: 1.3em;
min-width: 120pt;
}
.status-chooser-combo {
border: 1px solid transparent;
}
.status-chooser-combo.popup-combo-menu {
padding: .4em 0em;
border-radius: 4px;
border: 1px solid #5f5f5f;
}
.status-chooser-status-item,
.status-chooser-combo .popup-combobox-item {
spacing: .4em;
}
.system-status-icon {
icon-size: 1.09em;
}
.system-switch-user-submenu-icon {
icon-size: 24px;
border: 1px solid #8b8b8b;
}
.system-menu-action {
color: #e6e6e6;
border-radius: 4px;
padding: 6px;
}
.system-menu-action:hover {
color: white;
background-color: rgba(255,255,255,0.1);
}
.system-menu-action > StIcon {
icon-size: 32px;
}
/* Overview */
#overview {
@ -794,11 +799,6 @@ StScrollBar StButton#vhandle:active {
height: 24px;
}
.empty-dash-drop-target {
width: 24px;
height: 24px;
}
/* Search Box */
#searchEntry {
@ -886,18 +886,14 @@ StScrollBar StButton#vhandle:active {
}
.app-view-controls {
width: 250px;
padding-bottom: 32px;
}
.app-view-control {
padding: 4px 32px;
padding: 4px 16px;
}
.app-view-control:focus {
padding: 3px 31px;
}
.search-display > StBoxLayout,
.all-apps > StBoxLayout,
.frequent-apps > StBoxLayout {
@ -1119,7 +1115,7 @@ StScrollBar StButton#vhandle:active {
padding: 4px;
}
.lg-extensions-list {
.lg-extension-list {
padding: 4px;
spacing: 6px;
}
@ -1147,6 +1143,11 @@ StScrollBar StButton#vhandle:active {
/* Calendar popup */
#calendarEventsArea {
/* this is the width of the second column of the popup */
min-width: 320px;
}
.calendar-vertical-separator {
-stipple-width: 1px;
-stipple-color: #505050;
@ -1182,8 +1183,7 @@ StScrollBar StButton#vhandle:active {
background-image: url("calendar-arrow-right.svg");
}
.calendar-change-month-back:hover,
.calendar-change-month-back:focus {
.calendar-change-month-back:hover {
background-color: #999999;
}
.calendar-change-month-back:active {
@ -1201,8 +1201,7 @@ StScrollBar StButton#vhandle:active {
background-image: url("calendar-arrow-left.svg");
}
.calendar-change-month-forward:hover,
.calendar-change-month-forward:focus {
.calendar-change-month-forward:hover {
background-color: #999999;
}
.calendar-change-month-forward:active {
@ -1223,8 +1222,7 @@ StScrollBar StButton#vhandle:active {
height: 2.4em;
}
.calendar-day-base:hover,
.calendar-day-base:focus {
.calendar-day-base:hover {
background-color: #777777;
}
@ -1283,40 +1281,32 @@ StScrollBar StButton#vhandle:active {
color: #333333;
}
.events-table {
width: 320px;
spacing-columns: 6pt;
padding: 0 1.4em;
.events-header-vbox {
spacing: 6pt;
padding-right: .5em;
}
.events-table:ltr {
padding-right: 1.9em;
.events-header-vbox:rtl {
padding-left: .5em;
}
.events-table:rtl {
padding-left: 1.9em;
.events-header-hbox {
padding: 0.3em 1.4em;
}
.events-day-header {
font-weight: bold;
color: #999999;
padding-left: 0.4em;
padding-top: 1.2em;
}
.events-day-header:first-child {
padding-top: 0;
padding: 0.4em 1.4em 0em 1.4em;
}
.events-day-header:rtl {
padding-left: 0;
padding-right: 0.4em;
padding: 0em 1.4em 0.4em 1.4em;
}
.events-day-dayname {
color: rgba(153, 153, 153, 1.0);
text-align: left;
min-width: 20px;
}
.events-day-dayname:rtl {
@ -1334,12 +1324,23 @@ StScrollBar StButton#vhandle:active {
.events-day-task {
color: rgba(153, 153, 153, 1.0);
padding-left: 8pt;
}
.events-day-task:rtl {
padding-left: 0px;
padding-right: 8pt;
.events-day-name-box {
min-width: 15pt;
}
.events-time-box {
min-width: 48pt;
padding-right: 12pt;
}
.events-time-box:rtl {
padding-right: 0px;
padding-left: 12pt;
}
.events-event-box {
}
.url-highlighter {
@ -1452,13 +1453,11 @@ StScrollBar StButton#vhandle:active {
}
.notification-button {
-st-natural-width: 140px;
padding: 4px 4px 5px;
padding: 4px 42px 5px;
}
.notification-button:focus {
-st-natural-width: 138px;
padding: 3px 4px 4px;
padding: 3px 41px 4px;
}
.notification-icon-button {
@ -2196,18 +2195,6 @@ StScrollBar StButton#vhandle:active {
/* Login Dialog */
.framed-user-icon {
border: 2px solid #8b8b8b;
border-radius: 5px;
width: 48pt;
height: 48pt;
background-size: contain;
}
.framed-user-icon:hover {
border: 2px solid #bbbbbb;
}
.login-dialog-banner {
font-size: 10pt;
font-weight: bold;
@ -2237,10 +2224,6 @@ StScrollBar StButton#vhandle:active {
min-width: 350px;
}
.login-dialog-button-box {
spacing: 5px;
}
.login-dialog-prompt-login-hint-message {
font-size: 10.5pt;
}
@ -2324,10 +2307,6 @@ StScrollBar StButton#vhandle:active {
padding-top: 1em;
}
.login-dialog-user-selection-box .login-dialog-not-listed-label {
padding-left: 2px;
}
.login-dialog-not-listed-button:focus .login-dialog-not-listed-label,
.login-dialog-not-listed-button:hover .login-dialog-not-listed-label {
color: #E8E8E8;
@ -2345,7 +2324,6 @@ StScrollBar StButton#vhandle:active {
padding-top: 24px;
padding-bottom: 12px;
spacing: 8px;
width: 23em;
}
.login-dialog-prompt-label {
@ -2353,21 +2331,56 @@ StScrollBar StButton#vhandle:active {
font-size: 14px;
}
.login-dialog-session-list-button StIcon {
icon-size: 1.25em;
.login-dialog-prompt-entry {
width: 15em;
}
.login-dialog-session-list {
color: #ffffff;
font-size: 10.5pt;
}
.login-dialog-session-list-button {
color: #8b8b8b;
padding: 4px;
}
.login-dialog-session-list-button:focus {
background-color: #4c4c4c;
}
.login-dialog-session-list-button:hover,
.login-dialog-session-list-button:active {
color: white;
background-color: #4c4c4c;
}
.login-dialog-logo-bin {
padding: 24px 0px;
.login-dialog-session-list-button:hover {
font-weight: bold;
}
.login-dialog-session-list-scroll-view {
background-gradient-start: rgba(80,80,80,0.3);
background-gradient-end: rgba(80,80,80,0.7);
background-gradient-direction: vertical;
box-shadow: inset 0px 2px 4px rgba(0,0,0,0.9);
border-radius: 8px;
border: 1px solid rgba(80,80,80,1.0);
padding: .5em;
}
.login-dialog-session-list-item:focus {
background-color: #666666;
}
.login-dialog-session-list-triangle {
padding-right: .5em;
}
.login-dialog-session-list-item-box {
spacing: .25em;
}
.login-dialog-session-list-item-dot {
width: .75em;
height: .75em;
}
.login-dialog .modal-dialog-button-box {
@ -2430,11 +2443,6 @@ StScrollBar StButton#vhandle:active {
/* Screen shield */
#panel.lock-screen,
#screenShieldNotifications {
background-color: rgba(0,0,0,0.3);
}
.screen-shield-background {
background: black;
box-shadow: 0px 4px 8px rgba(0,0,0,0.9);
@ -2479,27 +2487,33 @@ StScrollBar StButton#vhandle:active {
}
#screenShieldNotifications {
border-radius: 8px;
background-color: rgba(0.0, 0.0, 0.0, 0.9);
border: 2px solid #868686;
max-height: 500px;
padding: 12px;
padding: 18px 0;
box-shadow: .5em .5em 20px rgba(0, 0, 0, 0.5);
}
.screen-shield-notifications-box {
spacing: 12px;
width: 30em;
spacing: 18px;
max-width: 34em;
}
.screen-shield-notification-source {
padding: 3px 6px;
padding: 13px 24px;
spacing: 5px;
}
.screen-shield-notification-label {
font-size: 1.2em;
font-weight: bold;
padding: 0px 0px 0px 12px;
padding: 0px 18px;
color: #babdb6;
}
.screen-shield-notification-count-text {
padding: 0px 0px 0px 12px;
padding: 0px 18px;
}
/* Remove background from notifications, otherwise
@ -2517,31 +2531,6 @@ StScrollBar StButton#vhandle:active {
padding-bottom: 0px;
}
#screenShieldNotifications .notification-button,
#screenShieldNotifications .notification-icon-button {
border: 1px rgba(255,255,255,0.5);
}
#screenShieldNotifications StScrollBar StBin#trough {
background-color: rgba(0,0,0,0.2);
}
#screenShieldNotifications StScrollBar StButton#vhandle,
#screenShieldNotifications StScrollBar StButton#hhandle {
background-color: rgba(0,0,0,0.3);
border: none;
}
#screenShieldNotifications StScrollBar StButton#vhandle:hover,
#screenShieldNotifications StScrollBar StButton#hhandle {
background-color: rgba(0,0,0,0.6);
}
#screenShieldNotifications StScrollBar StButton#vhandle:active,
#screenShieldNotifications StScrollBar StButton#hhandle {
background-color: rgba(0,0,0,0.8);
}
.input-source-switcher-symbol {
font-size: 34pt;
width: 96px;

View File

@ -46,7 +46,6 @@
<xi:include href="doc-gen-org.gnome.Shell.SearchProvider.xml"/>
<xi:include href="doc-gen-org.gnome.Shell.SearchProvider2.xml"/>
<xi:include href="xml/shell-global.xml"/>
<xi:include href="xml/shell-keybinding-modes.xml"/>
<xi:include href="xml/shell-wm.xml"/>
<xi:include href="xml/shell-xfixes-cursor.xml"/>
<xi:include href="xml/shell-util.xml"/>

View File

@ -17,7 +17,6 @@ misc/config.js: misc/config.js.in Makefile
jsdir = $(pkgdatadir)/js
nobase_dist_js_DATA = \
gdm/authPrompt.js \
gdm/batch.js \
gdm/fingerprint.js \
gdm/loginDialog.js \
@ -34,13 +33,10 @@ nobase_dist_js_DATA = \
misc/jsParse.js \
misc/loginManager.js \
misc/modemManager.js \
misc/objectManager.js \
misc/params.js \
misc/smartcardManager.js \
misc/util.js \
perf/core.js \
ui/altTab.js \
ui/animation.js \
ui/appDisplay.js \
ui/appFavorites.js \
ui/backgroundMenu.js \
@ -72,7 +68,6 @@ nobase_dist_js_DATA = \
ui/sessionMode.js \
ui/shellEntry.js \
ui/shellMountOperation.js \
ui/slider.js \
ui/notificationDaemon.js \
ui/osdWindow.js \
ui/overview.js \
@ -82,9 +77,7 @@ nobase_dist_js_DATA = \
ui/pointerWatcher.js \
ui/popupMenu.js \
ui/remoteSearch.js \
ui/remoteMenu.js \
ui/runDialog.js \
ui/screencast.js \
ui/screenshot.js \
ui/screenShield.js \
ui/scripting.js \
@ -98,10 +91,10 @@ nobase_dist_js_DATA = \
ui/status/power.js \
ui/status/volume.js \
ui/status/bluetooth.js \
ui/status/system.js \
ui/switcherPopup.js \
ui/tweener.js \
ui/unlockDialog.js \
ui/userMenu.js \
ui/userWidget.js \
ui/viewSelector.js \
ui/wanda.js \

View File

@ -1,506 +0,0 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Lang = imports.lang;
const Signals = imports.signals;
const St = imports.gi.St;
const Animation = imports.ui.animation;
const Batch = imports.gdm.batch;
const GdmUtil = imports.gdm.util;
const Params = imports.misc.params;
const ShellEntry = imports.ui.shellEntry;
const Tweener = imports.ui.tweener;
const UserWidget = imports.ui.userWidget;
const DEFAULT_BUTTON_WELL_ICON_SIZE = 24;
const DEFAULT_BUTTON_WELL_ANIMATION_DELAY = 1.0;
const DEFAULT_BUTTON_WELL_ANIMATION_TIME = 0.3;
const MESSAGE_FADE_OUT_ANIMATION_TIME = 0.5;
const AuthPromptMode = {
UNLOCK_ONLY: 0,
UNLOCK_OR_LOG_IN: 1
};
const AuthPromptStatus = {
NOT_VERIFYING: 0,
VERIFYING: 1,
VERIFICATION_FAILED: 2,
VERIFICATION_SUCCEEDED: 3
};
const BeginRequestType = {
PROVIDE_USERNAME: 0,
DONT_PROVIDE_USERNAME: 1
};
const AuthPrompt = new Lang.Class({
Name: 'AuthPrompt',
_init: function(gdmClient, mode) {
this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
this._gdmClient = gdmClient;
this._mode = mode;
let reauthenticationOnly;
if (this._mode == AuthPromptMode.UNLOCK_ONLY)
reauthenticationOnly = true;
else if (this._mode == AuthPromptMode.UNLOCK_OR_LOG_IN)
reauthenticationOnly = false;
this._userVerifier = new GdmUtil.ShellUserVerifier(this._gdmClient, { reauthenticationOnly: reauthenticationOnly });
this._userVerifier.connect('ask-question', Lang.bind(this, this._onAskQuestion));
this._userVerifier.connect('show-message', Lang.bind(this, this._onShowMessage));
this._userVerifier.connect('verification-failed', Lang.bind(this, this._onVerificationFailed));
this._userVerifier.connect('verification-complete', Lang.bind(this, this._onVerificationComplete));
this._userVerifier.connect('reset', Lang.bind(this, this._onReset));
this._userVerifier.connect('show-login-hint', Lang.bind(this, this._onShowLoginHint));
this._userVerifier.connect('hide-login-hint', Lang.bind(this, this._onHideLoginHint));
this._userVerifier.connect('smartcard-status-changed', Lang.bind(this, this._onSmartcardStatusChanged));
this.smartcardDetected = this._userVerifier.smartcardDetected;
this.connect('next', Lang.bind(this, function() {
this.updateSensitivity(false);
this.startSpinning();
if (this._queryingService) {
this._userVerifier.answerQuery(this._queryingService, this._entry.text);
} else {
this._preemptiveAnswer = this._entry.text;
}
}));
this.actor = new St.BoxLayout({ style_class: 'login-dialog-prompt-layout',
vertical: true });
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
this.actor.connect('key-press-event',
Lang.bind(this, function(actor, event) {
if (event.get_key_symbol() == Clutter.KEY_Escape) {
this.cancel();
}
}));
this._userWell = new St.Bin({ x_fill: true,
x_align: St.Align.START });
this.actor.add(this._userWell,
{ x_align: St.Align.START,
x_fill: true,
y_fill: true,
expand: true });
this._label = new St.Label({ style_class: 'login-dialog-prompt-label' });
this.actor.add(this._label,
{ expand: true,
x_fill: true,
y_fill: true,
x_align: St.Align.START });
this._entry = new St.Entry({ style_class: 'login-dialog-prompt-entry',
can_focus: true });
ShellEntry.addContextMenu(this._entry, { isPassword: true });
this.actor.add(this._entry,
{ expand: true,
x_fill: true,
y_fill: false,
x_align: St.Align.START });
this._entry.grab_key_focus();
this._message = new St.Label({ opacity: 0 });
this._message.clutter_text.line_wrap = true;
this.actor.add(this._message, { x_fill: true });
this._loginHint = new St.Label({ style_class: 'login-dialog-prompt-login-hint-message' });
this.actor.add(this._loginHint);
this._buttonBox = new St.BoxLayout({ style_class: 'login-dialog-button-box',
vertical: false });
this.actor.add(this._buttonBox,
{ expand: true,
x_align: St.Align.MIDDLE,
y_align: St.Align.END });
this._defaultButtonWell = new St.Widget();
this._defaultButtonWellActor = null;
this._initButtons();
let spinnerIcon = global.datadir + '/theme/process-working.svg';
this._spinner = new Animation.AnimatedIcon(spinnerIcon, DEFAULT_BUTTON_WELL_ICON_SIZE);
this._spinner.actor.opacity = 0;
this._spinner.actor.show();
this._defaultButtonWell.add_child(this._spinner.actor);
},
_onDestroy: function() {
this._userVerifier.clear();
this._userVerifier.disconnectAll();
this._userVerifier = null;
},
_initButtons: function() {
this.cancelButton = new St.Button({ style_class: 'modal-dialog-button',
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
reactive: true,
can_focus: true,
label: _("Cancel") });
this.cancelButton.connect('clicked',
Lang.bind(this, function() {
this.cancel();
}));
this._buttonBox.add(this.cancelButton,
{ expand: false,
x_fill: false,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.END });
this._buttonBox.add(this._defaultButtonWell,
{ expand: true,
x_fill: false,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.MIDDLE });
this.nextButton = new St.Button({ style_class: 'modal-dialog-button',
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
reactive: true,
can_focus: true,
label: _("Next") });
this.nextButton.connect('clicked',
Lang.bind(this, function() {
this.emit('next');
}));
this.nextButton.add_style_pseudo_class('default');
this._buttonBox.add(this.nextButton,
{ expand: false,
x_fill: false,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.END });
this._updateNextButtonSensitivity(this._entry.text.length > 0);
this._entry.clutter_text.connect('text-changed',
Lang.bind(this, function() {
if (!this._userVerifier.hasPendingMessages)
this._fadeOutMessage();
this._updateNextButtonSensitivity(this._entry.text.length > 0);
}));
this._entry.clutter_text.connect('activate', Lang.bind(this, function() {
this.emit('next');
}));
},
_onAskQuestion: function(verifier, serviceName, question, passwordChar) {
if (this._preemptiveAnswer) {
this._userVerifier.answerQuery(this._queryingService, this._preemptiveAnswer);
this._preemptiveAnswer = null;
return;
}
if (this._queryingService)
this.clear();
this._queryingService = serviceName;
this.setPasswordChar(passwordChar);
this.setQuestion(question);
if (passwordChar) {
if (this._userVerifier.reauthenticating)
this.nextButton.label = _("Unlock");
else
this.nextButton.label = C_("button", "Sign In");
} else {
this.nextButton.label = _("Next");
}
this.updateSensitivity(true);
this.emit('prompted');
},
_onSmartcardStatusChanged: function() {
this.smartcardDetected = this._userVerifier.smartcardDetected;
// Don't reset on smartcard insertion if we're already verifying
// and the smartcard is the main service
if (this._userVerifier.serviceIsDefault(GdmUtil.SMARTCARD_SERVICE_NAME) &&
this.verificationStatus == AuthPromptStatus.VERIFYING &&
this.smartcardDetected)
return;
if (this.verificationStatus != AuthPromptStatus.VERIFICATION_SUCCEEDED)
this.reset();
},
_onShowMessage: function(userVerifier, message, styleClass) {
this.setMessage(message, styleClass);
this.emit('prompted');
},
_onVerificationFailed: function() {
this.clear();
this.updateSensitivity(true);
this.setActorInDefaultButtonWell(null);
this.verificationStatus = AuthPromptStatus.VERIFICATION_FAILED;
},
_onVerificationComplete: function() {
this.verificationStatus = AuthPromptStatus.VERIFICATION_SUCCEEDED;
},
_onReset: function() {
if (this.verificationStatus != AuthPromptStatus.VERIFICATION_SUCCEEDED) {
this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
this.reset();
}
},
_onShowLoginHint: function(verifier, message) {
this.setHint(message);
},
_onHideLoginHint: function() {
this.setHint(null);
},
addActorToDefaultButtonWell: function(actor) {
this._defaultButtonWell.add_child(actor);
actor.add_constraint(new Clutter.AlignConstraint({ source: this._spinner.actor,
align_axis: Clutter.AlignAxis.BOTH,
factor: 0.5 }));
},
setActorInDefaultButtonWell: function(actor, animate) {
if (!this._defaultButtonWellActor &&
!actor)
return;
let oldActor = this._defaultButtonWellActor;
if (oldActor)
Tweener.removeTweens(oldActor);
let isSpinner;
if (actor == this._spinner.actor)
isSpinner = true;
else
isSpinner = false;
if (this._defaultButtonWellActor != actor && oldActor) {
if (!animate) {
oldActor.opacity = 0;
} else {
Tweener.addTween(oldActor,
{ opacity: 0,
time: DEFAULT_BUTTON_WELL_ANIMATION_TIME,
delay: DEFAULT_BUTTON_WELL_ANIMATION_DELAY,
transition: 'linear',
onCompleteScope: this,
onComplete: function() {
if (isSpinner) {
if (this._spinner)
this._spinner.stop();
}
}
});
}
}
if (actor) {
if (isSpinner)
this._spinner.play();
if (!animate)
actor.opacity = 255;
else
Tweener.addTween(actor,
{ opacity: 255,
time: DEFAULT_BUTTON_WELL_ANIMATION_TIME,
delay: DEFAULT_BUTTON_WELL_ANIMATION_DELAY,
transition: 'linear' });
}
this._defaultButtonWellActor = actor;
},
startSpinning: function() {
this.setActorInDefaultButtonWell(this._spinner.actor, true);
},
stopSpinning: function() {
this.setActorInDefaultButtonWell(null, false);
},
clear: function() {
this._entry.text = '';
this.stopSpinning();
},
setPasswordChar: function(passwordChar) {
this._entry.clutter_text.set_password_char(passwordChar);
this._entry.menu.isPassword = passwordChar != '';
},
setQuestion: function(question) {
this._label.set_text(question);
this._label.show();
this._entry.show();
this._loginHint.opacity = 0;
this._loginHint.show();
this._entry.grab_key_focus();
},
getAnswer: function() {
let text = this._entry.get_text();
return text;
},
_fadeOutMessage: function() {
if (this._message.opacity == 0)
return;
Tweener.removeTweens(this._message);
Tweener.addTween(this._message,
{ opacity: 0,
time: MESSAGE_FADE_OUT_ANIMATION_TIME,
transition: 'easeOutQuad'
});
},
setMessage: function(message, styleClass) {
if (message) {
Tweener.removeTweens(this._message);
this._message.text = message;
this._message.styleClass = styleClass;
this._message.opacity = 255;
} else {
this._message.opacity = 0;
}
},
_updateNextButtonSensitivity: function(sensitive) {
this.nextButton.reactive = sensitive;
this.nextButton.can_focus = sensitive;
},
updateSensitivity: function(sensitive) {
this._updateNextButtonSensitivity(sensitive);
this._entry.reactive = sensitive;
this._entry.clutter_text.editable = sensitive;
},
hide: function() {
this.setActorInDefaultButtonWell(null, true);
this.actor.hide();
this._loginHint.opacity = 0;
this.setUser(null);
this.updateSensitivity(true);
this._entry.set_text('');
},
setUser: function(user) {
if (user) {
let userWidget = new UserWidget.UserWidget(user);
this._userWell.set_child(userWidget.actor);
} else {
this._userWell.set_child(null);
}
},
setHint: function(message) {
if (message) {
this._loginHint.set_text(message)
this._loginHint.opacity = 255;
} else {
this._loginHint.opacity = 0;
this._loginHint.set_text('');
}
},
reset: function() {
let oldStatus = this.verificationStatus;
this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
if (oldStatus == AuthPromptStatus.VERIFYING)
this._userVerifier.cancel();
this._queryingService = null;
this.clear();
this._message.opacity = 0;
this.setUser(null);
this.stopSpinning();
this.setHint(null);
if (oldStatus == AuthPromptStatus.VERIFICATION_FAILED)
this.emit('failed');
let beginRequestType;
if (this._mode == AuthPromptMode.UNLOCK_ONLY) {
// The user is constant at the unlock screen
beginRequestType = BeginRequestType.PROVIDE_USERNAME;
} else if (this.smartcardDetected &&
this._userVerifier.serviceIsForeground(GdmUtil.SMARTCARD_SERVICE_NAME)) {
// We don't need to know the username if the user preempted the login screen
// with a smartcard.
beginRequestType = BeginRequestType.DONT_PROVIDE_USERNAME;
} else {
// In all other cases, we should get the username up front.
beginRequestType = BeginRequestType.PROVIDE_USERNAME;
}
this.emit('reset', beginRequestType);
},
addCharacter: function(unichar) {
if (!this._entry.visible)
return;
this._entry.grab_key_focus();
this._entry.clutter_text.insert_unichar(unichar);
},
begin: function(params) {
params = Params.parse(params, { userName: null,
hold: null });
this.updateSensitivity(false);
let hold = params.hold;
if (!hold)
hold = new Batch.Hold();
this._userVerifier.begin(params.userName, hold);
this.verificationStatus = AuthPromptStatus.VERIFYING;
},
finish: function(onComplete) {
if (!this._userVerifier.hasPendingMessages) {
onComplete();
return;
}
let signalId = this._userVerifier.connect('no-more-messages',
Lang.bind(this, function() {
this._userVerifier.disconnect(signalId);
onComplete();
}));
},
cancel: function() {
this.reset();
this.emit('cancelled');
}
});
Signals.addSignalMethods(AuthPrompt.prototype);

View File

@ -19,38 +19,73 @@
*/
const AccountsService = imports.gi.AccountsService;
const Atk = imports.gi.Atk;
const Clutter = imports.gi.Clutter;
const Gdm = imports.gi.Gdm;
const CtrlAltTab = imports.ui.ctrlAltTab;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const Lang = imports.lang;
const Pango = imports.gi.Pango;
const Signals = imports.signals;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Gdm = imports.gi.Gdm;
const AuthPrompt = imports.gdm.authPrompt;
const Batch = imports.gdm.batch;
const BoxPointer = imports.ui.boxpointer;
const CtrlAltTab = imports.ui.ctrlAltTab;
const Fprint = imports.gdm.fingerprint;
const GdmUtil = imports.gdm.util;
const Layout = imports.ui.layout;
const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main;
const PopupMenu = imports.ui.popupMenu;
const Realmd = imports.gdm.realmd;
const ModalDialog = imports.ui.modalDialog;
const Panel = imports.ui.panel;
const PanelMenu = imports.ui.panelMenu;
const Tweener = imports.ui.tweener;
const UserMenu = imports.ui.userMenu;
const UserWidget = imports.ui.userWidget;
const _FADE_ANIMATION_TIME = 0.25;
const _SCROLL_ANIMATION_TIME = 0.5;
const _TIMED_LOGIN_IDLE_THRESHOLD = 5.0;
const _LOGO_ICON_HEIGHT = 48;
const _LOGO_ICON_HEIGHT = 16;
const WORK_SPINNER_ICON_SIZE = 24;
const WORK_SPINNER_ANIMATION_DELAY = 1.0;
const WORK_SPINNER_ANIMATION_TIME = 0.3;
let _loginDialog = null;
const LogoMenuButton = new Lang.Class({
Name: 'LogoMenuButton',
Extends: PanelMenu.Button,
_init: function() {
this.parent(0.0, null, true);
this._settings = new Gio.Settings({ schema: GdmUtil.LOGIN_SCREEN_SCHEMA });
this._settings.connect('changed::' + GdmUtil.LOGO_KEY,
Lang.bind(this, this._updateLogo));
this._iconBin = new St.Bin();
this.actor.add_actor(this._iconBin);
this._updateLogo();
},
_updateLogo: function() {
let path = this._settings.get_string(GdmUtil.LOGO_KEY);
let icon = null;
if (path) {
let file = Gio.file_new_for_path(path);
let cache = St.TextureCache.get_default();
icon = cache.load_uri_async(file.get_uri(), -1, _LOGO_ICON_HEIGHT);
}
this._iconBin.set_child(icon);
}
});
const UserListItem = new Lang.Class({
Name: 'UserListItem',
@ -68,8 +103,8 @@ const UserListItem = new Lang.Class({
x_align: St.Align.START,
x_fill: true });
this._userAvatar = new UserWidget.Avatar(this.user,
{ styleClass: 'login-dialog-user-list-item-icon' });
this._userAvatar = new UserMenu.UserAvatarWidget(this.user,
{ styleClass: 'login-dialog-user-list-item-icon' });
layout.add(this._userAvatar.actor);
let textLayout = new St.BoxLayout({ style_class: 'login-dialog-user-list-item-text-box',
vertical: true });
@ -287,124 +322,209 @@ const UserList = new Lang.Class({
});
Signals.addSignalMethods(UserList.prototype);
const SessionMenuButton = new Lang.Class({
Name: 'SessionMenuButton',
const SessionListItem = new Lang.Class({
Name: 'SessionListItem',
_init: function(id, name) {
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,
x_align: St.Align.START });
this._box = new St.BoxLayout({ style_class: 'login-dialog-session-list-item-box' });
this.actor.add_actor(this._box);
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
this._dot = new St.DrawingArea({ style_class: 'login-dialog-session-list-item-dot' });
this._dot.connect('repaint', Lang.bind(this, this._onRepaintDot));
this._box.add_actor(this._dot);
this.setShowDot(false);
let label = new St.Label({ style_class: 'login-dialog-session-list-item-label',
text: name });
this.actor.label_actor = label;
this._box.add_actor(label);
},
setShowDot: function(show) {
if (show)
this._dot.opacity = 255;
else
this._dot.opacity = 0;
},
_onRepaintDot: function(area) {
let cr = area.get_context();
let [width, height] = area.get_surface_size();
let color = area.get_theme_node().get_foreground_color();
cr.setSourceRGBA (color.red / 255,
color.green / 255,
color.blue / 255,
color.alpha / 255);
cr.arc(width / 2, height / 2, width / 3, 0, 2 * Math.PI);
cr.fill();
cr.$dispose();
},
_onClicked: function() {
this.emit('activate');
}
});
Signals.addSignalMethods(SessionListItem.prototype);
const SessionList = new Lang.Class({
Name: 'SessionList',
_init: function() {
let gearIcon = new St.Icon({ icon_name: 'emblem-system-symbolic' });
this.actor = new St.Bin();
this._box = new St.BoxLayout({ style_class: 'login-dialog-session-list',
vertical: true});
this.actor.child = this._box;
this._button = new St.Button({ style_class: 'login-dialog-session-list-button',
reactive: true,
track_hover: true,
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
can_focus: true,
accessible_name: _("Choose Session"),
accessible_role: Atk.Role.MENU,
child: gearIcon });
x_fill: true,
y_fill: true });
let box = new St.BoxLayout();
this._button.add_actor(box);
this.actor = new St.Bin({ child: this._button });
this._triangle = new St.Label({ style_class: 'login-dialog-session-list-triangle',
text: '\u25B8' });
box.add_actor(this._triangle);
this._menu = new PopupMenu.PopupMenu(this._button, 0, St.Side.TOP);
Main.uiGroup.add_actor(this._menu.actor);
this._menu.actor.hide();
let label = new St.Label({ style_class: 'login-dialog-session-list-label',
text: _("Session…") });
box.add_actor(label);
this._menu.connect('open-state-changed',
Lang.bind(this, function(menu, isOpen) {
if (isOpen)
this._button.add_style_pseudo_class('active');
else
this._button.remove_style_pseudo_class('active');
}));
let subtitle = new PopupMenu.PopupMenuItem(_("Session"), { style_class: 'popup-subtitle-menu-item',
reactive: false });
this._menu.addMenuItem(subtitle);
this._manager = new PopupMenu.PopupMenuManager({ actor: this._button });
this._manager.addMenu(this._menu);
this._button.connect('clicked', Lang.bind(this, function() {
this._menu.toggle();
}));
this._items = {};
this._activeSessionId = null;
this._button.connect('clicked',
Lang.bind(this, this._onClicked));
this._box.add_actor(this._button);
this._scrollView = new St.ScrollView({ style_class: 'login-dialog-session-list-scroll-view'});
this._scrollView.set_policy(Gtk.PolicyType.NEVER,
Gtk.PolicyType.AUTOMATIC);
this._box.add_actor(this._scrollView);
this._itemList = new St.BoxLayout({ style_class: 'login-dialog-session-item-list',
vertical: true });
this._scrollView.add_actor(this._itemList);
this._scrollView.hide();
this.isOpen = false;
this._populate();
},
open: function() {
if (this.isOpen)
return;
this._button.add_style_pseudo_class('open');
this._scrollView.show();
this._triangle.set_text('\u25BE');
this.isOpen = true;
},
close: function() {
if (!this.isOpen)
return;
this._button.remove_style_pseudo_class('open');
this._scrollView.hide();
this._triangle.set_text('\u25B8');
this.isOpen = false;
},
_onClicked: function() {
if (!this.isOpen)
this.open();
else
this.close();
},
updateSensitivity: function(sensitive) {
this._button.reactive = sensitive;
this._button.can_focus = sensitive;
this._menu.close(BoxPointer.PopupAnimation.NONE);
},
_updateOrnament: function() {
let itemIds = Object.keys(this._items);
for (let i = 0; i < itemIds.length; i++) {
if (itemIds[i] == this._activeSessionId)
this._items[itemIds[i]].setOrnament(PopupMenu.Ornament.DOT);
else
this._items[itemIds[i]].setOrnament(PopupMenu.Ornament.NONE);
}
for (let id in this._items)
this._items[id].actor.reactive = sensitive;
},
setActiveSession: function(sessionId) {
if (sessionId == this._activeSessionId)
return;
if (this._activeSessionId)
this._items[this._activeSessionId].setShowDot(false);
this._items[sessionId].setShowDot(true);
this._activeSessionId = sessionId;
this._updateOrnament();
this.emit('session-activated', this._activeSessionId);
},
close: function() {
this._menu.close();
},
_populate: function() {
this._itemList.destroy_all_children();
this._activeSessionId = null;
this._items = {};
let ids = Gdm.get_session_ids();
ids.sort();
if (ids.length <= 1) {
this._box.hide();
this._button.hide();
return;
} else {
this._button.show();
this._box.show();
}
for (let i = 0; i < ids.length; i++) {
let [sessionName, sessionDescription] = Gdm.get_session_name_and_description(ids[i]);
let id = ids[i];
let item = new PopupMenu.PopupMenuItem(sessionName);
this._menu.addMenuItem(item);
this._items[id] = item;
let item = new SessionListItem(ids[i], sessionName);
this._itemList.add_actor(item.actor);
this._items[ids[i]] = item;
if (!this._activeSessionId)
this.setActiveSession(id);
this.setActiveSession(ids[i]);
item.connect('activate', Lang.bind(this, function() {
this.setActiveSession(id);
}));
item.connect('activate',
Lang.bind(this, function() {
this.setActiveSession(item.id);
}));
}
}
});
Signals.addSignalMethods(SessionMenuButton.prototype);
Signals.addSignalMethods(SessionList.prototype);
const LoginDialog = new Lang.Class({
Name: 'LoginDialog',
Extends: ModalDialog.ModalDialog,
_init: function(parentActor) {
this.actor = new St.Widget({ accessible_role: Atk.Role.WINDOW,
style_class: 'login-dialog',
visible: false });
this.actor.add_constraint(new Layout.MonitorConstraint({ primary: true }));
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
parentActor.add_child(this.actor);
this.parent({ shellReactive: true,
styleClass: 'login-dialog',
parentActor: parentActor,
keybindingMode: Shell.KeyBindingMode.LOGIN_SCREEN,
shouldFadeIn: false });
this.connect('destroy',
Lang.bind(this, this._onDestroy));
this.connect('opened',
Lang.bind(this, this._onOpened));
this._userManager = AccountsService.UserManager.get_default()
let gdmClient = new Gdm.Client();
this._greeterClient = new Gdm.Client();
if (GLib.getenv('GDM_GREETER_TEST') != '1') {
this._greeter = gdmClient.get_greeter_sync(null);
this._greeter = this._greeterClient.get_greeter_sync(null);
this._greeter.connect('default-session-name-changed',
Lang.bind(this, this._onDefaultSessionChanged));
@ -415,6 +535,15 @@ const LoginDialog = new Lang.Class({
Lang.bind(this, this._onTimedLoginRequested));
}
this._userVerifier = new GdmUtil.ShellUserVerifier(this._greeterClient);
this._userVerifier.connect('ask-question', Lang.bind(this, this._askQuestion));
this._userVerifier.connect('show-message', Lang.bind(this, this._showMessage));
this._userVerifier.connect('verification-failed', Lang.bind(this, this._verificationFailed));
this._userVerifier.connect('reset', Lang.bind(this, this._reset));
this._userVerifier.connect('show-login-hint', Lang.bind(this, this._showLoginHint));
this._userVerifier.connect('hide-login-hint', Lang.bind(this, this._hideLoginHint));
this._verifyingUser = false;
this._settings = new Gio.Settings({ schema: GdmUtil.LOGIN_SCREEN_SCHEMA });
this._settings.connect('changed::' + GdmUtil.BANNER_MESSAGE_KEY,
@ -423,20 +552,10 @@ const LoginDialog = new Lang.Class({
Lang.bind(this, this._updateBanner));
this._settings.connect('changed::' + GdmUtil.DISABLE_USER_LIST_KEY,
Lang.bind(this, this._updateDisableUserList));
this._settings.connect('changed::' + GdmUtil.LOGO_KEY,
Lang.bind(this, this._updateLogo));
this._textureCache = St.TextureCache.get_default();
this._textureCache.connect('texture-file-changed',
Lang.bind(this, this._updateLogoTexture));
this._userSelectionBox = new St.BoxLayout({ style_class: 'login-dialog-user-selection-box',
vertical: true,
visible: false });
this._userSelectionBox.add_constraint(new Clutter.AlignConstraint({ source: this.actor,
align_axis: Clutter.AlignAxis.BOTH,
factor: 0.5 }));
this.actor.add_child(this._userSelectionBox);
vertical: true });
this.contentLayout.add(this._userSelectionBox);
this._bannerLabel = new St.Label({ style_class: 'login-dialog-banner',
text: '' });
@ -449,18 +568,61 @@ const LoginDialog = new Lang.Class({
x_fill: true,
y_fill: true });
this._authPrompt = new AuthPrompt.AuthPrompt(gdmClient, AuthPrompt.AuthPromptMode.UNLOCK_OR_LOG_IN);
this._authPrompt.connect('prompted', Lang.bind(this, this._onPrompted));
this._authPrompt.connect('reset', Lang.bind(this, this._onReset));
this._authPrompt.hide();
this.setInitialKeyFocus(this._userList.actor);
this._authPrompt.actor.add_constraint(new Clutter.AlignConstraint({ source: this.actor,
align_axis: Clutter.AlignAxis.BOTH,
factor: 0.5 }));
this._promptBox = new St.BoxLayout({ style_class: 'login-dialog-prompt-layout',
vertical: true });
this.contentLayout.add(this._promptBox,
{ expand: true,
x_fill: true,
y_fill: true,
x_align: St.Align.START });
this._promptUser = new St.Bin({ x_fill: true,
x_align: St.Align.START });
this._promptBox.add(this._promptUser,
{ x_align: St.Align.START,
x_fill: true,
y_fill: true,
expand: true });
this._promptLabel = new St.Label({ style_class: 'login-dialog-prompt-label' });
this.actor.add_child(this._authPrompt.actor);
this._userList.actor.add_constraint(new Clutter.BindConstraint({ source: this._authPrompt.actor,
coordinate: Clutter.BindCoordinate.WIDTH }));
this._promptBox.add(this._promptLabel,
{ expand: true,
x_fill: true,
y_fill: true,
x_align: St.Align.START });
this._promptEntry = new St.Entry({ style_class: 'login-dialog-prompt-entry',
can_focus: true });
this._promptEntryTextChangedId = 0;
this._promptEntryActivateId = 0;
this._promptBox.add(this._promptEntry,
{ expand: true,
x_fill: true,
y_fill: false,
x_align: St.Align.START });
this._promptMessage = new St.Label({ visible: false });
this._promptBox.add(this._promptMessage, { x_fill: true });
this._promptLoginHint = new St.Label({ style_class: 'login-dialog-prompt-login-hint-message' });
this._promptLoginHint.hide();
this._promptBox.add(this._promptLoginHint);
this._signInButton = null;
this._workSpinner = null;
this._sessionList = new SessionList();
this._sessionList.connect('session-activated',
Lang.bind(this, function(list, sessionId) {
this._greeter.call_select_session_sync (sessionId, null);
}));
this._promptBox.add(this._sessionList.actor,
{ expand: true,
x_fill: false,
y_fill: true,
x_align: St.Align.START });
this._promptBox.hide();
// translators: this message is shown below the user list on the
// login screen. It can be activated to reveal an entry for
@ -475,30 +637,13 @@ const LoginDialog = new Lang.Class({
x_align: St.Align.START,
x_fill: true });
this._notListedButton.connect('clicked',
Lang.bind(this, function() {
this._authPrompt.cancelButton.show();
this._hideUserListAskForUsernameAndBeginVerification();
}));
this._notListedButton.hide();
this._notListedButton.connect('clicked', Lang.bind(this, this._hideUserListAndLogIn));
this._userSelectionBox.add(this._notListedButton,
{ expand: false,
x_align: St.Align.START,
x_fill: true });
this._logoBin = new St.Bin({ style_class: 'login-dialog-logo-bin', y_expand: true });
this._logoBin.set_y_align(Clutter.ActorAlign.END);
this._logoBin.add_constraint(new Clutter.AlignConstraint({ source: this.actor,
align_axis: Clutter.AlignAxis.X_AXIS,
factor: 0.5 }));
this._logoBin.add_constraint(new Clutter.AlignConstraint({ source: this.actor,
align_axis: Clutter.AlignAxis.Y_AXIS,
factor: 1.0 }));
this.actor.add_child(this._logoBin);
this._updateLogo();
if (!this._userManager.is_loaded)
this._userManagerLoadedId = this._userManager.connect('notify::is-loaded',
Lang.bind(this, function() {
@ -516,26 +661,20 @@ const LoginDialog = new Lang.Class({
this._onUserListActivated(item);
}));
this._sessionMenuButton = new SessionMenuButton();
this._sessionMenuButton.connect('session-activated',
Lang.bind(this, function(list, sessionId) {
this._greeter.call_select_session_sync (sessionId, null);
}));
this._sessionMenuButton.actor.opacity = 0;
this._sessionMenuButton.actor.show();
this._authPrompt.addActorToDefaultButtonWell(this._sessionMenuButton.actor);
},
_updateDisableUserList: function() {
let disableUserList = this._settings.get_boolean(GdmUtil.DISABLE_USER_LIST_KEY);
// If this is the first time around, set initial focus
if (this._disableUserList == undefined && disableUserList)
this.setInitialKeyFocus(this._promptEntry);
if (disableUserList != this._disableUserList) {
this._disableUserList = disableUserList;
if (this._authPrompt.verificationStatus == AuthPrompt.AuthPromptStatus.NOT_VERIFYING)
this._authPrompt.reset();
if (!this._verifyingUser)
this._reset();
}
},
@ -551,118 +690,248 @@ const LoginDialog = new Lang.Class({
}
},
_updateLogoTexture: function(cache, uri) {
if (this._logoFileUri != uri)
return;
let icon = null;
if (this._logoFileUri)
icon = this._textureCache.load_uri_async(this._logoFileUri,
-1, _LOGO_ICON_HEIGHT);
this._logoBin.set_child(icon);
},
_updateLogo: function() {
let path = this._settings.get_string(GdmUtil.LOGO_KEY);
this._logoFileUri = path ? Gio.file_new_for_path(path).get_uri() : null;
this._updateLogoTexture(this._textureCache, this._logoFileUri);
},
_onPrompted: function() {
this._sessionMenuButton.updateSensitivity(true);
if (this._shouldShowSessionMenuButton())
this._authPrompt.setActorInDefaultButtonWell(this._sessionMenuButton.actor);
this._showPrompt();
},
_onReset: function(authPrompt, beginRequest) {
this._sessionMenuButton.updateSensitivity(true);
_reset: function() {
this._userVerifier.clear();
this._updateSensitivity(true);
this._promptMessage.hide();
this._user = null;
this._verifyingUser = false;
if (beginRequest == AuthPrompt.BeginRequestType.PROVIDE_USERNAME) {
if (this._disableUserList) {
this._authPrompt.cancelButton.hide();
this._hideUserListAskForUsernameAndBeginVerification();
} else {
this._showUserList();
}
} else {
this._authPrompt.cancelButton.hide();
this._hideUserListAndBeginVerification();
}
if (this._disableUserList)
this._hideUserListAndLogIn();
else
this._showUserList();
},
_verificationFailed: function() {
this._promptEntry.text = '';
this._updateSensitivity(true);
this._setWorking(false);
},
_onDefaultSessionChanged: function(client, sessionId) {
this._sessionMenuButton.setActiveSession(sessionId);
this._sessionList.setActiveSession(sessionId);
},
_shouldShowSessionMenuButton: function() {
if (this._authPrompt.verifyingUser)
return true;
if (!this._user)
return false;
if (this._user.is_logged_in)
return false;
return true;
_showMessage: function(userVerifier, message, styleClass) {
if (message) {
this._promptMessage.text = message;
this._promptMessage.styleClass = styleClass;
this._promptMessage.show();
} else {
this._promptMessage.hide();
}
},
_showPrompt: function() {
if (this._authPrompt.actor.visible)
return;
this._authPrompt.actor.opacity = 0;
this._authPrompt.actor.show();
Tweener.addTween(this._authPrompt.actor,
_showLoginHint: function(verifier, message) {
this._promptLoginHint.set_text(message)
this._promptLoginHint.show();
this._promptLoginHint.opacity = 255;
},
_hideLoginHint: function() {
this._promptLoginHint.hide();
this._promptLoginHint.set_text('');
},
cancel: function() {
if (this._verifyingUser)
this._userVerifier.cancel();
else
this._reset();
},
_showPrompt: function(forSecret) {
this._sessionList.actor.hide();
this._promptLabel.show();
this._promptEntry.show();
this._promptLoginHint.opacity = 0;
this._promptLoginHint.show();
this._promptBox.opacity = 0;
this._promptBox.show();
Tweener.addTween(this._promptBox,
{ opacity: 255,
time: _FADE_ANIMATION_TIME,
transition: 'easeOutQuad' });
if ((this._user && !this._user.is_logged_in()) || this._verifyingUser)
this._sessionList.actor.show();
this._promptEntry.grab_key_focus();
let hold = new Batch.Hold();
let tasks = [function() {
this._prepareDialog(forSecret, hold);
},
hold];
let batch = new Batch.ConcurrentBatch(this, tasks);
return batch.run();
},
_showRealmLoginHint: function(realmManager, hint) {
if (!hint)
_prepareDialog: function(forSecret, hold) {
this._workSpinner = new Panel.AnimatedIcon('process-working.svg', WORK_SPINNER_ICON_SIZE);
this._workSpinner.actor.opacity = 0;
this._workSpinner.actor.show();
this.buttonLayout.visible = true;
this.clearButtons();
if (!this._disableUserList || this._verifyingUser)
this.addButton({ action: Lang.bind(this, this.cancel),
label: _("Cancel"),
key: Clutter.Escape },
{ expand: true,
x_fill: false,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.MIDDLE });
this.buttonLayout.add(this._workSpinner.actor,
{ expand: false,
x_fill: false,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.MIDDLE });
this._signInButton = this.addButton({ action: Lang.bind(this, function() {
hold.release();
}),
label: forSecret ? C_("button", "Sign In") : _("Next"),
default: true },
{ expand: false,
x_fill: false,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.MIDDLE });
this._updateSignInButtonSensitivity(this._promptEntry.text.length > 0);
this._promptEntryTextChangedId =
this._promptEntry.clutter_text.connect('text-changed',
Lang.bind(this, function() {
this._updateSignInButtonSensitivity(this._promptEntry.text.length > 0);
}));
this._promptEntryActivateId =
this._promptEntry.clutter_text.connect('activate', function() {
hold.release();
});
},
_updateSensitivity: function(sensitive) {
this._promptEntry.reactive = sensitive;
this._promptEntry.clutter_text.editable = sensitive;
this._sessionList.updateSensitivity(sensitive);
this._updateSignInButtonSensitivity(sensitive);
},
_updateSignInButtonSensitivity: function(sensitive) {
if (this._signInButton) {
this._signInButton.reactive = sensitive;
this._signInButton.can_focus = sensitive;
}
},
_hidePrompt: function() {
this.setButtons([]);
if (this._promptEntryTextChangedId > 0) {
this._promptEntry.clutter_text.disconnect(this._promptEntryTextChangedId);
this._promptEntryTextChangedId = 0;
}
if (this._promptEntryActivateId > 0) {
this._promptEntry.clutter_text.disconnect(this._promptEntryActivateId);
this._promptEntryActivateId = 0;
}
this._setWorking(false);
this._promptBox.hide();
this._promptLoginHint.hide();
this._promptUser.set_child(null);
this._updateSensitivity(true);
this._promptEntry.set_text('');
this._sessionList.close();
this._promptLoginHint.hide();
this.clearButtons();
this._workSpinner = null;
this._signInButton = null;
},
_setWorking: function(working) {
if (!this._workSpinner)
return;
hint = hint.replace(/%U/g, 'user');
hint = hint.replace(/%D/g, 'DOMAIN');
hint = hint.replace(/%[^UD]/g, '');
// Translators: this message is shown below the username entry field
// to clue the user in on how to login to the local network realm
this._authPrompt.setHint(_("(e.g., user or %s)").format(hint));
if (working) {
this._workSpinner.play();
Tweener.addTween(this._workSpinner.actor,
{ opacity: 255,
delay: WORK_SPINNER_ANIMATION_DELAY,
time: WORK_SPINNER_ANIMATION_TIME,
transition: 'linear'
});
} else {
Tweener.addTween(this._workSpinner.actor,
{ opacity: 0,
time: WORK_SPINNER_ANIMATION_TIME,
transition: 'linear',
onCompleteScope: this,
onComplete: function() {
if (this._workSpinner)
this._workSpinner.stop();
}
});
}
},
_askForUsernameAndBeginVerification: function() {
this._authPrompt.setPasswordChar('');
this._authPrompt.setQuestion(_("Username: "));
_askQuestion: function(verifier, serviceName, question, passwordChar) {
this._promptLabel.set_text(question);
let realmManager = new Realmd.Manager();
let realmSignalId = realmManager.connect('login-format-changed',
Lang.bind(this, this._showRealmLoginHint));
this._showRealmLoginHint(realmManager.loginFormat);
this._updateSensitivity(true);
this._promptEntry.set_text('');
this._promptEntry.clutter_text.set_password_char(passwordChar);
let nextSignalId = this._authPrompt.connect('next',
Lang.bind(this, function() {
this._authPrompt.disconnect(nextSignalId);
this._authPrompt.updateSensitivity(false);
let answer = this._authPrompt.getAnswer();
this._authPrompt.clear();
this._authPrompt.cancelButton.show();
this._authPrompt.startSpinning();
this._authPrompt.begin({ userName: answer });
let tasks = [function() {
return this._showPrompt(!!passwordChar);
},
realmManager.disconnect(realmSignalId)
realmManager.release();
}));
this._showPrompt();
function() {
let text = this._promptEntry.get_text();
this._updateSensitivity(false);
this._setWorking(true);
this._userVerifier.answerQuery(serviceName, text);
}];
let batch = new Batch.ConsecutiveBatch(this, tasks);
return batch.run();
},
_askForUsernameAndLogIn: function() {
this._promptLabel.set_text(_("Username: "));
this._promptEntry.set_text('');
this._promptEntry.clutter_text.set_password_char('');
let tasks = [this._showPrompt,
function() {
let userName = this._promptEntry.get_text();
this._promptEntry.reactive = false;
return this._beginVerificationForUser(userName);
}];
let batch = new Batch.ConsecutiveBatch(this, tasks);
return batch.run();
},
_startSession: function(serviceName) {
Tweener.addTween(this.actor,
Tweener.addTween(this.dialogLayout,
{ opacity: 0,
time: _FADE_ANIMATION_TIME,
transition: 'easeOutQuad',
@ -671,7 +940,7 @@ const LoginDialog = new Lang.Class({
for (let i = 0; i < children.length; i++) {
if (children[i] != Main.layoutManager.screenShieldGroup)
children[i].opacity = this.actor.opacity;
children[i].opacity = this.dialogLayout.opacity;
}
},
onUpdateScope: this,
@ -685,9 +954,15 @@ const LoginDialog = new Lang.Class({
},
_onSessionOpened: function(client, serviceName) {
this._authPrompt.finish(Lang.bind(this, function() {
if (!this._userVerifier.hasPendingMessages) {
this._startSession(serviceName);
}));
} else {
let signalId = this._userVerifier.connect('no-more-messages',
Lang.bind(this, function() {
this._userVerifier.disconnect(signalId);
this._startSession(serviceName);
}));
}
},
_waitForItemForUser: function(userName) {
@ -819,42 +1094,38 @@ const LoginDialog = new Lang.Class({
this._userSelectionBox.visible = expanded;
},
_hideUserList: function() {
_hideUserListAndLogIn: function() {
this._setUserListExpanded(false);
if (this._userSelectionBox.visible)
GdmUtil.cloneAndFadeOutActor(this._userSelectionBox);
},
_hideUserListAskForUsernameAndBeginVerification: function() {
this._hideUserList();
this._askForUsernameAndBeginVerification();
},
_hideUserListAndBeginVerification: function() {
this._hideUserList();
this._authPrompt.begin();
GdmUtil.cloneAndFadeOutActor(this._userSelectionBox);
this._askForUsernameAndLogIn();
},
_showUserList: function() {
this._authPrompt.hide();
this._sessionMenuButton.close();
this._authPrompt.cancelButton.show();
this._hidePrompt();
this._setUserListExpanded(true);
this._notListedButton.show();
this._userList.actor.grab_key_focus();
},
_beginVerificationForItem: function(item) {
this._authPrompt.setUser(item.user);
let userName = item.user.get_user_name();
_beginVerificationForUser: function(userName) {
let hold = new Batch.Hold();
this._authPrompt.begin({ userName: userName,
hold: hold });
this._userVerifier.begin(userName, hold);
this._verifyingUser = true;
return hold;
},
_beginVerificationForItem: function(item) {
let userWidget = new UserWidget.UserWidget(item.user);
this._promptUser.set_child(userWidget.actor);
let tasks = [function() {
let userName = item.user.get_user_name();
return this._beginVerificationForUser(userName);
}];
let batch = new Batch.ConsecutiveBatch(this, tasks);
return batch.run();
},
_onUserListActivated: function(activatedItem) {
let tasks = [function() {
return GdmUtil.cloneAndFadeOutActor(this._userSelectionBox);
@ -897,23 +1168,21 @@ const LoginDialog = new Lang.Class({
}));
},
open: function() {
Main.ctrlAltTabManager.addGroup(this.actor,
_onOpened: function() {
Main.ctrlAltTabManager.addGroup(this.dialogLayout,
_("Login Window"),
'dialog-password-symbolic',
{ sortGroup: CtrlAltTab.SortGroup.MIDDLE });
this._userList.actor.grab_key_focus();
this.actor.show();
return true;
},
close: function() {
this.parent();
Main.ctrlAltTabManager.removeGroup(this.dialogLayout);
},
addCharacter: function(unichar) {
this._authPrompt.addCharacter(unichar);
this._promptEntry.clutter_text.insert_unichar(unichar);
},
});
Signals.addSignalMethods(LoginDialog.prototype);

View File

@ -63,7 +63,7 @@ const Manager = new Lang.Class({
Lang.bind(this, this._reloadRealms))
this._realms = {};
this._signalId = this._aggregateProvider.connect('g-properties-changed',
this._aggregateProvider.connect('g-properties-changed',
Lang.bind(this, function(proxy, properties) {
if ('Realms' in properties.deep_unpack())
this._reloadRealms();
@ -106,7 +106,7 @@ const Manager = new Lang.Class({
realm.connect('g-properties-changed',
Lang.bind(this, function(proxy, properties) {
if ('Configured' in properties.deep_unpack())
this._reloadRealm(realm);
this._reloadRealm();
}));
},
@ -134,18 +134,6 @@ const Manager = new Lang.Class({
this._updateLoginFormat();
return this._loginFormat;
},
release: function() {
Service(Gio.DBus.system,
'org.freedesktop.realmd',
'/org/freedesktop/realmd',
function(service) {
service.ReleaseRemote();
});
this._aggregateProvider.disconnect(this._signalId);
this._realms = { };
this._updateLoginFormat();
}
});
Signals.addSignalMethods(Manager.prototype)

View File

@ -6,26 +6,21 @@ const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Signals = imports.signals;
const St = imports.gi.St;
const Batch = imports.gdm.batch;
const Fprint = imports.gdm.fingerprint;
const Realmd = imports.gdm.realmd;
const Main = imports.ui.main;
const Params = imports.misc.params;
const ShellEntry = imports.ui.shellEntry;
const SmartcardManager = imports.misc.smartcardManager;
const Tweener = imports.ui.tweener;
const PASSWORD_SERVICE_NAME = 'gdm-password';
const FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint';
const SMARTCARD_SERVICE_NAME = 'gdm-smartcard';
const FADE_ANIMATION_TIME = 0.16;
const CLONE_FADE_ANIMATION_TIME = 0.25;
const LOGIN_SCREEN_SCHEMA = 'org.gnome.login-screen';
const PASSWORD_AUTHENTICATION_KEY = 'enable-password-authentication';
const FINGERPRINT_AUTHENTICATION_KEY = 'enable-fingerprint-authentication';
const SMARTCARD_AUTHENTICATION_KEY = 'enable-smartcard-authentication';
const BANNER_MESSAGE_KEY = 'banner-message-enable';
const BANNER_MESSAGE_TEXT_KEY = 'banner-message-text';
const ALLOWED_FAILURES_KEY = 'allowed-failures';
@ -120,34 +115,12 @@ const ShellUserVerifier = new Lang.Class({
this._client = client;
this._settings = new Gio.Settings({ schema: LOGIN_SCREEN_SCHEMA });
this._settings.connect('changed',
Lang.bind(this, function() {
this._updateDefaultService();
}));
this._updateDefaultService();
this._fprintManager = new Fprint.FprintManager();
this._smartcardManager = SmartcardManager.getSmartcardManager();
// We check for smartcards right away, since an inserted smartcard
// at startup should result in immediately initiating authentication.
// This is different than fingeprint readers, where we only check them
// after a user has been picked.
this._checkForSmartcard();
this._smartcardManager.connect('smartcard-inserted',
Lang.bind(this, function() {
this._checkForSmartcard();
}));
this._smartcardManager.connect('smartcard-removed',
Lang.bind(this, function() {
this._checkForSmartcard();
}));
this._realmManager = new Realmd.Manager();
this._messageQueue = [];
this._messageQueueTimeoutId = 0;
this.hasPendingMessages = false;
this.reauthenticating = false;
this._failCounter = 0;
},
@ -156,7 +129,6 @@ const ShellUserVerifier = new Lang.Class({
this._cancellable = new Gio.Cancellable();
this._hold = hold;
this._userName = userName;
this.reauthenticating = false;
this._checkForFingerprintReader();
@ -174,10 +146,8 @@ const ShellUserVerifier = new Lang.Class({
if (this._cancellable)
this._cancellable.cancel();
if (this._userVerifier) {
if (this._userVerifier)
this._userVerifier.call_cancel_sync(null);
this.clear();
}
},
clear: function() {
@ -195,14 +165,14 @@ const ShellUserVerifier = new Lang.Class({
},
answerQuery: function(serviceName, answer) {
if (!this.hasPendingMessages) {
if (!this._userVerifier.hasPendingMessages) {
this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
} else {
let signalId = this.connect('no-more-messages',
Lang.bind(this, function() {
this.disconnect(signalId);
this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
}));
let signalId = this._userVerifier.connect('no-more-messages',
Lang.bind(this, function() {
this._userVerifier.disconnect(signalId);
this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
}));
}
},
@ -272,28 +242,6 @@ const ShellUserVerifier = new Lang.Class({
}));
},
_checkForSmartcard: function() {
let smartcardDetected;
if (!this._settings.get_boolean(SMARTCARD_AUTHENTICATION_KEY))
smartcardDetected = false;
else if (this.reauthenticating)
smartcardDetected = this._smartcardManager.hasInsertedLoginToken();
else
smartcardDetected = this._smartcardManager.hasInsertedTokens();
if (smartcardDetected != this.smartcardDetected) {
this.smartcardDetected = smartcardDetected;
if (this.smartcardDetected)
this._preemptingService = SMARTCARD_SERVICE_NAME;
else if (this._preemptingService == SMARTCARD_SERVICE_NAME)
this._preemptingService = null;
this.emit('smartcard-status-changed');
}
},
_reportInitError: function(where, error) {
logError(error, where);
this._hold.release();
@ -319,7 +267,6 @@ const ShellUserVerifier = new Lang.Class({
return;
}
this.reauthenticating = true;
this._connectSignals();
this._beginVerification();
this._hold.release();
@ -350,35 +297,11 @@ const ShellUserVerifier = new Lang.Class({
this._userVerifier.connect('verification-complete', Lang.bind(this, this._onVerificationComplete));
},
_getForegroundService: function() {
if (this._preemptingService)
return this._preemptingService;
return this._defaultService;
},
serviceIsForeground: function(serviceName) {
return serviceName == this._getForegroundService();
},
serviceIsDefault: function(serviceName) {
return serviceName == this._defaultService;
},
_updateDefaultService: function() {
if (this._settings.get_boolean(PASSWORD_AUTHENTICATION_KEY))
this._defaultService = PASSWORD_SERVICE_NAME;
else if (this._settings.get_boolean(SMARTCARD_AUTHENTICATION_KEY))
this._defaultService = SMARTCARD_SERVICE_NAME;
else if (this._settings.get_boolean(FINGERPRINT_AUTHENTICATION_KEY))
this._defaultService = FINGERPRINT_SERVICE_NAME;
},
_beginVerification: function() {
this._hold.acquire();
if (this._userName) {
this._userVerifier.call_begin_verification_for_user(this._getForegroundService(),
this._userVerifier.call_begin_verification_for_user(PASSWORD_SERVICE_NAME,
this._userName,
this._cancellable,
Lang.bind(this, function(obj, result) {
@ -394,7 +317,7 @@ const ShellUserVerifier = new Lang.Class({
this._hold.release();
}));
if (this._haveFingerprintReader && !this.serviceIsForeground(FINGERPRINT_SERVICE_NAME)) {
if (this._haveFingerprintReader) {
this._hold.acquire();
this._userVerifier.call_begin_verification_for_user(FINGERPRINT_SERVICE_NAME,
@ -414,7 +337,7 @@ const ShellUserVerifier = new Lang.Class({
}));
}
} else {
this._userVerifier.call_begin_verification(this._getForegroundService(),
this._userVerifier.call_begin_verification(PASSWORD_SERVICE_NAME,
this._cancellable,
Lang.bind(this, function(obj, result) {
try {
@ -432,36 +355,58 @@ const ShellUserVerifier = new Lang.Class({
},
_onInfo: function(client, serviceName, info) {
if (this.serviceIsForeground(serviceName)) {
this._queueMessage(info, 'login-dialog-message-info');
} else if (serviceName == FINGERPRINT_SERVICE_NAME &&
// We don't display fingerprint messages, because they
// have words like UPEK in them. Instead we use the messages
// as a cue to display our own message.
if (serviceName == FINGERPRINT_SERVICE_NAME &&
this._haveFingerprintReader) {
// We don't show fingeprint messages directly since it's
// not the main auth service. Instead we use the messages
// as a cue to display our own message.
// Translators: this message is shown below the password entry field
// to indicate the user can swipe their finger instead
this.emit('show-login-hint', _("(or swipe finger)"));
} else if (serviceName == PASSWORD_SERVICE_NAME) {
this._queueMessage(info, 'login-dialog-message-info');
}
},
_onProblem: function(client, serviceName, problem) {
if (!this.serviceIsForeground(serviceName))
// we don't want to show auth failed messages to
// users who haven't enrolled their fingerprint.
if (serviceName != PASSWORD_SERVICE_NAME)
return;
this._queueMessage(problem, 'login-dialog-message-warning');
},
_showRealmLoginHint: function() {
if (this._realmManager.loginFormat) {
let hint = this._realmManager.loginFormat;
hint = hint.replace(/%U/g, 'user');
hint = hint.replace(/%D/g, 'DOMAIN');
hint = hint.replace(/%[^UD]/g, '');
// Translators: this message is shown below the username entry field
// to clue the user in on how to login to the local network realm
this.emit('show-login-hint',
_("(e.g., user or %s)").format(hint));
}
},
_onInfoQuery: function(client, serviceName, question) {
if (!this.serviceIsForeground(serviceName))
// We only expect questions to come from the main auth service
if (serviceName != PASSWORD_SERVICE_NAME)
return;
this._showRealmLoginHint();
this._realmLoginHintSignalId = this._realmManager.connect('login-format-changed',
Lang.bind(this, this._showRealmLoginHint));
this.emit('ask-question', serviceName, question, '');
},
_onSecretInfoQuery: function(client, serviceName, secretQuestion) {
if (!this.serviceIsForeground(serviceName))
// We only expect secret requests to come from the main auth service
if (serviceName != PASSWORD_SERVICE_NAME)
return;
this.emit('ask-question', serviceName, secretQuestion, '\u25cf');
@ -470,7 +415,6 @@ const ShellUserVerifier = new Lang.Class({
_onReset: function() {
// Clear previous attempts to authenticate
this._failCounter = 0;
this._updateDefaultService();
this.emit('reset');
},
@ -499,24 +443,24 @@ const ShellUserVerifier = new Lang.Class({
this._failCounter < this._settings.get_int(ALLOWED_FAILURES_KEY);
if (canRetry) {
if (!this.hasPendingMessages) {
if (!this._userVerifier.hasPendingMessages) {
this._retry();
} else {
let signalId = this.connect('no-more-messages',
Lang.bind(this, function() {
this.disconnect(signalId);
this._retry();
}));
let signalId = this._userVerifier.connect('no-more-messages',
Lang.bind(this, function() {
this._userVerifier.disconnect(signalId);
this._retry();
}));
}
} else {
if (!this.hasPendingMessages) {
if (!this._userVerifier.hasPendingMessages) {
this._cancelAndReset();
} else {
let signalId = this.connect('no-more-messages',
Lang.bind(this, function() {
this.disconnect(signalId);
this._cancelAndReset();
}));
let signalId = this._userVerifier.connect('no-more-messages',
Lang.bind(this, function() {
this._userVerifier.disconnect(signalId);
this._cancelAndReset();
}));
}
}
@ -527,11 +471,16 @@ const ShellUserVerifier = new Lang.Class({
// if the password service fails, then cancel everything.
// But if, e.g., fingerprint fails, still give
// password authentication a chance to succeed
if (this.serviceIsForeground(serviceName)) {
if (serviceName == PASSWORD_SERVICE_NAME) {
this._verificationFailed(true);
}
this.emit('hide-login-hint');
if (this._realmLoginHintSignalId) {
this._realmManager.disconnect(this._realmLoginHintSignalId);
this._realmLoginHintSignalId = 0;
}
},
});
Signals.addSignalMethods(ShellUserVerifier.prototype);

View File

@ -58,7 +58,6 @@ const Map = new Lang.Class({
_init: function(iterable) {
this._pool = { };
this._size = 0;
if (iterable) {
for (let i = 0; i < iterable.length; i++) {
@ -100,7 +99,6 @@ const Map = new Lang.Class({
node.value = value;
} else {
this._pool[hash] = { key: key, value: value };
this._size++;
}
},
@ -110,7 +108,6 @@ const Map = new Lang.Class({
if (node && _sameValue(node.key, key)) {
delete this._pool[hash];
this._size--;
return [node.key, node.value];
} else {
return [null, null];
@ -139,6 +136,6 @@ const Map = new Lang.Class({
},
size: function() {
return this._size;
return Object.getOwnPropertyNames(this._pool).length;
},
});

View File

@ -1,275 +0,0 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Params = imports.misc.params;
const Signals = imports.signals;
// Specified in the D-Bus specification here:
// http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager
const ObjectManagerIface = <interface name="org.freedesktop.DBus.ObjectManager">
<method name="GetManagedObjects">
<arg name="objects" type="a{oa{sa{sv}}}" direction="out"/>
</method>
<signal name="InterfacesAdded">
<arg name="objectPath" type="o"/>
<arg name="interfaces" type="a{sa{sv}}" />
</signal>
<signal name="InterfacesRemoved">
<arg name="objectPath" type="o"/>
<arg name="interfaces" type="as" />
</signal>
</interface>
const ObjectManagerInfo = Gio.DBusInterfaceInfo.new_for_xml(ObjectManagerIface);
const ObjectManager = new Lang.Class({
Name: 'ObjectManager',
_init: function(params) {
params = Params.parse(params, { connection: null,
name: null,
objectPath: null,
knownInterfaces: null,
cancellable: null,
onLoaded: null });
this._connection = params.connection;
this._serviceName = params.name;
this._managerPath = params.objectPath;
this._cancellable = params.cancellable;
this._managerProxy = new Gio.DBusProxy({ g_connection: this._connection,
g_interface_name: ObjectManagerInfo.name,
g_interface_info: ObjectManagerInfo,
g_name: this._serviceName,
g_object_path: this._managerPath,
g_flags: Gio.DBusProxyFlags.NONE });
this._interfaceInfos = {};
this._objects = {};
this._interfaces = {};
this._pendingProxies = [];
this._onLoaded = params.onLoaded;
if (params.knownInterfaces)
this._registerInterfaces(params.knownInterfaces);
this._managerProxy.init_async(GLib.PRIORITY_DEFAULT,
this._cancellable,
Lang.bind(this, this._onManagerProxyLoaded));
},
_addInterface: function(objectPath, interfaceName, onFinished) {
let info = this._interfaceInfos[interfaceName];
if (!info)
return;
let proxy = new Gio.DBusProxy({ g_connection: this._connection,
g_name: this._serviceName,
g_object_path: objectPath,
g_interface_name: interfaceName,
g_interface_info: info,
g_flags: Gio.DBusProxyFlags.NONE });
this._pendingProxies.push(proxy);
proxy.init_async(GLib.PRIORITY_DEFAULT,
this._cancellable,
Lang.bind(this, function(initable, result) {
let index = this._pendingProxies.indexOf(proxy);
if (index >= 0)
this._pendingProxies.splice(index, 1);
let error = null;
try {
initable.init_finish(result);
} catch(e) {
logError(e, 'could not initialize proxy for interface ' + interfaceName);
if (onFinished)
onFinished();
return;
}
let isNewObject;
if (!this._objects[objectPath]) {
this._objects[objectPath] = {};
isNewObject = true;
} else {
isNewObject = false;
}
this._objects[objectPath][interfaceName] = proxy;
if (!this._interfaces[interfaceName])
this._interfaces[interfaceName] = [];
this._interfaces[interfaceName].push(proxy);
if (isNewObject)
this.emit('object-added', objectPath);
this.emit('interface-added', interfaceName, proxy);
if (onFinished)
onFinished();
}));
},
_removeInterface: function(objectPath, interfaceName) {
if (!this._objects[objectPath])
return;
let proxy = this._objects[objectPath][interfaceName];
if (this._interfaces[interfaceName]) {
let index = this._interfaces[interfaceName].indexOf(proxy);
if (index >= 0)
this._interfaces[interfaceName].splice(index, 1);
if (this._interfaces[interfaceName].length == 0)
delete this._interfaces[interfaceName];
}
this.emit('interface-removed', interfaceName, proxy);
this._objects[objectPath][interfaceName] = null;
if (Object.keys(this._objects[objectPath]).length == 0) {
delete this._objects[objectPath];
this.emit('object-removed', objectPath);
}
},
_onManagerProxyLoaded: function(initable, result) {
let error = null;
try {
initable.init_finish(result);
} catch(e) {
logError(e, 'could not initialize object manager for object ' + params.name);
if (this._onLoaded)
this._onLoaded();
return;
}
this._managerProxy.connectSignal('InterfacesAdded',
Lang.bind(this, function(objectManager, sender, [objectPath, interfaces]) {
let interfaceNames = Object.keys(interfaces);
for (let i = 0; i < interfaceNames.length; i++)
this._addInterface(objectPath, interfaceNames[i]);
}));
this._managerProxy.connectSignal('InterfacesRemoved',
Lang.bind(this, function(objectManager, sender, [objectPath, interfaceNames]) {
for (let i = 0; i < interfaceNames.length; i++)
this._removeInterface(objectPath, interfaceNames[i]);
}));
this._managerProxy.GetManagedObjectsRemote(Lang.bind(this, function(result, error) {
if (!result) {
if (error) {
logError(error, 'could not get remote objects for service ' + this._serviceName + ' path ' + this._managerPath);
}
if (this._onLoaded)
this._onLoaded();
return;
}
let [objects] = result;
if (Object.keys(this._interfaceInfos).length == 0) {
if (this._onLoaded)
this._onLoaded();
return;
}
let numLoadInhibitors = 0;
// First inhibitor is to prevent onLoaded from getting
// called until all interfaces have started being added.
// Subsequent inhibitors are to prevent onLoaded from getting
// called until all interfaces finish getting added.
numLoadInhibitors++;
let objectPaths = Object.keys(objects);
for (let i = 0; i < objectPaths.length; i++) {
let objectPath = objectPaths[i];
let object = objects[objectPath];
let interfaceNames = Object.keys(object);
for (let j = 0; j < interfaceNames.length; j++) {
let interfaceName = interfaceNames[j];
numLoadInhibitors++;
this._addInterface(objectPath,
interfaceName,
Lang.bind(this, function() {
numLoadInhibitors--;
if (numLoadInhibitors == 0) {
if (this._onLoaded)
this._onLoaded();
}
}));
}
}
numLoadInhibitors--;
if (numLoadInhibitors == 0) {
if (this._onLoaded)
this._onLoaded();
}
}));
},
_registerInterfaces: function(interfaces) {
for (let i = 0; i < interfaces.length; i++) {
let info = Gio.DBusInterfaceInfo.new_for_xml(interfaces[i]);
this._interfaceInfos[info.name] = info;
}
},
getProxy: function(objectPath, interfaceName) {
let object = this._objects[objectPath];
if (!object)
return null;
return object[interfaceName];
},
getProxiesForInterface: function(interfaceName) {
let proxyList = this._interfaces[interfaceName];
if (!proxyList)
return [];
return proxyList;
},
getAllProxies: function() {
let proxies = [];
let objectPaths = Object.keys(this._objects);
for (let i = 0; i < objectPaths.length; i++) {
let object = this._objects[objectPaths];
let interfaceNames = Object.keys(object);
for (let j = 0; i < interfaceNames.length; i++) {
let interfaceName = interfaceNames[i];
if (object[interfaceName])
proxies.push(object(interfaceName));
}
}
return proxies;
}
});
Signals.addSignalMethods(ObjectManager.prototype);

View File

@ -1,142 +0,0 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const ObjectManager = imports.misc.objectManager;
const _SMARTCARD_SERVICE_DBUS_NAME = "org.gnome.SettingsDaemon.Smartcard";
const SmartcardManagerIface = <interface name="org.gnome.SettingsDaemon.Smartcard.Manager">
<method name="GetLoginToken">
<arg name="token" type="o" direction="out"/>
</method>
<method name="GetInsertedTokens">
<arg name="tokens" type="ao" direction="out"/>
</method>
</interface>;
const SmartcardTokenIface = <interface name="org.gnome.SettingsDaemon.Smartcard.Token">
<property name="Name" type="s" access="read"/>
<property name="Driver" type="o" access="read"/>
<property name="IsInserted" type="b" access="read"/>
<property name="UsedToLogin" type="b" access="read"/>
</interface>;
const SmartcardDriverIface = <interface name="org.gnome.SettingsDaemon.Smartcard.Driver">
<property name="Library" type="s" access="read"/>
<property name="Description" type="s" access="read"/>
</interface>;
let _smartcardManager = null;
function getSmartcardManager() {
if (_smartcardManager == null)
_smartcardManager = new SmartcardManager();
return _smartcardManager;
}
const SmartcardManager = new Lang.Class({
Name: 'SmartcardManager',
_init: function() {
this._objectManager = new ObjectManager.ObjectManager({ connection: Gio.DBus.session,
name: _SMARTCARD_SERVICE_DBUS_NAME,
objectPath: '/org/gnome/SettingsDaemon/Smartcard',
knownInterfaces: [ SmartcardManagerIface,
SmartcardTokenIface,
SmartcardDriverIface ],
onLoaded: Lang.bind(this, this._onLoaded) });
this._insertedTokens = {};
this._removedTokens = {};
this._loginToken = null;
},
_onLoaded: function() {
let tokens = this._objectManager.getProxiesForInterface('org.gnome.SettingsDaemon.Smartcard.Token');
for (let i = 0; i < tokens.length; i++)
this._addToken(tokens[i]);
this._objectManager.connect('interface-added', Lang.bind(this, function(objectManager, interfaceName, proxy) {
if (interfaceName == 'org.gnome.SettingsDaemon.Smartcard.Token')
this._addToken(proxy);
}));
this._objectManager.connect('interface-removed', Lang.bind(this, function(objectManager, interfaceName, proxy) {
if (interfaceName == 'org.gnome.SettingsDaemon.Smartcard.Token')
this._removeToken(proxy);
}));
},
_updateToken: function(token) {
let objectPath = token.get_object_path();
delete this._insertedTokens[objectPath];
delete this._removedTokens[objectPath];
if (token.IsInserted)
this._insertedTokens[objectPath] = token;
else
this._removedTokens[objectPath] = token;
if (token.UsedToLogin)
this._loginToken = token;
},
_addToken: function(token) {
this._updateToken(token);
token.connect('g-properties-changed',
Lang.bind(this, function(proxy, properties) {
if ('IsInserted' in properties.deep_unpack()) {
this._updateToken(token);
if (token.IsInserted) {
this.emit('smartcard-inserted', token.Name);
} else {
this.emit('smartcard-removed', token.Name);
}
}
}));
// Emit a smartcard-inserted at startup if it's already plugged in
if (token.IsInserted)
this.emit('smartcard-inserted', token.Name);
},
_removeToken: function(token) {
let objectPath = token.get_object_path();
if (objectPath) {
if (this._removedTokens[objectPath] == token) {
delete this._removedTokens[objectPath];
}
if (this._insertedTokens[objectPath] == token) {
delete this._insertedTokens[objectPath];
this.emit('smartcard-removed', token.Name);
}
}
token.disconnectAll();
},
hasInsertedTokens: function() {
return Object.keys(this._insertedTokens).length > 0;
},
hasInsertedLoginToken: function() {
if (!this._loginToken)
return false;
if (!this._loginToken.IsInserted)
return false;
return true;
}
});
Signals.addSignalMethods(SmartcardManager.prototype);

View File

@ -18,7 +18,7 @@ const _urlRegexp = new RegExp(
'(^|' + _leadingJunk + ')' +
'(' +
'(?:' +
'(?:http|https|ftp)://' + // scheme://
'[a-z][\\w-]+://' + // scheme://
'|' +
'www\\d{0,3}[.]' + // www.
'|' +

View File

@ -233,10 +233,8 @@ const AppSwitcherPopup = new Lang.Class({
_finish : function(timestamp) {
let appIcon = this._items[this._selectedIndex];
if (this._currentWindow < 0)
appIcon.app.activate_window(appIcon.cachedWindows[0], timestamp);
else
Main.activateWindow(appIcon.cachedWindows[this._currentWindow], timestamp);
let window = this._currentWindow > 0 ? this._currentWindow : 0;
appIcon.app.activate_window(appIcon.cachedWindows[window], timestamp);
this.parent();
},
@ -436,11 +434,8 @@ const AppSwitcher = new Lang.Class({
this._arrows = [];
let windowTracker = Shell.WindowTracker.get_default();
let settings = new Gio.Settings({ schema: 'org.gnome.shell.app-switcher' });
let workspace = settings.get_boolean('current-workspace-only') ? global.screen.get_active_workspace()
: null;
let allWindows = global.display.get_tab_list(Meta.TabList.NORMAL,
global.screen, workspace);
global.screen, null);
// Construct the AppIcons, add to the popup
for (let i = 0; i < apps.length; i++) {
@ -450,9 +445,7 @@ const AppSwitcher = new Lang.Class({
appIcon.cachedWindows = allWindows.filter(function(w) {
return windowTracker.get_window_app (w) == appIcon.app;
});
if (workspace == null || appIcon.cachedWindows.length > 0) {
this._addIcon(appIcon);
}
this._addIcon(appIcon);
}
this._curApp = -1;

View File

@ -1,84 +0,0 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const St = imports.gi.St;
const Signals = imports.signals;
const Atk = imports.gi.Atk;
const ANIMATED_ICON_UPDATE_TIMEOUT = 100;
const Animation = new Lang.Class({
Name: 'Animation',
_init: function(filename, width, height, speed) {
this.actor = new St.Bin();
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
this._speed = speed;
this._isLoaded = false;
this._isPlaying = false;
this._timeoutId = 0;
this._frame = 0;
this._animations = St.TextureCache.get_default().load_sliced_image (filename, width, height,
Lang.bind(this, this._animationsLoaded));
this.actor.set_child(this._animations);
},
play: function() {
if (this._isLoaded && this._timeoutId == 0) {
if (this._frame == 0)
this._showFrame(0);
this._timeoutId = Mainloop.timeout_add(this._speed, Lang.bind(this, this._update));
}
this._isPlaying = true;
},
stop: function() {
if (this._timeoutId > 0) {
Mainloop.source_remove(this._timeoutId);
this._timeoutId = 0;
}
this._isPlaying = false;
},
_showFrame: function(frame) {
let oldFrameActor = this._animations.get_child_at_index(this._frame);
if (oldFrameActor)
oldFrameActor.hide();
this._frame = (frame % this._animations.get_n_children());
let newFrameActor = this._animations.get_child_at_index(this._frame);
if (newFrameActor)
newFrameActor.show();
},
_update: function() {
this._showFrame(this._frame + 1);
return true;
},
_animationsLoaded: function() {
this._isLoaded = true;
if (this._isPlaying)
this.play();
},
_onDestroy: function() {
this.stop();
}
});
const AnimatedIcon = new Lang.Class({
Name: 'AnimatedIcon',
Extends: Animation,
_init: function(filename, size) {
this.parent(filename, size, size, ANIMATED_ICON_UPDATE_TIMEOUT);
}
});

View File

@ -187,9 +187,6 @@ const AllView = new Lang.Class({
_init: function() {
this.parent();
this._grid.actor.y_align = Clutter.ActorAlign.START;
this._grid.actor.y_expand = true;
let box = new St.BoxLayout({ vertical: true });
this._stack = new St.Widget({ layout_manager: new AllViewLayout() });
this._stack.add_actor(this._grid.actor);
@ -279,12 +276,8 @@ const AllView = new Lang.Class({
this._eventBlocker.reactive = isOpen;
this._currentPopup = isOpen ? popup : null;
this._updateIconOpacities(isOpen);
if (isOpen) {
if (isOpen)
this._ensureIconVisible(popup.actor);
this._grid.actor.y = popup.parentOffset;
} else {
this._grid.actor.y = 0;
}
}));
},
@ -323,8 +316,6 @@ const FrequentView = new Lang.Class({
loadApps: function() {
let mostUsed = this._usage.get_most_used ("");
for (let i = 0; i < mostUsed.length; i++) {
if (!mostUsed[i].get_app_info().should_show())
continue;
let appIcon = new AppIcon(mostUsed[i]);
this._grid.addItem(appIcon.actor, -1);
}
@ -336,31 +327,6 @@ const Views = {
ALL: 1
};
const ControlsBoxLayout = Lang.Class({
Name: 'ControlsBoxLayout',
Extends: Clutter.BoxLayout,
/**
* Override the BoxLayout behavior to use the maximum preferred width of all
* buttons for each child
*/
vfunc_get_preferred_width: function(container, forHeight) {
let maxMinWidth = 0;
let maxNaturalWidth = 0;
for (let child = container.get_first_child();
child;
child = child.get_next_sibling()) {
let [minWidth, natWidth] = child.get_preferred_width(forHeight);
maxMinWidth = Math.max(maxMinWidth, minWidth);
maxNaturalWidth = Math.max(maxNaturalWidth, natWidth);
}
let childrenCount = container.get_n_children();
let totalSpacing = this.spacing * (childrenCount - 1);
return [maxMinWidth * childrenCount + totalSpacing,
maxNaturalWidth * childrenCount + totalSpacing];
}
});
const AppDisplay = new Lang.Class({
Name: 'AppDisplay',
@ -375,9 +341,6 @@ const AppDisplay = new Lang.Class({
global.settings.connect('changed::app-folder-categories', Lang.bind(this, function() {
Main.queueDeferredWork(this._allAppsWorkId);
}));
this._privacySettings = new Gio.Settings({ schema: 'org.gnome.desktop.privacy' });
this._privacySettings.connect('changed::remember-app-usage',
Lang.bind(this, this._updateFrequentVisibility));
this._views = [];
@ -404,10 +367,9 @@ const AppDisplay = new Lang.Class({
x_expand: true, y_expand: true });
this.actor.add(this._viewStack, { expand: true });
let layout = new ControlsBoxLayout({ homogeneous: true });
let layout = new Clutter.BoxLayout({ homogeneous: true });
this._controls = new St.Widget({ style_class: 'app-view-controls',
layout_manager: layout });
layout.hookup_style(this._controls);
this.actor.add(new St.Bin({ child: this._controls }));
@ -422,7 +384,6 @@ const AppDisplay = new Lang.Class({
}));
}
this._showView(Views.FREQUENT);
this._updateFrequentVisibility();
// We need a dummy actor to catch the keyboard focus if the
// user Ctrl-Alt-Tabs here before the deferred work creates
@ -452,19 +413,6 @@ const AppDisplay = new Lang.Class({
}
},
_updateFrequentVisibility: function() {
let enabled = this._privacySettings.get_boolean('remember-app-usage');
this._views[Views.FREQUENT].control.visible = enabled;
let visibleViews = this._views.filter(function(v) {
return v.control.visible;
});
this._controls.visible = visibleViews.length > 1;
if (!enabled && this._views[Views.FREQUENT].view.actor.visible)
this._showView(Views.ALL);
},
_redisplay: function() {
this._redisplayFrequentApps();
this._redisplayAllApps();
@ -535,11 +483,11 @@ const AppSearchProvider = new Lang.Class({
},
getInitialResultSet: function(terms) {
this.searchSystem.setResults(this, this._appSys.initial_search(terms));
this.searchSystem.pushResults(this, this._appSys.initial_search(terms));
},
getSubsearchResultSet: function(previousResults, terms) {
this.searchSystem.setResults(this, this._appSys.subsearch(previousResults, terms));
this.searchSystem.pushResults(this, this._appSys.subsearch(previousResults, terms));
},
activateResult: function(app) {
@ -624,11 +572,7 @@ const FolderIcon = new Lang.Class({
// Position the popup above or below the source icon
if (side == St.Side.BOTTOM) {
this._popup.actor.show();
let closeButtonOffset = -this._popup.closeButton.translation_y;
let y = this.actor.y - this._popup.actor.height;
let yWithButton = y - closeButtonOffset;
this._popup.parentOffset = yWithButton < 0 ? -yWithButton : 0;
this._popup.actor.y = Math.max(y, closeButtonOffset);
this._popup.actor.y = this.actor.y - this._popup.actor.height;
this._popup.actor.hide();
} else {
this._popup.actor.y = this.actor.y + this.actor.height;
@ -651,7 +595,6 @@ const AppFolderPopup = new Lang.Class({
this._arrowSide = side;
this._isOpen = false;
this.parentOffset = 0;
this.actor = new St.Widget({ layout_manager: new Clutter.BinLayout(),
visible: false,
@ -675,31 +618,17 @@ const AppFolderPopup = new Lang.Class({
this.actor.add_actor(this._boxPointer.actor);
this._boxPointer.bin.set_child(this._view.actor);
this.closeButton = Util.makeCloseButton();
this.closeButton.connect('clicked', Lang.bind(this, this.popdown));
this.actor.add_actor(this.closeButton);
let closeButton = Util.makeCloseButton();
closeButton.connect('clicked', Lang.bind(this, this.popdown));
this.actor.add_actor(closeButton);
this._boxPointer.actor.bind_property('opacity', this.closeButton, 'opacity',
this._boxPointer.actor.bind_property('opacity', closeButton, 'opacity',
GObject.BindingFlags.SYNC_CREATE);
global.focus_manager.add_group(this.actor);
source.actor.connect('destroy', Lang.bind(this,
function() {
this.actor.destroy();
}));
this.actor.connect('key-press-event', Lang.bind(this, this._onKeyPress));
},
_onKeyPress: function(actor, event) {
if (!this._isOpen)
return false;
if (event.get_key_symbol() != Clutter.KEY_Escape)
return false;
this.popdown();
return true;
},
toggle: function() {
@ -714,7 +643,6 @@ const AppFolderPopup = new Lang.Class({
return;
this.actor.show();
this.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
this._boxPointer.setArrowActor(this._source.actor);
this._boxPointer.show(BoxPointer.PopupAnimation.FADE |

View File

@ -126,11 +126,6 @@ const BackgroundCache = new Lang.Class({
},
removeImageContent: function(content) {
let filename = content.get_filename();
if (filename && this._fileMonitors[filename])
delete this._fileMonitors[filename];
this._removeContent(this._images, content);
},
@ -142,40 +137,33 @@ const BackgroundCache = new Lang.Class({
cancellable: null,
onFinished: null });
let fileLoad = { filename: params.filename,
style: params.style,
shouldCopy: false,
monitorIndex: params.monitorIndex,
effects: params.effects,
onFinished: params.onFinished,
cancellable: new Gio.Cancellable(), };
this._pendingFileLoads.push(fileLoad);
if (params.cancellable) {
params.cancellable.connect(Lang.bind(this, function(c) {
fileLoad.cancellable.cancel();
}));
for (let i = 0; i < this._pendingFileLoads.length; i++) {
if (this._pendingFileLoads[i].filename == params.filename &&
this._pendingFileLoads[i].style == params.style) {
this._pendingFileLoads[i].callers.push({ shouldCopy: true,
monitorIndex: params.monitorIndex,
effects: params.effects,
onFinished: params.onFinished });
return;
}
}
this._pendingFileLoads.push({ filename: params.filename,
style: params.style,
callers: [{ shouldCopy: false,
monitorIndex: params.monitorIndex,
effects: params.effects,
onFinished: params.onFinished }] });
let content = new Meta.Background({ meta_screen: global.screen,
monitor: params.monitorIndex,
effects: params.effects });
content.load_file_async(params.filename,
params.style,
fileLoad.cancellable,
params.cancellable,
Lang.bind(this,
function(object, result) {
if (fileLoad.cancellable.is_cancelled()) {
if (params.cancellable && params.cancellable.is_cancelled()) {
if (params.onFinished)
params.onFinished(null);
this._removePendingFileLoad(fileLoad);
return;
}
return;
}
try {
content.load_file_finish(result);
@ -185,25 +173,22 @@ const BackgroundCache = new Lang.Class({
content = null;
}
let needsCopy = false;
for (let i = 0; i < this._pendingFileLoads.length; i++) {
let pendingLoad = this._pendingFileLoads[i];
if (pendingLoad.filename != params.filename ||
pendingLoad.style != params.style)
continue;
if (pendingLoad.cancellable.is_cancelled())
continue;
for (let j = 0; j < pendingLoad.callers.length; j++) {
if (pendingLoad.callers[j].onFinished) {
if (content && pendingLoad.callers[j].shouldCopy) {
content = object.copy(pendingLoad.callers[j].monitorIndex,
pendingLoad.callers[j].effects);
pendingLoad.cancellable.cancel();
if (pendingLoad.onFinished) {
if (content && needsCopy) {
content = object.copy(pendingLoad.monitorIndex,
pendingLoad.effects);
}
pendingLoad.callers[j].onFinished(content);
}
needsCopy = true;
pendingLoad.onFinished(content);
}
this._pendingFileLoads.splice(i, 1);
@ -211,15 +196,6 @@ const BackgroundCache = new Lang.Class({
}));
},
_removePendingFileLoad: function(fileLoad) {
for (let i = 0; i < this._pendingFileLoads.length; i++) {
if (this._pendingFileLoads[i].cancellable == fileLoad.cancellable) {
this._pendingFileLoads.splice(i, 1);
break;
}
}
},
getImageContent: function(params) {
params = Params.parse(params, { monitorIndex: 0,
style: null,
@ -450,7 +426,7 @@ const Background = new Lang.Class({
content.brightness = this._brightness;
content.vignette_sharpness = this._vignetteSharpness;
this._cache.removeImageContent(this._images[index].content);
this._cache.removeImageContent(content);
this._images[index].content = content;
this._watchCacheFile(filename);
},
@ -590,16 +566,7 @@ const Background = new Lang.Class({
}
let uri = this._settings.get_string(PICTURE_URI_KEY);
let filename;
if (GLib.uri_parse_scheme(uri) != null)
filename = Gio.File.new_for_uri(uri).get_path();
else
filename = uri;
if (!filename) {
this._setLoaded();
return;
}
let filename = Gio.File.new_for_uri(uri).get_path();
this._loadFile(filename);
},
@ -775,15 +742,11 @@ const BackgroundManager = new Lang.Class({
time: FADE_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() {
if (this._newBackground == newBackground) {
if (this.background == background) {
this.background = newBackground;
this._newBackground = null;
} else {
newBackground.actor.destroy();
background.actor.destroy();
}
background.actor.destroy();
this.emit('changed');
})
});

View File

@ -46,10 +46,8 @@ function addBackgroundMenu(actor) {
clickAction.connect('long-press', function(action, actor, state) {
if (state == Clutter.LongPressState.QUERY)
return action.get_button() == 1 && !actor._backgroundMenu.isOpen;
if (state == Clutter.LongPressState.ACTIVATE) {
if (state == Clutter.LongPressState.ACTIVATE)
openMenu();
actor._backgroundManager.ignoreRelease();
}
return true;
});
clickAction.connect('clicked', function(action) {
@ -62,7 +60,5 @@ function addBackgroundMenu(actor) {
actor._backgroundMenu.destroy();
actor._backgroundMenu = null;
actor._backgroundManager = null;
cursor.destroy();
});
}

View File

@ -589,12 +589,12 @@ const BoxPointer = new Lang.Class({
return St.Side.TOP;
break;
case St.Side.LEFT:
if (sourceAllocation.x2 + boxWidth > monitor.x + monitor.width &&
if (sourceAllocation.y2 + boxWidth > monitor.x + monitor.width &&
boxWidth < sourceAllocation.x1 - monitor.x)
return St.Side.RIGHT;
break;
case St.Side.RIGHT:
if (sourceAllocation.x1 - boxWidth < monitor.x &&
if (sourceAllocation.y1 - boxWidth < monitor.x &&
boxWidth < monitor.x + monitor.width - sourceAllocation.x2)
return St.Side.LEFT;
break;

View File

@ -443,18 +443,16 @@ const Calendar = new Lang.Class({
this.actor.add(this._topBox,
{ row: 0, col: 0, col_span: offsetCols + 7 });
this._backButton = new St.Button({ style_class: 'calendar-change-month-back',
can_focus: true });
this._topBox.add(this._backButton);
this._backButton.connect('clicked', Lang.bind(this, this._onPrevMonthButtonClicked));
let back = new St.Button({ style_class: 'calendar-change-month-back' });
this._topBox.add(back);
back.connect('clicked', Lang.bind(this, this._onPrevMonthButtonClicked));
this._monthLabel = new St.Label({style_class: 'calendar-month-label'});
this._topBox.add(this._monthLabel, { expand: true, x_fill: false, x_align: St.Align.MIDDLE });
this._forwardButton = new St.Button({ style_class: 'calendar-change-month-forward',
can_focus: true });
this._topBox.add(this._forwardButton);
this._forwardButton.connect('clicked', Lang.bind(this, this._onNextMonthButtonClicked));
let forward = new St.Button({ style_class: 'calendar-change-month-forward' });
this._topBox.add(forward);
forward.connect('clicked', Lang.bind(this, this._onNextMonthButtonClicked));
// Add weekday labels...
//
@ -513,12 +511,10 @@ const Calendar = new Lang.Class({
}
}
this._backButton.grab_key_focus();
this.setDate(newDate, false);
},
},
_onNextMonthButtonClicked: function() {
_onNextMonthButtonClicked: function() {
let newDate = new Date(this._selectedDate);
let oldMonth = newDate.getMonth();
if (oldMonth == 11) {
@ -537,9 +533,7 @@ const Calendar = new Lang.Class({
}
}
this._forwardButton.grab_key_focus();
this.setDate(newDate, false);
this.setDate(newDate, false);
},
_onSettingsChange: function() {
@ -596,8 +590,7 @@ const Calendar = new Lang.Class({
// nRows here means 6 weeks + one header + one navbar
let nRows = 8;
while (row < 8) {
let button = new St.Button({ label: iter.getDate().toString(),
can_focus: true });
let button = new St.Button({ label: iter.getDate().toString() });
let rtl = button.get_text_direction() == Clutter.TextDirection.RTL;
if (this._eventSource.isDummy)
@ -605,12 +598,8 @@ const Calendar = new Lang.Class({
let iterStr = iter.toUTCString();
button.connect('clicked', Lang.bind(this, function() {
this._shouldDateGrabFocus = true;
let newlySelectedDate = new Date(iterStr);
this.setDate(newlySelectedDate, false);
this._shouldDateGrabFocus = false;
}));
let hasEvents = this._eventSource.hasEvents(iter);
@ -635,6 +624,9 @@ const Calendar = new Lang.Class({
else if (iter.getMonth() != this._selectedDate.getMonth())
styleClass += ' calendar-other-month-day';
if (_sameDay(this._selectedDate, iter))
button.add_style_pseudo_class('active');
if (hasEvents)
styleClass += ' calendar-day-with-events'
@ -644,13 +636,6 @@ const Calendar = new Lang.Class({
this.actor.add(button,
{ row: row, col: offsetCols + (7 + iter.getDay() - this._weekStart) % 7 });
if (_sameDay(this._selectedDate, iter)) {
button.add_style_pseudo_class('active');
if (this._shouldDateGrabFocus)
button.grab_key_focus();
}
if (this._useWeekdate && iter.getDay() == 4) {
let label = new St.Label({ text: _getCalendarWeekForDate(iter).toString(),
style_class: 'calendar-day-base calendar-week-number'});
@ -675,7 +660,7 @@ const EventsList = new Lang.Class({
Name: 'EventsList',
_init: function() {
this.actor = new St.Table({ style_class: 'events-table' });
this.actor = new St.BoxLayout({ vertical: true, style_class: 'events-header-vbox'});
this._date = new Date();
this._desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
this._desktopSettings.connect('changed', Lang.bind(this, this._update));
@ -687,72 +672,55 @@ const EventsList = new Lang.Class({
this._eventSource.connect('changed', Lang.bind(this, this._update));
},
_addEvent: function(event, index, includeDayName) {
let dayString;
if (includeDayName)
dayString = _getEventDayAbbreviation(event.date.getDay());
else
dayString = '';
let dayLabel = new St.Label({ style_class: 'events-day-dayname',
text: dayString });
dayLabel.clutter_text.line_wrap = false;
dayLabel.clutter_text.ellipsize = false;
this.actor.add(dayLabel, { row: index, col: 0,
x_expand: false, x_align: St.Align.END,
y_fill: false, y_align: St.Align.START });
let clockFormat = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);
let timeString = _formatEventTime(event, clockFormat);
let timeLabel = new St.Label({ style_class: 'events-day-time',
text: timeString });
timeLabel.clutter_text.line_wrap = false;
timeLabel.clutter_text.ellipsize = false;
this.actor.add(timeLabel, { row: index, col: 1,
x_expand: false, x_align: St.Align.MIDDLE,
y_fill: false, y_align: St.Align.START });
let titleLabel = new St.Label({ style_class: 'events-day-task',
text: event.summary });
titleLabel.clutter_text.line_wrap = true;
titleLabel.clutter_text.ellipsize = false;
this.actor.add(titleLabel, { row: index, col: 2,
x_expand: true, x_align: St.Align.START,
y_fill: false, y_align: St.Align.START });
_addEvent: function(dayNameBox, timeBox, eventTitleBox, includeDayName, day, time, desc) {
if (includeDayName) {
dayNameBox.add(new St.Label( { style_class: 'events-day-dayname',
text: day } ),
{ x_fill: true } );
}
timeBox.add(new St.Label( { style_class: 'events-day-time',
text: time} ),
{ x_fill: true } );
eventTitleBox.add(new St.Label( { style_class: 'events-day-task',
text: desc} ));
},
_addPeriod: function(header, index, begin, end, includeDayName, showNothingScheduled) {
_addPeriod: function(header, begin, end, includeDayName, showNothingScheduled) {
let events = this._eventSource.getEvents(begin, end);
if (events.length == 0 && !showNothingScheduled)
return index;
let clockFormat = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);;
this.actor.add(new St.Label({ style_class: 'events-day-header', text: header }),
{ row: index, col: 0, col_span: 3,
// In theory, x_expand should be true here, but x_expand
// is a property of the column for StTable, ie all day cells
// get it too
x_expand: false, x_align: St.Align.START,
y_fill: false, y_align: St.Align.START });
index++;
if (events.length == 0 && !showNothingScheduled)
return;
let vbox = new St.BoxLayout( {vertical: true} );
this.actor.add(vbox);
vbox.add(new St.Label({ style_class: 'events-day-header', text: header }));
let box = new St.BoxLayout({style_class: 'events-header-hbox'});
let dayNameBox = new St.BoxLayout({ vertical: true, style_class: 'events-day-name-box' });
let timeBox = new St.BoxLayout({ vertical: true, style_class: 'events-time-box' });
let eventTitleBox = new St.BoxLayout({ vertical: true, style_class: 'events-event-box' });
box.add(dayNameBox, {x_fill: false});
box.add(timeBox, {x_fill: false});
box.add(eventTitleBox, {expand: true});
vbox.add(box);
for (let n = 0; n < events.length; n++) {
this._addEvent(events[n], index, includeDayName);
index++;
let event = events[n];
let dayString = _getEventDayAbbreviation(event.date.getDay());
let timeString = _formatEventTime(event, clockFormat);
let summaryString = event.summary;
this._addEvent(dayNameBox, timeBox, eventTitleBox, includeDayName, dayString, timeString, summaryString);
}
if (events.length == 0 && showNothingScheduled) {
let now = new Date();
/* Translators: Text to show if there are no events */
let nothingEvent = new CalendarEvent(now, now, _("Nothing Scheduled"), true);
this._addEvent(nothingEvent, index, false);
index++;
let timeString = _formatEventTime(nothingEvent, clockFormat);
this._addEvent(dayNameBox, timeBox, eventTitleBox, false, "", timeString, nothingEvent.summary);
}
return index;
},
_showOtherDay: function(day) {
@ -769,21 +737,20 @@ const EventsList = new Lang.Class({
else
/* Translators: Shown on calendar heading when selected day occurs on different year */
dayString = day.toLocaleFormat(C_("calendar heading", "%A, %B %d, %Y"));
this._addPeriod(dayString, 0, dayBegin, dayEnd, false, true);
this._addPeriod(dayString, dayBegin, dayEnd, false, true);
},
_showToday: function() {
this.actor.destroy_all_children();
let index = 0;
let now = new Date();
let dayBegin = _getBeginningOfDay(now);
let dayEnd = _getEndOfDay(now);
index = this._addPeriod(_("Today"), index, dayBegin, dayEnd, false, true);
this._addPeriod(_("Today"), dayBegin, dayEnd, false, true);
let tomorrowBegin = new Date(dayBegin.getTime() + 86400 * 1000);
let tomorrowEnd = new Date(dayEnd.getTime() + 86400 * 1000);
index = this._addPeriod(_("Tomorrow"), index, tomorrowBegin, tomorrowEnd, false, true);
this._addPeriod(_("Tomorrow"), tomorrowBegin, tomorrowEnd, false, true);
let dayInWeek = (dayEnd.getDay() - this._weekStart + 7) % 7;
@ -794,7 +761,7 @@ const EventsList = new Lang.Class({
*/
let thisWeekBegin = new Date(dayBegin.getTime() + 2 * 86400 * 1000);
let thisWeekEnd = new Date(dayEnd.getTime() + (6 - dayInWeek) * 86400 * 1000);
index = this._addPeriod(_("This week"), index, thisWeekBegin, thisWeekEnd, true, false);
this._addPeriod(_("This week"), thisWeekBegin, thisWeekEnd, true, false);
} else {
/* otherwise it's one of the two last days of the week ... show
* "Next week" and include events up until and including *next*
@ -802,7 +769,7 @@ const EventsList = new Lang.Class({
*/
let nextWeekBegin = new Date(dayBegin.getTime() + 2 * 86400 * 1000);
let nextWeekEnd = new Date(dayEnd.getTime() + (13 - dayInWeek) * 86400 * 1000);
index = this._addPeriod(_("Next week"), index, nextWeekBegin, nextWeekEnd, true, false);
this._addPeriod(_("Next week"), nextWeekBegin, nextWeekEnd, true, false);
}
},

View File

@ -31,7 +31,7 @@ function shouldAutorunMount(mount, forTransient) {
if (!volume || (!volume.allowAutorun && forTransient))
return false;
if (root.is_native() && isMountRootHidden(root))
if (!root.is_native() || isMountRootHidden(root))
return false;
return true;

View File

@ -25,7 +25,7 @@ const KeyringDialog = new Lang.Class({
this.prompt = new Shell.KeyringPrompt();
this.prompt.connect('show-password', Lang.bind(this, this._onShowPassword));
this.prompt.connect('show-confirm', Lang.bind(this, this._onShowConfirm));
this.prompt.connect('prompt-close', Lang.bind(this, this._onHidePrompt));
this.prompt.connect('hide-prompt', Lang.bind(this, this._onHidePrompt));
let mainContentBox = new St.BoxLayout({ style_class: 'prompt-dialog-main-layout',
vertical: false });
@ -63,17 +63,11 @@ const KeyringDialog = new Lang.Class({
this._cancelButton = this.addButton({ label: '',
action: Lang.bind(this, this._onCancelButton),
key: Clutter.Escape },
{ expand: true, x_fill: false, x_align: St.Align.START });
this.placeSpinner({ expand: false,
x_fill: false,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.MIDDLE });
key: Clutter.Escape });
this._continueButton = this.addButton({ label: '',
action: Lang.bind(this, this._onContinueButton),
default: true },
{ expand: false, x_fill: false, x_align: St.Align.END });
{ expand: true, x_fill: false, x_align: St.Align.END });
this.prompt.bind_property('cancel-label', this._cancelButton, 'label', GObject.BindingFlags.SYNC_CREATE);
this.prompt.bind_property('continue-label', this._continueButton, 'label', GObject.BindingFlags.SYNC_CREATE);
@ -149,19 +143,11 @@ const KeyringDialog = new Lang.Class({
},
_updateSensitivity: function(sensitive) {
if (this._passwordEntry) {
this._passwordEntry.reactive = sensitive;
this._passwordEntry.clutter_text.editable = sensitive;
}
if (this._confirmEntry) {
this._confirmEntry.reactive = sensitive;
this._confirmEntry.clutter_text.editable = sensitive;
}
this._passwordEntry.reactive = sensitive;
this._passwordEntry.clutter_text.editable = sensitive;
this._continueButton.can_focus = sensitive;
this._continueButton.reactive = sensitive;
this.setWorking(!sensitive);
},
_ensureOpen: function() {

View File

@ -16,7 +16,7 @@ const PolkitAgent = imports.gi.PolkitAgent;
const Components = imports.ui.components;
const ModalDialog = imports.ui.modalDialog;
const ShellEntry = imports.ui.shellEntry;
const UserWidget = imports.ui.userWidget;
const UserMenu = imports.ui.userMenu;
const DIALOG_ICON_SIZE = 48;
@ -31,6 +31,7 @@ const AuthenticationDialog = new Lang.Class({
this.message = message;
this.userNames = userNames;
this._wasDismissed = false;
this._completed = false;
let mainContentBox = new St.BoxLayout({ style_class: 'prompt-dialog-main-layout',
vertical: false });
@ -100,9 +101,9 @@ const AuthenticationDialog = new Lang.Class({
let userBox = new St.BoxLayout({ style_class: 'polkit-dialog-user-layout',
vertical: false });
messageBox.add(userBox);
this._userAvatar = new UserWidget.Avatar(this._user,
{ iconSize: DIALOG_ICON_SIZE,
styleClass: 'polkit-dialog-user-icon' });
this._userAvatar = new UserMenu.UserAvatarWidget(this._user,
{ iconSize: DIALOG_ICON_SIZE,
styleClass: 'polkit-dialog-user-icon' });
this._userAvatar.actor.hide();
userBox.add(this._userAvatar.actor,
{ x_fill: true,
@ -160,32 +161,26 @@ const AuthenticationDialog = new Lang.Class({
this._cancelButton = this.addButton({ label: _("Cancel"),
action: Lang.bind(this, this.cancel),
key: Clutter.Escape },
{ expand: true, x_fill: false, x_align: St.Align.START });
this.placeSpinner({ expand: false,
x_fill: false,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.MIDDLE });
key: Clutter.Escape });
this._okButton = this.addButton({ label: _("Authenticate"),
action: Lang.bind(this, this._onAuthenticateButtonPressed),
default: true },
{ expand: false, x_fill: false, x_align: St.Align.END });
{ expand: true, x_fill: false, x_align: St.Align.END });
this._doneEmitted = false;
this._identityToAuth = Polkit.UnixUser.new_for_name(userName);
this._cookie = cookie;
},
performAuthentication: function() {
this.destroySession();
this._session = new PolkitAgent.Session({ identity: this._identityToAuth,
cookie: this._cookie });
this._session.connect('completed', Lang.bind(this, this._onSessionCompleted));
this._session.connect('request', Lang.bind(this, this._onSessionRequest));
this._session.connect('show-error', Lang.bind(this, this._onSessionShowError));
this._session.connect('show-info', Lang.bind(this, this._onSessionShowInfo));
},
startAuthentication: function() {
this._session.initiate();
},
@ -207,14 +202,14 @@ const AuthenticationDialog = new Lang.Class({
log('polkitAuthenticationAgent: Failed to show modal dialog.' +
' Dismissing authentication request for action-id ' + this.actionId +
' cookie ' + this._cookie);
this._emitDone(true);
this._emitDone(false, true);
}
},
_emitDone: function(dismissed) {
_emitDone: function(keepVisible, dismissed) {
if (!this._doneEmitted) {
this._doneEmitted = true;
this.emit('done', dismissed);
this.emit('done', keepVisible, dismissed);
}
},
@ -224,7 +219,6 @@ const AuthenticationDialog = new Lang.Class({
this._okButton.can_focus = sensitive;
this._okButton.reactive = sensitive;
this.setWorking(!sensitive);
},
_onEntryActivate: function() {
@ -243,16 +237,12 @@ const AuthenticationDialog = new Lang.Class({
},
_onSessionCompleted: function(session, gainedAuthorization) {
if (this._completed || this._doneEmitted)
if (this._completed)
return;
this._completed = true;
/* Yay, all done */
if (gainedAuthorization) {
this._emitDone(false);
} else {
if (!gainedAuthorization) {
/* Unless we are showing an existing error message from the PAM
* module (the PAM module could be reporting the authentication
* error providing authentication-method specific information),
@ -268,10 +258,8 @@ const AuthenticationDialog = new Lang.Class({
this._infoMessageLabel.hide();
this._nullMessageLabel.hide();
}
/* Try and authenticate again */
this.performAuthentication();
}
this._emitDone(!gainedAuthorization, false);
},
_onSessionRequest: function(session, request, echo_on) {
@ -315,7 +303,6 @@ const AuthenticationDialog = new Lang.Class({
if (this._session) {
if (!this._completed)
this._session.cancel();
this._completed = false;
this._session = null;
}
},
@ -330,7 +317,7 @@ const AuthenticationDialog = new Lang.Class({
cancel: function() {
this._wasDismissed = true;
this.close(global.get_current_time());
this._emitDone(true);
this._emitDone(false, true);
},
});
Signals.addSignalMethods(AuthenticationDialog.prototype);
@ -340,6 +327,7 @@ const AuthenticationAgent = new Lang.Class({
_init: function() {
this._currentDialog = null;
this._isCompleting = false;
this._handle = null;
this._native = new Shell.PolkitAuthenticationAgent();
this._native.connect('initiate', Lang.bind(this, this._onInitiate));
@ -376,24 +364,45 @@ const AuthenticationAgent = new Lang.Class({
// discussion.
this._currentDialog.connect('done', Lang.bind(this, this._onDialogDone));
this._currentDialog.performAuthentication();
this._currentDialog.startAuthentication();
},
_onCancel: function(nativeAgent) {
this._completeRequest(false);
this._completeRequest(false, false);
},
_onDialogDone: function(dialog, dismissed) {
this._completeRequest(dismissed);
_onDialogDone: function(dialog, keepVisible, dismissed) {
this._completeRequest(keepVisible, dismissed);
},
_completeRequest: function(dismissed) {
_reallyCompleteRequest: function(dismissed) {
this._currentDialog.close();
this._currentDialog.destroySession();
this._currentDialog = null;
this._isCompleting = false;
this._native.complete(dismissed);
this._native.complete(dismissed)
},
_completeRequest: function(keepVisible, wasDismissed) {
if (this._isCompleting)
return;
this._isCompleting = true;
if (keepVisible) {
// Give the user 2 seconds to read 'Authentication Failure' before
// dismissing the dialog
Mainloop.timeout_add(2000,
Lang.bind(this,
function() {
this._reallyCompleteRequest(wasDismissed);
return false;
}));
} else {
this._reallyCompleteRequest(wasDismissed);
}
}
});
const Component = AuthenticationAgent;

View File

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

View File

@ -18,7 +18,7 @@ const Params = imports.misc.params;
const PopupMenu = imports.ui.popupMenu;
// See Notification.appendMessage
const SCROLLBACK_IMMEDIATE_TIME = 3 * 60; // 3 minutes
const SCROLLBACK_IMMEDIATE_TIME = 60; // 1 minute
const SCROLLBACK_RECENT_TIME = 15 * 60; // 15 minutes
const SCROLLBACK_RECENT_LENGTH = 20;
const SCROLLBACK_IDLE_LENGTH = 5;
@ -967,8 +967,7 @@ const ChatNotification = new Lang.Class({
let timeLabel = this._append({ body: this._formatTimestamp(lastMessageDate),
group: 'meta',
styles: ['chat-meta-message'],
childProps: { expand: true, x_fill: false,
x_align: St.Align.END },
childProps: { expand: true, x_fill: false },
noTimestamp: true,
timestamp: lastMessageTime });

View File

@ -58,10 +58,15 @@ const CtrlAltTabManager = new Lang.Class({
},
focusGroup: function(item, timestamp) {
if (item.focusCallback)
if (item.focusCallback) {
item.focusCallback(timestamp);
else
} else {
if (global.stage_input_mode == Shell.StageInputMode.NONREACTIVE ||
global.stage_input_mode == Shell.StageInputMode.NORMAL)
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
item.root.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
}
},
// Sort the items into a consistent order; panel first, tray last,
@ -84,25 +89,19 @@ const CtrlAltTabManager = new Lang.Class({
let items = this._items.filter(function (item) { return item.proxy.mapped; });
// And add the windows metacity would show in its Ctrl-Alt-Tab list
if (Main.sessionMode.hasWindows && !Main.overview.visible) {
if (!Main.overview.visible) {
let screen = global.screen;
let display = screen.get_display();
let windows = display.get_tab_list(Meta.TabList.DOCKS, screen, screen.get_active_workspace ());
let windowTracker = Shell.WindowTracker.get_default();
let textureCache = St.TextureCache.get_default();
for (let i = 0; i < windows.length; i++) {
let icon = null;
let iconName = null;
if (windows[i].get_window_type () == Meta.WindowType.DESKTOP) {
iconName = 'video-display-symbolic';
} else {
let app = windowTracker.get_window_app(windows[i]);
if (app)
icon = app.create_icon_texture(POPUP_APPICON_SIZE);
else
icon = textureCache.bind_pixbuf_property(windows[i], 'icon');
}
let icon;
let app = windowTracker.get_window_app(windows[i]);
if (app)
icon = app.create_icon_texture(POPUP_APPICON_SIZE);
else
icon = textureCache.bind_pixbuf_property(windows[i], 'icon');
items.push({ name: windows[i].title,
proxy: windows[i].get_compositor_private(),
focusCallback: Lang.bind(windows[i],
@ -110,7 +109,6 @@ const CtrlAltTabManager = new Lang.Class({
Main.activateWindow(this, timestamp);
}),
iconActor: icon,
iconName: iconName,
sortGroup: SortGroup.MIDDLE });
}
}
@ -132,6 +130,8 @@ const CtrlAltTabManager = new Lang.Class({
},
_focusWindows: function(timestamp) {
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
global.stage.key_focus = null;
global.screen.focus_default_window(timestamp);
}
});

View File

@ -287,7 +287,13 @@ const ShowAppsIcon = new Lang.Class({
},
handleDragOver: function(source, actor, x, y, time) {
if (!this._canRemoveApp(getAppFromSource(source)))
let app = getAppFromSource(source);
if (app == null)
return DND.DragMotionResult.NO_DROP;
let id = app.get_id();
let isFavorite = AppFavorites.getAppFavorites().isFavorite(id);
if (!isFavorite)
return DND.DragMotionResult.NO_DROP;
return DND.DragMotionResult.MOVE_DROP;
@ -295,7 +301,7 @@ const ShowAppsIcon = new Lang.Class({
acceptDrop: function(source, actor, x, y, time) {
let app = getAppFromSource(source);
if (!this._canRemoveApp(app))
if (app == null)
return false;
let id = app.get_id();
@ -320,16 +326,6 @@ const DragPlaceholderItem = new Lang.Class({
}
});
const EmptyDropTargetItem = new Lang.Class({
Name: 'EmptyDropTargetItem',
Extends: DashItemContainer,
_init: function() {
this.parent();
this.setChild(new St.Bin({ style_class: 'empty-dash-drop-target' }));
}
});
const DashActor = new Lang.Class({
Name: 'DashActor',
Extends: St.Widget,
@ -445,12 +441,6 @@ const Dash = new Lang.Class({
dragMotion: Lang.bind(this, this._onDragMotion)
};
DND.addDragMonitor(this._dragMonitor);
if (this._box.get_n_children() == 0) {
this._emptyDropTarget = new EmptyDropTargetItem();
this._box.insert_child_at_index(this._emptyDropTarget, 0);
this._emptyDropTarget.show(true);
}
},
_onDragCancelled: function() {
@ -467,7 +457,6 @@ const Dash = new Lang.Class({
_endDrag: function() {
this._clearDragPlaceholder();
this._clearEmptyDropTarget();
this._showAppsIcon.setDragApp(null);
DND.removeDragMonitor(this._dragMonitor);
},
@ -808,21 +797,9 @@ const Dash = new Lang.Class({
_clearDragPlaceholder: function() {
if (this._dragPlaceholder) {
this._animatingPlaceholdersCount++;
this._dragPlaceholder.animateOutAndDestroy();
this._dragPlaceholder.connect('destroy',
Lang.bind(this, function() {
this._animatingPlaceholdersCount--;
}));
this._dragPlaceholder = null;
}
this._dragPlaceholderPos = -1;
},
_clearEmptyDropTarget: function() {
if (this._emptyDropTarget) {
this._emptyDropTarget.animateOutAndDestroy();
this._emptyDropTarget = null;
this._dragPlaceholderPos = -1;
}
},
@ -850,18 +827,23 @@ const Dash = new Lang.Class({
numChildren--;
}
let pos;
if (!this._emptyDropTarget)
pos = Math.floor(y * numChildren / boxHeight);
else
pos = 0; // always insert at the top when dash is empty
let pos = Math.floor(y * numChildren / boxHeight);
if (pos != this._dragPlaceholderPos && pos <= numFavorites && this._animatingPlaceholdersCount == 0) {
this._dragPlaceholderPos = pos;
// Don't allow positioning before or after self
if (favPos != -1 && (pos == favPos || pos == favPos + 1)) {
this._clearDragPlaceholder();
if (this._dragPlaceholder) {
this._dragPlaceholder.animateOutAndDestroy();
this._animatingPlaceholdersCount++;
this._dragPlaceholder.connect('destroy',
Lang.bind(this, function() {
this._animatingPlaceholdersCount--;
}));
}
this._dragPlaceholder = null;
return DND.DragMotionResult.CONTINUE;
}
@ -886,9 +868,9 @@ const Dash = new Lang.Class({
// Remove the drag placeholder if we are not in the
// "favorites zone"
if (pos > numFavorites)
if (pos > numFavorites && this._dragPlaceholder) {
this._clearDragPlaceholder();
}
if (!this._dragPlaceholder)
return DND.DragMotionResult.NO_DROP;

View File

@ -49,6 +49,11 @@ const DateMenuButton = new Lang.Class({
menuAlignment = 1.0 - menuAlignment;
this.parent(menuAlignment);
// At this moment calendar menu is not keyboard navigable at
// all (so not accessible), so it doesn't make sense to set as
// role ATK_ROLE_MENU like other elements of the panel.
this.actor.accessible_role = Atk.Role.LABEL;
this._clockDisplay = new St.Label();
this.actor.add_actor(this._clockDisplay);
@ -80,22 +85,27 @@ const DateMenuButton = new Lang.Class({
vbox.add(this._calendar.actor);
let separator = new PopupMenu.PopupSeparatorMenuItem();
vbox.add(separator.actor, { y_align: St.Align.END, expand: true, y_fill: false });
separator.setColumnWidths(1);
vbox.add(separator.actor, {y_align: St.Align.END, expand: true, y_fill: false});
this._openCalendarItem = new PopupMenu.PopupMenuItem(_("Open Calendar"));
this._openCalendarItem.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
this._openCalendarItem.actor.can_focus = false;
vbox.add(this._openCalendarItem.actor, {y_align: St.Align.END, expand: true, y_fill: false});
this._openClocksItem = new PopupMenu.PopupMenuItem(_("Open Clocks"));
this._openClocksItem.connect('activate', Lang.bind(this, this._onOpenClocksActivate));
this._openClocksItem.actor.can_focus = false;
vbox.add(this._openClocksItem.actor, {y_align: St.Align.END, expand: true, y_fill: false});
Shell.AppSystem.get_default().connect('installed-changed',
Lang.bind(this, this._appInstalledChanged));
this._appInstalledChanged();
item = this.menu.addSettingsAction(_("Date & Time Settings"), 'gnome-datetime-panel.desktop');
if (item) {
item.actor.show_on_set_parent = false;
item.actor.can_focus = false;
item.actor.reparent(vbox);
this._dateAndTimeSeparator = separator;
}
@ -106,7 +116,12 @@ const DateMenuButton = new Lang.Class({
hbox.add(this._separator);
// Fill up the second column
hbox.add(this._eventList.actor, { expand: true, y_fill: false, y_align: St.Align.START });
vbox = new St.BoxLayout({ name: 'calendarEventsArea',
vertical: true });
hbox.add(vbox, { expand: true });
// Event list
vbox.add(this._eventList.actor, { expand: true });
// Whenever the menu is opened, select today
this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) {
@ -142,25 +157,24 @@ const DateMenuButton = new Lang.Class({
},
_appInstalledChanged: function() {
this._calendarApp = undefined;
this._updateEventsVisibility();
let app = Shell.AppSystem.get_default().lookup_app('gnome-clocks.desktop');
this._openClocksItem.actor.visible = app !== null;
},
_updateEventsVisibility: function() {
let visible = this._eventSource.hasCalendars;
this._openCalendarItem.actor.visible = visible &&
(this._getCalendarApp() != null);
this._openClocksItem.actor.visible = visible &&
(this._getClockApp() != null);
this._openCalendarItem.actor.visible = visible;
this._openClocksItem.actor.visible = visible;
this._separator.visible = visible;
this._eventList.actor.visible = visible;
if (visible) {
let alignment = 0.25;
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
alignment = 1.0 - alignment;
this.menu._arrowAlignment = alignment;
let alignment = 0.25;
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
alignment = 1.0 - alignment;
this.menu._arrowAlignment = alignment;
this._eventList.actor.get_parent().show();
} else {
this.menu._arrowAlignment = 0.5;
this.menu._arrowAlignment = 0.5;
this._eventList.actor.get_parent().hide();
}
},
@ -203,34 +217,18 @@ const DateMenuButton = new Lang.Class({
this._date.set_text(displayDate.toLocaleFormat(dateFormat));
},
_getCalendarApp: function() {
if (this._calendarApp !== undefined)
return this._calendarApp;
let apps = Gio.AppInfo.get_recommended_for_type('text/calendar');
if (apps && (apps.length > 0))
this._calendarApp = apps[0];
else
this._calendarApp = null;
return this._calendarApp;
},
_getClockApp: function() {
return Shell.AppSystem.get_default().lookup_app('gnome-clocks.desktop');
},
_onOpenCalendarActivate: function() {
this.menu.close();
let app = this._getCalendarApp();
if (app.get_id() == 'evolution.desktop')
app = Gio.DesktopAppInfo.new('evolution-calendar.desktop');
let app = Gio.AppInfo.get_default_for_type('text/calendar', false);
if (app.get_id() == 'evolution')
app = Gio.DesktopAppInfo.new('evolution-calendar');
app.launch([], global.create_app_launch_context());
},
_onOpenClocksActivate: function() {
this.menu.close();
let app = this._getClockApp();
let app = Shell.AppSystem.get_default().lookup_app('gnome-clocks.desktop');
app.activate();
}
});

View File

@ -1,7 +1,6 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const St = imports.gi.St;
const Lang = imports.lang;
@ -44,7 +43,9 @@ let dragMonitors = [];
function _getEventHandlerActor() {
if (!eventHandlerActor) {
eventHandlerActor = new Clutter.Actor({ width: 0, height: 0 });
eventHandlerActor = new Clutter.Rectangle();
eventHandlerActor.width = 0;
eventHandlerActor.height = 0;
Main.uiGroup.add_actor(eventHandlerActor);
// We connect to 'event' rather than 'captured-event' because the capturing phase doesn't happen
// when you've grabbed the pointer.
@ -148,16 +149,16 @@ const _Draggable = new Lang.Class({
_grabEvents: function() {
if (!this._eventsGrabbed) {
this._eventsGrabbed = Main.pushModal(_getEventHandlerActor());
if (this._eventsGrabbed)
Clutter.grab_pointer(_getEventHandlerActor());
Clutter.grab_pointer(_getEventHandlerActor());
Clutter.grab_keyboard(_getEventHandlerActor());
this._eventsGrabbed = true;
}
},
_ungrabEvents: function() {
if (this._eventsGrabbed) {
Clutter.ungrab_pointer();
Main.popModal(_getEventHandlerActor());
Clutter.ungrab_keyboard();
this._eventsGrabbed = false;
}
},
@ -290,19 +291,19 @@ const _Draggable = new Lang.Class({
this._dragOrigY = this._dragActor.y;
this._dragOrigScale = this._dragActor.scale_x;
// Set the actor's scale such that it will keep the same
// transformed size when it's reparented to the uiGroup
let [scaledWidth, scaledHeight] = this.actor.get_transformed_size();
this._dragActor.set_scale(scaledWidth / this.actor.width,
scaledHeight / this.actor.height);
this._dragActor.reparent(Main.uiGroup);
this._dragActor.raise_top();
Shell.util_set_hidden_from_pick(this._dragActor, true);
let [actorStageX, actorStageY] = this.actor.get_transformed_position();
this._dragOffsetX = actorStageX - this._dragStartX;
this._dragOffsetY = actorStageY - this._dragStartY;
this._dragActor.reparent(Main.uiGroup);
this._dragActor.raise_top();
Shell.util_set_hidden_from_pick(this._dragActor, true);
// Set the actor's scale such that it will keep the same
// transformed size when it's reparented to the uiGroup
let [scaledWidth, scaledHeight] = this.actor.get_transformed_size();
this.actor.set_scale(scaledWidth / this.actor.width,
scaledHeight / this.actor.height);
}
this._dragOrigOpacity = this._dragActor.opacity;
@ -359,65 +360,60 @@ const _Draggable = new Lang.Class({
return true;
},
_updateDragHover : function () {
let target = this._dragActor.get_stage().get_actor_at_pos(Clutter.PickMode.ALL,
this._dragX, this._dragY);
let dragEvent = {
x: this._dragX,
y: this._dragY,
dragActor: this._dragActor,
source: this.actor._delegate,
targetActor: target
};
for (let i = 0; i < dragMonitors.length; i++) {
let motionFunc = dragMonitors[i].dragMotion;
if (motionFunc) {
let result = motionFunc(dragEvent);
if (result != DragMotionResult.CONTINUE) {
global.set_cursor(DRAG_CURSOR_MAP[result]);
return false;
}
}
}
while (target) {
if (target._delegate && target._delegate.handleDragOver) {
let [r, targX, targY] = target.transform_stage_point(this._dragX, this._dragY);
// We currently loop through all parents on drag-over even if one of the children has handled it.
// We can check the return value of the function and break the loop if it's true if we don't want
// to continue checking the parents.
let result = target._delegate.handleDragOver(this.actor._delegate,
this._dragActor,
targX,
targY,
0);
if (result != DragMotionResult.CONTINUE) {
global.set_cursor(DRAG_CURSOR_MAP[result]);
return false;
}
}
target = target.get_parent();
}
global.set_cursor(Shell.Cursor.DND_IN_DRAG);
return false;
},
_queueUpdateDragHover: function() {
if (this._updateHoverId)
GLib.source_remove(this._updateHoverId);
this._updateHoverId = GLib.idle_add(GLib.PRIORITY_DEFAULT,
Lang.bind(this, this._updateDragHover));
},
_updateDragPosition : function (event) {
let [stageX, stageY] = event.get_coords();
this._dragX = stageX;
this._dragY = stageY;
this._dragActor.set_position(stageX + this._dragOffsetX,
stageY + this._dragOffsetY);
this._queueUpdateDragHover();
// If we are dragging, update the position
if (this._dragActor) {
this._dragActor.set_position(stageX + this._dragOffsetX,
stageY + this._dragOffsetY);
let target = this._dragActor.get_stage().get_actor_at_pos(Clutter.PickMode.ALL,
stageX, stageY);
// We call observers only once per motion with the innermost
// target actor. If necessary, the observer can walk the
// parent itself.
let dragEvent = {
x: stageX,
y: stageY,
dragActor: this._dragActor,
source: this.actor._delegate,
targetActor: target
};
for (let i = 0; i < dragMonitors.length; i++) {
let motionFunc = dragMonitors[i].dragMotion;
if (motionFunc) {
let result = motionFunc(dragEvent);
if (result != DragMotionResult.CONTINUE) {
global.set_cursor(DRAG_CURSOR_MAP[result]);
return true;
}
}
}
while (target) {
if (target._delegate && target._delegate.handleDragOver) {
let [r, targX, targY] = target.transform_stage_point(stageX, stageY);
// We currently loop through all parents on drag-over even if one of the children has handled it.
// We can check the return value of the function and break the loop if it's true if we don't want
// to continue checking the parents.
let result = target._delegate.handleDragOver(this.actor._delegate,
this._dragActor,
targX,
targY,
event.get_time());
if (result != DragMotionResult.CONTINUE) {
global.set_cursor(DRAG_CURSOR_MAP[result]);
return true;
}
}
target = target.get_parent();
}
global.set_cursor(Shell.Cursor.DND_IN_DRAG);
}
return true;
},
@ -517,11 +513,6 @@ const _Draggable = new Lang.Class({
},
_cancelDrag: function(eventTime) {
if (this._updateHoverId) {
GLib.source_remove(this._updateHoverId);
this._updateHoverId = 0;
}
this.emit('drag-cancelled', eventTime);
this._dragInProgress = false;
let [snapBackX, snapBackY, snapBackScale] = this._getRestoreLocation();

View File

@ -35,7 +35,7 @@ const GnomeSession = imports.misc.gnomeSession;
const Main = imports.ui.main;
const ModalDialog = imports.ui.modalDialog;
const Tweener = imports.ui.tweener;
const UserWidget = imports.ui.userWidget;
const UserMenu = imports.ui.userMenu;
let _endSessionDialog = null;
@ -360,9 +360,9 @@ const EndSessionDialog = new Lang.Class({
icon_size: _DIALOG_ICON_SIZE,
style_class: dialogContent.iconStyleClass });
} else {
let avatarWidget = new UserWidget.Avatar(this._user,
{ iconSize: _DIALOG_ICON_SIZE,
styleClass: dialogContent.iconStyleClass });
let avatarWidget = new UserMenu.UserAvatarWidget(this._user,
{ iconSize: _DIALOG_ICON_SIZE,
styleClass: dialogContent.iconStyleClass });
this._iconBin.child = avatarWidget.actor;
avatarWidget.update();
}
@ -420,7 +420,6 @@ const EndSessionDialog = new Lang.Class({
_startTimer: function() {
let startTime = GLib.get_monotonic_time();
this._secondsLeft = this._totalSecondsToStayOpen;
this._updateDescription();
this._timerId = Mainloop.timeout_add_seconds(1, Lang.bind(this,
function() {

View File

@ -10,7 +10,6 @@ const Clutter = imports.gi.Clutter;;
const Gettext = imports.gettext;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
@ -40,22 +39,6 @@ function _patchContainerClass(containerClass) {
};
}
function _patchLayoutClass(layoutClass, styleProps) {
if (styleProps)
layoutClass.prototype.hookup_style = function(container) {
container.connect('style-changed', Lang.bind(this, function() {
let node = container.get_theme_node();
for (let prop in styleProps)
this[prop] = node.get_length(styleProps[prop]);
}));
};
layoutClass.prototype.child_set = function(actor, props) {
let meta = this.get_child_meta(actor.get_parent(), actor);
for (let prop in props)
meta[prop] = props[prop];
};
}
function _makeLoggingFunc(func) {
return function() {
return func([].join.call(arguments, ', '));
@ -77,12 +60,6 @@ function init() {
_patchContainerClass(St.BoxLayout);
_patchContainerClass(St.Table);
_patchLayoutClass(Clutter.TableLayout, { row_spacing: 'spacing-rows',
column_spacing: 'spacing-columns' });
_patchLayoutClass(Clutter.GridLayout, { row_spacing: 'spacing-rows',
column_spacing: 'spacing-columns' });
_patchLayoutClass(Clutter.BoxLayout, { spacing: 'spacing' });
Clutter.Actor.prototype.toString = function() {
return St.describe_actor(this);
};

View File

@ -292,7 +292,7 @@ function disableAllExtensions() {
return;
if (initted) {
extensionOrder.slice().reverse().forEach(function(uuid) {
enabledExtensions.forEach(function(uuid) {
disableExtension(uuid);
});
}

View File

@ -32,9 +32,13 @@ const GrabHelper = new Lang.Class({
this._actors = [];
this._capturedEventId = 0;
this._keyFocusNotifyId = 0;
this._focusWindowChangedId = 0;
this._ignoreRelease = false;
this._isUngrabbingCount = 0;
this._modalCount = 0;
this._grabFocusCount = 0;
},
// addActor:
@ -114,36 +118,38 @@ const GrabHelper = new Lang.Class({
// grab:
// @params: A bunch of parameters, see below
//
// The general effect of a "grab" is to ensure that the passed in actor
// and all actors inside the grab get exclusive control of the mouse and
// keyboard, with the grab automatically being dropped if the user tries
// to dismiss it. The actor is passed in through @params.actor.
// Grabs the mouse and keyboard, according to the GrabHelper's
// parameters. If @newFocus is not %null, then the keyboard focus
// is moved to the first #StWidget:can-focus widget inside it.
//
// grab() can be called multiple times, with the scope of the grab being
// changed to a different actor every time. A nested grab does not have
// to have its grabbed actor inside the parent grab actors.
// The grab will automatically be dropped if:
// - The user clicks outside the grabbed actors
// - The user types Escape
// - The keyboard focus is moved outside the grabbed actors
// - A window is focused
//
// Grabs can be automatically dropped if the user tries to dismiss it
// in one of two ways: the user clicking outside the currently grabbed
// actor, or the user typing the Escape key.
// If @params.actor is not null, then it will be focused as the
// new actor. If you attempt to grab an already focused actor, the
// request to be focused will be ignored. The actor will not be
// added to the grab stack, so do not call a paired ungrab().
//
// If the user clicks outside the grabbed actors, and the clicked on
// actor is part of a previous grab in the stack, grabs will be popped
// until that grab is active. However, the click event will not be
// replayed to the actor.
// If @params contains { modal: true }, then grab() will push a modal
// on the owner of the GrabHelper. As long as there is at least one
// { modal: true } actor on the grab stack, the grab will be kept.
// When the last { modal: true } actor is ungrabbed, then the modal
// will be dropped. A modal grab can fail if there is already a grab
// in effect from aother application; in this case the function returns
// false and nothing happens. Non-modal grabs can never fail.
//
// If the user types the Escape key, one grab from the grab stack will
// be popped.
//
// When a grab is popped by user interacting as described above, if you
// pass a callback as @params.onUngrab, it will be called with %true.
//
// If @params.focus is not null, we'll set the key focus directly
// to that actor instead of navigating in @params.actor. This is for
// use cases like menus, where we want to grab the menu actor, but keep
// focus on the clicked on menu item.
// If @params contains { grabFocus: true }, then if you call grab()
// while the shell is outside the overview, it will set the stage
// input mode to %Shell.StageInputMode.FOCUSED, and ungrab() will
// revert it back, and re-focus the previously-focused window (if
// another window hasn't been explicitly focused before then).
grab: function(params) {
params = Params.parse(params, { actor: null,
modal: false,
grabFocus: false,
focus: null,
onUngrab: null });
@ -156,18 +162,24 @@ const GrabHelper = new Lang.Class({
params.savedFocus = focus;
if (!this._takeModalGrab())
if (params.modal && !this._takeModalGrab())
return false;
if (params.grabFocus && !this._takeFocusGrab(hadFocus))
return false;
this._grabStack.push(params);
if (params.focus) {
params.focus.grab_key_focus();
} else if (newFocus && hadFocus) {
} else if (newFocus && (hadFocus || params.grabFocus)) {
if (!newFocus.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false))
newFocus.grab_key_focus();
}
if ((params.grabFocus || params.modal) && !this._capturedEventId)
this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
return true;
},
@ -176,8 +188,6 @@ 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++;
@ -189,15 +199,58 @@ const GrabHelper = new Lang.Class({
if (this._modalCount > 0)
return;
global.stage.disconnect(this._capturedEventId);
this._capturedEventId = 0;
this._ignoreRelease = false;
Main.popModal(this._owner);
global.sync_pointer();
},
_takeFocusGrab: function(hadFocus) {
let firstGrab = (this._grabFocusCount == 0);
if (firstGrab) {
let metaDisplay = global.screen.get_display();
this._grabbedFromKeynav = hadFocus;
this._preGrabInputMode = global.stage_input_mode;
if (this._preGrabInputMode == Shell.StageInputMode.NONREACTIVE ||
this._preGrabInputMode == Shell.StageInputMode.NORMAL) {
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
}
this._keyFocusNotifyId = global.stage.connect('notify::key-focus', Lang.bind(this, this._onKeyFocusChanged));
this._focusWindowChangedId = metaDisplay.connect('notify::focus-window', Lang.bind(this, this._focusWindowChanged));
}
this._grabFocusCount++;
return true;
},
_releaseFocusGrab: function() {
this._grabFocusCount--;
if (this._grabFocusCount > 0)
return;
if (this._keyFocusNotifyId > 0) {
global.stage.disconnect(this._keyFocusNotifyId);
this._keyFocusNotifyId = 0;
}
if (this._focusWindowChangedId > 0) {
let metaDisplay = global.screen.get_display();
metaDisplay.disconnect(this._focusWindowChangedId);
this._focusWindowChangedId = 0;
}
let prePopInputMode = global.stage_input_mode;
if (this._grabbedFromKeynav) {
if (this._preGrabInputMode == Shell.StageInputMode.FOCUSED &&
prePopInputMode != Shell.StageInputMode.FULLSCREEN)
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
}
global.screen.focus_default_window(global.display.get_current_time_roundtrip());
},
// ignoreRelease:
//
// Make sure that the next button release event evaluated by the
@ -211,14 +264,10 @@ const GrabHelper = new Lang.Class({
// ungrab:
// @params: The parameters for the grab; see below.
//
// Pops @params.actor from the grab stack, potentially dropping
// the grab. If the actor is not on the grab stack, this call is
// ignored with no ill effects.
// Pops an actor from the grab stack, potentially dropping the grab.
//
// If the actor is not at the top of the grab stack, grabs are
// popped until the grabbed actor is at the top of the grab stack.
// The onUngrab callback for every grab is called for every popped
// grab with the parameter %false.
// If the actor that was popped from the grab stack was not the actor
// That was passed in, this call is ignored.
ungrab: function(params) {
params = Params.parse(params, { actor: this.currentGrab.actor,
isUser: false });
@ -227,6 +276,14 @@ const GrabHelper = new Lang.Class({
if (grabStackIndex < 0)
return;
// We may get key focus changes when calling onUngrab, which
// would cause an extra ungrab() on the next actor in the
// stack, which is wrong. Ignore key focus changes during the
// ungrab, and restore the saved key focus ourselves afterwards.
// We use a count as ungrab() may be re-entrant, as onUngrab()
// may ungrab additional actors.
this._isUngrabbingCount++;
let focus = global.stage.key_focus;
let hadFocus = focus && this._isWithinGrabbedActor(focus);
@ -241,7 +298,18 @@ const GrabHelper = new Lang.Class({
if (poppedGrab.onUngrab)
poppedGrab.onUngrab(params.isUser);
this._releaseModalGrab();
if (poppedGrab.modal)
this._releaseModalGrab();
if (poppedGrab.grabFocus)
this._releaseFocusGrab();
}
if (!this.grabbed && this._capturedEventId > 0) {
global.stage.disconnect(this._capturedEventId);
this._capturedEventId = 0;
this._ignoreRelease = false;
}
if (hadFocus) {
@ -249,6 +317,8 @@ const GrabHelper = new Lang.Class({
if (poppedGrab.savedFocus)
poppedGrab.savedFocus.grab_key_focus();
}
this._isUngrabbingCount--;
},
_onCapturedEvent: function(actor, event) {
@ -269,6 +339,9 @@ const GrabHelper = new Lang.Class({
return true;
}
if (!button && this._modalCount == 0)
return false;
if (this._isWithinGrabbedActor(event.get_source()))
return false;
@ -285,6 +358,21 @@ const GrabHelper = new Lang.Class({
return true;
}
return true;
return this._modalCount > 0;
},
_onKeyFocusChanged: function() {
if (this._isUngrabbingCount > 0)
return;
let focus = global.stage.key_focus;
if (!focus || !this._isWithinGrabbedActor(focus))
this.ungrab({ isUser: true });
},
_focusWindowChanged: function() {
let metaDisplay = global.screen.get_display();
if (metaDisplay.focus_window != null)
this.ungrab({ isUser: true });
}
});

View File

@ -118,25 +118,10 @@ const MonitorConstraint = new Lang.Class({
}
});
const Monitor = new Lang.Class({
Name: 'Monitor',
_init: function(index, geometry) {
this.index = index;
this.x = geometry.x;
this.y = geometry.y;
this.width = geometry.width;
this.height = geometry.height;
},
get inFullscreen() {
return global.screen.get_monitor_in_fullscreen(this.index);
}
})
const defaultParams = {
trackFullscreen: false,
affectsStruts: false,
affectsInputRegion: true
};
const LayoutManager = new Lang.Class({
@ -188,12 +173,10 @@ const LayoutManager = new Lang.Class({
global.stage.remove_actor(global.window_group);
this.uiGroup.add_actor(global.window_group);
global.stage.remove_actor(global.overlay_group);
this.uiGroup.add_actor(global.overlay_group);
global.stage.add_child(this.uiGroup);
this.overviewGroup = new St.Widget({ name: 'overviewGroup',
visible: false });
this.addChrome(this.overviewGroup);
this.screenShieldGroup = new St.Widget({ name: 'screenShieldGroup',
visible: false,
clip_to_allocation: true,
@ -244,24 +227,24 @@ const LayoutManager = new Lang.Class({
this._monitorsChanged();
},
// This is called by Main after everything else is constructed
// This is called by Main after everything else is constructed;
// it 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._loadBackground();
this._prepareStartupAnimation();
},
showOverview: function() {
this.overviewGroup.show();
_overviewShowing: function() {
this._inOverview = true;
this._updateVisibility();
this._queueUpdateRegions();
},
hideOverview: function() {
this.overviewGroup.hide();
_overviewHidden: function() {
this._inOverview = false;
this._updateVisibility();
this._queueUpdateRegions();
@ -278,7 +261,7 @@ const LayoutManager = new Lang.Class({
this.monitors = [];
let nMonitors = screen.get_n_monitors();
for (let i = 0; i < nMonitors; i++)
this.monitors.push(new Monitor(i, screen.get_monitor_geometry(i)));
this.monitors.push(screen.get_monitor_geometry(i));
if (nMonitors == 1) {
this.primaryIndex = this.bottomIndex = 0;
@ -300,10 +283,8 @@ const LayoutManager = new Lang.Class({
_updateHotCorners: function() {
// destroy old hot corners
this.hotCorners.forEach(function(corner) {
if (corner)
corner.destroy();
});
for (let i = 0; i < this.hotCorners.length; i++)
this.hotCorners[i].destroy();
this.hotCorners = [];
let size = this.panelBox.height;
@ -314,9 +295,9 @@ const LayoutManager = new Lang.Class({
let cornerX = this._rtl ? monitor.x + monitor.width : monitor.x;
let cornerY = monitor.y;
let haveTopLeftCorner = true;
if (i != this.primaryIndex) {
let haveTopLeftCorner = true;
// Check if we have a top left (right for RTL) corner.
// I.e. if there is no monitor directly above or to the left(right)
let besideX = this._rtl ? monitor.x + 1 : cornerX - 1;
@ -343,15 +324,14 @@ const LayoutManager = new Lang.Class({
break;
}
}
if (!haveTopLeftCorner)
continue;
}
if (haveTopLeftCorner) {
let corner = new HotCorner(this, monitor, cornerX, cornerY);
corner.setBarrierSize(size);
this.hotCorners.push(corner);
} else {
this.hotCorners.push(null);
}
let corner = new HotCorner(this, monitor, cornerX, cornerY);
corner.setBarrierSize(size);
this.hotCorners.push(corner);
}
this.emit('hot-corners-changed');
@ -428,8 +408,7 @@ const LayoutManager = new Lang.Class({
let size = this.panelBox.height;
this.hotCorners.forEach(function(corner) {
if (corner)
corner.setBarrierSize(size);
corner.setBarrierSize(size);
});
},
@ -528,10 +507,17 @@ const LayoutManager = new Lang.Class({
get focusIndex() {
let i = Main.layoutManager.primaryIndex;
if (global.stage.key_focus != null)
i = this.findIndexForActor(global.stage.key_focus);
else if (global.display.focus_window != null)
i = global.display.focus_window.get_monitor();
if (global.stage_input_mode == Shell.StageInputMode.FOCUSED ||
global.stage_input_mode == Shell.StageInputMode.FULLSCREEN) {
let focusActor = global.stage.key_focus;
if (focusActor)
i = this.findIndexForActor(focusActor);
} else {
let focusWindow = global.display.focus_window;
if (focusWindow)
i = focusWindow.get_monitor();
}
return i;
},
@ -550,25 +536,6 @@ const LayoutManager = new Lang.Class({
return this._keyboardIndex;
},
_loadBackground: function() {
this._systemBackground = new Background.SystemBackground();
this._systemBackground.actor.hide();
global.stage.insert_child_below(this._systemBackground.actor, null);
let constraint = new Clutter.BindConstraint({ source: global.stage,
coordinate: Clutter.BindCoordinate.ALL });
this._systemBackground.actor.add_constraint(constraint);
let signalId = this._systemBackground.connect('loaded', Lang.bind(this, function() {
this._systemBackground.disconnect(signalId);
this._systemBackground.actor.show();
global.stage.show();
this._prepareStartupAnimation();
}));
},
// Startup Animations
//
// We have two different animations, depending on whether we're a greeter
@ -589,13 +556,9 @@ const LayoutManager = new Lang.Class({
// screen. So, we set no_clear_hint at the end of the animation.
_prepareStartupAnimation: function() {
// During the initial transition, add a simple actor to block all events,
// so they don't get delivered to X11 windows that have been transformed.
this._coverPane = new Clutter.Actor({ opacity: 0,
width: global.screen_width,
height: global.screen_height,
reactive: true });
this.addChrome(this._coverPane);
// Set ourselves to FULLSCREEN input mode while the animation is running
// so events don't get delivered to X11 windows (which are distorted by the animation)
global.stage_input_mode = Shell.StageInputMode.FULLSCREEN;
if (Main.sessionMode.isGreeter) {
this.panelBox.translation_y = -this.panelBox.height;
@ -618,18 +581,35 @@ const LayoutManager = new Lang.Class({
global.window_group.set_clip(monitor.x, monitor.y, monitor.width, monitor.height);
}
this.emit('startup-prepared');
this._systemBackground = new Background.SystemBackground();
this._systemBackground.actor.hide();
// We're mostly prepared for the startup animation
// now, but since a lot is going on asynchronously
// during startup, let's defer the startup animation
// until the event loop is uncontended and idle.
// This helps to prevent us from running the animation
// when the system is bogged down
GLib.idle_add(GLib.PRIORITY_LOW, Lang.bind(this, function() {
this._startupAnimation();
return false;
}));
global.stage.insert_child_below(this._systemBackground.actor, null);
let constraint = new Clutter.BindConstraint({ source: global.stage,
coordinate: Clutter.BindCoordinate.ALL });
this._systemBackground.actor.add_constraint(constraint);
let signalId = this._systemBackground.connect('loaded',
Lang.bind(this, function() {
this._systemBackground.disconnect(signalId);
this._systemBackground.actor.show();
global.stage.show();
this.emit('startup-prepared');
// We're mostly prepared for the startup animation
// now, but since a lot is going on asynchronously
// during startup, let's defer the startup animation
// until the event loop is uncontended and idle.
// This helps to prevent us from running the animation
// when the system is bogged down
GLib.idle_add(GLib.PRIORITY_LOW,
Lang.bind(this, function() {
this._startupAnimation();
return false;
}));
}));
},
_startupAnimation: function() {
@ -665,8 +645,7 @@ const LayoutManager = new Lang.Class({
// we no longer need to clear the stage
global.stage.no_clear_hint = true;
this._coverPane.destroy();
this._coverPane = null;
global.stage_input_mode = Shell.StageInputMode.NORMAL;
this._systemBackground.actor.destroy();
this._systemBackground = null;
@ -687,6 +666,7 @@ const LayoutManager = new Lang.Class({
},
showKeyboard: function () {
this.keyboardBox.raise_top();
Tweener.addTween(this.keyboardBox,
{ anchor_y: this.keyboardBox.height,
time: KEYBOARD_ANIMATION_TIME,
@ -731,10 +711,11 @@ const LayoutManager = new Lang.Class({
// @actor: an actor to add to the chrome
// @params: (optional) additional params
//
// Adds @actor to the chrome, and extends the input region
// to include it. Changes in @actor's size, position, and
// visibility will automatically result in appropriate changes
// to the input region.
// Adds @actor to the chrome, and (unless %affectsInputRegion in
// @params is %false) extends the input region to include it.
// Changes in @actor's size, position, and visibility will
// automatically result in appropriate changes to the input
// region.
//
// If %affectsStruts in @params is %true (and @actor is along a
// screen edge), then @actor's size and position will also affect
@ -747,8 +728,6 @@ const LayoutManager = new Lang.Class({
// and shown otherwise)
addChrome: function(actor, params) {
this.uiGroup.add_actor(actor);
if (this.uiGroup.contains(global.top_window_group))
this.uiGroup.set_child_below_sibling(actor, global.top_window_group);
this._trackActor(actor, params);
},
@ -921,8 +900,13 @@ const LayoutManager = new Lang.Class({
},
_updateFullscreen: function() {
for (let i = 0; i < this.monitors.length; i++)
this.monitors[i].inFullscreen = global.screen.get_monitor_in_fullscreen (i);
this._updateVisibility();
this._queueUpdateRegions();
this.emit('fullscreen-changed');
},
_windowsRestacked: function() {
@ -950,7 +934,7 @@ const LayoutManager = new Lang.Class({
for (i = 0; i < this._trackedActors.length; i++) {
let actorData = this._trackedActors[i];
if (!wantsInputRegion && !actorData.affectsStruts)
if (!(actorData.affectsInputRegion && wantsInputRegion) && !actorData.affectsStruts)
continue;
let [x, y] = actorData.actor.get_transformed_position();
@ -960,8 +944,13 @@ const LayoutManager = new Lang.Class({
w = Math.round(w);
h = Math.round(h);
if (wantsInputRegion && actorData.actor.get_paint_visibility())
rects.push(new Meta.Rectangle({ x: x, y: y, width: w, height: h }));
if (actorData.affectsInputRegion && wantsInputRegion) {
let rect = new Meta.Rectangle({ x: x, y: y, width: w, height: h});
if (actorData.actor.get_paint_visibility() &&
!this.uiGroup.get_skip_paint(actorData.actor))
rects.push(rect);
}
if (actorData.affectsStruts) {
// Limit struts to the size of the screen
@ -1103,21 +1092,12 @@ const HotCorner = new Lang.Class({
}
if (size > 0) {
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) {
this._verticalBarrier = new Meta.Barrier({ display: global.display,
x1: this._x, x2: this._x, y1: this._y, y2: this._y + size,
directions: Meta.BarrierDirection.NEGATIVE_X });
this._horizontalBarrier = new Meta.Barrier({ display: global.display,
x1: this._x - size, x2: this._x, y1: this._y, y2: this._y,
directions: Meta.BarrierDirection.POSITIVE_Y });
} else {
this._verticalBarrier = new Meta.Barrier({ display: global.display,
x1: this._x, x2: this._x, y1: this._y, y2: this._y + size,
directions: Meta.BarrierDirection.POSITIVE_X });
this._horizontalBarrier = new Meta.Barrier({ display: global.display,
x1: this._x, x2: this._x + size, y1: this._y, y2: this._y,
directions: Meta.BarrierDirection.POSITIVE_Y });
}
this._verticalBarrier = new Meta.Barrier({ display: global.display,
x1: this._x, x2: this._x, y1: this._y, y2: this._y + size,
directions: Meta.BarrierDirection.POSITIVE_X });
this._horizontalBarrier = new Meta.Barrier({ display: global.display,
x1: this._x, x2: this._x + size, y1: this._y, y2: this._y,
directions: Meta.BarrierDirection.POSITIVE_Y });
this._pressureBarrier.addBarrier(this._verticalBarrier);
this._pressureBarrier.addBarrier(this._horizontalBarrier);
@ -1132,11 +1112,11 @@ const HotCorner = new Lang.Class({
height: 3,
reactive: true });
this._corner = new Clutter.Actor({ name: 'hot-corner',
width: 1,
height: 1,
opacity: 0,
reactive: true });
this._corner = new Clutter.Rectangle({ name: 'hot-corner',
width: 1,
height: 1,
opacity: 0,
reactive: true });
this._corner._delegate = this;
this.actor.add_child(this._corner);

View File

@ -308,6 +308,10 @@ const Result = new Lang.Class({
box.add(resultTxt);
let objLink = new ObjLink(this._lookingGlass, o);
box.add(objLink.actor);
let line = new Clutter.Rectangle({ name: 'Separator' });
let padBin = new St.Bin({ name: 'Separator', x_fill: true, y_fill: true });
padBin.add_actor(line);
this.actor.add(padBin);
}
});
@ -849,9 +853,8 @@ const LookingGlass = new Lang.Class({
this._updateFont();
// We want it to appear to slide out from underneath the panel
Main.uiGroup.add_actor(this.actor);
Main.uiGroup.set_child_below_sibling(this.actor,
Main.layoutManager.panelBox);
Main.layoutManager.panelBox.add_actor(this.actor);
this.actor.lower_bottom();
Main.layoutManager.panelBox.connect('allocation-changed',
Lang.bind(this, this._queueResize));
Main.layoutManager.keyboardBox.connect('allocation-changed',
@ -986,18 +989,28 @@ const LookingGlass = new Lang.Class({
_showCompletions: function(completions) {
if (!this._completionActor) {
this._completionActor = new St.Label({ name: 'LookingGlassAutoCompletionText', style_class: 'lg-completions-text' });
this._completionActor.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
this._completionActor.clutter_text.line_wrap = true;
let actor = new St.BoxLayout({ vertical: true });
this._completionText = new St.Label({ name: 'LookingGlassAutoCompletionText', style_class: 'lg-completions-text' });
this._completionText.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
this._completionText.clutter_text.line_wrap = true;
actor.add(this._completionText);
let line = new Clutter.Rectangle();
let padBin = new St.Bin({ x_fill: true, y_fill: true });
padBin.add_actor(line);
actor.add(padBin);
this._completionActor = actor;
this._evalBox.insert_child_below(this._completionActor, this._entryArea);
}
this._completionActor.set_text(completions.join(', '));
this._completionText.set_text(completions.join(', '));
// Setting the height to -1 allows us to get its actual preferred height rather than
// whatever was last given in set_height by Tweener.
this._completionActor.set_height(-1);
let [minHeight, naturalHeight] = this._completionActor.get_preferred_height(this._resultsArea.get_width());
let [minHeight, naturalHeight] = this._completionText.get_preferred_height(this._resultsArea.get_width());
// Don't reanimate if we are already visible
if (this._completionActor.visible) {
@ -1073,7 +1086,7 @@ const LookingGlass = new Lang.Class({
let availableHeight = primary.height - Main.layoutManager.keyboardBox.height;
let myHeight = Math.min(primary.height * 0.7, availableHeight * 0.9);
this.actor.x = (primary.width - myWidth) / 2;
this._hiddenY = Main.layoutManager.panelBox.height - myHeight - 4; // -4 to hide the top corners
this._hiddenY = this.actor.get_parent().height - myHeight - 4; // -4 to hide the top corners
this._targetY = this._hiddenY + myHeight;
this.actor.y = this._hiddenY;
this.actor.width = myWidth;

View File

@ -38,11 +38,9 @@ const Magnifier = imports.ui.magnifier;
const XdndHandler = imports.ui.xdndHandler;
const Util = imports.misc.util;
const OVERRIDES_SCHEMA = 'org.gnome.shell.overrides';
const DEFAULT_BACKGROUND_COLOR = Clutter.Color.from_pixel(0x2e3436ff);
const A11Y_SCHEMA = 'org.gnome.desktop.a11y.keyboard';
const STICKY_KEYS_ENABLE = 'stickykeys-enable';
let componentManager = null;
let panel = null;
let overview = null;
@ -70,8 +68,7 @@ let layoutManager = null;
let _startDate;
let _defaultCssStylesheet = null;
let _cssStylesheet = null;
let _a11ySettings = null;
let dynamicWorkspacesSchema = null;
let _overridesSettings = null;
function _sessionUpdated() {
_loadDefaultStylesheet();
@ -109,7 +106,6 @@ function start() {
function _sessionsLoaded() {
sessionMode.connect('updated', _sessionUpdated);
_initializePrefs();
_initializeUI();
shellDBusService = new ShellDBus.GnomeShell();
@ -118,17 +114,6 @@ function _sessionsLoaded() {
_sessionUpdated();
}
function _initializePrefs() {
let keys = new Gio.Settings({ schema: sessionMode.overridesSchema }).list_keys();
for (let i = 0; i < keys.length; i++)
Meta.prefs_override_preference_schema(keys[i], sessionMode.overridesSchema);
if (keys.indexOf('dynamic-workspaces') > -1)
dynamicWorkspacesSchema = sessionMode.overridesSchema;
else
dynamicWorkspacesSchema = 'org.gnome.mutter';
}
function _initializeUI() {
// Ensure ShellWindowTracker and ShellAppUsage are initialized; this will
// also initialize ShellAppSystem first. ShellAppSystem
@ -138,9 +123,11 @@ function _initializeUI() {
// and recalculate application associations, so to avoid
// races for now we initialize it here. It's better to
// be predictable anyways.
Shell.WindowTracker.get_default();
let tracker = Shell.WindowTracker.get_default();
Shell.AppUsage.get_default();
tracker.connect('startup-sequence-changed', _queueCheckWorkspaces);
_loadDefaultStylesheet();
// Setup the stage hierarchy early
@ -170,12 +157,9 @@ function _initializeUI() {
layoutManager.init();
overview.init();
_a11ySettings = new Gio.Settings({ schema: A11Y_SCHEMA });
global.display.connect('overlay-key', Lang.bind(overview, function () {
if (!_a11ySettings.get_boolean (STICKY_KEYS_ENABLE))
overview.toggle();
}));
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT,
false, -1, 1);
global.display.connect('overlay-key', Lang.bind(overview, overview.toggle));
// Provide the bus object for gnome-session to
// initiate logouts.
@ -195,6 +179,17 @@ function _initializeUI() {
Scripting.runPerfScript(module, perfOutput);
}
_overridesSettings = new Gio.Settings({ schema: OVERRIDES_SCHEMA });
_overridesSettings.connect('changed::dynamic-workspaces', _queueCheckWorkspaces);
global.screen.connect('notify::n-workspaces', _nWorkspacesChanged);
global.screen.connect('window-entered-monitor', _windowEnteredMonitor);
global.screen.connect('window-left-monitor', _windowLeftMonitor);
global.screen.connect('restacked', _windowsRestacked);
_nWorkspacesChanged();
ExtensionDownloader.init();
ExtensionSystem.init();
@ -208,12 +203,190 @@ function _initializeUI() {
if (keybindingMode == Shell.KeyBindingMode.NONE) {
keybindingMode = Shell.KeyBindingMode.NORMAL;
}
if (screenShield) {
screenShield.lockIfWasLocked();
}
});
}
let _workspaces = [];
let _checkWorkspacesId = 0;
/*
* When the last window closed on a workspace is a dialog or splash
* screen, we assume that it might be an initial window shown before
* the main window of an application, and give the app a grace period
* where it can map another window before we remove the workspace.
*/
const LAST_WINDOW_GRACE_TIME = 1000;
function _checkWorkspaces() {
let i;
let emptyWorkspaces = [];
if (!Meta.prefs_get_dynamic_workspaces()) {
_checkWorkspacesId = 0;
return false;
}
for (i = 0; i < _workspaces.length; i++) {
let lastRemoved = _workspaces[i]._lastRemovedWindow;
if ((lastRemoved &&
(lastRemoved.get_window_type() == Meta.WindowType.SPLASHSCREEN ||
lastRemoved.get_window_type() == Meta.WindowType.DIALOG ||
lastRemoved.get_window_type() == Meta.WindowType.MODAL_DIALOG)) ||
_workspaces[i]._keepAliveId)
emptyWorkspaces[i] = false;
else
emptyWorkspaces[i] = true;
}
let sequences = Shell.WindowTracker.get_default().get_startup_sequences();
for (i = 0; i < sequences.length; i++) {
let index = sequences[i].get_workspace();
if (index >= 0 && index <= global.screen.n_workspaces)
emptyWorkspaces[index] = false;
}
let windows = global.get_window_actors();
for (i = 0; i < windows.length; i++) {
let win = windows[i];
if (win.get_meta_window().is_on_all_workspaces())
continue;
let workspaceIndex = win.get_workspace();
emptyWorkspaces[workspaceIndex] = false;
}
// If we don't have an empty workspace at the end, add one
if (!emptyWorkspaces[emptyWorkspaces.length -1]) {
global.screen.append_new_workspace(false, global.get_current_time());
emptyWorkspaces.push(false);
}
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
let removingCurrentWorkspace = (emptyWorkspaces[activeWorkspaceIndex] &&
activeWorkspaceIndex < emptyWorkspaces.length - 1);
// Don't enter the overview when removing multiple empty workspaces at startup
let showOverview = (removingCurrentWorkspace &&
!emptyWorkspaces.every(function(x) { return x; }));
if (removingCurrentWorkspace) {
// "Merge" the empty workspace we are removing with the one at the end
wm.blockAnimations();
}
// Delete other empty workspaces; do it from the end to avoid index changes
for (i = emptyWorkspaces.length - 2; i >= 0; i--) {
if (emptyWorkspaces[i])
global.screen.remove_workspace(_workspaces[i], global.get_current_time());
}
if (removingCurrentWorkspace) {
global.screen.get_workspace_by_index(global.screen.n_workspaces - 1).activate(global.get_current_time());
wm.unblockAnimations();
if (!overview.visible && showOverview)
overview.show();
}
_checkWorkspacesId = 0;
return false;
}
function keepWorkspaceAlive(workspace, duration) {
if (workspace._keepAliveId)
Mainloop.source_remove(workspace._keepAliveId);
workspace._keepAliveId = Mainloop.timeout_add(duration, function() {
workspace._keepAliveId = 0;
_queueCheckWorkspaces();
return false;
});
}
function _windowRemoved(workspace, window) {
workspace._lastRemovedWindow = window;
_queueCheckWorkspaces();
Mainloop.timeout_add(LAST_WINDOW_GRACE_TIME, function() {
if (workspace._lastRemovedWindow == window) {
workspace._lastRemovedWindow = null;
_queueCheckWorkspaces();
}
return false;
});
}
function _windowLeftMonitor(metaScreen, monitorIndex, metaWin) {
// If the window left the primary monitor, that
// might make that workspace empty
if (monitorIndex == layoutManager.primaryIndex)
_queueCheckWorkspaces();
}
function _windowEnteredMonitor(metaScreen, monitorIndex, metaWin) {
// If the window entered the primary monitor, that
// might make that workspace non-empty
if (monitorIndex == layoutManager.primaryIndex)
_queueCheckWorkspaces();
}
function _windowsRestacked() {
// Figure out where the pointer is in case we lost track of
// it during a grab. (In particular, if a trayicon popup menu
// is dismissed, see if we need to close the message tray.)
global.sync_pointer();
}
function _queueCheckWorkspaces() {
if (_checkWorkspacesId == 0)
_checkWorkspacesId = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, _checkWorkspaces);
}
function _nWorkspacesChanged() {
let oldNumWorkspaces = _workspaces.length;
let newNumWorkspaces = global.screen.n_workspaces;
if (oldNumWorkspaces == newNumWorkspaces)
return false;
let lostWorkspaces = [];
if (newNumWorkspaces > oldNumWorkspaces) {
let w;
// Assume workspaces are only added at the end
for (w = oldNumWorkspaces; w < newNumWorkspaces; w++)
_workspaces[w] = global.screen.get_workspace_by_index(w);
for (w = oldNumWorkspaces; w < newNumWorkspaces; w++) {
let workspace = _workspaces[w];
workspace._windowAddedId = workspace.connect('window-added', _queueCheckWorkspaces);
workspace._windowRemovedId = workspace.connect('window-removed', _windowRemoved);
}
} else {
// Assume workspaces are only removed sequentially
// (e.g. 2,3,4 - not 2,4,7)
let removedIndex;
let removedNum = oldNumWorkspaces - newNumWorkspaces;
for (let w = 0; w < oldNumWorkspaces; w++) {
let workspace = global.screen.get_workspace_by_index(w);
if (_workspaces[w] != workspace) {
removedIndex = w;
break;
}
}
let lostWorkspaces = _workspaces.splice(removedIndex, removedNum);
lostWorkspaces.forEach(function(workspace) {
workspace.disconnect(workspace._windowAddedId);
workspace.disconnect(workspace._windowRemovedId);
});
}
_queueCheckWorkspaces();
return false;
}
function _loadDefaultStylesheet() {
if (!sessionMode.isPrimary)
return;
@ -264,8 +437,7 @@ function loadTheme() {
if (_cssStylesheet != null)
cssStylesheet = _cssStylesheet;
let theme = new St.Theme ({ application_stylesheet: cssStylesheet,
default_stylesheet: _defaultCssStylesheet });
let theme = new St.Theme ({ application_stylesheet: cssStylesheet });
if (previousTheme) {
let customStylesheets = previousTheme.get_custom_stylesheets();
@ -356,6 +528,8 @@ function pushModal(actor, params) {
Meta.disable_unredirect_for_screen(global.screen);
}
global.set_stage_input_mode(Shell.StageInputMode.FULLSCREEN);
modalCount += 1;
let actorDestroyId = actor.connect('destroy', function() {
let index = _findModal(actor);
@ -404,6 +578,7 @@ function popModal(actor, timestamp) {
if (focusIndex < 0) {
global.stage.set_key_focus(null);
global.end_modal(timestamp);
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
keybindingMode = Shell.KeyBindingMode.NORMAL;
throw new Error('incorrect pop');
@ -451,6 +626,7 @@ function popModal(actor, timestamp) {
return;
global.end_modal(timestamp);
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
Meta.enable_unredirect_for_screen(global.screen);
keybindingMode = Shell.KeyBindingMode.NORMAL;
}

View File

@ -97,65 +97,6 @@ function _fixMarkup(text, allowMarkup) {
return GLib.markup_escape_text(text, -1);
}
const FocusGrabber = new Lang.Class({
Name: 'FocusGrabber',
_init: function(actor) {
this._actor = actor;
this._prevKeyFocusActor = null;
this._focusActorChangedId = 0;
this._focused = false;
},
grabFocus: function() {
if (this._focused)
return;
this._prevFocusedWindow = global.display.focus_window;
this._prevKeyFocusActor = global.stage.get_key_focus();
this._focusActorChangedId = global.stage.connect('notify::key-focus', Lang.bind(this, this._focusActorChanged));
if (!this._actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false))
this._actor.grab_key_focus();
this._focused = true;
},
_focusUngrabbed: function() {
if (!this._focused)
return false;
if (this._focusActorChangedId > 0) {
global.stage.disconnect(this._focusActorChangedId);
this._focusActorChangedId = 0;
}
this._focused = false;
return true;
},
_focusActorChanged: function() {
let focusedActor = global.stage.get_key_focus();
if (!focusedActor || !this._actor.contains(focusedActor))
this._focusUngrabbed();
},
ungrabFocus: function() {
if (!this._focusUngrabbed())
return;
if (this._prevKeyFocusActor) {
global.stage.set_key_focus(this._prevKeyFocusActor);
this._prevKeyFocusActor = null;
} else {
let focusedActor = global.stage.get_key_focus();
if (focusedActor && this._actor.contains(focusedActor))
global.stage.set_key_focus(null);
}
}
});
const URLHighlighter = new Lang.Class({
Name: 'URLHighlighter',
@ -1424,10 +1365,6 @@ const SummaryItem = new Lang.Class({
this.notificationStackWidget.add_actor(this.notificationStackView);
this.closeButton = Util.makeCloseButton();
this.closeButton.connect('clicked', Lang.bind(this, function() {
source.destroy();
source.emit('done-displaying-content');
}));
this.notificationStackWidget.add_actor(this.closeButton);
this._stackedNotifications = [];
@ -1593,7 +1530,11 @@ const MessageTrayContextMenu = new Lang.Class({
},
_updateClearSensitivity: function() {
this._clearItem.setSensitive(this._tray.clearableCount > 0);
let sources = this._tray.getSources();
sources = sources.filter(function(source) {
return !source.trayIcon && !source.isChat && !source.resident;
});
this._clearItem.setSensitive(sources.length > 0);
},
setPosition: function(x, y) {
@ -1616,28 +1557,28 @@ const MessageTray = new Lang.Class({
this.actor = new St.Widget({ name: 'message-tray',
reactive: true,
track_hover: true,
layout_manager: new Clutter.BinLayout(),
x_expand: true,
y_expand: true,
y_align: Clutter.ActorAlign.START,
});
this.actor.connect('notify::hover', Lang.bind(this, this._onTrayHoverChanged));
this._notificationWidget = new St.Widget({ name: 'notification-container',
reactive: true,
track_hover: true,
y_align: Clutter.ActorAlign.START,
x_align: Clutter.ActorAlign.CENTER,
y_expand: true,
x_expand: true,
layout_manager: new Clutter.BinLayout() });
this._notificationWidget.connect('key-release-event', Lang.bind(this, this._onNotificationKeyRelease));
this._notificationWidget.connect('notify::hover', Lang.bind(this, this._onNotificationHoverChanged));
this.actor.add_actor(this._notificationWidget);
this._notificationBin = new St.Bin({ y_expand: true });
this._notificationBin.set_y_align(Clutter.ActorAlign.START);
this._notificationWidget.add_actor(this._notificationBin);
this._notificationWidget.hide();
this._notificationFocusGrabber = new FocusGrabber(this._notificationWidget);
this._notificationQueue = [];
this._notification = null;
this._notificationClickedId = 0;
@ -1692,19 +1633,19 @@ const MessageTray = new Lang.Class({
this._trayState = State.HIDDEN;
this._traySummoned = false;
this._useLongerNotificationLeftTimeout = false;
this._useLongerTrayLeftTimeout = false;
this._trayLeftTimeoutId = 0;
// pointerInNotification is sort of a misnomer -- it tracks whether
// pointerInTray is sort of a misnomer -- it tracks whether
// a message tray notification should expand. The value is
// partially driven by the hover state of the notification, but has
// partially driven by the hover state of the tray, but has
// a lot of complex state related to timeouts and the current
// state of the pointer when a notification pops up.
this._pointerInNotification = false;
this._pointerInTray = false;
// This tracks this._notificationWidget.hover and is used to fizzle
// out non-changing hover notifications in onNotificationHoverChanged.
this._notificationHovered = false;
// This tracks this.actor.hover and is used to fizzle
// out non-changing hover notifications in onTrayHoverChanged.
this._trayHovered = false;
this._keyboardVisible = false;
this._notificationClosed = false;
@ -1716,29 +1657,23 @@ const MessageTray = new Lang.Class({
this._desktopCloneState = State.HIDDEN;
this._notificationRemoved = false;
this._reNotifyAfterHideNotification = null;
this._inFullscreen = false;
this._desktopClone = null;
this._inCtrlAltTab = false;
this.clearableCount = 0;
this._lightboxes = [];
let lightboxContainers = [global.window_group,
Main.layoutManager.overviewGroup];
for (let i = 0; i < lightboxContainers.length; i++)
this._lightboxes.push(new Lightbox.Lightbox(lightboxContainers[i],
{ inhibitEvents: true,
fadeInTime: ANIMATION_TIME,
fadeOutTime: ANIMATION_TIME,
fadeFactor: 0.2
}));
this._lightbox = new Lightbox.Lightbox(global.overlay_group,
{ inhibitEvents: true,
fadeInTime: ANIMATION_TIME,
fadeOutTime: ANIMATION_TIME,
fadeFactor: 0.2
});
Main.layoutManager.trayBox.add_actor(this.actor);
Main.layoutManager.trayBox.add_actor(this._notificationWidget);
Main.layoutManager.trackChrome(this.actor);
Main.layoutManager.trackChrome(this._notificationWidget);
Main.layoutManager.trackChrome(this._closeButton);
global.screen.connect('in-fullscreen-changed', Lang.bind(this, this._updateState));
Main.layoutManager.connect('fullscreen-changed', Lang.bind(this, this._updateState));
Main.layoutManager.connect('hot-corners-changed', Lang.bind(this, this._hotCornersChanged));
// If the overview shows or hides while we're in
@ -1812,6 +1747,7 @@ const MessageTray = new Lang.Class({
let [x, y, mask] = global.get_pointer();
this._contextMenu.setPosition(Math.round(x), Math.round(y));
this._grabHelper.grab({ actor: this._contextMenu.actor,
grabFocus: true,
onUngrab: Lang.bind(this, function () {
this._contextMenu.close(BoxPointer.PopupAnimation.FULL);
})
@ -1857,12 +1793,12 @@ const MessageTray = new Lang.Class({
y == monitor.y + monitor.height - 1);
if (shouldDwell) {
// We only set up dwell timeout when the user is not hovering over the tray
// (!this._notificationHovered). This avoids bringing up the message tray after the
// (!this.actor.hover). This avoids bringing up the message tray after the
// user clicks on a notification with the pointer on the bottom pixel
// of the monitor. The _trayDwelling variable is used so that we only try to
// fire off one tray dwell - if it fails (because, say, the user has the mouse down),
// we don't try again until the user moves the mouse up and down again.
if (!this._trayDwelling && !this._notificationHovered && this._trayDwellTimeoutId == 0) {
if (!this._trayDwelling && !this.actor.hover && this._trayDwellTimeoutId == 0) {
// Save the interaction timestamp so we can detect user input
let focusWindow = global.display.focus_window;
this._trayDwellUserTime = focusWindow ? focusWindow.user_time : 0;
@ -1919,8 +1855,9 @@ const MessageTray = new Lang.Class({
_closeNotification: function() {
if (this._notificationState == State.SHOWN) {
this._closeButton.hide();
this._notification.emit('done-displaying');
this._notification.destroy();
this._notificationClosed = true;
this._updateState();
this._notificationClosed = false;
}
},
@ -1959,9 +1896,6 @@ const MessageTray = new Lang.Class({
this._summary.insert_child_at_index(summaryItem.actor, this._chatSummaryItemsCount);
}
if (!source.trayIcon && !source.isChat && !source.resident)
this.clearableCount++;
this._sources.set(source, obj);
obj.notifyId = source.connect('notify', Lang.bind(this, this._onNotify));
@ -2003,9 +1937,6 @@ const MessageTray = new Lang.Class({
if (source.isChat)
this._chatSummaryItemsCount--;
if (!source.trayIcon && !source.isChat && !source.resident)
this.clearableCount--;
source.disconnect(obj.notifyId);
source.disconnect(obj.destroyId);
source.disconnect(obj.mutedChangedId);
@ -2061,6 +1992,7 @@ const MessageTray = new Lang.Class({
}
let index = this._notificationQueue.indexOf(notification);
notification.destroy();
if (index != -1)
this._notificationQueue.splice(index, 1);
},
@ -2089,6 +2021,7 @@ const MessageTray = new Lang.Class({
hide: function() {
this._traySummoned = false;
this.actor.set_hover(false);
this._updateState();
},
@ -2155,21 +2088,25 @@ const MessageTray = new Lang.Class({
this._grabHelper.addActor(corner.actor);
},
_onNotificationHoverChanged: function() {
if (this._notificationWidget.hover == this._notificationHovered)
_onTrayHoverChanged: function() {
if (this.actor.hover == this._trayHovered)
return;
this._notificationHovered = this._notificationWidget.hover;
if (this._notificationHovered) {
this._trayHovered = this.actor.hover;
if (this._trayHovered) {
// No dwell inside notifications at the bottom of the screen
this._cancelTrayDwell();
this._useLongerNotificationLeftTimeout = false;
if (this._notificationLeftTimeoutId) {
Mainloop.source_remove(this._notificationLeftTimeoutId);
this._notificationLeftTimeoutId = 0;
this._notificationLeftMouseX = -1;
this._notificationLeftMouseY = -1;
// Don't do anything if the one pixel area at the bottom is hovered over while the tray is hidden.
if (this._trayState == State.HIDDEN && this._notificationState == State.HIDDEN)
return;
this._useLongerTrayLeftTimeout = false;
if (this._trayLeftTimeoutId) {
Mainloop.source_remove(this._trayLeftTimeoutId);
this._trayLeftTimeoutId = 0;
this._trayLeftMouseX = -1;
this._trayLeftMouseY = -1;
return;
}
@ -2178,32 +2115,32 @@ const MessageTray = new Lang.Class({
global.stage.get_actor_at_pos(Clutter.PickMode.ALL, this._showNotificationMouseX, this._showNotificationMouseY);
this._showNotificationMouseX = -1;
this._showNotificationMouseY = -1;
// Don't set this._pointerInNotification to true if the pointer was initially in the area where the notification
// Don't set this._pointerInTray to true if the pointer was initially in the area where the notification
// popped up. That way we will not be expanding notifications that happen to pop up over the pointer
// automatically. Instead, the user is able to expand the notification by mousing away from it and then
// mousing back in. Because this is an expected action, we set the boolean flag that indicates that a longer
// timeout should be used before popping down the notification.
if (this.actor.contains(actorAtShowNotificationPosition)) {
this._useLongerNotificationLeftTimeout = true;
this._useLongerTrayLeftTimeout = true;
return;
}
}
this._pointerInNotification = true;
this._pointerInTray = true;
this._updateState();
} else {
// We record the position of the mouse the moment it leaves the tray. These coordinates are used in
// this._onNotificationLeftTimeout() to determine if the mouse has moved far enough during the initial timeout for us
// this._onTrayLeftTimeout() to determine if the mouse has moved far enough during the initial timeout for us
// to consider that the user intended to leave the tray and therefore hide the tray. If the mouse is still
// close to its previous position, we extend the timeout once.
let [x, y, mods] = global.get_pointer();
this._notificationLeftMouseX = x;
this._notificationLeftMouseY = y;
this._trayLeftMouseX = x;
this._trayLeftMouseY = y;
// We wait just a little before hiding the message tray in case the user quickly moves the mouse back into it.
// We wait for a longer period if the notification popped up where the mouse pointer was already positioned.
// That gives the user more time to mouse away from the notification and mouse back in in order to expand it.
let timeout = this._useLongerNotificationLeftTimeout ? LONGER_HIDE_TIMEOUT * 1000 : HIDE_TIMEOUT * 1000;
this._notificationLeftTimeoutId = Mainloop.timeout_add(timeout, Lang.bind(this, this._onNotificationLeftTimeout));
let timeout = this._useLongerTrayLeftTimeout ? LONGER_HIDE_TIMEOUT * 1000 : HIDE_TIMEOUT * 1000;
this._trayLeftTimeoutId = Mainloop.timeout_add(timeout, Lang.bind(this, this._onTrayLeftTimeout));
}
},
@ -2227,22 +2164,22 @@ const MessageTray = new Lang.Class({
this._updateState();
},
_onNotificationLeftTimeout: function() {
_onTrayLeftTimeout: function() {
let [x, y, mods] = global.get_pointer();
// We extend the timeout once if the mouse moved no further than MOUSE_LEFT_ACTOR_THRESHOLD to either side or up.
// We don't check how far down the mouse moved because any point above the tray, but below the exit coordinate,
// is close to the tray.
if (this._notificationLeftMouseX > -1 &&
y > this._notificationLeftMouseY - MOUSE_LEFT_ACTOR_THRESHOLD &&
x < this._notificationLeftMouseX + MOUSE_LEFT_ACTOR_THRESHOLD &&
x > this._notificationLeftMouseX - MOUSE_LEFT_ACTOR_THRESHOLD) {
this._notificationLeftMouseX = -1;
this._notificationLeftTimeoutId = Mainloop.timeout_add(LONGER_HIDE_TIMEOUT * 1000,
Lang.bind(this, this._onNotificationLeftTimeout));
if (this._trayLeftMouseX > -1 &&
y > this._trayLeftMouseY - MOUSE_LEFT_ACTOR_THRESHOLD &&
x < this._trayLeftMouseX + MOUSE_LEFT_ACTOR_THRESHOLD &&
x > this._trayLeftMouseX - MOUSE_LEFT_ACTOR_THRESHOLD) {
this._trayLeftMouseX = -1;
this._trayLeftTimeoutId = Mainloop.timeout_add(LONGER_HIDE_TIMEOUT * 1000,
Lang.bind(this, this._onTrayLeftTimeout));
} else {
this._notificationLeftTimeoutId = 0;
this._useLongerNotificationLeftTimeout = false;
this._pointerInNotification = false;
this._trayLeftTimeoutId = 0;
this._useLongerTrayLeftTimeout = false;
this._pointerInTray = false;
this._updateNotificationTimeout(0);
this._updateState();
}
@ -2250,7 +2187,7 @@ const MessageTray = new Lang.Class({
},
_escapeTray: function() {
this._pointerInNotification = false;
this._pointerInTray = false;
this._traySummoned = false;
this._setClickedSummaryItem(null);
this._updateNotificationTimeout(0);
@ -2259,33 +2196,26 @@ const MessageTray = new Lang.Class({
// All of the logic for what happens when occurs here; the various
// event handlers merely update variables such as
// 'this._pointerInNotification', 'this._traySummoned', etc, and
// 'this._pointerInTray', 'this._traySummoned', etc, and
// _updateState() figures out what (if anything) needs to be done
// at the present time.
_updateState: function() {
// Notifications
let notificationQueue = this._notificationQueue.filter(function(n) {
return !n.acknowledged;
});
let hasNotifications = Main.sessionMode.hasNotifications;
this._notificationQueue = notificationQueue;
let notificationQueue = this._notificationQueue;
let notificationUrgent = notificationQueue.length > 0 && notificationQueue[0].urgency == Urgency.CRITICAL;
let notificationForFeedback = notificationQueue.length > 0 && notificationQueue[0].forFeedback;
let notificationsLimited = this._busy || Main.layoutManager.bottomMonitor.inFullscreen;
let notificationsPending = notificationQueue.length > 0 && (!notificationsLimited || notificationUrgent || notificationForFeedback) && hasNotifications;
let notificationsPending = notificationQueue.length > 0 && (!notificationsLimited || notificationUrgent || notificationForFeedback) && Main.sessionMode.hasNotifications;
let nextNotification = notificationQueue.length > 0 ? notificationQueue[0] : null;
let notificationPinned = this._pointerInNotification && !this._notificationRemoved;
let notificationPinned = this._pointerInTray && !this._notificationRemoved;
let notificationExpanded = this._notification && this._notification.expanded;
let notificationExpired = this._notificationTimeoutId == 0 &&
!(this._notification && this._notification.urgency == Urgency.CRITICAL) &&
!(this._notification && this._notification.focused) &&
!this._pointerInNotification;
let notificationLockedOut = !hasNotifications && this._notification;
let notificationMustClose = (this._notificationRemoved || notificationLockedOut ||
(notificationExpired && this._userActiveWhileNotificationShown) ||
this._notificationClosed || this._traySummoned);
let canShowNotification = notificationsPending && this._trayState == State.HIDDEN && !this._traySummoned;
!this._pointerInTray;
let notificationLockedOut = !Main.sessionMode.hasNotifications && this._notification;
let notificationMustClose = this._notificationRemoved || notificationLockedOut || (notificationExpired && this._userActiveWhileNotificationShown) || this._notificationClosed;
let canShowNotification = notificationsPending && this._trayState == State.HIDDEN;
if (this._notificationState == State.HIDDEN) {
if (canShowNotification)
@ -2299,6 +2229,12 @@ const MessageTray = new Lang.Class({
this._ensureNotificationFocused();
}
let notificationsVisible = this._notificationState != State.HIDDEN;
let notificationsDone = !notificationsVisible && !notificationsPending;
let mustHideTray = ((notificationsPending && notificationUrgent)
|| notificationsVisible || !Main.sessionMode.hasNotifications);
// Summary notification
let haveClickedSummaryItem = this._clickedSummaryItem != null;
let summarySourceIsMainNotificationSource = (haveClickedSummaryItem && this._notification &&
@ -2306,15 +2242,10 @@ const MessageTray = new Lang.Class({
let canShowSummaryBoxPointer = this._trayState == State.SHOWN;
// We only have sources with empty notification stacks for legacy tray icons. Currently, we never attempt
// to show notifications for legacy tray icons, but this would be necessary if we did.
let requestedNotificationStackIsEmpty = (haveClickedSummaryItem &&
this._clickedSummaryItemMouseButton == 1 &&
this._clickedSummaryItem.source.notifications.length == 0);
let wrongSummaryNotificationStack = (haveClickedSummaryItem &&
this._clickedSummaryItemMouseButton == 1 &&
let requestedNotificationStackIsEmpty = (this._clickedSummaryItemMouseButton == 1 && this._clickedSummaryItem.source.notifications.length == 0);
let wrongSummaryNotificationStack = (this._clickedSummaryItemMouseButton == 1 &&
this._summaryBoxPointer.bin.child != this._clickedSummaryItem.notificationStackWidget);
let wrongSummaryRightClickMenu = (haveClickedSummaryItem &&
this._clickedSummaryItemMouseButton == 3 &&
this._clickedSummaryItem.rightClickMenu != null &&
let wrongSummaryRightClickMenu = (this._clickedSummaryItemMouseButton == 3 &&
this._summaryBoxPointer.bin.child != this._clickedSummaryItem.rightClickMenu);
let wrongSummaryBoxPointer = (haveClickedSummaryItem &&
(wrongSummaryNotificationStack || wrongSummaryRightClickMenu));
@ -2323,7 +2254,7 @@ const MessageTray = new Lang.Class({
if (haveClickedSummaryItem && !summarySourceIsMainNotificationSource && canShowSummaryBoxPointer && !requestedNotificationStackIsEmpty)
this._showSummaryBoxPointer();
} else if (this._summaryBoxPointerState == State.SHOWN) {
if (!haveClickedSummaryItem || !canShowSummaryBoxPointer || wrongSummaryBoxPointer || !hasNotifications) {
if (!haveClickedSummaryItem || !canShowSummaryBoxPointer || wrongSummaryBoxPointer || mustHideTray) {
this._hideSummaryBoxPointer();
if (wrongSummaryBoxPointer)
this._showSummaryBoxPointer();
@ -2333,7 +2264,7 @@ const MessageTray = new Lang.Class({
// Tray itself
let trayIsVisible = (this._trayState == State.SHOWING ||
this._trayState == State.SHOWN);
let trayShouldBeVisible = this._traySummoned && !this._keyboardVisible && hasNotifications;
let trayShouldBeVisible = this._traySummoned && !this._keyboardVisible && !mustHideTray;
if (!trayIsVisible && trayShouldBeVisible)
trayShouldBeVisible = this._showTray();
else if (trayIsVisible && !trayShouldBeVisible)
@ -2377,6 +2308,7 @@ const MessageTray = new Lang.Class({
_showTray: function() {
if (!this._grabHelper.grab({ actor: this.actor,
modal: true,
onUngrab: Lang.bind(this, this._escapeTray) })) {
this._traySummoned = false;
return false;
@ -2389,8 +2321,7 @@ const MessageTray = new Lang.Class({
transition: 'easeOutQuad'
});
for (let i = 0; i < this._lightboxes.length; i++)
this._lightboxes[i].show();
this._lightbox.show();
return true;
},
@ -2413,7 +2344,7 @@ const MessageTray = new Lang.Class({
if (this._desktopClone)
this._desktopClone.destroy();
let cloneSource = Main.overview.visible ? Main.layoutManager.overviewGroup : global.window_group;
let cloneSource = Main.overview.visible ? global.overlay_group : global.window_group;
this._desktopClone = new Clutter.Clone({ source: cloneSource,
clip: new Clutter.Geometry(this._bottomMonitorGeometry) });
Main.uiGroup.insert_child_above(this._desktopClone, cloneSource);
@ -2445,8 +2376,7 @@ const MessageTray = new Lang.Class({
// which would happen if GrabHelper ungrabbed for us.
// This is a no-op in that case.
this._grabHelper.ungrab({ actor: this.actor });
for (let i = 0; i < this._lightboxes.length; i++)
this._lightboxes[i].hide();
this._lightbox.hide();
},
_hideDesktopClone: function() {
@ -2495,7 +2425,7 @@ const MessageTray = new Lang.Class({
let [x, y, mods] = global.get_pointer();
// We save the position of the mouse at the time when we started showing the notification
// in order to determine if the notification popped up under it. We make that check if
// the user starts moving the mouse and _onNotificationHoverChanged() gets called. We don't
// the user starts moving the mouse and _onTrayHoverChanged() gets called. We don't
// expand the notification if it just happened to pop up under the mouse unless the user
// explicitly mouses away from it and then mouses back in.
this._showNotificationMouseX = x;
@ -2557,13 +2487,13 @@ const MessageTray = new Lang.Class({
_notificationTimeout: function() {
let [x, y, mods] = global.get_pointer();
if (y > this._lastSeenMouseY + 10 && !this._notificationHovered) {
if (y > this._lastSeenMouseY + 10 && !this.actor.hover) {
// The mouse is moving towards the notification, so don't
// hide it yet. (We just create a new timeout (and destroy
// the old one) each time because the bookkeeping is
// simpler.)
this._updateNotificationTimeout(1000);
} else if (this._useLongerNotificationLeftTimeout && !this._notificationLeftTimeoutId &&
} else if (this._useLongerTrayLeftTimeout && !this._trayLeftTimeoutId &&
(x != this._lastSeenMouseX || y != this._lastSeenMouseY)) {
// Refresh the timeout if the notification originally
// popped up under the pointer, and the pointer is hovering
@ -2580,7 +2510,19 @@ const MessageTray = new Lang.Class({
},
_hideNotification: function() {
this._notificationFocusGrabber.ungrabFocus();
// HACK!
// There seems to be a reentrancy issue in calling .ungrab() here,
// which causes _updateState to be called before _notificationState
// becomes HIDING. That hides the notification again, nullifying the
// object but not setting _notificationState (and that's the weird part)
// As then _notificationState is stuck into SHOWN but _notification
// is null, every new _updateState fails and the message tray is
// lost forever.
//
// See more at https://bugzilla.gnome.org/show_bug.cgi?id=683986
this._notificationState = State.HIDING;
this._grabHelper.ungrab({ actor: this._notification.actor });
if (this._notificationExpandedId) {
this._notification.disconnect(this._notificationExpandedId);
@ -2595,12 +2537,12 @@ const MessageTray = new Lang.Class({
this._notificationUnfocusedId = 0;
}
this._useLongerNotificationLeftTimeout = false;
if (this._notificationLeftTimeoutId) {
Mainloop.source_remove(this._notificationLeftTimeoutId);
this._notificationLeftTimeoutId = 0;
this._notificationLeftMouseX = -1;
this._notificationLeftMouseY = -1;
this._useLongerTrayLeftTimeout = false;
if (this._trayLeftTimeoutId) {
Mainloop.source_remove(this._trayLeftTimeoutId);
this._trayLeftTimeoutId = 0;
this._trayLeftMouseX = -1;
this._trayLeftMouseY = -1;
}
if (this._notificationRemoved) {
@ -2630,9 +2572,14 @@ const MessageTray = new Lang.Class({
if (notification.isTransient)
notification.destroy(NotificationDestroyedReason.EXPIRED);
this._closeButton.hide();
this._pointerInNotification = false;
this._notificationRemoved = false;
this._closeButton.hide();
this._pointerInTray = false;
// Clutter will send a leave-event the next time the mouse
// moves, but we need to set this here now to update the
// state machine.
this.actor.hover = false;
this._notificationBin.child = null;
this._notificationWidget.hide();
},
@ -2645,16 +2592,16 @@ const MessageTray = new Lang.Class({
},
_expandNotification: function(autoExpanding) {
// Don't focus notifications that are auto-expanding.
if (!autoExpanding)
this._ensureNotificationFocused();
if (!this._notificationExpandedId)
this._notificationExpandedId =
this._notification.connect('expanded',
Lang.bind(this, this._onNotificationExpanded));
// Don't animate changes in notifications that are auto-expanding.
this._notification.expand(!autoExpanding);
// Don't focus notifications that are auto-expanding.
if (!autoExpanding)
this._ensureNotificationFocused();
},
_onNotificationExpanded: function() {
@ -2678,7 +2625,8 @@ const MessageTray = new Lang.Class({
},
_ensureNotificationFocused: function() {
this._notificationFocusGrabber.grabFocus();
this._grabHelper.grab({ actor: this._notification.actor,
grabFocus: true });
},
_onSourceDoneDisplayingContent: function(source, closeTray) {
@ -2691,34 +2639,38 @@ const MessageTray = new Lang.Class({
},
_showSummaryBoxPointer: function() {
let child;
let summaryItem = this._clickedSummaryItem;
if (this._clickedSummaryItemMouseButton == 1) {
// Acknowledge all our notifications
summaryItem.source.notifications.forEach(function(n) { n.acknowledged = true; });
child = summaryItem.notificationStackWidget;
let closeButton = summaryItem.closeButton;
closeButton.show();
summaryItem.prepareNotificationStackForShowing();
} else if (this._clickedSummaryItemMouseButton == 3) {
child = summaryItem.rightClickMenu;
}
// If the user clicked the middle mouse button, or the item
// doesn't have a right-click menu, do nothing.
if (!child)
return;
this._summaryBoxPointerItem = summaryItem;
this._summaryBoxPointerItem = this._clickedSummaryItem;
this._summaryBoxPointerContentUpdatedId = this._summaryBoxPointerItem.connect('content-updated',
Lang.bind(this, this._onSummaryBoxPointerContentUpdated));
this._sourceDoneDisplayingId = this._summaryBoxPointerItem.source.connect('done-displaying-content',
Lang.bind(this, this._onSourceDoneDisplayingContent));
this._summaryBoxPointer.bin.child = child;
let hasRightClickMenu = this._summaryBoxPointerItem.rightClickMenu != null;
if (this._clickedSummaryItemMouseButton == 1 || !hasRightClickMenu) {
let newQueue = [];
for (let i = 0; i < this._notificationQueue.length; i++) {
let notification = this._notificationQueue[i];
let sameSource = this._summaryBoxPointerItem.source == notification.source;
if (sameSource)
notification.acknowledged = true;
else
newQueue.push(notification);
}
this._notificationQueue = newQueue;
this._summaryBoxPointer.bin.child = this._summaryBoxPointerItem.notificationStackWidget;
let closeButton = this._summaryBoxPointerItem.closeButton;
closeButton.show();
this._summaryBoxPointerCloseClickedId = closeButton.connect('clicked', Lang.bind(this, this._hideSummaryBoxPointer));
this._summaryBoxPointerItem.prepareNotificationStackForShowing();
} else if (this._clickedSummaryItemMouseButton == 3) {
this._summaryBoxPointer.bin.child = this._clickedSummaryItem.rightClickMenu;
this._summaryBoxPointerCloseClickedId = 0;
}
this._grabHelper.grab({ actor: this._summaryBoxPointer.bin.child,
grabFocus: true,
onUngrab: Lang.bind(this, this._onSummaryBoxPointerUngrabbed) });
this._summaryBoxPointer.actor.opacity = 0;
@ -2797,7 +2749,10 @@ const MessageTray = new Lang.Class({
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;
@ -2824,14 +2779,17 @@ const MessageTray = new Lang.Class({
this._summaryBoxPointerState = State.HIDDEN;
this._summaryBoxPointer.bin.child = null;
let sourceNotificationStackDoneShowing = null;
if (doneShowingNotificationStack) {
let source = this._summaryBoxPointerItem.source;
this._summaryBoxPointerItem.doneShowingNotificationStack();
this._summaryBoxPointerItem = null;
sourceNotificationStackDoneShowing = this._summaryBoxPointerItem.source;
}
if (source.isTransient && !this._reNotifyAfterHideNotification)
source.destroy(NotificationDestroyedReason.EXPIRED);
this._summaryBoxPointerItem = null;
if (sourceNotificationStackDoneShowing) {
if (sourceNotificationStackDoneShowing.isTransient && !this._reNotifyAfterHideNotification)
sourceNotificationStackDoneShowing.destroy(NotificationDestroyedReason.EXPIRED);
if (this._reNotifyAfterHideNotification) {
this._onNotify(this._reNotifyAfterHideNotification.source, this._reNotifyAfterHideNotification);
this._reNotifyAfterHideNotification = null;

View File

@ -14,7 +14,6 @@ const Atk = imports.gi.Atk;
const Params = imports.misc.params;
const Animation = imports.ui.animation;
const Layout = imports.ui.layout;
const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main;
@ -23,10 +22,6 @@ const Tweener = imports.ui.tweener;
const OPEN_AND_CLOSE_TIME = 0.1;
const FADE_OUT_DIALOG_TIME = 1.0;
const WORK_SPINNER_ICON_SIZE = 24;
const WORK_SPINNER_ANIMATION_DELAY = 1.0;
const WORK_SPINNER_ANIMATION_TIME = 0.3;
const State = {
OPENED: 0,
CLOSED: 1,
@ -70,27 +65,31 @@ const ModalDialog = new Lang.Class({
this._group.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
this._group.connect('key-release-event', Lang.bind(this, this._onKeyReleaseEvent));
this.backgroundStack = new St.Widget({ layout_manager: new Clutter.BinLayout() });
this._backgroundBin = new St.Bin({ child: this.backgroundStack,
x_fill: true, y_fill: true });
this._backgroundBin = new St.Bin();
this._monitorConstraint = new Layout.MonitorConstraint();
this._backgroundBin.add_constraint(this._monitorConstraint);
this._group.add_actor(this._backgroundBin);
this.dialogLayout = new St.BoxLayout({ style_class: 'modal-dialog',
vertical: true });
if (params.styleClass != null)
if (params.styleClass != null) {
this.dialogLayout.add_style_class_name(params.styleClass);
}
if (!this._shellReactive) {
this._lightbox = new Lightbox.Lightbox(this._group,
{ inhibitEvents: true });
this._lightbox.highlight(this._backgroundBin);
let stack = new Shell.Stack();
this._backgroundBin.child = stack;
this._eventBlocker = new Clutter.Actor({ reactive: true });
this.backgroundStack.add_actor(this._eventBlocker);
stack.add_actor(this._eventBlocker);
stack.add_actor(this.dialogLayout);
} else {
this._backgroundBin.child = this.dialogLayout;
}
this.backgroundStack.add_actor(this.dialogLayout);
this.contentLayout = new St.BoxLayout({ vertical: true });
@ -111,8 +110,6 @@ const ModalDialog = new Lang.Class({
this._initialKeyFocus = this.dialogLayout;
this._initialKeyFocusDestroyId = 0;
this._savedKeyFocus = null;
this._workSpinner = null;
},
destroy: function() {
@ -186,42 +183,6 @@ const ModalDialog = new Lang.Class({
return button;
},
placeSpinner: function(layoutInfo) {
let spinnerIcon = global.datadir + '/theme/process-working.svg';
this._workSpinner = new Animation.AnimatedIcon(spinnerIcon, WORK_SPINNER_ICON_SIZE);
this._workSpinner.actor.opacity = 0;
this._workSpinner.actor.show();
this.buttonLayout.add(this._workSpinner.actor, layoutInfo);
},
setWorking: function(working) {
if (!this._workSpinner)
return;
Tweener.removeTweens(this._workSpinner.actor);
if (working) {
this._workSpinner.play();
Tweener.addTween(this._workSpinner.actor,
{ opacity: 255,
delay: WORK_SPINNER_ANIMATION_DELAY,
time: WORK_SPINNER_ANIMATION_TIME,
transition: 'linear'
});
} else {
Tweener.addTween(this._workSpinner.actor,
{ opacity: 0,
time: WORK_SPINNER_ANIMATION_TIME,
transition: 'linear',
onCompleteScope: this,
onComplete: function() {
if (this._workSpinner)
this._workSpinner.stop();
}
});
}
},
_onKeyPressEvent: function(object, event) {
this._pressedKey = event.get_key_symbol();
},
@ -356,9 +317,8 @@ const ModalDialog = new Lang.Class({
if (this._savedKeyFocus) {
this._savedKeyFocus.grab_key_focus();
this._savedKeyFocus = null;
} else {
} else
this._initialKeyFocus.grab_key_focus();
}
if (!this._shellReactive)
this._eventBlocker.lower_bottom();

View File

@ -717,8 +717,8 @@ const Source = new Lang.Class({
this.notifications.length > 0)
return false;
let id = global.stage.connect('deactivate', Lang.bind(this, function () {
global.stage.disconnect(id);
let id = global.connect('notify::stage-input-mode', Lang.bind(this, function () {
global.disconnect(id);
this.trayIcon.click(event);
}));

View File

@ -8,7 +8,6 @@ const Layout = imports.ui.layout;
const Main = imports.ui.main;
const Mainloop = imports.mainloop;
const Tweener = imports.ui.tweener;
const Meta = imports.gi.Meta;
const HIDE_TIMEOUT = 1500;
const FADE_TIME = 0.1;
@ -72,7 +71,6 @@ const OsdWindow = new Lang.Class({
Name: 'OsdWindow',
_init: function() {
this._popupSize = 0;
this.actor = new St.Widget({ x_expand: true,
y_expand: true,
x_align: Clutter.ActorAlign.CENTER,
@ -82,15 +80,6 @@ const OsdWindow = new Lang.Class({
vertical: true });
this.actor.add_actor(this._box);
this._box.connect('style-changed', Lang.bind(this, this._onStyleChanged));
this._box.connect('notify::height', Lang.bind(this,
function() {
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this,
function() {
this._box.width = this._box.height;
}));
}));
this._icon = new St.Icon();
this._box.add(this._icon, { expand: true });
@ -107,7 +96,7 @@ const OsdWindow = new Lang.Class({
Lang.bind(this, this._monitorsChanged));
this._monitorsChanged();
Main.uiGroup.add_child(this.actor);
Main.layoutManager.addChrome(this.actor, { affectsInputRegion: false });
},
setIcon: function(icon) {
@ -136,10 +125,8 @@ const OsdWindow = new Lang.Class({
return;
if (!this.actor.visible) {
Meta.disable_unredirect_for_screen(global.screen);
this.actor.show();
this.actor.opacity = 0;
this.actor.get_parent().set_child_above_sibling(this.actor, null);
Tweener.addTween(this.actor,
{ opacity: 255,
@ -158,20 +145,16 @@ const OsdWindow = new Lang.Class({
return;
Mainloop.source_remove(this._hideTimeoutId);
this._hideTimeoutId = 0;
this._hide();
},
_hide: function() {
this._hideTimeoutId = 0;
Tweener.addTween(this.actor,
{ opacity: 0,
time: FADE_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() {
this._reset();
Meta.enable_unredirect_for_screen(global.screen);
})
});
onComplete: Lang.bind(this, this._reset) });
},
_reset: function() {
@ -186,25 +169,11 @@ const OsdWindow = new Lang.Class({
let scalew = monitor.width / 640.0;
let scaleh = monitor.height / 480.0;
let scale = Math.min(scalew, scaleh);
this._popupSize = 110 * Math.max(1, scale);
let size = 110 * Math.max(1, scale);
this._box.set_size(size, size);
this._box.translation_y = monitor.height / 4;
this._icon.icon_size = this._popupSize / 2;
this._box.style_changed();
},
_onStyleChanged: function() {
let themeNode = this._box.get_theme_node();
let horizontalPadding = themeNode.get_horizontal_padding();
let verticalPadding = themeNode.get_vertical_padding();
let topBorder = themeNode.get_border_width(St.Side.TOP);
let bottomBorder = themeNode.get_border_width(St.Side.BOTTOM);
let leftBorder = themeNode.get_border_width(St.Side.LEFT);
let rightBorder = themeNode.get_border_width(St.Side.RIGHT);
let minWidth = this._popupSize - verticalPadding - leftBorder - rightBorder;
let minHeight = this._popupSize - horizontalPadding - topBorder - bottomBorder;
this._box.style = 'min-height: %dpx;'.format(Math.max(minWidth, minHeight));
this._icon.icon_size = size / 2;
}
});

View File

@ -11,6 +11,7 @@ const Shell = imports.gi.Shell;
const Gdk = imports.gi.Gdk;
const Background = imports.ui.background;
const Dash = imports.ui.dash;
const DND = imports.ui.dnd;
const LayoutManager = imports.ui.layout;
const Main = imports.ui.main;
@ -19,6 +20,7 @@ const OverviewControls = imports.ui.overviewControls;
const Panel = imports.ui.panel;
const Params = imports.misc.params;
const Tweener = imports.ui.tweener;
const ViewSelector = imports.ui.viewSelector;
const WorkspaceThumbnail = imports.ui.workspaceThumbnail;
// Time for initial animation going into Overview mode
@ -29,7 +31,7 @@ const ANIMATION_TIME = 0.25;
// and don't want the shading animation to get cut off
const SHADE_ANIMATION_TIME = .20;
const DND_WINDOW_SWITCH_TIMEOUT = 750;
const DND_WINDOW_SWITCH_TIMEOUT = 1250;
const OVERVIEW_ACTIVATION_TIMEOUT = 0.5;
@ -115,7 +117,7 @@ const Overview = new Lang.Class({
let monitor = Main.layoutManager.primaryMonitor;
this._desktopFade = new St.Bin();
Main.layoutManager.overviewGroup.add_child(this._desktopFade);
global.overlay_group.add_actor(this._desktopFade);
let layout = new Clutter.BinLayout();
this._stack = new Clutter.Actor({ layout_manager: layout });
@ -131,8 +133,17 @@ const Overview = new Lang.Class({
y_expand: true });
this._overview._delegate = this;
this._groupStack = new St.Widget({ layout_manager: new Clutter.BinLayout(),
x_expand: true, y_expand: true,
clip_to_allocation: true });
this._group = new St.BoxLayout({ name: 'overview-group',
reactive: true,
x_expand: true, y_expand: true });
this._groupStack.add_actor(this._group);
this._backgroundGroup = new Meta.BackgroundGroup();
Main.layoutManager.overviewGroup.add_child(this._backgroundGroup);
global.overlay_group.add_child(this._backgroundGroup);
this._backgroundGroup.hide();
this._bgManagers = [];
this._activationTime = 0;
@ -146,13 +157,14 @@ const Overview = new Lang.Class({
// During transitions, we raise this to the top to avoid having the overview
// area be reactive; it causes too many issues such as double clicks on
// Dash elements, or mouseover handlers in the workspaces.
this._coverPane = new Clutter.Actor({ opacity: 0,
reactive: true });
this._stack.add_actor(this._coverPane);
this._coverPane = new Clutter.Rectangle({ opacity: 0,
reactive: true });
this._overview.add_actor(this._coverPane);
this._coverPane.connect('event', Lang.bind(this, function (actor, event) { return true; }));
this._stack.hide();
this._stack.add_actor(this._overview);
Main.layoutManager.overviewGroup.add_child(this._stack);
global.overlay_group.add_actor(this._stack);
this._coverPane.hide();
@ -165,6 +177,7 @@ const Overview = new Lang.Class({
Main.xdndHandler.connect('drag-end', Lang.bind(this, this._onDragEnd));
global.screen.connect('restacked', Lang.bind(this, this._onRestacked));
this._group.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
this._windowSwitchTimeoutId = 0;
this._windowSwitchTimestamp = 0;
@ -263,13 +276,28 @@ const Overview = new Lang.Class({
this._overview.add_actor(this._searchEntryBin);
// Create controls
this._controls = new OverviewControls.ControlsManager(this._searchEntry);
this._dash = this._controls.dash;
this._viewSelector = this._controls.viewSelector;
this._dash = new Dash.Dash();
this._viewSelector = new ViewSelector.ViewSelector(this._searchEntry,
this._dash.showAppsButton);
this._thumbnailsBox = new WorkspaceThumbnail.ThumbnailsBox();
this._controls = new OverviewControls.ControlsManager(this._dash,
this._thumbnailsBox,
this._viewSelector);
this._controls.dashActor.x_align = Clutter.ActorAlign.START;
this._controls.dashActor.y_expand = true;
// Put the dash in a separate layer to allow content to be centered
this._groupStack.add_actor(this._controls.dashActor);
// Pack all the actors into the group
this._group.add_actor(this._controls.dashSpacer);
this._group.add(this._viewSelector.actor, { x_fill: true,
expand: true });
this._group.add_actor(this._controls.thumbnailsActor);
// Add our same-line elements after the search entry
this._overview.add(this._controls.actor, { y_fill: true, expand: true });
this._controls.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
this._overview.add(this._groupStack, { y_fill: true, expand: true });
this._stack.add_actor(this._controls.indicatorActor);
@ -433,7 +461,6 @@ const Overview = new Lang.Class({
beginItemDrag: function(source) {
this.emit('item-drag-begin');
this._inDrag = true;
},
cancelledItemDrag: function(source) {
@ -442,12 +469,10 @@ const Overview = new Lang.Class({
endItemDrag: function(source) {
this.emit('item-drag-end');
this._inDrag = false;
},
beginWindowDrag: function(source) {
this.emit('window-drag-begin');
this._inDrag = true;
},
cancelledWindowDrag: function(source) {
@ -456,31 +481,24 @@ const Overview = new Lang.Class({
endWindowDrag: function(source) {
this.emit('window-drag-end');
this._inDrag = false;
},
// show:
//
// Animates the overview visible and grabs mouse and keyboard input
show: function() {
show : function() {
if (this.isDummy)
return;
if (this._shown)
return;
this._shown = true;
if (!this._syncGrab())
if (!this._syncInputMode())
return;
Main.layoutManager.showOverview();
this._animateVisible();
},
focusSearch: function() {
this.show();
this._searchEntry.grab_key_focus();
},
fadeInDesktop: function() {
this._desktopFade.opacity = 0;
this._desktopFade.show();
@ -522,6 +540,8 @@ const Overview = new Lang.Class({
//
// Disable unredirection while in the overview
Meta.disable_unredirect_for_screen(global.screen);
this._stack.show();
this._backgroundGroup.show();
this._viewSelector.show();
this._stack.opacity = 0;
@ -562,7 +582,7 @@ const Overview = new Lang.Class({
this._animateNotVisible();
this._shown = false;
this._syncGrab();
this._syncInputMode();
},
toggle: function() {
@ -584,8 +604,6 @@ const Overview = new Lang.Class({
shouldToggleByCornerOrButton: function() {
if (this.animationInProgress)
return false;
if (this._inDrag)
return false;
if (this._activationTime == 0 || Date.now() / 1000 - this._activationTime > OVERVIEW_ACTIVATION_TIMEOUT)
return true;
return false;
@ -593,8 +611,8 @@ const Overview = new Lang.Class({
//// Private methods ////
_syncGrab: function() {
// We delay grab changes during animation so that when removing the
_syncInputMode: function() {
// We delay input mode changes during animation so that when removing the
// overview we don't have a problem with the release of a press/release
// going to an application.
if (this.animationInProgress)
@ -612,12 +630,16 @@ const Overview = new Lang.Class({
return false;
}
}
} else {
global.stage_input_mode = Shell.StageInputMode.FULLSCREEN;
}
} else {
if (this._modal) {
Main.popModal(this._overview);
this._modal = false;
}
else if (global.stage_input_mode == Shell.StageInputMode.FULLSCREEN)
global.stage_input_mode = Shell.StageInputMode.NORMAL;
}
return true;
},
@ -656,7 +678,7 @@ const Overview = new Lang.Class({
if (!this._shown)
this._animateNotVisible();
this._syncGrab();
this._syncInputMode();
global.sync_pointer();
},
@ -666,19 +688,20 @@ const Overview = new Lang.Class({
this._viewSelector.hide();
this._desktopFade.hide();
this._coverPane.hide();
this._backgroundGroup.hide();
this._stack.hide();
this.visible = false;
this.animationInProgress = false;
this._coverPane.hide();
this.emit('hidden');
// Handle any calls to show* while we were hiding
if (this._shown)
this._animateVisible();
else
Main.layoutManager.hideOverview();
this._syncGrab();
this._syncInputMode();
// Fake a pointer event if requested
if (this._needsFakePointerEvent) {

View File

@ -1,18 +1,15 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const GObject = imports.gi.GObject;
const Clutter = imports.gi.Clutter;
const Lang = imports.lang;
const Meta = imports.gi.Meta;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
const Dash = imports.ui.dash;
const Main = imports.ui.main;
const Params = imports.misc.params;
const Tweener = imports.ui.tweener;
const ViewSelector = imports.ui.viewSelector;
const WorkspaceThumbnail = imports.ui.workspaceThumbnail;
const SIDE_CONTROLS_ANIMATION_TIME = 0.16;
@ -247,7 +244,6 @@ const ThumbnailsSlider = new Lang.Class({
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this.updateSlide));
this.actor.connect('notify::hover', Lang.bind(this, this.updateSlide));
this._thumbnailsBox.actor.bind_property('visible', this.actor, 'visible', GObject.BindingFlags.SYNC_CREATE);
},
_getAlwaysZoomOut: function() {
@ -273,11 +269,6 @@ const ThumbnailsSlider = new Lang.Class({
return alwaysZoomOut;
},
getNonExpandedWidth: function() {
let child = this.actor.get_first_child();
return child.get_theme_node().get_length('visible-width');
},
getSlide: function() {
if (!this.visible)
return 0;
@ -289,16 +280,18 @@ const ThumbnailsSlider = new Lang.Class({
let child = this.actor.get_first_child();
let preferredHeight = child.get_preferred_height(-1)[1];
let expandedWidth = child.get_preferred_width(preferredHeight)[1];
let visibleWidth = child.get_theme_node().get_length('visible-width');
return this.getNonExpandedWidth() / expandedWidth;
return visibleWidth / expandedWidth;
},
getVisibleWidth: function() {
let alwaysZoomOut = this._getAlwaysZoomOut();
if (alwaysZoomOut)
return this.parent();
else
return this.getNonExpandedWidth();
let child = this.actor.get_first_child();
return child.get_theme_node().get_length('visible-width');
}
});
@ -316,10 +309,6 @@ const DashSlider = new Lang.Class({
// available allocation
this._dash.actor.x_expand = true;
this._dash.actor.y_expand = true;
this.actor.x_align = Clutter.ActorAlign.START;
this.actor.y_expand = true;
this.actor.add_actor(this._dash.actor);
this._dash.connect('icon-size-changed', Lang.bind(this, this.updateSlide));
@ -490,77 +479,39 @@ const MessagesIndicator = new Lang.Class({
const ControlsManager = new Lang.Class({
Name: 'ControlsManager',
_init: function(searchEntry) {
this.dash = new Dash.Dash();
this._dashSlider = new DashSlider(this.dash);
this._dashSpacer = new DashSpacer();
this._dashSpacer.setDashActor(this._dashSlider.actor);
_init: function(dash, thumbnails, viewSelector) {
this._dashSlider = new DashSlider(dash);
this.dashActor = this._dashSlider.actor;
this.dashSpacer = new DashSpacer();
this.dashSpacer.setDashActor(this.dashActor);
this._thumbnailsBox = new WorkspaceThumbnail.ThumbnailsBox();
this._thumbnailsSlider = new ThumbnailsSlider(this._thumbnailsBox);
this._thumbnailsSlider = new ThumbnailsSlider(thumbnails);
this.thumbnailsActor = this._thumbnailsSlider.actor;
this.viewSelector = new ViewSelector.ViewSelector(searchEntry,
this.dash.showAppsButton);
this.viewSelector.connect('page-changed', Lang.bind(this, this._setVisibility));
this.viewSelector.connect('page-empty', Lang.bind(this, this._onPageEmpty));
this._indicator = new MessagesIndicator(this.viewSelector);
this._indicator = new MessagesIndicator(viewSelector);
this.indicatorActor = this._indicator.actor;
this.actor = new St.Widget({ layout_manager: new Clutter.BinLayout(),
reactive: true,
x_expand: true, y_expand: true,
clip_to_allocation: true });
this._group = new St.BoxLayout({ name: 'overview-group',
x_expand: true, y_expand: true });
this.actor.add_actor(this._group);
this.actor.add_actor(this._dashSlider.actor);
this._group.add_actor(this._dashSpacer);
this._group.add(this.viewSelector.actor, { x_fill: true,
expand: true });
this._group.add_actor(this._thumbnailsSlider.actor);
this._group.connect('notify::allocation', Lang.bind(this, this._updateWorkspacesGeometry));
this._viewSelector = viewSelector;
this._viewSelector.connect('page-changed', Lang.bind(this, this._setVisibility));
this._viewSelector.connect('page-empty', Lang.bind(this, this._onPageEmpty));
Main.overview.connect('showing', Lang.bind(this, this._updateSpacerVisibility));
Main.overview.connect('item-drag-begin', Lang.bind(this,
function() {
let activePage = this.viewSelector.getActivePage();
let activePage = this._viewSelector.getActivePage();
if (activePage != ViewSelector.ViewPage.WINDOWS)
this.viewSelector.fadeHalf();
this._viewSelector.fadeHalf();
}));
Main.overview.connect('item-drag-end', Lang.bind(this,
function() {
this.viewSelector.fadeIn();
this._viewSelector.fadeIn();
}));
Main.overview.connect('item-drag-cancelled', Lang.bind(this,
function() {
this.viewSelector.fadeIn();
this._viewSelector.fadeIn();
}));
},
_updateWorkspacesGeometry: function() {
let [x, y] = this.actor.get_transformed_position();
let [width, height] = this.actor.get_transformed_size();
let geometry = { x: x, y: y, width: width, height: height };
let spacing = this.actor.get_theme_node().get_length('spacing');
let dashWidth = this._dashSlider.getVisibleWidth() + spacing;
let thumbnailsWidth = this._thumbnailsSlider.getNonExpandedWidth() + spacing;
geometry.width -= dashWidth;
geometry.width -= thumbnailsWidth;
if (this.actor.get_text_direction() == Clutter.TextDirection.LTR)
geometry.x += dashWidth;
else
geometry.x += thumbnailsWidth;
this.viewSelector.setWorkspacesFullGeometry(geometry);
},
_setVisibility: function() {
// Ignore the case when we're leaving the overview, since
// actors will be made visible again when entering the overview
@ -570,7 +521,7 @@ const ControlsManager = new Lang.Class({
(Main.overview.animationInProgress && !Main.overview.visibleTarget))
return;
let activePage = this.viewSelector.getActivePage();
let activePage = this._viewSelector.getActivePage();
let dashVisible = (activePage == ViewSelector.ViewPage.WINDOWS ||
activePage == ViewSelector.ViewPage.APPS);
let thumbnailsVisible = (activePage == ViewSelector.ViewPage.WINDOWS);
@ -590,8 +541,8 @@ const ControlsManager = new Lang.Class({
if (Main.overview.animationInProgress && !Main.overview.visibleTarget)
return;
let activePage = this.viewSelector.getActivePage();
this._dashSpacer.visible = (activePage == ViewSelector.ViewPage.WINDOWS);
let activePage = this._viewSelector.getActivePage();
this.dashSpacer.visible = (activePage == ViewSelector.ViewPage.WINDOWS);
},
_onPageEmpty: function() {

View File

@ -15,14 +15,12 @@ const Signals = imports.signals;
const Atk = imports.gi.Atk;
const Animation = imports.ui.animation;
const Config = imports.misc.config;
const CtrlAltTab = imports.ui.ctrlAltTab;
const DND = imports.ui.dnd;
const Overview = imports.ui.overview;
const PopupMenu = imports.ui.popupMenu;
const PanelMenu = imports.ui.panelMenu;
const RemoteMenu = imports.ui.remoteMenu;
const Main = imports.ui.main;
const Tweener = imports.ui.tweener;
@ -30,6 +28,7 @@ const PANEL_ICON_SIZE = 24;
const BUTTON_DND_ACTIVATION_TIMEOUT = 250;
const ANIMATED_ICON_UPDATE_TIMEOUT = 100;
const SPINNER_ANIMATION_TIME = 0.2;
// To make sure the panel corners blend nicely with the panel,
@ -75,6 +74,81 @@ function _unpremultiply(color) {
blue: blue, alpha: color.alpha });
};
const Animation = new Lang.Class({
Name: 'Animation',
_init: function(filename, width, height, speed) {
this.actor = new St.Bin();
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
this._speed = speed;
this._isLoaded = false;
this._isPlaying = false;
this._timeoutId = 0;
this._frame = 0;
this._animations = St.TextureCache.get_default().load_sliced_image (filename, width, height,
Lang.bind(this, this._animationsLoaded));
this.actor.set_child(this._animations);
},
play: function() {
if (this._isLoaded && this._timeoutId == 0) {
if (this._frame == 0)
this._showFrame(0);
this._timeoutId = Mainloop.timeout_add(this._speed, Lang.bind(this, this._update));
}
this._isPlaying = true;
},
stop: function() {
if (this._timeoutId > 0) {
Mainloop.source_remove(this._timeoutId);
this._timeoutId = 0;
}
this._isPlaying = false;
},
_showFrame: function(frame) {
let oldFrameActor = this._animations.get_child_at_index(this._frame);
if (oldFrameActor)
oldFrameActor.hide();
this._frame = (frame % this._animations.get_n_children());
let newFrameActor = this._animations.get_child_at_index(this._frame);
if (newFrameActor)
newFrameActor.show();
},
_update: function() {
this._showFrame(this._frame + 1);
return true;
},
_animationsLoaded: function() {
this._isLoaded = true;
if (this._isPlaying)
this.play();
},
_onDestroy: function() {
this.stop();
}
});
const AnimatedIcon = new Lang.Class({
Name: 'AnimatedIcon',
Extends: Animation,
_init: function(name, size) {
this.parent(global.datadir + '/theme/' + name, size, size, ANIMATED_ICON_UPDATE_TIMEOUT);
}
});
const TextShadower = new Lang.Class({
Name: 'TextShadower',
@ -184,7 +258,6 @@ const AppMenuButton = new Lang.Class({
this._actionGroupNotifyId = 0;
let bin = new St.Bin({ name: 'appMenu' });
bin.connect('style-changed', Lang.bind(this, this._onStyleChanged));
this.actor.add_actor(bin);
this.actor.bind_property("reactive", this.actor, "can-focus", 0);
@ -215,25 +288,27 @@ const AppMenuButton = new Lang.Class({
this._visible = !Main.overview.visible;
if (!this._visible)
this.actor.hide();
this._overviewHidingId = Main.overview.connect('hiding', Lang.bind(this, function () {
Main.overview.connect('hiding', Lang.bind(this, function () {
this.show();
}));
this._overviewShowingId = Main.overview.connect('showing', Lang.bind(this, function () {
Main.overview.connect('showing', Lang.bind(this, function () {
this.hide();
}));
this._stop = true;
this._spinner = null;
this._spinner = new AnimatedIcon('process-working.svg',
PANEL_ICON_SIZE);
this._container.add_actor(this._spinner.actor);
this._spinner.actor.hide();
this._spinner.actor.lower_bottom();
let tracker = Shell.WindowTracker.get_default();
let appSys = Shell.AppSystem.get_default();
this._focusAppNotifyId =
tracker.connect('notify::focus-app', Lang.bind(this, this._focusAppChanged));
this._appStateChangedSignalId =
appSys.connect('app-state-changed', Lang.bind(this, this._onAppStateChanged));
this._switchWorkspaceNotifyId =
global.window_manager.connect('switch-workspace', Lang.bind(this, this._sync));
tracker.connect('notify::focus-app', Lang.bind(this, this._focusAppChanged));
appSys.connect('app-state-changed', Lang.bind(this, this._onAppStateChanged));
global.window_manager.connect('switch-workspace', Lang.bind(this, this._sync));
this._sync();
},
@ -279,34 +354,19 @@ const AppMenuButton = new Lang.Class({
onCompleteScope: this });
},
_onStyleChanged: function(actor) {
let node = actor.get_theme_node();
let [success, icon] = node.lookup_url('spinner-image', false);
if (!success || this._spinnerIcon == icon)
return;
this._spinnerIcon = icon;
this._spinner = new Animation.AnimatedIcon(this._spinnerIcon, PANEL_ICON_SIZE);
this._container.add_actor(this._spinner.actor);
this._spinner.actor.hide();
this._spinner.actor.lower_bottom();
},
_onIconBoxStyleChanged: function() {
let node = this._iconBox.get_theme_node();
this._iconBottomClip = node.get_length('app-icon-bottom-clip');
this._updateIconBoxClip();
},
_syncIcon: function() {
let icon = this._targetApp.get_faded_icon(2 * PANEL_ICON_SIZE, this._iconBox.text_direction);
this._iconBox.set_child(icon);
},
_onIconThemeChanged: function() {
if (this._iconBox.child == null)
return;
this._syncIcon();
this._iconBox.child.destroy();
let icon = this._targetApp.get_faded_icon(2 * PANEL_ICON_SIZE);
this._iconBox.set_child(icon);
},
_updateIconBoxClip: function() {
@ -325,10 +385,6 @@ const AppMenuButton = new Lang.Class({
this._stop = true;
this.actor.reactive = true;
if (this._spinner == null)
return;
Tweener.addTween(this._spinner.actor,
{ opacity: 0,
time: SPINNER_ANIMATION_TIME,
@ -345,10 +401,6 @@ const AppMenuButton = new Lang.Class({
startAnimation: function() {
this._stop = false;
this.actor.reactive = false;
if (this._spinner == null)
return;
this._spinner.play();
this._spinner.actor.show();
},
@ -411,9 +463,6 @@ const AppMenuButton = new Lang.Class({
}
this._label.actor.allocate(childBox, flags);
if (this._spinner == null)
return;
if (direction == Clutter.TextDirection.LTR) {
childBox.x1 = Math.floor(iconWidth / 2) + this._label.actor.width;
childBox.x2 = childBox.x1 + this._spinner.actor.width;
@ -452,7 +501,7 @@ const AppMenuButton = new Lang.Class({
// If the app has just lost focus to the panel, pretend
// nothing happened; otherwise you can't keynav to the
// app menu.
if (global.stage.key_focus != null)
if (global.stage_input_mode == Shell.StageInputMode.FOCUSED)
return;
}
this._sync();
@ -497,20 +546,14 @@ const AppMenuButton = new Lang.Class({
}
if (targetApp == this._targetApp) {
if (targetApp &&
targetApp.get_state() != Shell.AppState.STARTING &&
targetApp.get_state() != Shell.AppState.BUSY) {
if (targetApp && targetApp.get_state() != Shell.AppState.STARTING) {
this.stopAnimation();
this._maybeSetMenu();
} else if (targetApp &&
targetApp.get_state() == Shell.AppState.BUSY) {
this.startAnimation();
}
return;
}
if (this._spinner)
this._spinner.actor.hide();
this._spinner.actor.hide();
if (this._iconBox.child != null)
this._iconBox.child.destroy();
this._iconBox.hide();
@ -529,14 +572,15 @@ const AppMenuButton = new Lang.Class({
}
this._targetApp = targetApp;
let icon = targetApp.get_faded_icon(2 * PANEL_ICON_SIZE);
this._label.setText(targetApp.get_name());
this.setName(targetApp.get_name());
this._syncIcon();
this._iconBox.set_child(icon);
this._iconBox.show();
if (targetApp.get_state() == Shell.AppState.STARTING ||
targetApp.get_state() == Shell.AppState.BUSY)
if (targetApp.get_state() == Shell.AppState.STARTING)
this.startAnimation();
else
this._maybeSetMenu();
@ -548,11 +592,11 @@ const AppMenuButton = new Lang.Class({
let menu;
if (this._targetApp.action_group && this._targetApp.menu) {
if (this.menu instanceof RemoteMenu.RemoteMenu &&
if (this.menu instanceof PopupMenu.RemoteMenu &&
this.menu.actionGroup == this._targetApp.action_group)
return;
menu = new RemoteMenu.RemoteMenu(this.actor, this._targetApp.menu, this._targetApp.action_group);
menu = new PopupMenu.RemoteMenu(this.actor, this._targetApp.menu, this._targetApp.action_group);
menu.connect('activate', Lang.bind(this, function() {
let win = this._targetApp.get_windows()[0];
win.check_alive(global.get_current_time());
@ -572,33 +616,6 @@ const AppMenuButton = new Lang.Class({
this.setMenu(menu);
this._menuManager.addMenu(menu);
},
destroy: function() {
if (this._appStateChangedSignalId > 0) {
let appSys = Shell.AppSystem.get_default();
appSys.disconnect(this._appStateChangedSignalId);
this._appStateChangedSignalId = 0;
}
if (this._focusAppNotifyId > 0) {
let tracker = Shell.WindowTracker.get_default();
tracker.disconnect(this._focusAppNotifyId);
this._focusAppNotifyId = 0;
}
if (this._overviewHidingId > 0) {
Main.overview.disconnect(this._overviewHidingId);
this._overviewHidingId = 0;
}
if (this._overviewShowingId > 0) {
Main.overview.disconnect(this._overviewShowingId);
this._overviewShowingId = 0;
}
if (this._switchWorkspaceNotifyId > 0) {
global.window_manager.disconnect(this._switchWorkspaceNotifyId);
this._switchWorkspaceNotifyId = 0;
}
this.parent();
}
});
@ -856,9 +873,10 @@ const PANEL_ITEM_IMPLEMENTATIONS = {
'volume': imports.ui.status.volume.Indicator,
'battery': imports.ui.status.power.Indicator,
'lockScreen': imports.ui.status.lockScreenMenu.Indicator,
'logo': imports.gdm.loginDialog.LogoMenuButton,
'keyboard': imports.ui.status.keyboard.InputSourceIndicator,
'powerMenu': imports.gdm.powerMenu.PowerMenuButton,
'system': imports.ui.status.system.Indicator,
'userMenu': imports.ui.userMenu.UserMenuButton
};
if (Config.HAVE_BLUETOOTH)
@ -884,7 +902,7 @@ const Panel = new Lang.Class({
this.statusArea = {};
this.menuManager = new PopupMenu.PopupMenuManager(this, { keybindingMode: Shell.KeyBindingMode.TOPBAR_POPUP });
this.menuManager = new PopupMenu.PopupMenuManager(this);
this._leftBox = new St.BoxLayout({ name: 'panelLeft' });
this.actor.add_actor(this._leftBox);
@ -1038,18 +1056,17 @@ const Panel = new Lang.Class({
return true;
},
toggleAppMenu: function() {
openAppMenu: function() {
let indicator = this.statusArea.appMenu;
if (!indicator) // appMenu not supported by current session mode
return;
let menu = indicator.menu;
if (!indicator.actor.reactive)
if (!indicator.actor.reactive || menu.isOpen)
return;
menu.toggle();
if (menu.isOpen)
menu.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
menu.open();
menu.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
},
set boxOpacity(value) {

View File

@ -110,7 +110,6 @@ const Button = new Lang.Class({
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
this.actor.connect('key-press-event', Lang.bind(this, this._onSourceKeyPress));
this.actor.connect('notify::visible', Lang.bind(this, this._onVisibilityChanged));
if (dontCreateMenu)
this.menu = new PopupMenu.PopupDummyMenu(this.actor);
@ -184,18 +183,7 @@ const Button = new Lang.Class({
return false;
},
_onVisibilityChanged: function() {
if (!this.menu)
return;
if (!this.actor.visible)
this.menu.close();
},
_onMenuKeyPress: function(actor, event) {
if (global.focus_manager.navigate_from_event(event))
return true;
let symbol = event.get_key_symbol();
if (symbol == Clutter.KEY_Left || symbol == Clutter.KEY_Right) {
let group = global.focus_manager.get_group(this.actor);

File diff suppressed because it is too large Load Diff

View File

@ -1,199 +0,0 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Atk = imports.gi.Atk;
const GLib = imports.gi.GLib;
const GObject = imports.gi.GObject;
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const ShellMenu = imports.gi.ShellMenu;
const St = imports.gi.St;
const PopupMenu = imports.ui.popupMenu;
function stripMnemonics(label) {
if (!label)
return '';
// remove all underscores that are not followed by another underscore
return label.replace(/_([^_])/, '$1');
}
function _insertItem(menu, trackerItem, position) {
let mapper;
if (trackerItem.get_is_separator())
mapper = new RemoteMenuSeparatorItemMapper(trackerItem);
else if (trackerItem.get_has_submenu())
mapper = new RemoteMenuSubmenuItemMapper(trackerItem);
else
mapper = new RemoteMenuItemMapper(trackerItem);
let item = mapper.menuItem;
menu.addMenuItem(item, position);
}
function _removeItem(menu, position) {
let items = menu._getMenuItems();
items[position].destroy();
}
const RemoteMenuSeparatorItemMapper = new Lang.Class({
Name: 'RemoteMenuSeparatorItemMapper',
_init: function(trackerItem) {
this._trackerItem = trackerItem;
this.menuItem = new PopupMenu.PopupSeparatorMenuItem();
this._trackerItem.connect('notify::label', Lang.bind(this, this._updateLabel));
this._updateLabel();
this.menuItem.connect('destroy', function() {
trackerItem.run_dispose();
});
},
_updateLabel: function() {
this.menuItem.label.text = stripMnemonics(this._trackerItem.label);
},
});
const RequestSubMenu = new Lang.Class({
Name: 'RequestSubMenu',
Extends: PopupMenu.PopupSubMenuMenuItem,
_init: function() {
this.parent('');
this._requestOpen = false;
},
_setOpenState: function(open) {
this.emit('request-open', open);
this._requestOpen = open;
},
_getOpenState: function() {
return this._requestOpen;
},
});
const RemoteMenuSubmenuItemMapper = new Lang.Class({
Name: 'RemoteMenuSubmenuItemMapper',
_init: function(trackerItem) {
this._trackerItem = trackerItem;
this.menuItem = new RequestSubMenu();
this._trackerItem.connect('notify::label', Lang.bind(this, this._updateLabel));
this._updateLabel();
this._tracker = Shell.MenuTracker.new_for_item_submenu(this._trackerItem,
_insertItem.bind(null, this.menuItem.menu),
_removeItem.bind(null, this.menuItem.menu));
this.menuItem.connect('request-open', Lang.bind(this, function(menu, open) {
this._trackerItem.request_submenu_shown(open);
}));
this._trackerItem.connect('notify::submenu-shown', Lang.bind(this, function() {
this.menuItem.setSubmenuShown(this._trackerItem.get_submenu_shown());
}));
this.menuItem.connect('destroy', function() {
trackerItem.run_dispose();
});
},
destroy: function() {
this._tracker.destroy();
this.parent();
},
_updateLabel: function() {
this.menuItem.label.text = stripMnemonics(this._trackerItem.label);
},
});
const RemoteMenuItemMapper = new Lang.Class({
Name: 'RemoteMenuItemMapper',
_init: function(trackerItem) {
this._trackerItem = trackerItem;
this.menuItem = new PopupMenu.PopupBaseMenuItem();
this._label = new St.Label();
this.menuItem.addActor(this._label);
this.menuItem.actor.label_actor = this._label;
this.menuItem.connect('activate', Lang.bind(this, function() {
this._trackerItem.activated();
}));
this._trackerItem.bind_property('visible', this.menuItem.actor, 'visible', GObject.BindingFlags.SYNC_CREATE);
this._trackerItem.connect('notify::label', Lang.bind(this, this._updateLabel));
this._trackerItem.connect('notify::sensitive', Lang.bind(this, this._updateSensitivity));
this._trackerItem.connect('notify::role', Lang.bind(this, this._updateRole));
this._trackerItem.connect('notify::toggled', Lang.bind(this, this._updateDecoration));
this._updateLabel();
this._updateSensitivity();
this._updateRole();
this.menuItem.connect('destroy', function() {
trackerItem.run_dispose();
});
},
_updateLabel: function() {
this._label.text = stripMnemonics(this._trackerItem.label);
},
_updateSensitivity: function() {
this.menuItem.setSensitive(this._trackerItem.sensitive);
},
_updateDecoration: function() {
let ornamentForRole = {};
ornamentForRole[ShellMenu.MenuTrackerItemRole.RADIO] = PopupMenu.Ornament.DOT;
ornamentForRole[ShellMenu.MenuTrackerItemRole.CHECK] = PopupMenu.Ornament.CHECK;
let ornament = PopupMenu.Ornament.NONE;
if (this._trackerItem.toggled)
ornament = ornamentForRole[this._trackerItem.role];
this.menuItem.setOrnament(ornament);
},
_updateRole: function() {
let a11yRoles = {};
a11yRoles[ShellMenu.MenuTrackerItemRole.NORMAL] = Atk.Role.MENU_ITEM;
a11yRoles[ShellMenu.MenuTrackerItemRole.RADIO] = Atk.Role.RADIO_MENU_ITEM;
a11yRoles[ShellMenu.MenuTrackerItemRole.CHECK] = Atk.Role.CHECK_MENU_ITEM;
let a11yRole = a11yRoles[this._trackerItem.role];
this.menuItem.actor.accessible_role = a11yRole;
this._updateDecoration();
},
});
const RemoteMenu = new Lang.Class({
Name: 'RemoteMenu',
Extends: PopupMenu.PopupMenu,
_init: function(sourceActor, model, actionGroup) {
this.parent(sourceActor, 0.0, St.Side.TOP);
this._model = model;
this._actionGroup = actionGroup;
this._tracker = Shell.MenuTracker.new(this._actionGroup,
this._model,
null, /* action namespace */
_insertItem.bind(null, this),
_removeItem.bind(null, this));
},
destroy: function() {
this._tracker.destroy();
this.parent();
},
});

View File

@ -7,6 +7,7 @@ const Lang = imports.lang;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
const FileUtils = imports.misc.fileUtils;
const Search = imports.ui.search;
const KEY_FILE_GROUP = 'Shell Search Provider';
@ -59,114 +60,108 @@ var SearchProviderProxy = Gio.DBusProxy.makeProxyWrapper(SearchProviderIface);
var SearchProvider2Proxy = Gio.DBusProxy.makeProxyWrapper(SearchProvider2Iface);
function loadRemoteSearchProviders(addProviderCallback) {
let objectPaths = {};
let loadedProviders = [];
let data = { loadedProviders: [],
objectPaths: {},
addProviderCallback: addProviderCallback };
FileUtils.collectFromDatadirsAsync('search-providers',
{ loadedCallback: remoteProvidersLoaded,
processFile: loadRemoteSearchProvider,
data: data
});
}
function loadRemoteSearchProvider(file) {
let keyfile = new GLib.KeyFile();
let path = file.get_path();
function loadRemoteSearchProvider(file, info, data) {
let keyfile = new GLib.KeyFile();
let path = file.get_path();
try {
keyfile.load_from_file(path, 0);
} catch(e) {
return;
}
if (!keyfile.has_group(KEY_FILE_GROUP))
return;
let remoteProvider;
try {
let group = KEY_FILE_GROUP;
let busName = keyfile.get_string(group, 'BusName');
let objectPath = keyfile.get_string(group, 'ObjectPath');
if (objectPaths[objectPath])
return;
let appInfo = null;
try {
let desktopId = keyfile.get_string(group, 'DesktopId');
appInfo = Gio.DesktopAppInfo.new(desktopId);
} catch (e) {
log('Ignoring search provider ' + path + ': missing DesktopId');
return;
}
let version = '1';
try {
version = keyfile.get_string(group, 'Version');
} catch (e) {
// ignore error
}
if (version >= 2)
remoteProvider = new RemoteSearchProvider2(appInfo, busName, objectPath);
else
remoteProvider = new RemoteSearchProvider(appInfo, busName, objectPath);
objectPaths[objectPath] = remoteProvider;
loadedProviders.push(remoteProvider);
} catch(e) {
log('Failed to add search provider %s: %s'.format(path, e.toString()));
}
try {
keyfile.load_from_file(path, 0);
} catch(e) {
return;
}
let dataDirs = GLib.get_system_data_dirs();
dataDirs.forEach(function(dataDir) {
let path = GLib.build_filenamev([dataDir, 'gnome-shell', 'search-providers']);
let dir = Gio.File.new_for_path(path);
let fileEnum;
try {
fileEnum = dir.enumerate_children('standard::name,standard::type',
Gio.FileQueryInfoFlags.NONE, null);
} catch (e) {
fileEnum = null;
}
if (fileEnum != null) {
let info;
while ((info = fileEnum.next_file(null)))
loadRemoteSearchProvider(fileEnum.get_child(info));
}
});
if (!keyfile.has_group(KEY_FILE_GROUP))
return;
let remoteProvider;
try {
let group = KEY_FILE_GROUP;
let busName = keyfile.get_string(group, 'BusName');
let objectPath = keyfile.get_string(group, 'ObjectPath');
if (data.objectPaths[objectPath])
return;
let appInfo = null;
try {
let desktopId = keyfile.get_string(group, 'DesktopId');
appInfo = Gio.DesktopAppInfo.new(desktopId);
} catch (e) {
log('Ignoring search provider ' + path + ': missing DesktopId');
return;
}
let version = '1';
try {
version = keyfile.get_string(group, 'Version');
} catch (e) {
// ignore error
}
if (version >= 2)
remoteProvider = new RemoteSearchProvider2(appInfo, busName, objectPath);
else
remoteProvider = new RemoteSearchProvider(appInfo, busName, objectPath);
data.objectPaths[objectPath] = remoteProvider;
data.loadedProviders.push(remoteProvider);
} catch(e) {
log('Failed to add search provider %s: %s'.format(path, e.toString()));
}
}
function remoteProvidersLoaded(loadState) {
let searchSettings = new Gio.Settings({ schema: Search.SEARCH_PROVIDERS_SCHEMA });
let sortOrder = searchSettings.get_strv('sort-order');
// Special case gnome-control-center to be always active and always first
sortOrder.unshift('gnome-control-center.desktop');
loadedProviders.sort(function(providerA, providerB) {
let idxA, idxB;
let appIdA, appIdB;
loadState.loadedProviders.sort(
function(providerA, providerB) {
let idxA, idxB;
let appIdA, appIdB;
appIdA = providerA.appInfo.get_id();
appIdB = providerB.appInfo.get_id();
appIdA = providerA.appInfo.get_id();
appIdB = providerB.appInfo.get_id();
idxA = sortOrder.indexOf(appIdA);
idxB = sortOrder.indexOf(appIdB);
idxA = sortOrder.indexOf(appIdA);
idxB = sortOrder.indexOf(appIdB);
// if no provider is found in the order, use alphabetical order
if ((idxA == -1) && (idxB == -1)) {
let nameA = providerA.appInfo.get_name();
let nameB = providerB.appInfo.get_name();
// if 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);
}
return GLib.utf8_collate(nameA, nameB);
}
// if providerA isn't found, it's sorted after providerB
if (idxA == -1)
return 1;
// if providerA isn't found, it's sorted after providerB
if (idxA == -1)
return 1;
// if providerB isn't found, it's sorted after providerA
if (idxB == -1)
return -1;
// if providerB isn't found, it's sorted after providerA
if (idxB == -1)
return -1;
// finally, if both providers are found, return their order in the list
return (idxA - idxB);
});
// finally, if both providers are found, return their order in the list
return (idxA - idxB);
});
loadedProviders.forEach(addProviderCallback);
loadState.loadedProviders.forEach(
function(provider) {
loadState.addProviderCallback(provider);
});
}
const RemoteSearchProvider = new Lang.Class({
@ -192,9 +187,7 @@ const RemoteSearchProvider = new Lang.Class({
createIcon: function(size, meta) {
let gicon;
if (meta['icon']) {
gicon = Gio.icon_deserialize(meta['icon']);
} else if (meta['gicon']) {
if (meta['gicon']) {
gicon = Gio.icon_new_for_string(meta['gicon']);
} else if (meta['icon-data']) {
let [width, height, rowStride, hasAlpha,
@ -210,7 +203,7 @@ const RemoteSearchProvider = new Lang.Class({
_getResultsFinished: function(results, error) {
if (error)
return;
this.searchSystem.setResults(this, results[0]);
this.searchSystem.pushResults(this, results[0]);
},
getInitialResultSet: function(terms) {
@ -222,7 +215,7 @@ const RemoteSearchProvider = new Lang.Class({
this._cancellable);
} catch(e) {
log('Error calling GetInitialResultSet for provider %s: %s'.format(this.id, e.toString()));
this.searchSystem.setResults(this, []);
this.searchSystem.pushResults(this, []);
}
},
@ -235,7 +228,7 @@ const RemoteSearchProvider = new Lang.Class({
this._cancellable);
} catch(e) {
log('Error calling GetSubsearchResultSet for provider %s: %s'.format(this.id, e.toString()));
this.searchSystem.setResults(this, []);
this.searchSystem.pushResults(this, []);
}
},
@ -247,12 +240,8 @@ const RemoteSearchProvider = new Lang.Class({
let metas = results[0];
let resultMetas = [];
for (let i = 0; i < metas.length; i++) {
for (let prop in metas[i]) {
// we can use the serialized icon variant directly
if (prop != 'icon')
metas[i][prop] = metas[i][prop].deep_unpack();
}
for (let prop in metas[i])
metas[i][prop] = metas[i][prop].deep_unpack();
resultMetas.push({ id: metas[i]['id'],
name: metas[i]['name'],
description: metas[i]['description'],

View File

@ -35,8 +35,7 @@ const RunDialog = new Lang.Class({
Extends: ModalDialog.ModalDialog,
_init : function() {
this.parent({ styleClass: 'run-dialog',
destroyOnClose: false });
this.parent({ styleClass: 'run-dialog' });
this._lockdownSettings = new Gio.Settings({ schema: LOCKDOWN_SCHEMA });
this._terminalSettings = new Gio.Settings({ schema: TERMINAL_SCHEMA });

View File

@ -23,7 +23,6 @@ const Main = imports.ui.main;
const Overview = imports.ui.overview;
const MessageTray = imports.ui.messageTray;
const ShellDBus = imports.ui.shellDBus;
const SmartcardManager = imports.misc.smartcardManager;
const Tweener = imports.ui.tweener;
const Util = imports.misc.util;
@ -31,7 +30,6 @@ const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver';
const LOCK_ENABLED_KEY = 'lock-enabled';
const LOCK_DELAY_KEY = 'lock-delay';
const LOCKED_STATE_STR = 'screenShield.locked';
// fraction of screen height the arrow must reach before completing
// the slide up automatically
const ARROW_DRAG_THRESHOLD = 0.1;
@ -216,7 +214,6 @@ const NotificationsBox = new Lang.Class({
if (musicNotification != null &&
this._musicBin.child == null) {
musicNotification.acknowledged = true;
if (musicNotification.actor.get_parent() != null)
musicNotification.actor.get_parent().remove_actor(musicNotification.actor);
this._musicBin.child = musicNotification.actor;
@ -249,7 +246,6 @@ const NotificationsBox = new Lang.Class({
sourceCountChangedId: 0,
sourceTitleChangedId: 0,
sourceUpdatedId: 0,
sourceNotifyId: 0,
musicNotification: null,
sourceBox: null,
titleLabel: null,
@ -260,12 +256,6 @@ const NotificationsBox = new Lang.Class({
this._showSource(source, obj, obj.sourceBox);
this._notificationBox.add(obj.sourceBox, { x_fill: false, x_align: St.Align.START });
if (obj.musicNotification) {
obj.sourceNotifyId = source.connect('notify', Lang.bind(this, function(source, notification) {
notification.acknowledged = true;
}));
}
obj.sourceCountChangedId = source.connect('count-updated', Lang.bind(this, function(source) {
this._countChanged(source, obj);
}));
@ -346,8 +336,6 @@ const NotificationsBox = new Lang.Class({
if (obj.musicNotification) {
this._musicBin.child = null;
obj.musicNotification = null;
source.disconnect(obj.sourceNotifyId);
}
source.disconnect(obj.sourceDestroyId);
@ -517,13 +505,6 @@ const ScreenShield = new Lang.Class({
this._screenSaverDBus = new ShellDBus.ScreenSaverDBus(this);
this._smartcardManager = SmartcardManager.getSmartcardManager();
this._smartcardManager.connect('smartcard-inserted',
Lang.bind(this, function() {
if (this._isLocked)
this._liftShield(true, 0);
}));
this._inhibitor = null;
this._aboutToSuspend = false;
this._loginManager = LoginManager.getLoginManager();
@ -581,7 +562,6 @@ const ScreenShield = new Lang.Class({
this._bgManagers[i].destroy();
this._bgManagers = [];
this._backgroundGroup.destroy_all_children();
for (let i = 0; i < Main.layoutManager.monitors.length; i++)
this._createBackground(i);
@ -728,8 +708,6 @@ const ScreenShield = new Lang.Class({
},
_onDragEnd: function(action, actor, eventX, eventY, modifiers) {
if (this._lockScreenState != MessageTray.State.HIDING)
return;
if (this._lockScreenGroup.y < -(ARROW_DRAG_THRESHOLD * global.stage.height)) {
// Complete motion automatically
let [velocity, velocityX, velocityY] = this._dragAction.get_velocity(0);
@ -772,19 +750,6 @@ const ScreenShield = new Lang.Class({
}
}
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;
}
if (!this._becomeModal()) {
// We could not become modal, so we can't activate the
// screenshield. The user is probably very upset at this
@ -798,6 +763,19 @@ const ScreenShield = new Lang.Class({
return;
}
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._activationTime == 0)
@ -935,6 +913,7 @@ const ScreenShield = new Lang.Class({
}
this._dialog.connect('failed', Lang.bind(this, this._onUnlockFailed));
this._dialog.connect('unlocked', Lang.bind(this, this._onUnlockSucceded));
}
this._dialog.allowCancel = allowCancel;
@ -944,6 +923,10 @@ const ScreenShield = new Lang.Class({
this._resetLockScreen(true, false);
},
_onUnlockSucceded: function() {
this.deactivate(true);
},
_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
@ -1129,30 +1112,13 @@ const ScreenShield = new Lang.Class({
},
deactivate: function(animate) {
this._dialog.finish(Lang.bind(this, function() {
this._finishDeactivate(animate);
}));
},
_finishDeactivate: function(animate) {
this._hideLockScreen(animate, 0);
if (this._hasLockScreen)
this._clearLockScreen();
if (Main.sessionMode.currentMode == 'lock-screen')
Main.sessionMode.popMode('lock-screen');
if (Main.sessionMode.currentMode == 'unlock-dialog')
Main.sessionMode.popMode('unlock-dialog');
if (this._dialog && !this._isGreeter)
this._dialog.popModal();
if (this._isModal) {
Main.popModal(this.actor);
this._isModal = false;
}
Tweener.addTween(this._lockDialogGroup, {
scale_x: 0,
scale_y: 0,
@ -1164,12 +1130,21 @@ const ScreenShield = new Lang.Class({
},
_completeDeactivate: function() {
if (this._hasLockScreen)
this._clearLockScreen();
if (this._dialog && !this._isGreeter) {
this._dialog.destroy();
this._dialog = null;
}
this._lightbox.hide();
if (this._isModal) {
Main.popModal(this.actor);
this._isModal = false;
}
this.actor.hide();
if (this._becameActiveId != 0) {
@ -1187,7 +1162,6 @@ const ScreenShield = new Lang.Class({
this._isLocked = false;
this.emit('active-changed');
this.emit('locked-changed');
global.set_runtime_state(LOCKED_STATE_STR, null);
},
activate: function(animate) {
@ -1204,7 +1178,6 @@ const ScreenShield = new Lang.Class({
}
this._resetLockScreen(animate, animate);
global.set_runtime_state(LOCKED_STATE_STR, GLib.Variant.new('b', true));
// We used to set isActive and emit active-changed here,
// but now we do that from lockScreenShown, which means
@ -1226,26 +1199,10 @@ const ScreenShield = new Lang.Class({
return;
}
// Clear the clipboard - otherwise, its contents may be leaked
// to unauthorized parties by pasting into the unlock dialog's
// password entry and unmasking the entry
St.Clipboard.get_default().set_text(St.ClipboardType.CLIPBOARD, '');
St.Clipboard.get_default().set_text(St.ClipboardType.PRIMARY, '');
this._isLocked = true;
this.activate(animate);
this.emit('locked-changed');
},
// If the previous shell crashed, and gnome-session restarted us, then re-lock
lockIfWasLocked: function() {
let wasLocked = global.get_runtime_state('b', LOCKED_STATE_STR);
if (wasLocked === null)
return;
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
this.lock(false);
}));
}
});
Signals.addSignalMethods(ScreenShield.prototype);

View File

@ -1,140 +0,0 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const Hash = imports.misc.hash;
const Main = imports.ui.main;
const ScreencastIface = <interface name="org.gnome.Shell.Screencast">
<method name="Screencast">
<arg type="s" direction="in" name="file_template"/>
<arg type="a{sv}" direction="in" name="options"/>
<arg type="b" direction="out" name="success"/>
<arg type="s" direction="out" name="filename_used"/>
</method>
<method name="ScreencastArea">
<arg type="i" direction="in" name="x"/>
<arg type="i" direction="in" name="y"/>
<arg type="i" direction="in" name="width"/>
<arg type="i" direction="in" name="height"/>
<arg type="s" direction="in" name="file_template"/>
<arg type="a{sv}" direction="in" name="options"/>
<arg type="b" direction="out" name="success"/>
<arg type="s" direction="out" name="filename_used"/>
</method>
<method name="StopScreencast">
<arg type="b" direction="out" name="success"/>
</method>
</interface>;
const ScreencastService = new Lang.Class({
Name: 'ScreencastService',
_init: function() {
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(ScreencastIface, this);
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell/Screencast');
Gio.DBus.session.own_name('org.gnome.Shell.Screencast', Gio.BusNameOwnerFlags.REPLACE, null, null);
this._recorders = new Hash.Map();
Main.sessionMode.connect('updated',
Lang.bind(this, this._sessionModeChanged));
},
_ensureRecorderForSender: function(sender) {
let recorder = this._recorders.get(sender);
if (!recorder) {
recorder = new Shell.Recorder({ stage: global.stage });
recorder._watchNameId =
Gio.bus_watch_name(Gio.BusType.SESSION, sender, 0, null,
Lang.bind(this, this._onNameVanished));
this._recorders.set(sender, recorder);
}
return recorder;
},
_sessionModeChanged: function() {
if (Main.sessionMode.allowScreencast)
return;
for (let sender in this._recorders.keys())
this._recorders.delete(sender);
},
_onNameVanished: function(connection, name) {
this._stopRecordingForSender(name);
},
_stopRecordingForSender: function(sender) {
let recorder = this._recorders.get(sender);
if (!recorder)
return false;
Gio.bus_unwatch_name(recorder._watchNameId);
recorder.close();
this._recorders.delete(sender);
return true;
},
_applyOptionalParameters: function(recorder, options) {
for (let option in options)
options[option] = options[option].deep_unpack();
if (options['pipeline'])
recorder.set_pipeline(options['pipeline']);
if (options['framerate'])
recorder.set_framerate(options['framerate']);
if (options['draw-cursor'])
recorder.set_draw_cursor(options['draw-cursor']);
},
ScreencastAsync: function(params, invocation) {
let returnValue = [false, ''];
if (!Main.sessionMode.allowScreencast)
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
let sender = invocation.get_sender();
let recorder = this._ensureRecorderForSender(sender);
if (!recorder.is_recording()) {
let [fileTemplate, options] = params;
recorder.set_file_template(fileTemplate);
this._applyOptionalParameters(recorder, options);
let [success, fileName] = recorder.record();
returnValue = [success, fileName ? fileName : ''];
}
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
},
ScreencastAreaAsync: function(params, invocation) {
let returnValue = [false, ''];
if (!Main.sessionMode.allowScreencast)
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
let sender = invocation.get_sender();
let recorder = this._ensureRecorderForSender(sender);
if (!recorder.is_recording()) {
let [x, y, width, height, fileTemplate, options] = params;
recorder.set_file_template(fileTemplate);
recorder.set_area(x, y, width, height);
this._applyOptionalParameters(recorder, options);
let [success, fileName] = recorder.record();
returnValue = [success, fileName ? fileName : ''];
}
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
},
StopScreencastAsync: function(params, invocation) {
let success = this._stopRecordingForSender(invocation.get_sender());
invocation.return_value(GLib.Variant.new('(b)', [success]));
}
});

View File

@ -31,7 +31,7 @@ const SearchSystem = new Lang.Class({
let remoteIndex = this._remoteProviders.indexOf(provider);
if (remoteIndex != -1)
this._remoteProviders.splice(remoteIndex, 1);
this._remoteProviders.splice(index, 1);
},
getProviders: function() {
@ -51,7 +51,7 @@ const SearchSystem = new Lang.Class({
this._previousResults = [];
},
setResults: function(provider, results) {
pushResults: function(provider, results) {
let i = this._providers.indexOf(provider);
if (i == -1)
return;

View File

@ -180,82 +180,13 @@ const GridSearchResult = new Lang.Class({
}
});
const SearchResultsBase = new Lang.Class({
Name: 'SearchResultsBase',
const ListSearchResults = new Lang.Class({
Name: 'ListSearchResults',
_init: function(provider) {
this.provider = provider;
this._terms = [];
this.actor = new St.BoxLayout({ style_class: 'search-section',
vertical: true });
this._resultDisplayBin = new St.Bin({ x_fill: true,
y_fill: true });
this.actor.add(this._resultDisplayBin, { expand: true });
let separator = new Separator.HorizontalSeparator({ style_class: 'search-section-separator' });
this.actor.add(separator.actor);
},
destroy: function() {
this.actor.destroy();
this._terms = [];
},
_clearResultDisplay: function() {
},
clear: function() {
this._clearResultDisplay();
this.actor.hide();
},
_keyFocusIn: function(icon) {
this.emit('key-focus-in', icon);
},
_setMoreIconVisible: function(visible) {
},
updateSearch: function(providerResults, terms, callback) {
this._terms = terms;
if (providerResults.length == 0) {
this._clearResultDisplay();
this.actor.hide();
callback();
} else {
let maxResults = this._getMaxDisplayedResults();
let results = providerResults.slice(0, maxResults);
let hasMoreResults = results.length < providerResults.length;
this.provider.getResultMetas(results, Lang.bind(this, function(metas) {
this.clear();
// To avoid CSS transitions causing flickering when
// the first search result stays the same, we hide the
// content while filling in the results.
this.actor.hide();
this._clearResultDisplay();
this._renderResults(metas);
this._setMoreIconVisible(hasMoreResults && this.provider.canLaunchSearch);
this.actor.show();
callback();
}));
}
}
});
const ListSearchResults = new Lang.Class({
Name: 'ListSearchResults',
Extends: SearchResultsBase,
_init: function(provider) {
this.parent(provider);
this._container = new St.BoxLayout({ style_class: 'search-section-content' });
this.actor = new St.BoxLayout({ style_class: 'search-section-content' });
this.providerIcon = new ProviderIcon(provider);
this.providerIcon.connect('clicked', Lang.bind(this,
function() {
@ -263,27 +194,48 @@ const ListSearchResults = new Lang.Class({
Main.overview.toggle();
}));
this._container.add(this.providerIcon, { x_fill: false,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.START });
this.actor.add(this.providerIcon, { x_fill: false,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.START });
this._content = new St.BoxLayout({ style_class: 'list-search-results',
vertical: true });
this._container.add(this._content, { expand: true });
this.actor.add(this._content, { expand: true });
this._resultDisplayBin.set_child(this._container);
this._notDisplayedResult = [];
this._terms = [];
this._pendingClear = false;
},
_setMoreIconVisible: function(visible) {
this.providerIcon.moreIcon.visible = true;
getResultsForDisplay: function() {
let alreadyVisible = this._pendingClear ? 0 : this.getVisibleResultCount();
let canDisplay = MAX_LIST_SEARCH_RESULTS_ROWS - alreadyVisible;
let newResults = this._notDisplayedResult.splice(0, canDisplay);
return newResults;
},
_getMaxDisplayedResults: function() {
return MAX_LIST_SEARCH_RESULTS_ROWS;
getVisibleResultCount: function() {
return this._content.get_n_children();
},
_renderResults: function(metas) {
hasMoreResults: function() {
return this._notDisplayedResult.length > 0;
},
setResults: function(results, terms) {
// copy the lists
this._notDisplayedResult = results.slice(0);
this._terms = terms.slice(0);
this._pendingClear = true;
},
_keyFocusIn: function(icon) {
this.emit('key-focus-in', icon);
},
renderResults: function(metas) {
for (let i = 0; i < metas.length; i++) {
let display = new ListSearchResult(this.provider, metas[i], this._terms);
display.actor.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
@ -291,12 +243,13 @@ const ListSearchResults = new Lang.Class({
}
},
_clearResultDisplay: function () {
clear: function () {
this._content.destroy_all_children();
this._pendingClear = false;
},
getFirstResult: function() {
if (this._content.get_n_children() > 0)
if (this.getVisibleResultCount() > 0)
return this._content.get_child_at_index(0)._delegate;
else
return null;
@ -306,24 +259,50 @@ Signals.addSignalMethods(ListSearchResults.prototype);
const GridSearchResults = new Lang.Class({
Name: 'GridSearchResults',
Extends: SearchResultsBase,
_init: function(provider) {
this.parent(provider);
this.provider = provider;
this._grid = new IconGrid.IconGrid({ rowLimit: MAX_GRID_SEARCH_RESULTS_ROWS,
xAlign: St.Align.START });
this._bin = new St.Bin({ x_align: St.Align.MIDDLE });
this._bin.set_child(this._grid.actor);
this.actor = new St.Bin({ x_align: St.Align.MIDDLE });
this._resultDisplayBin.set_child(this._bin);
this.actor.set_child(this._grid.actor);
this._notDisplayedResult = [];
this._terms = [];
this._pendingClear = false;
},
_getMaxDisplayedResults: function() {
return this._grid.childrenInRow(this._bin.width) * this._grid.getRowLimit();
getResultsForDisplay: function() {
let alreadyVisible = this._pendingClear ? 0 : this._grid.visibleItemsCount();
let canDisplay = this._grid.childrenInRow(this.actor.width) * this._grid.getRowLimit()
- alreadyVisible;
let newResults = this._notDisplayedResult.splice(0, canDisplay);
return newResults;
},
_renderResults: function(metas) {
getVisibleResultCount: function() {
return this._grid.visibleItemsCount();
},
hasMoreResults: function() {
return this._notDisplayedResult.length > 0;
},
setResults: function(results, terms) {
// copy the lists
this._notDisplayedResult = results.slice(0);
this._terms = terms.slice(0);
this._pendingClear = true;
},
_keyFocusIn: function(icon) {
this.emit('key-focus-in', icon);
},
renderResults: function(metas) {
for (let i = 0; i < metas.length; i++) {
let display = new GridSearchResult(this.provider, metas[i], this._terms);
display.actor.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
@ -331,12 +310,13 @@ const GridSearchResults = new Lang.Class({
}
},
_clearResultDisplay: function () {
clear: function () {
this._grid.removeAll();
this._pendingClear = false;
},
getFirstResult: function() {
if (this._grid.visibleItemsCount() > 0)
if (this.getVisibleResultCount() > 0)
return this._grid.getItemAtIndex(0)._delegate;
else
return null;
@ -386,9 +366,9 @@ const SearchResults = new Lang.Class({
this._content.add(this._statusBin, { expand: true });
this._statusBin.add_actor(this._statusText);
this._providers = this._searchSystem.getProviders();
this._providerDisplays = {};
this._providerMeta = [];
for (let i = 0; i < this._providers.length; i++) {
this.createProviderDisplay(this._providers[i]);
this.createProviderMeta(this._providers[i]);
}
this._highlightDefault = false;
@ -406,33 +386,61 @@ const SearchResults = new Lang.Class({
Util.ensureActorVisibleInScrollView(this._scrollView, icon);
},
createProviderDisplay: function(provider) {
let providerDisplay = null;
createProviderMeta: function(provider) {
let providerBox = new St.BoxLayout({ style_class: 'search-section',
vertical: true });
let providerIcon = null;
let resultDisplay = null;
if (provider.appInfo) {
providerDisplay = new ListSearchResults(provider);
resultDisplay = new ListSearchResults(provider);
providerIcon = resultDisplay.providerIcon;
} else {
providerDisplay = new GridSearchResults(provider);
resultDisplay = new GridSearchResults(provider);
}
providerDisplay.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
this._providerDisplays[provider.id] = providerDisplay;
this._content.add(providerDisplay.actor);
resultDisplay.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
let resultDisplayBin = new St.Bin({ child: resultDisplay.actor,
x_fill: true,
y_fill: true });
providerBox.add(resultDisplayBin, { expand: true });
let separator = new Separator.HorizontalSeparator({ style_class: 'search-section-separator' });
providerBox.add(separator.actor);
this._providerMeta.push({ provider: provider,
actor: providerBox,
icon: providerIcon,
resultDisplay: resultDisplay });
this._content.add(providerBox);
},
destroyProviderDisplay: function(provider) {
this._providerDisplays[provider.id].destroy();
delete this._providerDisplays[provider.id];
destroyProviderMeta: function(provider) {
for (let i=0; i < this._providerMeta.length; i++) {
let meta = this._providerMeta[i];
if (meta.provider == provider) {
meta.actor.destroy();
this._providerMeta.splice(i, 1);
break;
}
}
},
_clearDisplay: function() {
for (let i = 0; i < this._providers.length; i++) {
let provider = this._providers[i];
let providerDisplay = this._providerDisplays[provider.id];
providerDisplay.clear();
for (let i = 0; i < this._providerMeta.length; i++) {
let meta = this._providerMeta[i];
meta.resultDisplay.clear();
meta.actor.hide();
}
},
_clearDisplayForProvider: function(provider) {
let meta = this._metaForProvider(provider);
meta.resultDisplay.clear();
meta.actor.hide();
},
reset: function() {
this._searchSystem.reset();
this._statusBin.hide();
@ -446,17 +454,20 @@ const SearchResults = new Lang.Class({
this._statusBin.show();
},
_metaForProvider: function(provider) {
return this._providerMeta[this._providers.indexOf(provider)];
},
_maybeSetInitialSelection: function() {
let newDefaultResult = null;
for (let i = 0; i < this._providers.length; i++) {
let provider = this._providers[i];
let display = this._providerDisplays[provider.id];
for (let i = 0; i < this._providerMeta.length; i++) {
let meta = this._providerMeta[i];
if (!display.actor.visible)
if (!meta.actor.visible)
continue;
let firstResult = display.getFirstResult();
let firstResult = meta.resultDisplay.getFirstResult();
if (firstResult) {
newDefaultResult = firstResult;
break; // select this one!
@ -476,14 +487,11 @@ const SearchResults = new Lang.Class({
_updateStatusText: function () {
let haveResults = false;
for (let i = 0; i < this._providers.length; i++) {
let provider = this._providers[i];
let display = this._providerDisplays[provider.id];
if (display.getFirstResult()) {
for (let i = 0; i < this._providerMeta.length; ++i)
if (this._providerMeta[i].resultDisplay.getFirstResult()) {
haveResults = true;
break;
}
}
if (!haveResults) {
this._statusText.set_text(_("No results."));
@ -496,12 +504,42 @@ const SearchResults = new Lang.Class({
_updateResults: function(searchSystem, results) {
let terms = searchSystem.getTerms();
let [provider, providerResults] = results;
let display = this._providerDisplays[provider.id];
let meta = this._metaForProvider(provider);
display.updateSearch(providerResults, terms, Lang.bind(this, function() {
if (providerResults.length == 0) {
this._clearDisplayForProvider(provider);
meta.resultDisplay.setResults([], []);
this._maybeSetInitialSelection();
this._updateStatusText();
}));
} else {
meta.resultDisplay.setResults(providerResults, terms);
let results = meta.resultDisplay.getResultsForDisplay();
if (meta.icon)
meta.icon.moreIcon.visible =
meta.resultDisplay.hasMoreResults() &&
provider.canLaunchSearch;
provider.getResultMetas(results, Lang.bind(this, function(metas) {
this._clearDisplayForProvider(provider);
meta.actor.show();
// Hiding drops the key focus if we have it
let focus = global.stage.get_key_focus();
// To avoid CSS transitions causing flickering when
// the first search result stays the same, we hide the
// content while filling in the results.
this._content.hide();
meta.resultDisplay.renderResults(metas);
this._maybeSetInitialSelection();
this._updateStatusText();
this._content.show();
if (this._content.contains(focus))
global.stage.set_key_focus(focus);
}));
}
},
activateDefault: function() {

View File

@ -16,7 +16,6 @@ const _modes = {
'restrictive': {
parentMode: null,
stylesheetName: 'gnome-shell.css',
overridesSchema: 'org.gnome.shell.overrides',
hasOverview: false,
showCalendarEvents: false,
allowSettings: false,
@ -46,7 +45,7 @@ const _modes = {
unlockDialog: imports.gdm.loginDialog.LoginDialog,
components: ['polkitAgent'],
panel: {
left: [],
left: ['logo'],
center: ['dateMenu'],
right: ['a11yGreeter', 'display', 'keyboard',
'volume', 'battery', 'powerMenu']
@ -60,7 +59,7 @@ const _modes = {
unlockDialog: undefined,
components: ['polkitAgent', 'telepathyClient'],
panel: {
left: [],
left: ['userMenu'],
center: [],
right: ['lockScreen']
},
@ -72,13 +71,24 @@ const _modes = {
unlockDialog: undefined,
components: ['polkitAgent', 'telepathyClient'],
panel: {
left: [],
left: ['userMenu'],
center: [],
right: ['a11y', 'keyboard', 'lockScreen']
},
panelStyle: 'unlock-screen'
},
'initial-setup': {
hasWindows: true,
isPrimary: true,
components: ['keyring'],
panel: {
left: [],
center: ['dateMenu'],
right: ['a11yGreeter', 'keyboard', 'volume', 'battery']
}
},
'user': {
hasOverview: true,
showCalendarEvents: true,
@ -97,7 +107,7 @@ const _modes = {
left: ['activities', 'appMenu'],
center: ['dateMenu'],
right: ['a11y', 'keyboard', 'volume', 'bluetooth',
'network', 'battery', 'system']
'network', 'battery', 'userMenu']
}
}
};
@ -185,10 +195,6 @@ const SessionMode = new Lang.Class({
return this._modeStack[this._modeStack.length - 1];
},
get allowScreencast() {
return this.components.indexOf('recorder') != -1;
},
_sync: function() {
let params = this._modes[this.currentMode];
let defaults;

View File

@ -12,7 +12,6 @@ const ExtensionDownloader = imports.ui.extensionDownloader;
const ExtensionUtils = imports.misc.extensionUtils;
const Hash = imports.misc.hash;
const Main = imports.ui.main;
const Screencast = imports.ui.screencast;
const Screenshot = imports.ui.screenshot;
const GnomeShellIface = <interface name="org.gnome.Shell">
@ -21,7 +20,6 @@ const GnomeShellIface = <interface name="org.gnome.Shell">
<arg type="b" direction="out" name="success" />
<arg type="s" direction="out" name="result" />
</method>
<method name="FocusSearch"/>
<method name="ShowOSD">
<arg type="a{sv}" direction="in" name="params"/>
</method>
@ -41,7 +39,6 @@ const GnomeShellIface = <interface name="org.gnome.Shell">
<signal name="AcceleratorActivated">
<arg name="action" type="u" />
<arg name="deviceid" type="u" />
<arg name="timestamp" type="u" />
</signal>
<property name="Mode" type="s" access="read" />
<property name="OverviewActive" type="b" access="readwrite" />
@ -73,15 +70,14 @@ const GnomeShell = new Lang.Class({
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell');
this._extensionsService = new GnomeShellExtensions();
this._screencastService = new Screencast.ScreencastService();
this._screenshotService = new Screenshot.ScreenshotService();
this._grabbedAccelerators = new Hash.Map();
this._grabbers = new Hash.Map();
global.display.connect('accelerator-activated', Lang.bind(this,
function(display, action, deviceid, timestamp) {
this._emitAcceleratorActivated(action, deviceid, timestamp);
function(display, action, deviceid) {
this._emitAcceleratorActivated(action, deviceid);
}));
},
@ -101,7 +97,7 @@ const GnomeShell = new Lang.Class({
*/
Eval: function(code) {
if (!global.settings.get_boolean('development-tools'))
return [false, ''];
return [false, null];
let returnValue;
let success;
@ -118,10 +114,6 @@ const GnomeShell = new Lang.Class({
return [success, returnValue];
},
FocusSearch: function() {
Main.overview.focusSearch();
},
ShowOSD: function(params) {
for (let param in params)
params[param] = params[param].deep_unpack();
@ -167,7 +159,7 @@ const GnomeShell = new Lang.Class({
return invocation.return_value(GLib.Variant.new('(b)', [ungrabSucceeded]));
},
_emitAcceleratorActivated: function(action, deviceid, timestamp) {
_emitAcceleratorActivated: function(action, deviceid) {
let destination = this._grabbedAccelerators.get(action);
if (!destination)
return;
@ -178,7 +170,7 @@ const GnomeShell = new Lang.Class({
this._dbusImpl.get_object_path(),
info ? info.name : null,
'AcceleratorActivated',
GLib.Variant.new('(uuu)', [action, deviceid, timestamp]));
GLib.Variant.new('(uu)', [action, deviceid]));
},
_grabAcceleratorForSender: function(accelerator, flags, sender) {

View File

@ -14,7 +14,9 @@ const EntryMenu = new Lang.Class({
Name: 'ShellEntryMenu',
Extends: PopupMenu.PopupMenu,
_init: function(entry) {
_init: function(entry, params) {
params = Params.parse (params, { isPassword: false });
this.parent(entry, 0, St.Side.TOP);
this.actor.add_style_class_name('entry-context-menu');
@ -35,6 +37,8 @@ const EntryMenu = new Lang.Class({
this._pasteItem = item;
this._passwordItem = null;
if (params.isPassword)
this._makePasswordItem();
Main.uiGroup.add_actor(this.actor);
this.actor.hide();
@ -49,21 +53,19 @@ const EntryMenu = new Lang.Class({
},
get isPassword() {
return this._passwordItem != null;
return this._passwordItem != null;
},
set isPassword(v) {
if (v == this.isPassword)
return;
if (v == this.isPassword)
return;
if (v) {
this._makePasswordItem();
this._entry.input_purpose = Gtk.InputPurpose.PASSWORD;
} else {
this._passwordItem.destroy();
this._passwordItem = null;
this._entry.input_purpose = Gtk.InputPurpose.FREE_FORM;
}
if (v)
this._makePasswordItem();
else {
this._passwordItem.destroy();
this._passwordItem = null;
}
},
open: function(animate) {
@ -80,6 +82,11 @@ const EntryMenu = new Lang.Class({
this.actor.grab_key_focus();
},
close: function(animate) {
this._entry.grab_key_focus();
this.parent(animate);
},
_updateCopyItem: function() {
let selection = this._entry.clutter_text.get_selection();
this._copyItem.setSensitive(!this._entry.clutter_text.password_char &&
@ -153,10 +160,7 @@ function addContextMenu(entry, params) {
if (entry.menu)
return;
params = Params.parse (params, { isPassword: false });
entry.menu = new EntryMenu(entry);
entry.menu.isPassword = params.isPassword;
entry.menu = new EntryMenu(entry, params);
entry._menuManager = new PopupMenu.PopupMenuManager({ actor: entry });
entry._menuManager.addMenu(entry.menu);

View File

@ -1,210 +0,0 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Cairo = imports.cairo;
const Clutter = imports.gi.Clutter;
const Lang = imports.lang;
const St = imports.gi.St;
const Signals = imports.signals;
const SLIDER_SCROLL_STEP = 0.05; /* Slider scrolling step in % */
const Slider = new Lang.Class({
Name: "Slider",
_init: function(value) {
if (isNaN(value))
// Avoid spreading NaNs around
throw TypeError('The slider value must be a number');
this._value = Math.max(Math.min(value, 1), 0);
this.actor = new St.DrawingArea({ style_class: 'slider',
can_focus: true,
reactive: true });
this.actor.connect('repaint', Lang.bind(this, this._sliderRepaint));
this.actor.connect('button-press-event', Lang.bind(this, this._startDragging));
this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
this.actor.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
this._releaseId = this._motionId = 0;
this._dragging = false;
},
setValue: function(value) {
if (isNaN(value))
throw TypeError('The slider value must be a number');
this._value = Math.max(Math.min(value, 1), 0);
this.actor.queue_repaint();
},
_sliderRepaint: function(area) {
let cr = area.get_context();
let themeNode = area.get_theme_node();
let [width, height] = area.get_surface_size();
let handleRadius = themeNode.get_length('-slider-handle-radius');
let handleBorderWidth = themeNode.get_length('-slider-handle-border-width');
let [hasHandleColor, handleBorderColor] =
themeNode.lookup_color('-slider-handle-border-color', false);
let sliderHeight = themeNode.get_length('-slider-height');
let sliderBorderWidth = themeNode.get_length('-slider-border-width');
let sliderBorderRadius = Math.min(width, sliderHeight) / 2;
let sliderBorderColor = themeNode.get_color('-slider-border-color');
let sliderColor = themeNode.get_color('-slider-background-color');
let sliderActiveBorderColor = themeNode.get_color('-slider-active-border-color');
let sliderActiveColor = themeNode.get_color('-slider-active-background-color');
const TAU = Math.PI * 2;
let handleX = handleRadius + (width - 2 * handleRadius) * this._value;
cr.arc(sliderBorderRadius + sliderBorderWidth, height / 2, sliderBorderRadius, TAU * 1/4, TAU * 3/4);
cr.lineTo(handleX, (height - sliderHeight) / 2);
cr.lineTo(handleX, (height + sliderHeight) / 2);
cr.lineTo(sliderBorderRadius + sliderBorderWidth, (height + sliderHeight) / 2);
Clutter.cairo_set_source_color(cr, sliderActiveColor);
cr.fillPreserve();
Clutter.cairo_set_source_color(cr, sliderActiveBorderColor);
cr.setLineWidth(sliderBorderWidth);
cr.stroke();
cr.arc(width - sliderBorderRadius - sliderBorderWidth, height / 2, sliderBorderRadius, TAU * 3/4, TAU * 1/4);
cr.lineTo(handleX, (height + sliderHeight) / 2);
cr.lineTo(handleX, (height - sliderHeight) / 2);
cr.lineTo(width - sliderBorderRadius - sliderBorderWidth, (height - sliderHeight) / 2);
Clutter.cairo_set_source_color(cr, sliderColor);
cr.fillPreserve();
Clutter.cairo_set_source_color(cr, sliderBorderColor);
cr.setLineWidth(sliderBorderWidth);
cr.stroke();
let handleY = height / 2;
let color = themeNode.get_foreground_color();
Clutter.cairo_set_source_color(cr, color);
cr.arc(handleX, handleY, handleRadius, 0, 2 * Math.PI);
cr.fillPreserve();
if (hasHandleColor && handleBorderWidth) {
Clutter.cairo_set_source_color(cr, handleBorderColor);
cr.setLineWidth(handleBorderWidth);
cr.stroke();
}
cr.$dispose();
},
_startDragging: function(actor, event) {
this.startDragging(event);
},
startDragging: function(event) {
if (this._dragging)
return false;
this._dragging = true;
let device = event.get_device();
device.grab(this.actor);
this._grabbedDevice = device;
this._releaseId = this.actor.connect('button-release-event', Lang.bind(this, this._endDragging));
this._motionId = this.actor.connect('motion-event', Lang.bind(this, this._motionEvent));
let absX, absY;
[absX, absY] = event.get_coords();
this._moveHandle(absX, absY);
return true;
},
_endDragging: function() {
if (this._dragging) {
this.actor.disconnect(this._releaseId);
this.actor.disconnect(this._motionId);
this._grabbedDevice.ungrab();
this._grabbedDevice = null;
this._dragging = false;
this.emit('drag-end');
}
return true;
},
scroll: function(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.min(Math.max(0, this._value + delta), 1);
this.actor.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();
this._moveHandle(absX, absY);
return true;
},
_onKeyPressEvent: function (actor, event) {
let key = event.get_key_symbol();
if (key == Clutter.KEY_Right || key == Clutter.KEY_Left) {
let delta = key == Clutter.KEY_Right ? 0.1 : -0.1;
this._value = Math.max(0, Math.min(this._value + delta, 1));
this._slider.queue_repaint();
this.emit('value-changed', this._value);
this.emit('drag-end');
return true;
}
return false;
},
_moveHandle: function(absX, absY) {
let relX, relY, sliderX, sliderY;
[sliderX, sliderY] = this.actor.get_transformed_position();
relX = absX - sliderX;
relY = absY - sliderY;
let width = this.actor.width;
let handleRadius = this.actor.get_theme_node().get_length('-slider-handle-radius');
let newvalue;
if (relX < handleRadius)
newvalue = 0;
else if (relX > width - handleRadius)
newvalue = 1;
else
newvalue = (relX - handleRadius) / (width - 2 * handleRadius);
this._value = newvalue;
this.actor.queue_repaint();
this.emit('value-changed', this._value);
},
get value() {
return this._value;
}
});
Signals.addSignalMethods(Slider.prototype);

View File

@ -68,6 +68,9 @@ const ATIndicator = new Lang.Class({
let mouseKeys = this._buildItem(_("Mouse Keys"), A11Y_SCHEMA, KEY_MOUSE_KEYS_ENABLED);
this.menu.addMenuItem(mouseKeys);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addSettingsAction(_("Universal Access Settings"), 'gnome-universal-access-panel.desktop');
this._syncMenuVisibility();
},

View File

@ -13,6 +13,13 @@ const NotificationDaemon = imports.ui.notificationDaemon;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const ConnectionState = {
DISCONNECTED: 0,
CONNECTED: 1,
DISCONNECTING: 2,
CONNECTING: 3
}
const Indicator = new Lang.Class({
Name: 'BTIndicator',
Extends: PanelMenu.SystemStatusButton,
@ -20,38 +27,261 @@ const Indicator = new Lang.Class({
_init: function() {
this.parent('bluetooth-disabled-symbolic', _("Bluetooth"));
// The Bluetooth menu only appears when Bluetooth is in use,
// so just statically build it with a "Turn Off" menu item.
this._item = new PopupMenu.PopupSubMenuMenuItem(_("Bluetooth"), true);
this._item.icon.icon_name = 'bluetooth-active-symbolic';
this._item.menu.addAction(_("Turn Off"), Lang.bind(this, function() {
this._applet.killswitch_state = GnomeBluetooth.KillswitchState.SOFT_BLOCKED;
}));
this._item.menu.addSettingsAction(_("Bluetooth Settings"), 'gnome-bluetooth-panel.desktop');
this._applet = new GnomeBluetoothApplet.Applet();
this._applet.connect('devices-changed', Lang.bind(this, this._sync));
this._sync();
this._killswitch = new PopupMenu.PopupSwitchMenuItem(_("Bluetooth"), false);
this._applet.connect('notify::killswitch-state', Lang.bind(this, this._updateKillswitch));
this._killswitch.connect('toggled', Lang.bind(this, function() {
let current_state = this._applet.killswitch_state;
if (current_state != GnomeBluetooth.KillswitchState.HARD_BLOCKED &&
current_state != GnomeBluetooth.KillswitchState.NO_ADAPTER) {
this._applet.killswitch_state = this._killswitch.state ?
GnomeBluetooth.KillswitchState.UNBLOCKED:
GnomeBluetooth.KillswitchState.SOFT_BLOCKED;
} else
this._killswitch.setToggleState(false);
}));
this._discoverable = new PopupMenu.PopupSwitchMenuItem(_("Visibility"), this._applet.discoverable);
this._applet.connect('notify::discoverable', Lang.bind(this, function() {
this._discoverable.setToggleState(this._applet.discoverable);
}));
this._discoverable.connect('toggled', Lang.bind(this, function() {
this._applet.discoverable = this._discoverable.state;
}));
this._updateKillswitch();
this.menu.addMenuItem(this._killswitch);
this.menu.addMenuItem(this._discoverable);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._fullMenuItems = [new PopupMenu.PopupSeparatorMenuItem(),
new PopupMenu.PopupMenuItem(_("Send Files to Device…")),
new PopupMenu.PopupMenuItem(_("Set Up a New Device…")),
new PopupMenu.PopupSeparatorMenuItem()];
this._hasDevices = false;
this._fullMenuItems[1].connect('activate', function() {
GLib.spawn_command_line_async('bluetooth-sendto');
});
this._fullMenuItems[2].connect('activate', function() {
GLib.spawn_command_line_async('bluetooth-wizard');
});
for (let i = 0; i < this._fullMenuItems.length; i++) {
let item = this._fullMenuItems[i];
this.menu.addMenuItem(item);
}
this._deviceItemPosition = 3;
this._deviceItems = [];
this._applet.connect('devices-changed', Lang.bind(this, this._updateDevices));
this._updateDevices();
this._applet.connect('notify::show-full-menu', Lang.bind(this, this._updateFullMenu));
this._updateFullMenu();
this.menu.addSettingsAction(_("Bluetooth Settings"), 'gnome-bluetooth-panel.desktop');
this._applet.connect('pincode-request', Lang.bind(this, this._pinRequest));
this._applet.connect('confirm-request', Lang.bind(this, this._confirmRequest));
this._applet.connect('auth-request', Lang.bind(this, this._authRequest));
this._applet.connect('auth-service-request', Lang.bind(this, this._authServiceRequest));
this._applet.connect('cancel-request', Lang.bind(this, this._cancelRequest));
},
_sync: function() {
let connectedDevices = this._applet.get_devices().filter(function(device) {
return device.connected;
});
let nDevices = connectedDevices.length;
_updateKillswitch: function() {
let current_state = this._applet.killswitch_state;
let on = current_state == GnomeBluetooth.KillswitchState.UNBLOCKED;
let has_adapter = current_state != GnomeBluetooth.KillswitchState.NO_ADAPTER;
let can_toggle = current_state != GnomeBluetooth.KillswitchState.NO_ADAPTER &&
current_state != GnomeBluetooth.KillswitchState.HARD_BLOCKED;
let on = nDevices > 0;
this.mainIcon.visible = on;
this.actor.visible = on;
this._killswitch.setToggleState(on);
if (can_toggle)
this._killswitch.setStatus(null);
else
/* TRANSLATORS: this means that bluetooth was disabled by hardware rfkill */
this._killswitch.setStatus(_("hardware disabled"));
if (on)
this._item.status.text = ngettext("%d Connected Device", "%d Connected Devices").format(nDevices);
this.actor.visible = has_adapter;
if (on) {
this._discoverable.actor.show();
this.setIcon('bluetooth-active-symbolic');
} else {
this._discoverable.actor.hide();
this.setIcon('bluetooth-disabled-symbolic');
}
},
_updateDevices: function() {
let devices = this._applet.get_devices();
let newlist = [ ];
for (let i = 0; i < this._deviceItems.length; i++) {
let item = this._deviceItems[i];
let destroy = true;
for (let j = 0; j < devices.length; j++) {
if (item._device.device_path == devices[j].device_path) {
this._updateDeviceItem(item, devices[j]);
destroy = false;
break;
}
}
if (destroy)
item.destroy();
else
newlist.push(item);
}
this._deviceItems = newlist;
this._hasDevices = newlist.length > 0;
for (let i = 0; i < devices.length; i++) {
let d = devices[i];
if (d._item)
continue;
let item = this._createDeviceItem(d);
if (item) {
this.menu.addMenuItem(item, this._deviceItemPosition + this._deviceItems.length);
this._deviceItems.push(item);
this._hasDevices = true;
}
}
},
_updateDeviceItem: function(item, device) {
if (!device.can_connect && device.capabilities == GnomeBluetoothApplet.Capabilities.NONE) {
item.destroy();
return;
}
let prevDevice = item._device;
let prevCapabilities = prevDevice.capabilities;
let prevCanConnect = prevDevice.can_connect;
// adopt the new device object
item._device = device;
device._item = item;
// update properties
item.label.text = device.alias;
if (prevCapabilities != device.capabilities ||
prevCanConnect != device.can_connect) {
// need to rebuild the submenu
item.menu.removeAll();
this._buildDeviceSubMenu(item, device);
}
// update connected property
if (device.can_connect)
item._connectedMenuItem.setToggleState(device.connected);
},
_createDeviceItem: function(device) {
if (!device.can_connect && device.capabilities == GnomeBluetoothApplet.Capabilities.NONE)
return null;
let item = new PopupMenu.PopupSubMenuMenuItem(device.alias);
// adopt the device object, and add a back link
item._device = device;
device._item = item;
this._buildDeviceSubMenu(item, device);
return item;
},
_buildDeviceSubMenu: function(item, device) {
if (device.can_connect) {
let menuitem = new PopupMenu.PopupSwitchMenuItem(_("Connection"), device.connected);
item._connected = device.connected;
item._connectedMenuItem = menuitem;
menuitem.connect('toggled', Lang.bind(this, function() {
if (item._connected > ConnectionState.CONNECTED) {
// operation already in progress, revert
// (should not happen anyway)
menuitem.setToggleState(menuitem.state);
}
if (item._connected) {
item._connected = ConnectionState.DISCONNECTING;
menuitem.setStatus(_("disconnecting..."));
this._applet.disconnect_device(item._device.device_path, function(applet, success) {
if (success) { // apply
item._connected = ConnectionState.DISCONNECTED;
menuitem.setToggleState(false);
} else { // revert
item._connected = ConnectionState.CONNECTED;
menuitem.setToggleState(true);
}
menuitem.setStatus(null);
});
} else {
item._connected = ConnectionState.CONNECTING;
menuitem.setStatus(_("connecting..."));
this._applet.connect_device(item._device.device_path, function(applet, success) {
if (success) { // apply
item._connected = ConnectionState.CONNECTED;
menuitem.setToggleState(true);
} else { // revert
item._connected = ConnectionState.DISCONNECTED;
menuitem.setToggleState(false);
}
menuitem.setStatus(null);
});
}
}));
item.menu.addMenuItem(menuitem);
}
if (device.capabilities & GnomeBluetoothApplet.Capabilities.OBEX_PUSH) {
item.menu.addAction(_("Send Files…"), Lang.bind(this, function() {
this._applet.send_to_address(device.bdaddr, device.alias);
}));
}
switch (device.type) {
case GnomeBluetoothApplet.Type.KEYBOARD:
item.menu.addSettingsAction(_("Keyboard Settings"), 'gnome-keyboard-panel.desktop');
break;
case GnomeBluetoothApplet.Type.MOUSE:
item.menu.addSettingsAction(_("Mouse Settings"), 'gnome-mouse-panel.desktop');
break;
case GnomeBluetoothApplet.Type.HEADSET:
case GnomeBluetoothApplet.Type.HEADPHONES:
case GnomeBluetoothApplet.Type.OTHER_AUDIO:
item.menu.addSettingsAction(_("Sound Settings"), 'gnome-sound-panel.desktop');
break;
default:
break;
}
},
_updateFullMenu: function() {
if (this._applet.show_full_menu) {
this._showAll(this._fullMenuItems);
if (this._hasDevices)
this._showAll(this._deviceItems);
} else {
this._hideAll(this._fullMenuItems);
this._hideAll(this._deviceItems);
}
},
_showAll: function(items) {
for (let i = 0; i < items.length; i++)
items[i].actor.show();
},
_hideAll: function(items) {
for (let i = 0; i < items.length; i++)
items[i].actor.hide();
},
_destroyAll: function(items) {
for (let i = 0; i < items.length; i++)
items[i].destroy();
},
_ensureSource: function() {
@ -62,14 +292,9 @@ const Indicator = new Lang.Class({
}
},
_authRequest: function(applet, device_path, name, long_name) {
_authRequest: function(applet, device_path, name, long_name, uuid) {
this._ensureSource();
this._source.notify(new AuthNotification(this._source, this._applet, device_path, name, long_name));
},
_authServiceRequest: function(applet, device_path, name, long_name, uuid) {
this._ensureSource();
this._source.notify(new AuthServiceNotification(this._source, this._applet, device_path, name, long_name, uuid));
this._source.notify(new AuthNotification(this._source, this._applet, device_path, name, long_name, uuid));
},
_confirmRequest: function(applet, device_path, name, long_name, pin) {
@ -91,34 +316,6 @@ const AuthNotification = new Lang.Class({
Name: 'AuthNotification',
Extends: MessageTray.Notification,
_init: function(source, applet, device_path, name, long_name) {
this.parent(source,
_("Bluetooth"),
_("Authorization request from %s").format(name),
{ customContent: true });
this.setResident(true);
this._applet = applet;
this._devicePath = device_path;
this.addBody(_("Device %s wants to pair with this computer").format(long_name));
this.addButton('allow', _("Allow"));
this.addButton('deny', _("Deny"));
this.connect('action-invoked', Lang.bind(this, function(self, action) {
if (action == 'allow')
this._applet.agent_reply_confirm(this._devicePath, true);
else
this._applet.agent_reply_confirm(this._devicePath, false);
this.destroy();
}));
}
});
const AuthServiceNotification = new Lang.Class({
Name: 'AuthServiceNotification',
Extends: MessageTray.Notification,
_init: function(source, applet, device_path, name, long_name, uuid) {
this.parent(source,
_("Bluetooth"),
@ -137,14 +334,14 @@ const AuthServiceNotification = new Lang.Class({
this.connect('action-invoked', Lang.bind(this, function(self, action) {
switch (action) {
case 'always-grant':
this._applet.agent_reply_auth_service(this._devicePath, true, true);
this._applet.agent_reply_auth(this._devicePath, true, true);
break;
case 'grant':
this._applet.agent_reply_auth_service(this._devicePath, true, false);
this._applet.agent_reply_auth(this._devicePath, true, false);
break;
case 'reject':
default:
this._applet.agent_reply_auth_service(this._devicePath, false, false);
this._applet.agent_reply_auth(this._devicePath, false, false);
}
this.destroy();
}));
@ -166,7 +363,7 @@ const ConfirmNotification = new Lang.Class({
this._applet = applet;
this._devicePath = device_path;
this.addBody(_("Device %s wants to pair with this computer").format(long_name));
this.addBody(_("Please confirm whether the Passkey '%06d' matches the one on the device.").format(pin));
this.addBody(_("Please confirm whether the PIN '%06d' matches the one on the device.").format(pin));
/* Translators: this is the verb, not the noun */
this.addButton('matches', _("Matches"));

View File

@ -33,9 +33,9 @@ const KEY_INPUT_SOURCES = 'sources';
const INPUT_SOURCE_TYPE_XKB = 'xkb';
const INPUT_SOURCE_TYPE_IBUS = 'ibus';
// This is the longest we'll keep the keyboard frozen until an input
// This is longest we'll keep the keyboard frozen until an input
// source is active.
const MAX_INPUT_SOURCE_ACTIVATION_TIME = 4000; // ms
const MAX_INPUT_SOURCE_ACTIVATION_TIME = 2000; // ms
const BUS_NAME = 'org.gnome.SettingsDaemon.Keyboard';
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Keyboard';
@ -57,7 +57,10 @@ function releaseKeyboard() {
}
function holdKeyboard() {
global.freeze_keyboard(global.get_current_time());
if (Main.modalCount > 0)
global.display.freeze_keyboard(global.get_current_time());
else
global.display.grab_keyboard(global.get_current_time());
}
const IBusManager = new Lang.Class({
@ -72,24 +75,26 @@ const IBusManager = new Lang.Class({
this._readyCallback = readyCallback;
this._candidatePopup = new IBusCandidatePopup.CandidatePopup();
this._ibus = null;
this._panelService = null;
this._engines = {};
this._ready = false;
this._registerPropertiesId = 0;
this._currentEngineName = null;
this._ibus = IBus.Bus.new_async();
this._ibus.connect('connected', Lang.bind(this, this._onConnected));
this._ibus.connect('disconnected', Lang.bind(this, this._clear));
// 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._nameWatcherId = Gio.DBus.session.watch_name(IBus.SERVICE_IBUS,
Gio.BusNameWatcherFlags.NONE,
Lang.bind(this, this._onNameAppeared),
Lang.bind(this, this._clear));
},
_clear: function() {
if (this._panelService)
this._panelService.destroy();
if (this._ibus)
this._ibus.destroy();
this._ibus = null;
this._panelService = null;
this._candidatePopup.setPanelService(null);
this._engines = {};
@ -101,12 +106,18 @@ const IBusManager = new Lang.Class({
this._readyCallback(false);
},
_onNameAppeared: function() {
this._ibus = IBus.Bus.new_async();
this._ibus.connect('connected', Lang.bind(this, this._onConnected));
},
_onConnected: function() {
this._ibus.list_engines_async(-1, null, Lang.bind(this, this._initEngines));
this._ibus.request_name_async(IBus.SERVICE_PANEL,
IBus.BusNameFlag.REPLACE_EXISTING,
-1, null,
Lang.bind(this, this._initPanelService));
this._ibus.connect('disconnected', Lang.bind(this, this._clear));
},
_initEngines: function(ibus, result) {
@ -128,6 +139,9 @@ const IBusManager = new Lang.Class({
this._panelService = new IBus.PanelService({ connection: this._ibus.get_connection(),
object_path: IBus.PATH_PANEL });
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._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) {
@ -156,9 +170,6 @@ const IBusManager = new Lang.Class({
},
_engineChanged: function(bus, engineName) {
if (!this._ready)
return;
this._currentEngineName = engineName;
if (this._registerPropertiesId != 0)
@ -249,6 +260,7 @@ const InputSource = new Lang.Class({
},
activate: function() {
holdKeyboard();
this.emit('activate');
},
});
@ -281,11 +293,11 @@ const InputSourcePopup = new Lang.Class({
this._select(this._selectedIndex);
},
_keyPressHandler: function(keysym, backwards, action) {
_keyPressHandler: function(keysym, _ignored, action) {
if (action == this._action)
this._select(backwards ? this._previous() : this._next());
this._select(this._next());
else if (action == this._actionBackward)
this._select(backwards ? this._next() : this._previous());
this._select(this._previous());
else if (keysym == Clutter.Left)
this._select(this._previous());
else if (keysym == Clutter.Right)
@ -355,14 +367,13 @@ const InputSourceIndicator = new Lang.Class({
this._keybindingAction =
Main.wm.addKeybinding('switch-input-source',
new Gio.Settings({ schema: "org.gnome.desktop.wm.keybindings" }),
Meta.KeyBindingFlags.REVERSES,
Meta.KeyBindingFlags.NONE,
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,
Meta.KeyBindingFlags.NONE,
Shell.KeyBindingMode.ALL,
Lang.bind(this, this._switchInputSource));
this._settings = new Gio.Settings({ schema: DESKTOP_INPUT_SOURCES_SCHEMA });
@ -388,8 +399,6 @@ const InputSourceIndicator = new Lang.Class({
if (error)
log(error.message);
});
this._keyboardManager.g_default_timeout = MAX_INPUT_SOURCE_ACTIVATION_TIME;
global.display.connect('modifiers-accelerator-activated', Lang.bind(this, this._modifiersSwitcher));
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
@ -398,6 +407,8 @@ 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;
@ -427,7 +438,7 @@ const InputSourceIndicator = new Lang.Class({
let sourceIndexes = Object.keys(this._inputSources);
if (sourceIndexes.length == 0) {
releaseKeyboard();
return true;
return;
}
let is = this._currentSource;
@ -442,7 +453,6 @@ const InputSourceIndicator = new Lang.Class({
nextIndex += 1;
is.activate();
return true;
},
_switchInputSource: function(display, screen, window, binding) {
@ -450,19 +460,18 @@ const InputSourceIndicator = new Lang.Class({
return;
// HACK: Fall back on simple input source switching since we
// can't show a popup switcher while a GrabHelper grab is in
// effect without considerable work to consolidate the usage
// of pushModal/popModal and grabHelper. See
// can't show a popup switcher while the message tray is up
// without considerable work to consolidate the usage of
// pushModal/popModal and grabHelper. See
// https://bugzilla.gnome.org/show_bug.cgi?id=695143 .
if (Main.keybindingMode == Shell.KeyBindingMode.MESSAGE_TRAY ||
Main.keybindingMode == Shell.KeyBindingMode.TOPBAR_POPUP) {
if (Main.keybindingMode == Shell.KeyBindingMode.MESSAGE_TRAY) {
this._modifiersSwitcher();
return;
}
let popup = new InputSourcePopup(this._mruSources, this._keybindingAction, this._keybindingActionBackward);
let modifiers = binding.get_modifiers();
let backwards = modifiers & Meta.VirtualModifier.SHIFT_MASK;
let backwards = false; // Not using this
if (!popup.show(backwards, binding.get_name(), binding.get_mask()))
popup.destroy();
},
@ -476,7 +485,7 @@ const InputSourceIndicator = new Lang.Class({
[oldSource, this._currentSource] = [this._currentSource, newSource];
if (oldSource) {
oldSource.menuItem.setOrnament(PopupMenu.Ornament.NONE);
oldSource.menuItem.setShowDot(false);
oldSource.indicatorLabel.hide();
}
@ -494,7 +503,7 @@ const InputSourceIndicator = new Lang.Class({
this.actor.show();
newSource.menuItem.setOrnament(PopupMenu.Ornament.DOT);
newSource.menuItem.setShowDot(true);
newSource.indicatorLabel.show();
this._buildPropSection(newSource.properties);
@ -518,7 +527,6 @@ const InputSourceIndicator = new Lang.Class({
this._inputSources = {};
this._ibusSources = {};
this._currentSource = null;
let inputSourcesByShortName = {};
@ -547,8 +555,10 @@ const InputSourceIndicator = new Lang.Class({
let is = new InputSource(type, id, displayName, shortName, i);
is.connect('activate', Lang.bind(this, function() {
holdKeyboard();
this._keyboardManager.SetInputSourceRemote(is.index, releaseKeyboard);
let inVariant = new GLib.Variant('(u)', [is.index]);
this._keyboardManager.call('SetInputSource', inVariant, 0,
MAX_INPUT_SOURCE_ACTIVATION_TIME,
null, releaseKeyboard);
}));
if (!(is.shortName in inputSourcesByShortName))
@ -718,8 +728,7 @@ const InputSourceIndicator = new Lang.Class({
item.prop = prop;
radioGroup.push(item);
item.radioGroup = radioGroup;
item.setOrnament(prop.get_state() == IBus.PropState.CHECKED ?
PopupMenu.Ornament.DOT : PopupMenu.Ornament.NONE);
item.setShowDot(prop.get_state() == IBus.PropState.CHECKED);
item.connect('activate', Lang.bind(this, function() {
if (item.prop.get_state() == IBus.PropState.CHECKED)
return;
@ -727,12 +736,12 @@ const InputSourceIndicator = new Lang.Class({
let group = item.radioGroup;
for (let i = 0; i < group.length; ++i) {
if (group[i] == item) {
item.setOrnament(PopupMenu.Ornament.DOT);
item.setShowDot(true);
item.prop.set_state(IBus.PropState.CHECKED);
this._ibusManager.activateProperty(item.prop.get_key(),
IBus.PropState.CHECKED);
} else {
group[i].setOrnament(PopupMenu.Ornament.NONE);
group[i].setShowDot(false);
group[i].prop.set_state(IBus.PropState.UNCHECKED);
this._ibusManager.activateProperty(group[i].prop.get_key(),
IBus.PropState.UNCHECKED);

File diff suppressed because it is too large Load Diff

View File

@ -2,15 +2,39 @@
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const UPower = imports.gi.UPowerGlib;
const St = imports.gi.St;
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const BUS_NAME = 'org.gnome.SettingsDaemon.Power';
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Power';
const UPDeviceType = {
UNKNOWN: 0,
AC_POWER: 1,
BATTERY: 2,
UPS: 3,
MONITOR: 4,
MOUSE: 5,
KEYBOARD: 6,
PDA: 7,
PHONE: 8,
MEDIA_PLAYER: 9,
TABLET: 10,
COMPUTER: 11
};
const UPDeviceState = {
UNKNOWN: 0,
CHARGING: 1,
DISCHARGING: 2,
EMPTY: 3,
FULLY_CHARGED: 4,
PENDING_CHARGE: 5,
PENDING_DISCHARGE: 6
};
const PowerManagerInterface = <interface name="org.gnome.SettingsDaemon.Power">
<method name="GetDevices">
<arg type="a(susdut)" direction="out" />
@ -31,73 +55,96 @@ const Indicator = new Lang.Class({
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._sync));
this._sync();
}));
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._item = new PopupMenu.PopupSubMenuMenuItem(_("Battery"), true);
this._item.menu.addSettingsAction(_("Power Settings"), 'gnome-power-panel.desktop');
this.menu.addMenuItem(this._item);
this._deviceItems = [ ];
this._hasPrimary = false;
this._primaryDeviceId = null;
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
this._sessionUpdated();
this._batteryItem = new PopupMenu.PopupMenuItem('', { reactive: false });
this._primaryPercentage = new St.Label({ style_class: 'popup-battery-percentage' });
this._batteryItem.addActor(this._primaryPercentage, { align: St.Align.END });
this.menu.addMenuItem(this._batteryItem);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._otherDevicePosition = 2;
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addSettingsAction(_("Power Settings"), 'gnome-power-panel.desktop');
},
_sessionUpdated: function() {
let sensitive = !Main.sessionMode.isLocked && !Main.sessionMode.isGreeter;
this.menu.setSensitive(sensitive);
},
_statusForDevice: function(device) {
let [device_id, device_type, icon, percentage, state, seconds] = device;
if (state == UPower.DeviceState.FULLY_CHARGED)
return _("Fully Charged");
let time = Math.round(seconds / 60);
if (time == 0) {
// 0 is reported when UPower does not have enough data
// to estimate battery life
return _("Estimating…");
}
let minutes = time % 60;
let hours = Math.floor(time / 60);
if (state == UPower.DeviceState.DISCHARGING) {
// Translators: this is <hours>:<minutes> Remaining (<percentage>)
return _("%d\u2236%d Remaining (%d%%)".format(hours, minutes, percentage));
}
if (state == UPower.DeviceState.CHARGING) {
// Translators: this is <hours>:<minutes> Until Full (<percentage>)
return _("%d\u2236%d Until Full (%d%%)".format(hours, minutes, percentage));
}
// state is one of PENDING_CHARGING, PENDING_DISCHARGING
return _("Estimating…");
},
_syncStatusLabel: function() {
_readPrimaryDevice: function() {
this._proxy.GetPrimaryDeviceRemote(Lang.bind(this, function(result, error) {
if (error) {
this._item.actor.hide();
this._hasPrimary = false;
this._primaryDeviceId = null;
this._batteryItem.actor.hide();
return;
}
let [[device_id, device_type, icon, percentage, state, seconds]] = result;
if (device_type == UPDeviceType.BATTERY) {
this._hasPrimary = true;
let time = Math.round(seconds / 60);
if (time == 0) {
// 0 is reported when UPower does not have enough data
// to estimate battery life
this._batteryItem.label.text = _("Estimating…");
} else {
let minutes = time % 60;
let hours = Math.floor(time / 60);
let timestring;
if (time >= 60) {
if (minutes == 0) {
timestring = ngettext("%d hour remaining", "%d hours remaining", hours).format(hours);
} else {
/* TRANSLATORS: this is a time string, as in "%d hours %d minutes remaining" */
let template = _("%d %s %d %s remaining");
timestring = template.format (hours, ngettext("hour", "hours", hours), minutes, ngettext("minute", "minutes", minutes));
}
} else
timestring = ngettext("%d minute remaining", "%d minutes remaining", minutes).format(minutes);
this._batteryItem.label.text = timestring;
}
this._primaryPercentage.text = C_("percent of battery remaining", "%d%%").format(Math.round(percentage));
this._batteryItem.actor.show();
} else {
this._hasPrimary = false;
this._batteryItem.actor.hide();
}
this._primaryDeviceId = device_id;
}));
},
_readOtherDevices: function() {
this._proxy.GetDevicesRemote(Lang.bind(this, function(result, error) {
this._deviceItems.forEach(function(i) { i.destroy(); });
this._deviceItems = [];
if (error) {
return;
}
let [device] = result;
let [device_id, device_type] = device;
if (device_type == UPower.DeviceKind.BATTERY) {
this._item.status.text = this._statusForDevice(device);
this._item.actor.show();
} else {
this._item.actor.hide();
let position = 0;
let [devices] = result;
for (let i = 0; i < devices.length; i++) {
let [device_id, device_type] = devices[i];
if (device_type == UPDeviceType.AC_POWER || device_id == this._primaryDeviceId)
continue;
let item = new DeviceItem (devices[i]);
this._deviceItems.push(item);
this.menu.addMenuItem(item, this._otherDevicePosition + position);
position++;
}
}));
},
@ -109,15 +156,71 @@ const Indicator = new Lang.Class({
if (icon) {
let gicon = Gio.icon_new_for_string(icon);
this.setGIcon(gicon);
this._item.icon.gicon = gicon;
hasIcon = true;
}
this.mainIcon.visible = hasIcon;
this.actor.visible = hasIcon;
},
_sync: function() {
_devicesChanged: function() {
this._syncIcon();
this._syncStatusLabel();
this._readPrimaryDevice();
this._readOtherDevices();
}
});
const DeviceItem = new Lang.Class({
Name: 'DeviceItem',
Extends: PopupMenu.PopupBaseMenuItem,
_init: function(device) {
this.parent({ reactive: false });
let [device_id, device_type, icon, percentage, state, time] = device;
this._box = new St.BoxLayout({ style_class: 'popup-device-menu-item' });
this._label = new St.Label({ text: this._deviceTypeToString(device_type) });
this._icon = new St.Icon({ gicon: Gio.icon_new_for_string(icon),
style_class: 'popup-menu-icon' });
this._box.add_actor(this._icon);
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' });
this.addActor(percentLabel, { align: St.Align.END });
//FIXME: ideally we would like to expose this._label and percentLabel
this.actor.label_actor = percentLabel;
},
_deviceTypeToString: function(type) {
switch (type) {
case UPDeviceType.AC_POWER:
return _("AC Adapter");
case UPDeviceType.BATTERY:
return _("Laptop Battery");
case UPDeviceType.UPS:
return _("UPS");
case UPDeviceType.MONITOR:
return _("Monitor");
case UPDeviceType.MOUSE:
return _("Mouse");
case UPDeviceType.KEYBOARD:
return _("Keyboard");
case UPDeviceType.PDA:
return _("PDA");
case UPDeviceType.PHONE:
return _("Cell Phone");
case UPDeviceType.MEDIA_PLAYER:
return _("Media Player");
case UPDeviceType.TABLET:
return _("Tablet");
case UPDeviceType.COMPUTER:
return _("Computer");
default:
return C_("device", "Unknown");
}
}
});

View File

@ -1,358 +0,0 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const AccountsService = imports.gi.AccountsService;
const Gdm = imports.gi.Gdm;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Clutter = imports.gi.Clutter;
const BoxPointer = imports.ui.boxpointer;
const GnomeSession = imports.misc.gnomeSession;
const LoginManager = imports.misc.loginManager;
const Main = imports.ui.main;
const ModalDialog = imports.ui.modalDialog;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const Util = imports.misc.util;
const UserWidget = imports.ui.userWidget;
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 ALWAYS_SHOW_LOG_OUT_KEY = 'always-show-log-out';
const MAX_USERS_IN_SESSION_DIALOG = 5;
const SystemdLoginSessionIface = <interface name='org.freedesktop.login1.Session'>
<property name="Id" type="s" access="read"/>
<property name="Remote" type="b" access="read"/>
<property name="Class" type="s" access="read"/>
<property name="Type" type="s" access="read"/>
<property name="State" type="s" access="read"/>
</interface>;
const SystemdLoginSession = Gio.DBusProxy.makeProxyWrapper(SystemdLoginSessionIface);
const Indicator = new Lang.Class({
Name: 'SystemIndicator',
Extends: PanelMenu.SystemStatusButton,
_init: function() {
this.parent('system-shutdown-symbolic', _("System"));
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._session = new GnomeSession.SessionManager();
this._haveShutdown = true;
this._loginManager = LoginManager.getLoginManager();
this._userManager = AccountsService.UserManager.get_default();
this._user = this._userManager.get_user(GLib.get_user_name());
this._createSubMenu();
this._userManager.connect('notify::is-loaded',
Lang.bind(this, this._updateMultiUser));
this._userManager.connect('notify::has-multiple-users',
Lang.bind(this, this._updateMultiUser));
this._userManager.connect('user-added',
Lang.bind(this, this._updateMultiUser));
this._userManager.connect('user-removed',
Lang.bind(this, this._updateMultiUser));
this._lockdownSettings.connect('changed::' + DISABLE_USER_SWITCH_KEY,
Lang.bind(this, this._updateMultiUser));
this._lockdownSettings.connect('changed::' + DISABLE_LOG_OUT_KEY,
Lang.bind(this, this._updateMultiUser));
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._updateMultiUser));
this._updateSwitchUser();
this._updateMultiUser();
// Whether shutdown is available or not depends on both lockdown
// settings (disable-log-out) and Polkit policy - the latter doesn't
// notify, so we update the menu item each time the menu opens or
// the lockdown setting changes, which should be close enough.
this.menu.connect('open-state-changed', Lang.bind(this,
function(menu, open) {
if (!open)
return;
this._updateHaveShutdown();
}));
this._lockdownSettings.connect('changed::' + DISABLE_LOG_OUT_KEY,
Lang.bind(this, this._updateHaveShutdown));
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
this._sessionUpdated();
},
_sessionUpdated: function() {
this._updateLockScreen();
this._updatePowerOff();
this._settingsAction.visible = Main.sessionMode.allowSettings;
},
_updateMultiUser: function() {
let shouldShowInMode = !Main.sessionMode.isLocked && !Main.sessionMode.isGreeter;
let hasSwitchUser = this._updateSwitchUser();
let hasLogout = this._updateLogout();
this._switchUserSubMenu.actor.visible = shouldShowInMode && (hasSwitchUser || hasLogout);
},
_updateSwitchUser: function() {
let allowSwitch = !this._lockdownSettings.get_boolean(DISABLE_USER_SWITCH_KEY);
let multiUser = this._userManager.can_switch() && this._userManager.has_multiple_users;
let visible = allowSwitch && multiUser;
this._loginScreenItem.actor.visible = visible;
return visible;
},
_updateLogout: function() {
let allowLogout = !this._lockdownSettings.get_boolean(DISABLE_LOG_OUT_KEY);
let alwaysShow = global.settings.get_boolean(ALWAYS_SHOW_LOG_OUT_KEY);
let systemAccount = this._user.system_account;
let localAccount = this._user.local_account;
let multiUser = this._userManager.has_multiple_users;
let multiSession = Gdm.get_session_ids().length > 1;
let visible = allowLogout && (alwaysShow || multiUser || multiSession || systemAccount || !localAccount);
this._logoutItem.actor.visible = visible;
return visible;
},
_updateSwitchUserSubMenu: function() {
this._switchUserSubMenu.label.text = this._user.get_real_name();
let iconFile = this._user.get_icon_file();
if (iconFile && !GLib.file_test(iconFile, GLib.FileTest.EXISTS))
iconFile = null;
if (iconFile) {
let file = Gio.File.new_for_path(iconFile);
let gicon = new Gio.FileIcon({ file: file });
this._switchUserSubMenu.icon.gicon = gicon;
} else {
this._switchUserSubMenu.icon_name = 'avatar-default-symbolic';
}
},
_updateLockScreen: function() {
let showLock = !Main.sessionMode.isLocked && !Main.sessionMode.isGreeter;
let allowLockScreen = !this._lockdownSettings.get_boolean(DISABLE_LOCK_SCREEN_KEY);
this._lockScreenAction.visible = showLock && allowLockScreen && LoginManager.canLock();
},
_updateHaveShutdown: function() {
this._session.CanShutdownRemote(Lang.bind(this,
function(result, error) {
if (!error) {
this._haveShutdown = result[0];
this._updatePowerOff();
}
}));
},
_updatePowerOff: function() {
this._powerOffAction.visible = this._haveShutdown && !Main.sessionMode.isLocked;
},
_createActionButton: function(iconName, accessibleName) {
let icon = new St.Button({ reactive: true,
can_focus: true,
track_hover: true,
accessible_name: accessibleName,
style_class: 'system-menu-action' });
icon.child = new St.Icon({ icon_name: iconName });
return icon;
},
_createSubMenu: function() {
let item;
this._switchUserSubMenu = new PopupMenu.PopupSubMenuMenuItem('', true);
this._switchUserSubMenu.icon.style_class = 'system-switch-user-submenu-icon';
item = new PopupMenu.PopupMenuItem(_("Switch User"));
item.connect('activate', Lang.bind(this, this._onLoginScreenActivate));
this._switchUserSubMenu.menu.addMenuItem(item);
this._loginScreenItem = item;
item = new PopupMenu.PopupMenuItem(_("Log Out"));
item.connect('activate', Lang.bind(this, this._onQuitSessionActivate));
this._switchUserSubMenu.menu.addMenuItem(item);
this._logoutItem = item;
this._user.connect('notify::is-loaded', Lang.bind(this, this._updateSwitchUserSubMenu));
this._user.connect('changed', Lang.bind(this, this._updateSwitchUserSubMenu));
this.menu.addMenuItem(this._switchUserSubMenu);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
let hbox = new St.BoxLayout({ style_class: 'system-menu-actions-box' });
this._settingsAction = this._createActionButton('preferences-system-symbolic', _("Settings"));
this._settingsAction.connect('clicked', Lang.bind(this, this._onSettingsClicked));
hbox.add(this._settingsAction, { expand: true, x_fill: false });
this._lockScreenAction = this._createActionButton('changes-prevent-symbolic', _("Lock"));
this._lockScreenAction.connect('clicked', Lang.bind(this, this._onLockScreenClicked));
hbox.add(this._lockScreenAction, { expand: true, x_fill: false });
this._powerOffAction = this._createActionButton('system-shutdown-symbolic', _("Power Off"));
this._powerOffAction.connect('clicked', Lang.bind(this, this._onPowerOffClicked));
hbox.add(this._powerOffAction, { expand: true, x_fill: false });
item = new PopupMenu.PopupBaseMenuItem({ reactive: false,
can_focus: false });
item.addActor(hbox, { expand: true });
this.menu.addMenuItem(item);
},
_onSettingsClicked: function() {
this.menu.itemActivated();
let app = Shell.AppSystem.get_default().lookup_app('gnome-control-center.desktop');
Main.overview.hide();
app.activate();
},
_onLockScreenClicked: function() {
this.menu.itemActivated(BoxPointer.PopupAnimation.NONE);
Main.overview.hide();
Main.screenShield.lock(true);
},
_onLoginScreenActivate: function() {
this.menu.itemActivated(BoxPointer.PopupAnimation.NONE);
Main.overview.hide();
if (Main.screenShield)
Main.screenShield.lock(false);
Gdm.goto_login_session_sync(null);
},
_onQuitSessionActivate: function() {
Main.overview.hide();
this._session.LogoutRemote(0);
},
_openSessionWarnDialog: function(sessions) {
let dialog = new ModalDialog.ModalDialog();
let subjectLabel = new St.Label({ style_class: 'end-session-dialog-subject',
text: _("Other users are logged in.") });
dialog.contentLayout.add(subjectLabel, { y_fill: true,
y_align: St.Align.START });
let descriptionLabel = new St.Label({ style_class: 'end-session-dialog-description'});
descriptionLabel.set_text(_("Shutting down might cause them to lose unsaved work."));
descriptionLabel.clutter_text.line_wrap = true;
dialog.contentLayout.add(descriptionLabel, { x_fill: true,
y_fill: true,
y_align: St.Align.START });
let scrollView = new St.ScrollView({ style_class: 'end-session-dialog-app-list' });
scrollView.add_style_class_name('vfade');
scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
dialog.contentLayout.add(scrollView, { x_fill: true, y_fill: true });
let userList = new St.BoxLayout({ vertical: true });
scrollView.add_actor(userList);
for (let i = 0; i < sessions.length; i++) {
let session = sessions[i];
let userEntry = new St.BoxLayout({ style_class: 'login-dialog-user-list-item',
vertical: false });
let avatar = new UserWidget.Avatar(session.user);
avatar.update();
userEntry.add(avatar.actor);
let userLabelText = "";;
let userName = session.user.get_real_name() ?
session.user.get_real_name() : session.username;
if (session.info.remote)
/* Translators: Remote here refers to a remote session, like a ssh login */
userLabelText = _("%s (remote)").format(userName);
else if (session.info.type == "tty")
/* Translators: Console here refers to a tty like a VT console */
userLabelText = _("%s (console)").format(userName);
else
userLabelText = userName;
let textLayout = new St.BoxLayout({ style_class: 'login-dialog-user-list-item-text-box',
vertical: true });
textLayout.add(new St.Label({ text: userLabelText }),
{ y_fill: false,
y_align: St.Align.MIDDLE,
expand: true });
userEntry.add(textLayout, { expand: true });
userList.add(userEntry, { x_fill: true });
}
let cancelButton = { label: _("Cancel"),
action: function() { dialog.close(); },
key: Clutter.Escape };
let powerOffButton = { label: _("Power Off"), action: Lang.bind(this, function() {
dialog.close();
this._session.ShutdownRemote();
}), default: true };
dialog.setButtons([cancelButton, powerOffButton]);
dialog.open();
},
_onPowerOffClicked: function() {
this.menu.itemActivated();
Main.overview.hide();
this._loginManager.listSessions(Lang.bind(this, function(result) {
let sessions = [];
let n = 0;
for (let i = 0; i < result.length; i++) {
let[id, uid, userName, seat, sessionPath] = result[i];
let proxy = new SystemdLoginSession(Gio.DBus.system,
'org.freedesktop.login1',
sessionPath);
if (proxy.Class != 'user')
continue;
if (proxy.State == 'closing')
continue;
if (proxy.Id == GLib.getenv('XDG_SESSION_ID'))
continue;
sessions.push({ user: this._userManager.get_user(userName),
username: userName,
info: { type: proxy.Type,
remote: proxy.Remote }
});
// limit the number of entries
n++;
if (n == MAX_USERS_IN_SESSION_DIALOG)
break;
}
if (n != 0)
this._openSessionWarnDialog(sessions);
else
this._session.ShutdownRemote();
}));
}
});

View File

@ -9,7 +9,8 @@ const Signals = imports.signals;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const Slider = imports.ui.slider;
const VOLUME_ADJUSTMENT_STEP = 0.05; /* Volume adjustment step in % */
const VOLUME_NOTIFY_ID = 1;
@ -29,25 +30,21 @@ function getMixerControl() {
const StreamSlider = new Lang.Class({
Name: 'StreamSlider',
_init: function(control) {
_init: function(control, title) {
this._control = control;
this.item = new PopupMenu.PopupBaseMenuItem({ activate: false });
this.item = new PopupMenu.PopupMenuSection();
this._slider = new Slider.Slider(0);
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._icon = new St.Icon({ style_class: 'popup-menu-icon' });
this.item.addActor(this._icon, { align: St.Align.MIDDLE });
this.item.addActor(this._slider.actor, { expand: true });
this.item.actor.connect('button-press-event', Lang.bind(this, function(actor, event) {
this._slider.startDragging(event);
}));
this.item.addMenuItem(this._title);
this.item.addMenuItem(this._slider);
this._stream = null;
this._shouldShow = true;
},
get stream() {
@ -89,7 +86,8 @@ const StreamSlider = new Lang.Class({
_updateVisibility: function() {
let visible = this._shouldBeVisible();
this.item.actor.visible = visible;
this._title.actor.visible = visible;
this._slider.actor.visible = visible;
},
scroll: function(event) {
@ -183,17 +181,11 @@ const OutputStreamSlider = new Lang.Class({
this._portChangedId = 0;
},
_updateSliderIcon: function() {
this._icon.icon_name = (this._hasHeadphones ?
'audio-headphones-symbolic' :
'audio-speakers-symbolic');
},
_portChanged: function() {
let hasHeadphones = this._findHeadphones(this._stream);
if (hasHeadphones != this._hasHeadphones) {
this._hasHeadphones = hasHeadphones;
this._updateSliderIcon();
this.emit('headphones-changed', this._hasHeadphones);
}
}
});
@ -202,11 +194,10 @@ const InputStreamSlider = new Lang.Class({
Name: 'InputStreamSlider',
Extends: StreamSlider,
_init: function(control) {
this.parent(control);
_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));
this._icon.icon_name = 'audio-input-microphone-symbolic';
},
_connectStream: function(stream) {
@ -254,13 +245,17 @@ const VolumeMenu = new Lang.Class({
this._control.connect('default-sink-changed', Lang.bind(this, this._readOutput));
this._control.connect('default-source-changed', Lang.bind(this, this._readInput));
this._output = new OutputStreamSlider(this._control);
/* 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);
this._input = new InputStreamSlider(this._control, _("Microphone"));
this.addMenuItem(this._input.item);
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
@ -308,9 +303,18 @@ const Indicator = new Lang.Class({
this.actor.visible = (icon != null);
this.setIcon(icon);
}));
this._volumeMenu.connect('headphones-changed', Lang.bind(this, function(menu, value) {
this._headphoneIcon.visible = value;
}));
this._headphoneIcon = this.addIcon(new Gio.ThemedIcon({ name: 'headphones-symbolic' }));
this._headphoneIcon.visible = false;
this.menu.addMenuItem(this._volumeMenu);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addSettingsAction(_("Sound Settings"), 'gnome-sound-panel.desktop');
this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
},

View File

@ -1,7 +1,6 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const AccountsService = imports.gi.AccountsService;
const Atk = imports.gi.Atk;
const Clutter = imports.gi.Clutter;
const Gdm = imports.gi.Gdm;
const Gio = imports.gi.Gio;
@ -13,13 +12,14 @@ const Signals = imports.signals;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Layout = imports.ui.layout;
const Main = imports.ui.main;
const ModalDialog = imports.ui.modalDialog;
const Panel = imports.ui.panel;
const ShellEntry = imports.ui.shellEntry;
const Tweener = imports.ui.tweener;
const UserMenu = imports.ui.userMenu;
const UserWidget = imports.ui.userWidget;
const AuthPrompt = imports.gdm.authPrompt;
const Batch = imports.gdm.batch;
const GdmUtil = imports.gdm.util;
const LoginDialog = imports.gdm.loginDialog;
@ -29,35 +29,95 @@ const IDLE_TIMEOUT = 2 * 60;
const UnlockDialog = new Lang.Class({
Name: 'UnlockDialog',
Extends: ModalDialog.ModalDialog,
_init: function(parentActor) {
this.actor = new St.Widget({ accessible_role: Atk.Role.WINDOW,
style_class: 'login-dialog',
visible: false });
this.actor.add_constraint(new Layout.MonitorConstraint({ primary: true }));
parentActor.add_child(this.actor);
this.parent({ shellReactive: true,
styleClass: 'login-dialog',
keybindingMode: Shell.KeyBindingMode.UNLOCK_SCREEN,
parentActor: parentActor
});
this._userManager = AccountsService.UserManager.get_default();
this._userName = GLib.get_user_name();
this._user = this._userManager.get_user(this._userName);
this._promptBox = new St.BoxLayout({ vertical: true });
this.actor.add_child(this._promptBox);
this._promptBox.add_constraint(new Clutter.AlignConstraint({ source: this.actor,
align_axis: Clutter.AlignAxis.BOTH,
factor: 0.5 }));
this._failCounter = 0;
this._firstQuestion = true;
this._authPrompt = new AuthPrompt.AuthPrompt(new Gdm.Client(), AuthPrompt.AuthPromptMode.UNLOCK_ONLY);
this._authPrompt.connect('failed', Lang.bind(this, this._fail));
this._authPrompt.connect('cancelled', Lang.bind(this, this._fail));
this._authPrompt.connect('reset', Lang.bind(this, this._onReset));
this._authPrompt.setPasswordChar('\u25cf');
this._authPrompt.nextButton.label = _("Unlock");
this._greeterClient = new Gdm.Client();
this._userVerifier = new GdmUtil.ShellUserVerifier(this._greeterClient, { reauthenticationOnly: true });
this._userVerified = false;
this._promptBox.add_child(this._authPrompt.actor);
this._userVerifier.connect('ask-question', Lang.bind(this, this._onAskQuestion));
this._userVerifier.connect('show-message', Lang.bind(this, this._showMessage));
this._userVerifier.connect('verification-complete', Lang.bind(this, this._onVerificationComplete));
this._userVerifier.connect('verification-failed', Lang.bind(this, this._onVerificationFailed));
this._userVerifier.connect('reset', Lang.bind(this, this._onReset));
this._userVerifier.connect('show-login-hint', Lang.bind(this, this._showLoginHint));
this._userVerifier.connect('hide-login-hint', Lang.bind(this, this._hideLoginHint));
this._userWidget = new UserWidget.UserWidget(this._user);
this.contentLayout.add_actor(this._userWidget.actor);
this._promptLayout = new St.BoxLayout({ style_class: 'login-dialog-prompt-layout',
vertical: true });
this._promptLabel = new St.Label({ style_class: 'login-dialog-prompt-label' });
this._promptLayout.add(this._promptLabel,
{ x_align: St.Align.START });
this._promptEntry = new St.Entry({ style_class: 'login-dialog-prompt-entry',
can_focus: true });
this._promptEntry.clutter_text.connect('activate', Lang.bind(this, this._doUnlock));
this._promptEntry.clutter_text.set_password_char('\u25cf');
ShellEntry.addContextMenu(this._promptEntry, { isPassword: true });
this.setInitialKeyFocus(this._promptEntry);
this._promptEntry.clutter_text.connect('text-changed', Lang.bind(this, function() {
this._updateOkButtonSensitivity(this._promptEntry.text.length > 0);
}));
this._promptLayout.add(this._promptEntry,
{ expand: true,
x_fill: true });
this.contentLayout.add_actor(this._promptLayout);
this._promptMessage = new St.Label({ visible: false });
this.contentLayout.add(this._promptMessage, { x_fill: true });
this._promptLoginHint = new St.Label({ style_class: 'login-dialog-prompt-login-hint' });
this._promptLoginHint.hide();
this.contentLayout.add_actor(this._promptLoginHint);
this._workSpinner = new Panel.AnimatedIcon('process-working.svg', LoginDialog.WORK_SPINNER_ICON_SIZE);
this._workSpinner.actor.opacity = 0;
this.allowCancel = false;
this.buttonLayout.visible = true;
this.addButton({ label: _("Cancel"),
action: Lang.bind(this, this._escape),
key: Clutter.KEY_Escape },
{ expand: true,
x_fill: false,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.MIDDLE });
this.buttonLayout.add(this._workSpinner.actor,
{ expand: false,
x_fill: false,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.MIDDLE });
this._okButton = this.addButton({ label: _("Unlock"),
action: Lang.bind(this, this._doUnlock),
default: true },
{ expand: false,
x_fill: false,
y_fill: false,
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')) {
@ -70,100 +130,196 @@ const UnlockDialog = new Lang.Class({
x_align: St.Align.START,
x_fill: true });
this._otherUserButton.connect('clicked', Lang.bind(this, this._otherUserClicked));
this._promptBox.add_child(this._otherUserButton);
this.dialogLayout.add(this._otherUserButton,
{ x_align: St.Align.START,
x_fill: false });
} else {
this._otherUserButton = null;
}
this._authPrompt.reset();
this._updateSensitivity(true);
Main.ctrlAltTabManager.addGroup(this.actor, _("Unlock Window"), 'dialog-password-symbolic');
let batch = new Batch.Hold();
this._userVerifier.begin(this._userName, batch);
Main.ctrlAltTabManager.addGroup(this.dialogLayout, _("Unlock Window"), 'dialog-password-symbolic');
this._idleMonitor = new GnomeDesktop.IdleMonitor();
this._idleWatchId = this._idleMonitor.add_idle_watch(IDLE_TIMEOUT * 1000, Lang.bind(this, this._escape));
},
_updateSensitivity: function(sensitive) {
this._authPrompt.updateSensitivity(sensitive);
this._promptEntry.reactive = sensitive;
this._promptEntry.clutter_text.editable = sensitive;
this._updateOkButtonSensitivity(sensitive && this._promptEntry.text.length > 0);
if (this._otherUserButton) {
this._otherUserButton.reactive = sensitive;
this._otherUserButton.can_focus = sensitive;
}
},
_fail: function() {
this.emit('failed');
_updateOkButtonSensitivity: function(sensitive) {
this._okButton.reactive = sensitive;
this._okButton.can_focus = sensitive;
},
_onReset: function(authPrompt, beginRequest) {
let userName;
if (beginRequest == AuthPrompt.BeginRequestType.PROVIDE_USERNAME) {
this._authPrompt.setUser(this._user);
userName = this._userName;
_setWorking: function(working) {
if (working) {
this._workSpinner.play();
Tweener.addTween(this._workSpinner.actor,
{ opacity: 255,
delay: LoginDialog.WORK_SPINNER_ANIMATION_DELAY,
time: LoginDialog.WORK_SPINNER_ANIMATION_TIME,
transition: 'linear'
});
} else {
userName = null;
Tweener.addTween(this._workSpinner.actor,
{ opacity: 0,
time: LoginDialog.WORK_SPINNER_ANIMATION_TIME,
transition: 'linear',
onCompleteScope: this,
onComplete: function() {
this._workSpinner.stop();
}
});
}
},
_showMessage: function(userVerifier, message, styleClass) {
if (message) {
this._promptMessage.text = message;
this._promptMessage.styleClass = styleClass;
GdmUtil.fadeInActor(this._promptMessage);
} else {
GdmUtil.fadeOutActor(this._promptMessage);
}
},
_onAskQuestion: function(verifier, serviceName, question, passwordChar) {
if (this._firstQuestion && this._firstQuestionAnswer) {
this._userVerifier.answerQuery(serviceName, this._firstQuestionAnswer);
this._firstQuestionAnswer = null;
this._firstQuestion = false;
return;
}
this._authPrompt.begin({ userName: userName });
this._promptLabel.text = question;
if (!this._firstQuestion)
this._promptEntry.text = '';
else
this._firstQuestion = false;
this._promptEntry.clutter_text.set_password_char(passwordChar);
this._promptEntry.menu.isPassword = passwordChar != '';
this._currentQuery = serviceName;
this._updateSensitivity(true);
this._setWorking(false);
},
_showLoginHint: function(verifier, message) {
this._promptLoginHint.set_text(message)
GdmUtil.fadeInActor(this._promptLoginHint);
},
_hideLoginHint: function() {
GdmUtil.fadeOutActor(this._promptLoginHint);
},
_doUnlock: function() {
if (this._firstQuestion) {
// we haven't received a query yet, so stash the answer
// and make ourself non-reactive
// the actual reply to GDM will be sent as soon as asked
this._firstQuestionAnswer = this._promptEntry.text;
this._updateSensitivity(false);
this._setWorking(true);
return;
}
if (!this._currentQuery)
return;
let query = this._currentQuery;
this._currentQuery = null;
this._updateSensitivity(false);
this._setWorking(true);
this._userVerifier.answerQuery(query, this._promptEntry.text);
},
_finishUnlock: function() {
this._userVerifier.clear();
this.emit('unlocked');
},
_onVerificationComplete: function() {
this._userVerified = true;
if (!this._userVerifier.hasPendingMessages) {
this._finishUnlock();
} else {
let signalId = this._userVerifier.connect('no-more-messages',
Lang.bind(this, function() {
this._userVerifier.disconnect(signalId);
this._finishUnlock();
}));
}
},
_onReset: function() {
if (!this._userVerified) {
this._userVerifier.clear();
this.emit('failed');
}
},
_onVerificationFailed: function() {
this._currentQuery = null;
this._firstQuestion = true;
this._userVerified = false;
this._promptEntry.text = '';
this._promptEntry.clutter_text.set_password_char('\u25cf');
this._promptEntry.menu.isPassword = true;
this._updateSensitivity(false);
this._setWorking(false);
},
_escape: function() {
if (this.allowCancel)
this._authPrompt.cancel();
if (this.allowCancel) {
this._userVerifier.cancel();
this.emit('failed');
}
},
_otherUserClicked: function(button, event) {
Gdm.goto_login_session_sync(null);
this._authPrompt.cancel();
this._userVerifier.cancel();
this.emit('failed');
},
destroy: function() {
this.popModal();
this.actor.destroy();
this._userVerifier.clear();
if (this._idleWatchId) {
this._idleMonitor.remove_watch(this._idleWatchId);
this._idleWatchId = 0;
}
this.parent();
},
cancel: function() {
this._authPrompt.cancel();
this._userVerifier.cancel(null);
this.destroy();
},
addCharacter: function(unichar) {
this._authPrompt.addCharacter(unichar);
this._promptEntry.clutter_text.insert_unichar(unichar);
},
finish: function(onComplete) {
this._authPrompt.finish(onComplete);
},
open: function(timestamp) {
this.actor.show();
if (this._isModal)
return true;
if (!Main.pushModal(this.actor, { timestamp: timestamp,
keybindingMode: Shell.KeyBindingMode.UNLOCK_SCREEN }))
return false;
this._isModal = true;
return true;
},
popModal: function(timestamp) {
if (this._isModal) {
Main.popModal(this.actor, timestamp);
this._isModal = false;
}
}
});
Signals.addSignalMethods(UnlockDialog.prototype);

998
js/ui/userMenu.js Normal file
View File

@ -0,0 +1,998 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const AccountsService = imports.gi.AccountsService;
const Gdm = imports.gi.Gdm;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Pango = imports.gi.Pango;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Tp = imports.gi.TelepathyGLib;
const Atk = imports.gi.Atk;
const Clutter = imports.gi.Clutter;
const BoxPointer = imports.ui.boxpointer;
const GnomeSession = imports.misc.gnomeSession;
const LoginManager = imports.misc.loginManager;
const Main = imports.ui.main;
const ModalDialog = imports.ui.modalDialog;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const Params = imports.misc.params;
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 ALWAYS_SHOW_LOG_OUT_KEY = 'always-show-log-out';
const SHOW_FULL_NAME_IN_TOP_BAR_KEY = 'show-full-name-in-top-bar';
const DIALOG_ICON_SIZE = 64;
const MAX_USERS_IN_SESSION_DIALOG = 5;
const IMStatus = {
AVAILABLE: 0,
BUSY: 1,
HIDDEN: 2,
AWAY: 3,
IDLE: 4,
OFFLINE: 5,
LAST: 6
};
const SystemdLoginSessionIface = <interface name='org.freedesktop.login1.Session'>
<property name="Id" type="s" access="read"/>
<property name="Remote" type="b" access="read"/>
<property name="Class" type="s" access="read"/>
<property name="Type" type="s" access="read"/>
<property name="State" type="s" access="read"/>
</interface>;
const SystemdLoginSession = Gio.DBusProxy.makeProxyWrapper(SystemdLoginSessionIface);
// Adapted from gdm/gui/user-switch-applet/applet.c
//
// Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
// Copyright (C) 2008,2009 Red Hat, Inc.
const UserAvatarWidget = new Lang.Class({
Name: 'UserAvatarWidget',
_init: function(user, params) {
this._user = user;
params = Params.parse(params, { reactive: false,
iconSize: DIALOG_ICON_SIZE,
styleClass: 'status-chooser-user-icon' });
this._iconSize = params.iconSize;
this.actor = new St.Bin({ style_class: params.styleClass,
track_hover: params.reactive,
reactive: params.reactive });
},
setSensitive: function(sensitive) {
this.actor.can_focus = sensitive;
this.actor.reactive = sensitive;
},
update: function() {
let iconFile = this._user.get_icon_file();
if (iconFile && !GLib.file_test(iconFile, GLib.FileTest.EXISTS))
iconFile = null;
if (iconFile) {
let file = Gio.File.new_for_path(iconFile);
this.actor.child = null;
this.actor.style = 'background-image: url("%s");'.format(iconFile);
} else {
this.actor.style = null;
this.actor.child = new St.Icon({ icon_name: 'avatar-default-symbolic',
icon_size: this._iconSize });
}
}
});
const IMStatusItem = new Lang.Class({
Name: 'IMStatusItem',
Extends: PopupMenu.PopupBaseMenuItem,
_init: function(label, iconName) {
this.parent();
this.actor.add_style_class_name('status-chooser-status-item');
this._icon = new St.Icon({ style_class: 'popup-menu-icon' });
this.addActor(this._icon);
if (iconName)
this._icon.icon_name = iconName;
this.label = new St.Label({ text: label });
this.actor.label_actor = this.label;
this.addActor(this.label);
}
});
const IMUserNameItem = new Lang.Class({
Name: 'IMUserNameItem',
Extends: PopupMenu.PopupBaseMenuItem,
_init: function() {
this.parent({ reactive: false,
can_focus: false,
style_class: 'status-chooser-user-name' });
this._wrapper = new Shell.GenericContainer();
this._wrapper.connect('get-preferred-width',
Lang.bind(this, this._wrapperGetPreferredWidth));
this._wrapper.connect('get-preferred-height',
Lang.bind(this, this._wrapperGetPreferredHeight));
this._wrapper.connect('allocate',
Lang.bind(this, this._wrapperAllocate));
this.addActor(this._wrapper, { expand: true, span: -1 });
this.label = new St.Label();
this.label.clutter_text.set_line_wrap(true);
this.label.clutter_text.set_ellipsize(Pango.EllipsizeMode.NONE);
this._wrapper.add_actor(this.label);
},
_wrapperGetPreferredWidth: function(actor, forHeight, alloc) {
alloc.min_size = 1;
alloc.natural_size = 1;
},
_wrapperGetPreferredHeight: function(actor, forWidth, alloc) {
[alloc.min_size, alloc.natural_size] = this.label.get_preferred_height(forWidth);
},
_wrapperAllocate: function(actor, box, flags) {
this.label.allocate(box, flags);
}
});
const IMStatusChooserItem = new Lang.Class({
Name: 'IMStatusChooserItem',
Extends: PopupMenu.PopupBaseMenuItem,
_init: function() {
this.parent({ reactive: false,
can_focus: false,
style_class: 'status-chooser' });
this._userManager = AccountsService.UserManager.get_default();
this._user = this._userManager.get_user(GLib.get_user_name());
this._avatar = new UserAvatarWidget(this._user, { reactive: true });
this._iconBin = new St.Button({ child: this._avatar.actor });
this.addActor(this._iconBin);
this._iconBin.connect('clicked', Lang.bind(this,
function() {
this.activate();
}));
this._section = new PopupMenu.PopupMenuSection();
this.addActor(this._section.actor);
this._name = new IMUserNameItem();
this._section.addMenuItem(this._name);
this._combo = new PopupMenu.PopupComboBoxMenuItem({ style_class: 'status-chooser-combo' });
this._section.addMenuItem(this._combo);
let item;
item = new IMStatusItem(_("Available"), 'user-available-symbolic');
this._combo.addMenuItem(item, IMStatus.AVAILABLE);
item = new IMStatusItem(_("Busy"), 'user-busy-symbolic');
this._combo.addMenuItem(item, IMStatus.BUSY);
item = new IMStatusItem(_("Invisible"), 'user-invisible-symbolic');
this._combo.addMenuItem(item, IMStatus.HIDDEN);
item = new IMStatusItem(_("Away"), 'user-away-symbolic');
this._combo.addMenuItem(item, IMStatus.AWAY);
item = new IMStatusItem(_("Idle"), 'user-idle-symbolic');
this._combo.addMenuItem(item, IMStatus.IDLE);
item = new IMStatusItem(_("Offline"), 'user-offline-symbolic');
this._combo.addMenuItem(item, IMStatus.OFFLINE);
this._combo.connect('active-item-changed',
Lang.bind(this, this._changeIMStatus));
this._presence = new GnomeSession.Presence();
this._presence.connectSignal('StatusChanged', Lang.bind(this, function(proxy, senderName, [status]) {
this._sessionStatusChanged(status);
}));
this._sessionPresenceRestored = false;
this._imPresenceRestored = false;
this._currentPresence = undefined;
this._accountMgr = Tp.AccountManager.dup();
this._accountMgr.connect('most-available-presence-changed',
Lang.bind(this, this._IMStatusChanged));
this._accountMgr.connect('account-enabled',
Lang.bind(this, this._IMAccountsChanged));
this._accountMgr.connect('account-disabled',
Lang.bind(this, this._IMAccountsChanged));
this._accountMgr.connect('account-removed',
Lang.bind(this, this._IMAccountsChanged));
this._accountMgr.connect('account-validity-changed',
Lang.bind(this, this._IMAccountsChanged));
this._accountMgr.prepare_async(null, Lang.bind(this,
function(mgr) {
this._IMAccountsChanged(mgr);
if (this._networkMonitor.network_available)
this._restorePresence();
else
this._setComboboxPresence(Tp.ConnectionPresenceType.OFFLINE);
}));
this._networkMonitor = Gio.NetworkMonitor.get_default();
this._networkMonitor.connect('network-changed',
Lang.bind(this, function(monitor, available) {
this._IMAccountsChanged(this._accountMgr);
if (available && !this._imPresenceRestored)
this._restorePresence();
}));
this._userLoadedId = this._user.connect('notify::is-loaded',
Lang.bind(this,
this._updateUser));
this._userChangedId = this._user.connect('changed',
Lang.bind(this,
this._updateUser));
this.actor.connect('notify::mapped', Lang.bind(this, function() {
if (this.actor.mapped)
this._updateUser();
}));
this.connect('sensitive-changed', function(sensitive) {
this._avatar.setSensitive(sensitive);
});
},
_restorePresence: function() {
let [presence, status, msg] = this._accountMgr.get_most_available_presence();
let savedPresence = global.settings.get_int('saved-im-presence');
if (savedPresence == presence) {
this._IMStatusChanged(this._accountMgr, presence, status, msg);
} else {
this._setComboboxPresence(savedPresence);
status = this._statusForPresence(savedPresence);
msg = msg ? msg : '';
this._accountMgr.set_all_requested_presences(savedPresence, status, msg);
}
},
destroy: function() {
// clean up signal handlers
if (this._userLoadedId != 0) {
this._user.disconnect(this._userLoadedId);
this._userLoadedId = 0;
}
if (this._userChangedId != 0) {
this._user.disconnect(this._userChangedId);
this._userChangedId = 0;
}
this.parent();
},
// Override getColumnWidths()/setColumnWidths() to make the item
// independent from the overall column layout of the menu
getColumnWidths: function() {
return [];
},
setColumnWidths: function(widths) {
},
_updateUser: function() {
if (this._user.is_loaded)
this._name.label.set_text(this._user.get_real_name());
else
this._name.label.set_text("");
this._avatar.update();
},
_statusForPresence: function(presence) {
switch(presence) {
case Tp.ConnectionPresenceType.AVAILABLE:
return 'available';
case Tp.ConnectionPresenceType.BUSY:
return 'busy';
case Tp.ConnectionPresenceType.OFFLINE:
return 'offline';
case Tp.ConnectionPresenceType.HIDDEN:
return 'hidden';
case Tp.ConnectionPresenceType.AWAY:
return 'away';
case Tp.ConnectionPresenceType.EXTENDED_AWAY:
return 'xa';
default:
return 'unknown';
}
},
_IMAccountsChanged: function(mgr) {
let accounts = mgr.get_valid_accounts().filter(function(account) {
return account.enabled;
});
let sensitive = accounts.length > 0 && this._networkMonitor.network_available;
this._combo.setSensitive(sensitive);
},
_IMStatusChanged: function(accountMgr, presence, status, message) {
if (!this._imPresenceRestored)
this._imPresenceRestored = true;
if (presence == this._currentPresence)
return;
this._currentPresence = presence;
this._setComboboxPresence(presence);
if (!this._sessionPresenceRestored) {
this._sessionStatusChanged(this._presence.status);
return;
}
if (presence == Tp.ConnectionPresenceType.AVAILABLE)
this._presence.status = GnomeSession.PresenceStatus.AVAILABLE;
// We ignore the actual value of _expectedPresence and never safe
// the first presence change after an "automatic" change, assuming
// that it is the response to our request; this is to account for
// mission control falling back to "similar" presences if an account
// type does not implement the requested presence.
if (!this._expectedPresence)
global.settings.set_int('saved-im-presence', presence);
else
this._expectedPresence = undefined;
},
_setComboboxPresence: function(presence) {
let activatedItem;
if (presence == Tp.ConnectionPresenceType.AVAILABLE)
activatedItem = IMStatus.AVAILABLE;
else if (presence == Tp.ConnectionPresenceType.BUSY)
activatedItem = IMStatus.BUSY;
else if (presence == Tp.ConnectionPresenceType.HIDDEN)
activatedItem = IMStatus.HIDDEN;
else if (presence == Tp.ConnectionPresenceType.AWAY)
activatedItem = IMStatus.AWAY;
else if (presence == Tp.ConnectionPresenceType.EXTENDED_AWAY)
activatedItem = IMStatus.IDLE;
else
activatedItem = IMStatus.OFFLINE;
this._combo.setActiveItem(activatedItem);
for (let i = 0; i < IMStatus.LAST; i++) {
if (i == IMStatus.AVAILABLE || i == IMStatus.OFFLINE)
continue; // always visible
this._combo.setItemVisible(i, i == activatedItem);
}
},
_changeIMStatus: function(menuItem, id) {
let [presence, s, msg] = this._accountMgr.get_most_available_presence();
let newPresence, status;
if (id == IMStatus.AVAILABLE) {
newPresence = Tp.ConnectionPresenceType.AVAILABLE;
} else if (id == IMStatus.OFFLINE) {
newPresence = Tp.ConnectionPresenceType.OFFLINE;
} else
return;
status = this._statusForPresence(newPresence);
msg = msg ? msg : '';
this._accountMgr.set_all_requested_presences(newPresence, status, msg);
},
getIMPresenceForSessionStatus: function(sessionStatus) {
// Restore the last user-set presence when coming back from
// BUSY/IDLE (otherwise the last user-set presence matches
// the current one)
if (sessionStatus == GnomeSession.PresenceStatus.AVAILABLE)
return global.settings.get_int('saved-im-presence');
if (sessionStatus == GnomeSession.PresenceStatus.BUSY) {
// Only change presence if the current one is "more present" than
// busy, or if coming back from idle
if (this._currentPresence == Tp.ConnectionPresenceType.AVAILABLE ||
this._currentPresence == Tp.ConnectionPresenceType.EXTENDED_AWAY)
return Tp.ConnectionPresenceType.BUSY;
}
if (sessionStatus == GnomeSession.PresenceStatus.IDLE) {
// Only change presence if the current one is "more present" than
// idle
if (this._currentPresence != Tp.ConnectionPresenceType.OFFLINE &&
this._currentPresence != Tp.ConnectionPresenceType.HIDDEN)
return Tp.ConnectionPresenceType.EXTENDED_AWAY;
}
return this._currentPresence;
},
_sessionStatusChanged: function(sessionStatus) {
if (!this._imPresenceRestored)
return;
let savedStatus = global.settings.get_int('saved-session-presence');
if (!this._sessionPresenceRestored) {
// We should never save/restore a status other than AVAILABLE
// or BUSY
if (savedStatus != GnomeSession.PresenceStatus.AVAILABLE &&
savedStatus != GnomeSession.PresenceStatus.BUSY)
savedStatus = GnomeSession.PresenceStatus.AVAILABLE;
if (sessionStatus != savedStatus) {
this._presence.status = savedStatus;
return;
}
this._sessionPresenceRestored = true;
}
if ((sessionStatus == GnomeSession.PresenceStatus.AVAILABLE ||
sessionStatus == GnomeSession.PresenceStatus.BUSY) &&
savedStatus != sessionStatus)
global.settings.set_int('saved-session-presence', sessionStatus);
let [presence, s, msg] = this._accountMgr.get_most_available_presence();
let newPresence, status;
let newPresence = this.getIMPresenceForSessionStatus(sessionStatus);
if (!newPresence || newPresence == presence)
return;
status = this._statusForPresence(newPresence);
msg = msg ? msg : '';
this._expectedPresence = newPresence;
this._accountMgr.set_all_requested_presences(newPresence, status, msg);
}
});
const UserMenuButton = new Lang.Class({
Name: 'UserMenuButton',
Extends: PanelMenu.Button,
_init: function() {
this.parent(0.0);
this.actor.accessible_role = Atk.Role.MENU;
let box = new St.BoxLayout({ name: 'panelUserMenu' });
this.actor.add_actor(box);
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();
this._user = this._userManager.get_user(GLib.get_user_name());
this._presence = new GnomeSession.Presence();
this._session = new GnomeSession.SessionManager();
this._haveShutdown = true;
this._haveSuspend = true;
this._accountMgr = Tp.AccountManager.dup();
this._loginManager = LoginManager.getLoginManager();
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
this._iconBox = new St.Bin();
box.add(this._iconBox, { y_align: St.Align.MIDDLE, y_fill: false });
let textureCache = St.TextureCache.get_default();
this._offlineIcon = new St.Icon({ icon_name: 'user-offline-symbolic',
style_class: 'popup-menu-icon' });
this._availableIcon = new St.Icon({ icon_name: 'user-available-symbolic',
style_class: 'popup-menu-icon' });
this._busyIcon = new St.Icon({ icon_name: 'user-busy-symbolic',
style_class: 'popup-menu-icon' });
this._invisibleIcon = new St.Icon({ icon_name: 'user-invisible-symbolic',
style_class: 'popup-menu-icon' });
this._awayIcon = new St.Icon({ icon_name: 'user-away-symbolic',
style_class: 'popup-menu-icon' });
this._idleIcon = new St.Icon({ icon_name: 'user-idle-symbolic',
style_class: 'popup-menu-icon' });
this._pendingIcon = new St.Icon({ icon_name: 'user-status-pending-symbolic',
style_class: 'popup-menu-icon' });
this._lockedIcon = new St.Icon({ icon_name: 'changes-prevent-symbolic',
style_class: 'popup-menu-icon' });
this._accountMgr.connect('most-available-presence-changed',
Lang.bind(this, this._updatePresenceIcon));
this._accountMgr.connect('account-enabled',
Lang.bind(this, this._onAccountEnabled));
this._accountMgr.connect('account-removed',
Lang.bind(this, this._onAccountRemoved));
this._accountMgr.prepare_async(null, Lang.bind(this,
function(mgr) {
let [presence, s, msg] = mgr.get_most_available_presence();
this._updatePresenceIcon(mgr, presence, s, msg);
this._setupAccounts();
}));
this._name = new St.Label();
this.actor.label_actor = this._name;
box.add(this._name, { y_align: St.Align.MIDDLE, y_fill: false });
this._userLoadedId = this._user.connect('notify::is-loaded', Lang.bind(this, this._updateUserName));
this._userChangedId = this._user.connect('changed', Lang.bind(this, this._updateUserName));
this._updateUserName();
this._createSubMenu();
this._updateSwitch(this._presence.status);
this._presence.connectSignal('StatusChanged', Lang.bind(this, function (proxy, senderName, [status]) {
this._updateSwitch(status);
}));
this._userManager.connect('notify::is-loaded',
Lang.bind(this, this._updateMultiUser));
this._userManager.connect('notify::has-multiple-users',
Lang.bind(this, this._updateMultiUser));
this._userManager.connect('user-added',
Lang.bind(this, this._updateMultiUser));
this._userManager.connect('user-removed',
Lang.bind(this, this._updateMultiUser));
this._lockdownSettings.connect('changed::' + DISABLE_USER_SWITCH_KEY,
Lang.bind(this, this._updateSwitchUser));
this._lockdownSettings.connect('changed::' + DISABLE_LOG_OUT_KEY,
Lang.bind(this, this._updateLogout));
this._lockdownSettings.connect('changed::' + DISABLE_LOCK_SCREEN_KEY,
Lang.bind(this, this._updateLockScreen));
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,
Lang.bind(this, this._updateUserName));
this._privacySettings.connect('changed::' + SHOW_FULL_NAME_IN_TOP_BAR_KEY,
Lang.bind(this, this._updateUserName));
this._updateSwitchUser();
this._updateLogout();
this._updateLockScreen();
this._updatesFile = Gio.File.new_for_path('/var/lib/PackageKit/prepared-update');
this._updatesMonitor = this._updatesFile.monitor(Gio.FileMonitorFlags.NONE, null);
this._updatesMonitor.connect('changed', Lang.bind(this, this._updateInstallUpdates));
// Whether shutdown is available or not depends on both lockdown
// settings (disable-log-out) and Polkit policy - the latter doesn't
// notify, so we update the menu item each time the menu opens or
// the lockdown setting changes, which should be close enough.
this.menu.connect('open-state-changed', Lang.bind(this,
function(menu, open) {
if (!open)
return;
this._updateHaveShutdown();
this._updateHaveSuspend();
}));
this._lockdownSettings.connect('changed::' + DISABLE_LOG_OUT_KEY,
Lang.bind(this, this._updateHaveShutdown));
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
if (Main.screenShield)
Main.screenShield.connect('locked-changed', Lang.bind(this, this._updatePresenceIcon));
this._sessionUpdated();
},
_sessionUpdated: function() {
this.actor.visible = !Main.sessionMode.isGreeter;
let allowSettings = Main.sessionMode.allowSettings;
this._statusChooser.setSensitive(allowSettings);
this._systemSettings.visible = allowSettings;
this.setSensitive(!Main.sessionMode.isLocked);
this._updatePresenceIcon();
this._updateUserName();
},
_onDestroy: function() {
this._user.disconnect(this._userLoadedId);
this._user.disconnect(this._userChangedId);
},
_updateUserName: function() {
let settings = this._privacySettings;
if (Main.sessionMode.isLocked)
settings = this._screenSaverSettings;
if (this._user.is_loaded && settings.get_boolean(SHOW_FULL_NAME_IN_TOP_BAR_KEY))
this._name.set_text(this._user.get_real_name());
else
this._name.set_text("");
},
_updateMultiUser: function() {
this._updateSwitchUser();
this._updateLogout();
},
_updateSwitchUser: function() {
let allowSwitch = !this._lockdownSettings.get_boolean(DISABLE_USER_SWITCH_KEY);
let multiUser = this._userManager.can_switch() && this._userManager.has_multiple_users;
this._loginScreenItem.actor.visible = allowSwitch && multiUser;
},
_updateLogout: function() {
let allowLogout = !this._lockdownSettings.get_boolean(DISABLE_LOG_OUT_KEY);
let alwaysShow = global.settings.get_boolean(ALWAYS_SHOW_LOG_OUT_KEY);
let systemAccount = this._user.system_account;
let localAccount = this._user.local_account;
let multiUser = this._userManager.has_multiple_users;
let multiSession = Gdm.get_session_ids().length > 1;
this._logoutItem.actor.visible = allowLogout && (alwaysShow || multiUser || multiSession || systemAccount || !localAccount);
},
_updateLockScreen: function() {
let allowLockScreen = !this._lockdownSettings.get_boolean(DISABLE_LOCK_SCREEN_KEY);
this._lockScreenItem.actor.visible = allowLockScreen && LoginManager.canLock();
},
_updateInstallUpdates: function() {
let haveUpdates = this._updatesFile.query_exists(null);
this._installUpdatesItem.actor.visible = haveUpdates && this._haveShutdown;
},
_updateHaveShutdown: function() {
this._session.CanShutdownRemote(Lang.bind(this,
function(result, error) {
if (!error) {
this._haveShutdown = result[0];
this._updateInstallUpdates();
this._updateSuspendOrPowerOff();
}
}));
},
_updateHaveSuspend: function() {
this._loginManager.canSuspend(Lang.bind(this,
function(result) {
this._haveSuspend = result;
this._updateSuspendOrPowerOff();
}));
},
_updateSuspendOrPowerOff: function() {
if (!this._suspendOrPowerOffItem)
return;
this._suspendOrPowerOffItem.actor.visible = this._haveShutdown || this._haveSuspend;
// If we can't power off show Suspend instead
// and disable the alt key
if (!this._haveShutdown) {
this._suspendOrPowerOffItem.updateText(_("Suspend"), null);
} else if (!this._haveSuspend) {
this._suspendOrPowerOffItem.updateText(_("Power Off"), null);
} else {
this._suspendOrPowerOffItem.updateText(_("Power Off"), _("Suspend"));
}
},
_updateSwitch: function(status) {
let active = status == GnomeSession.PresenceStatus.AVAILABLE;
this._notificationsSwitch.setToggleState(active);
},
_updatePresenceIcon: function(accountMgr, presence, status, message) {
if (Main.sessionMode.isLocked)
this._iconBox.child = this._lockedIcon;
else if (presence == Tp.ConnectionPresenceType.AVAILABLE)
this._iconBox.child = this._availableIcon;
else if (presence == Tp.ConnectionPresenceType.BUSY)
this._iconBox.child = this._busyIcon;
else if (presence == Tp.ConnectionPresenceType.HIDDEN)
this._iconBox.child = this._invisibleIcon;
else if (presence == Tp.ConnectionPresenceType.AWAY)
this._iconBox.child = this._awayIcon;
else if (presence == Tp.ConnectionPresenceType.EXTENDED_AWAY)
this._iconBox.child = this._idleIcon;
else
this._iconBox.child = this._offlineIcon;
if (Main.sessionMode.isLocked)
this._iconBox.visible = Main.screenShield.locked;
else
this._iconBox.visible = true;
},
_setupAccounts: function() {
let accounts = this._accountMgr.get_valid_accounts();
for (let i = 0; i < accounts.length; i++) {
accounts[i]._changingId = accounts[i].connect('notify::connection-status',
Lang.bind(this, this._updateChangingPresence));
}
this._updateChangingPresence();
},
_onAccountEnabled: function(accountMgr, account) {
if (!account._changingId)
account._changingId = account.connect('notify::connection-status',
Lang.bind(this, this._updateChangingPresence));
this._updateChangingPresence();
},
_onAccountRemoved: function(accountMgr, account) {
if (account._changingId) {
account.disconnect(account._changingId);
account._changingId = 0;
}
this._updateChangingPresence();
},
_updateChangingPresence: function() {
let accounts = this._accountMgr.get_valid_accounts();
let changing = false;
for (let i = 0; i < accounts.length; i++) {
if (accounts[i].connection_status == Tp.ConnectionStatus.CONNECTING) {
changing = true;
break;
}
}
if (changing) {
this._iconBox.child = this._pendingIcon;
} else {
let [presence, s, msg] = this._accountMgr.get_most_available_presence();
this._updatePresenceIcon(this._accountMgr, presence, s, msg);
}
},
_createSubMenu: function() {
let item;
item = new IMStatusChooserItem();
item.connect('activate', Lang.bind(this, this._onMyAccountActivate));
this.menu.addMenuItem(item);
this._statusChooser = item;
item = new PopupMenu.PopupSwitchMenuItem(_("Notifications"));
item.connect('toggled', Lang.bind(this, this._updatePresenceStatus));
this.menu.addMenuItem(item);
this._notificationsSwitch = item;
item = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addMenuItem(item);
item = new PopupMenu.PopupMenuItem(_("Settings"));
item.connect('activate', Lang.bind(this, this._onPreferencesActivate));
this.menu.addMenuItem(item);
this._systemSettings = item;
item = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addMenuItem(item);
item = new PopupMenu.PopupMenuItem(_("Switch User"));
item.connect('activate', Lang.bind(this, this._onLoginScreenActivate));
this.menu.addMenuItem(item);
this._loginScreenItem = item;
item = new PopupMenu.PopupMenuItem(_("Log Out"));
item.connect('activate', Lang.bind(this, this._onQuitSessionActivate));
this.menu.addMenuItem(item);
this._logoutItem = item;
item = new PopupMenu.PopupMenuItem(_("Lock"));
item.connect('activate', Lang.bind(this, this._onLockScreenActivate));
this.menu.addMenuItem(item);
this._lockScreenItem = item;
item = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addMenuItem(item);
item = new PopupMenu.PopupAlternatingMenuItem(_("Power Off"),
_("Suspend"));
this.menu.addMenuItem(item);
item.connect('activate', Lang.bind(this, this._onSuspendOrPowerOffActivate));
this._suspendOrPowerOffItem = item;
this._updateSuspendOrPowerOff();
item = new PopupMenu.PopupMenuItem(_("Install Updates & Restart"));
item.connect('activate', Lang.bind(this, this._onInstallUpdatesActivate));
this.menu.addMenuItem(item);
this._installUpdatesItem = item;
},
_updatePresenceStatus: function(item, event) {
let status;
if (item.state) {
status = GnomeSession.PresenceStatus.AVAILABLE;
} else {
status = GnomeSession.PresenceStatus.BUSY;
let [presence, s, msg] = this._accountMgr.get_most_available_presence();
let newPresence = this._statusChooser.getIMPresenceForSessionStatus(status);
if (newPresence != presence &&
newPresence == Tp.ConnectionPresenceType.BUSY)
Main.notify(_("Your chat status will be set to busy"),
_("Notifications are now disabled, including chat messages. Your online status has been adjusted to let others know that you might not see their messages."));
}
this._presence.status = status;
},
_onMyAccountActivate: function() {
Main.overview.hide();
let app = Shell.AppSystem.get_default().lookup_app('gnome-user-accounts-panel.desktop');
app.activate();
},
_onPreferencesActivate: function() {
Main.overview.hide();
let app = Shell.AppSystem.get_default().lookup_app('gnome-control-center.desktop');
app.activate();
},
_onLockScreenActivate: function() {
this.menu.close(BoxPointer.PopupAnimation.NONE);
Main.overview.hide();
Main.screenShield.lock(true);
},
_onLoginScreenActivate: function() {
this.menu.close(BoxPointer.PopupAnimation.NONE);
Main.overview.hide();
if (Main.screenShield)
Main.screenShield.lock(false);
Gdm.goto_login_session_sync(null);
},
_onQuitSessionActivate: function() {
Main.overview.hide();
this._session.LogoutRemote(0);
},
_onInstallUpdatesActivate: function() {
Main.overview.hide();
Util.spawn(['pkexec', '/usr/libexec/pk-trigger-offline-update']);
this._session.RebootRemote();
},
_openSessionWarnDialog: function(sessions) {
let dialog = new ModalDialog.ModalDialog();
let subjectLabel = new St.Label({ style_class: 'end-session-dialog-subject',
text: _("Other users are logged in.") });
dialog.contentLayout.add(subjectLabel, { y_fill: true,
y_align: St.Align.START });
let descriptionLabel = new St.Label({ style_class: 'end-session-dialog-description'});
descriptionLabel.set_text(_("Shutting down might cause them to lose unsaved work."));
descriptionLabel.clutter_text.line_wrap = true;
dialog.contentLayout.add(descriptionLabel, { x_fill: true,
y_fill: true,
y_align: St.Align.START });
let scrollView = new St.ScrollView({ style_class: 'end-session-dialog-app-list' });
scrollView.add_style_class_name('vfade');
scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
dialog.contentLayout.add(scrollView, { x_fill: true, y_fill: true });
let userList = new St.BoxLayout({ vertical: true });
scrollView.add_actor(userList);
for (let i = 0; i < sessions.length; i++) {
let session = sessions[i];
let userEntry = new St.BoxLayout({ style_class: 'login-dialog-user-list-item',
vertical: false });
let avatar = new UserAvatarWidget(session.user);
avatar.update();
userEntry.add(avatar.actor);
let userLabelText = "";;
let userName = session.user.get_real_name() ?
session.user.get_real_name() : session.username;
if (session.info.remote)
/* Translators: Remote here refers to a remote session, like a ssh login */
userLabelText = _("%s (remote)").format(userName);
else if (session.info.type == "tty")
/* Translators: Console here refers to a tty like a VT console */
userLabelText = _("%s (console)").format(userName);
else
userLabelText = userName;
let textLayout = new St.BoxLayout({ style_class: 'login-dialog-user-list-item-text-box',
vertical: true });
textLayout.add(new St.Label({ text: userLabelText }),
{ y_fill: false,
y_align: St.Align.MIDDLE,
expand: true });
userEntry.add(textLayout, { expand: true });
userList.add(userEntry, { x_fill: true });
}
let cancelButton = { label: _("Cancel"),
action: function() { dialog.close(); },
key: Clutter.Escape };
let powerOffButton = { label: _("Power Off"), action: Lang.bind(this, function() {
dialog.close();
this._session.ShutdownRemote();
}), default: true };
dialog.setButtons([cancelButton, powerOffButton]);
dialog.open();
},
_onSuspendOrPowerOffActivate: function() {
Main.overview.hide();
if (this._haveShutdown &&
this._suspendOrPowerOffItem.state == PopupMenu.PopupAlternatingMenuItemState.DEFAULT) {
this._loginManager.listSessions(Lang.bind(this,
function(result) {
let sessions = [];
let n = 0;
for (let i = 0; i < result.length; i++) {
let[id, uid, userName, seat, sessionPath] = result[i];
let proxy = new SystemdLoginSession(Gio.DBus.system,
'org.freedesktop.login1',
sessionPath);
if (proxy.Class != 'user')
continue;
if (proxy.State == 'closing')
continue;
if (proxy.Id == GLib.getenv('XDG_SESSION_ID'))
continue;
sessions.push({ user: this._userManager.get_user(userName),
username: userName,
info: { type: proxy.Type,
remote: proxy.Remote }
});
// limit the number of entries
n++;
if (n == MAX_USERS_IN_SESSION_DIALOG)
break;
}
if (n != 0)
this._openSessionWarnDialog(sessions);
else
this._session.ShutdownRemote();
}));
} else {
this.menu.close(BoxPointer.PopupAnimation.NONE);
this._loginManager.suspend();
}
}
});

View File

@ -3,56 +3,10 @@
//
// A widget showing the user avatar and name
const AccountsService = imports.gi.AccountsService;
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const St = imports.gi.St;
const Params = imports.misc.params;
const AVATAR_ICON_SIZE = 64;
// Adapted from gdm/gui/user-switch-applet/applet.c
//
// Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
// Copyright (C) 2008,2009 Red Hat, Inc.
const Avatar = new Lang.Class({
Name: 'Avatar',
_init: function(user, params) {
this._user = user;
params = Params.parse(params, { reactive: false,
iconSize: AVATAR_ICON_SIZE,
styleClass: 'framed-user-icon' });
this._iconSize = params.iconSize;
this.actor = new St.Bin({ style_class: params.styleClass,
track_hover: params.reactive,
reactive: params.reactive });
},
setSensitive: function(sensitive) {
this.actor.can_focus = sensitive;
this.actor.reactive = sensitive;
},
update: function() {
let iconFile = this._user.get_icon_file();
if (iconFile && !GLib.file_test(iconFile, GLib.FileTest.EXISTS))
iconFile = null;
if (iconFile) {
let file = Gio.File.new_for_path(iconFile);
this.actor.child = null;
this.actor.style = 'background-image: url("%s");'.format(iconFile);
} else {
this.actor.style = null;
this.actor.child = new St.Icon({ icon_name: 'avatar-default-symbolic',
icon_size: this._iconSize });
}
}
});
const UserMenu = imports.ui.userMenu;
const UserWidget = new Lang.Class({
Name: 'UserWidget',
@ -62,9 +16,8 @@ const UserWidget = new Lang.Class({
this.actor = new St.BoxLayout({ style_class: 'user-widget',
vertical: false });
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
this._avatar = new Avatar(user);
this._avatar = new UserMenu.UserAvatarWidget(user);
this.actor.add(this._avatar.actor,
{ x_fill: true, y_fill: true });
@ -83,7 +36,7 @@ const UserWidget = new Lang.Class({
this._updateUser();
},
_onDestroy: function() {
destroy: function() {
if (this._userLoadedId != 0) {
this._user.disconnect(this._userLoadedId);
this._userLoadedId = 0;
@ -93,6 +46,8 @@ const UserWidget = new Lang.Class({
this._user.disconnect(this._userChangedId);
this._userChangedId = 0;
}
this.actor.destroy();
},
_updateUser: function() {

View File

@ -150,14 +150,6 @@ const ViewSelector = new Lang.Class({
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._toggleAppsPage));
Main.wm.addKeybinding('toggle-overview',
new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }),
Meta.KeyBindingFlags.NONE,
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(Main.overview, Main.overview.toggle));
},
_toggleAppsPage: function() {
@ -186,10 +178,6 @@ const ViewSelector = new Lang.Class({
Main.overview.fadeInDesktop();
},
setWorkspacesFullGeometry: function(geom) {
this._workspacesDisplay.setWorkspacesFullGeometry(geom);
},
hide: function() {
this._workspacesDisplay.hide();
},
@ -508,12 +496,12 @@ const ViewSelector = new Lang.Class({
return;
this._searchSystem.registerProvider(provider);
this._searchResults.createProviderDisplay(provider);
this._searchResults.createProviderMeta(provider);
},
removeSearchProvider: function(provider) {
this._searchSystem.unregisterProvider(provider);
this._searchResults.destroyProviderDisplay(provider);
this._searchResults.destroyProviderMeta(provider);
},
getActivePage: function() {

View File

@ -134,9 +134,9 @@ const WandaSearchProvider = new Lang.Class({
getInitialResultSet: function(terms) {
if (terms.join(' ') == MAGIC_FISH_KEY) {
this.searchSystem.setResults(this, [ FISH_NAME ]);
this.searchSystem.pushResults(this, [ FISH_NAME ]);
} else {
this.searchSystem.setResults(this, []);
this.searchSystem.pushResults(this, []);
}
},

View File

@ -4,7 +4,6 @@ const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
@ -67,209 +66,6 @@ function getWindowDimmer(actor) {
}
}
/*
* When the last window closed on a workspace is a dialog or splash
* screen, we assume that it might be an initial window shown before
* the main window of an application, and give the app a grace period
* where it can map another window before we remove the workspace.
*/
const LAST_WINDOW_GRACE_TIME = 1000;
const WorkspaceTracker = new Lang.Class({
Name: 'WorkspaceTracker',
_init: function(wm) {
this._wm = wm;
this._workspaces = [];
this._checkWorkspacesId = 0;
let tracker = Shell.WindowTracker.get_default();
tracker.connect('startup-sequence-changed', Lang.bind(this, this._queueCheckWorkspaces));
global.screen.connect('notify::n-workspaces', Lang.bind(this, this._nWorkspacesChanged));
global.screen.connect('window-entered-monitor', Lang.bind(this, this._windowEnteredMonitor));
global.screen.connect('window-left-monitor', Lang.bind(this, this._windowLeftMonitor));
global.screen.connect('restacked', Lang.bind(this, this._windowsRestacked));
this._workspaceSettings = new Gio.Settings({ schema: Main.dynamicWorkspacesSchema });
this._workspaceSettings.connect('changed::dynamic-workspaces', Lang.bind(this, this._queueCheckWorkspaces));
this._nWorkspacesChanged();
},
_checkWorkspaces: function() {
let i;
let emptyWorkspaces = [];
if (!Meta.prefs_get_dynamic_workspaces()) {
this._checkWorkspacesId = 0;
return false;
}
for (i = 0; i < this._workspaces.length; i++) {
let lastRemoved = this._workspaces[i]._lastRemovedWindow;
if ((lastRemoved &&
(lastRemoved.get_window_type() == Meta.WindowType.SPLASHSCREEN ||
lastRemoved.get_window_type() == Meta.WindowType.DIALOG ||
lastRemoved.get_window_type() == Meta.WindowType.MODAL_DIALOG)) ||
this._workspaces[i]._keepAliveId)
emptyWorkspaces[i] = false;
else
emptyWorkspaces[i] = true;
}
let sequences = Shell.WindowTracker.get_default().get_startup_sequences();
for (i = 0; i < sequences.length; i++) {
let index = sequences[i].get_workspace();
if (index >= 0 && index <= global.screen.n_workspaces)
emptyWorkspaces[index] = false;
}
let windows = global.get_window_actors();
for (i = 0; i < windows.length; i++) {
let win = windows[i];
if (win.get_meta_window().is_on_all_workspaces())
continue;
let workspaceIndex = win.get_workspace();
emptyWorkspaces[workspaceIndex] = false;
}
// If we don't have an empty workspace at the end, add one
if (!emptyWorkspaces[emptyWorkspaces.length -1]) {
global.screen.append_new_workspace(false, global.get_current_time());
emptyWorkspaces.push(false);
}
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
let removingCurrentWorkspace = (emptyWorkspaces[activeWorkspaceIndex] &&
activeWorkspaceIndex < emptyWorkspaces.length - 1);
// Don't enter the overview when removing multiple empty workspaces at startup
let showOverview = (removingCurrentWorkspace &&
!emptyWorkspaces.every(function(x) { return x; }));
if (removingCurrentWorkspace) {
// "Merge" the empty workspace we are removing with the one at the end
this._wm.blockAnimations();
}
// Delete other empty workspaces; do it from the end to avoid index changes
for (i = emptyWorkspaces.length - 2; i >= 0; i--) {
if (emptyWorkspaces[i])
global.screen.remove_workspace(this._workspaces[i], global.get_current_time());
}
if (removingCurrentWorkspace) {
global.screen.get_workspace_by_index(global.screen.n_workspaces - 1).activate(global.get_current_time());
this._wm.unblockAnimations();
if (!Main.overview.visible && showOverview)
Main.overview.show();
}
this._checkWorkspacesId = 0;
return false;
},
keepWorkspaceAlive: function(workspace, duration) {
if (workspace._keepAliveId)
Mainloop.source_remove(workspace._keepAliveId);
workspace._keepAliveId = Mainloop.timeout_add(duration, Lang.bind(this, function() {
workspace._keepAliveId = 0;
this._queueCheckWorkspaces();
return false;
}));
},
_windowRemoved: function(workspace, window) {
workspace._lastRemovedWindow = window;
this._queueCheckWorkspaces();
Mainloop.timeout_add(LAST_WINDOW_GRACE_TIME, Lang.bind(this, function() {
if (workspace._lastRemovedWindow == window) {
workspace._lastRemovedWindow = null;
this._queueCheckWorkspaces();
}
return false;
}));
},
_windowLeftMonitor: function(metaScreen, monitorIndex, metaWin) {
// If the window left the primary monitor, that
// might make that workspace empty
if (monitorIndex == Main.layoutManager.primaryIndex)
this._queueCheckWorkspaces();
},
_windowEnteredMonitor: function(metaScreen, monitorIndex, metaWin) {
// If the window entered the primary monitor, that
// might make that workspace non-empty
if (monitorIndex == Main.layoutManager.primaryIndex)
this._queueCheckWorkspaces();
},
_windowsRestacked: function() {
// Figure out where the pointer is in case we lost track of
// it during a grab. (In particular, if a trayicon popup menu
// is dismissed, see if we need to close the message tray.)
global.sync_pointer();
},
_queueCheckWorkspaces: function() {
if (this._checkWorkspacesId == 0)
this._checkWorkspacesId = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, this._checkWorkspaces));
},
_nWorkspacesChanged: function() {
let oldNumWorkspaces = this._workspaces.length;
let newNumWorkspaces = global.screen.n_workspaces;
if (oldNumWorkspaces == newNumWorkspaces)
return false;
let lostWorkspaces = [];
if (newNumWorkspaces > oldNumWorkspaces) {
let w;
// Assume workspaces are only added at the end
for (w = oldNumWorkspaces; w < newNumWorkspaces; w++)
this._workspaces[w] = global.screen.get_workspace_by_index(w);
for (w = oldNumWorkspaces; w < newNumWorkspaces; w++) {
let workspace = this._workspaces[w];
workspace._windowAddedId = workspace.connect('window-added', Lang.bind(this, this._queueCheckWorkspaces));
workspace._windowRemovedId = workspace.connect('window-removed', Lang.bind(this, this._windowRemoved));
}
} else {
// Assume workspaces are only removed sequentially
// (e.g. 2,3,4 - not 2,4,7)
let removedIndex;
let removedNum = oldNumWorkspaces - newNumWorkspaces;
for (let w = 0; w < oldNumWorkspaces; w++) {
let workspace = global.screen.get_workspace_by_index(w);
if (this._workspaces[w] != workspace) {
removedIndex = w;
break;
}
}
let lostWorkspaces = this._workspaces.splice(removedIndex, removedNum);
lostWorkspaces.forEach(function(workspace) {
workspace.disconnect(workspace._windowAddedId);
workspace.disconnect(workspace._windowRemovedId);
});
}
this._queueCheckWorkspaces();
return false;
}
});
const WindowManager = new Lang.Class({
Name: 'WindowManager',
@ -340,90 +136,6 @@ const WindowManager = new Lang.Class({
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('switch-to-workspace-1',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('switch-to-workspace-2',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('switch-to-workspace-3',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('switch-to-workspace-4',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('switch-to-workspace-5',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('switch-to-workspace-6',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('switch-to-workspace-7',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('switch-to-workspace-8',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('switch-to-workspace-9',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('switch-to-workspace-10',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('switch-to-workspace-11',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('switch-to-workspace-12',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('move-to-workspace-1',
Shell.KeyBindingMode.NORMAL,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('move-to-workspace-2',
Shell.KeyBindingMode.NORMAL,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('move-to-workspace-3',
Shell.KeyBindingMode.NORMAL,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('move-to-workspace-4',
Shell.KeyBindingMode.NORMAL,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('move-to-workspace-5',
Shell.KeyBindingMode.NORMAL,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('move-to-workspace-6',
Shell.KeyBindingMode.NORMAL,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('move-to-workspace-7',
Shell.KeyBindingMode.NORMAL,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('move-to-workspace-8',
Shell.KeyBindingMode.NORMAL,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('move-to-workspace-9',
Shell.KeyBindingMode.NORMAL,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('move-to-workspace-10',
Shell.KeyBindingMode.NORMAL,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('move-to-workspace-11',
Shell.KeyBindingMode.NORMAL,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('move-to-workspace-12',
Shell.KeyBindingMode.NORMAL,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('switch-applications',
Shell.KeyBindingMode.NORMAL,
Lang.bind(this, this._startAppSwitcher));
@ -460,9 +172,8 @@ const WindowManager = new Lang.Class({
this.addKeybinding('open-application-menu',
new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }),
Meta.KeyBindingFlags.NONE,
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.TOPBAR_POPUP,
Lang.bind(this, this._toggleAppMenu));
Shell.KeyBindingMode.NORMAL,
Lang.bind(this, this._openAppMenu));
Main.overview.connect('showing', Lang.bind(this, function() {
for (let i = 0; i < this._dimmedWindows.length; i++)
@ -472,19 +183,6 @@ const WindowManager = new Lang.Class({
for (let i = 0; i < this._dimmedWindows.length; i++)
this._dimWindow(this._dimmedWindows[i]);
}));
if (Main.sessionMode.hasWorkspaces)
this._workspaceTracker = new WorkspaceTracker(this);
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT,
false, -1, 1);
},
keepWorkspaceAlive: function(workspace, duration) {
if (!this._workspaceTracker)
return;
this._workspaceTracker.keepWorkspaceAlive(workspace, duration);
},
setCustomKeybindingHandler: function(name, modes, handler) {
@ -833,7 +531,7 @@ const WindowManager = new Lang.Class({
},
_switchWorkspace : function(shellwm, from, to, direction) {
if (!Main.sessionMode.hasWorkspaces || !this._shouldAnimate()) {
if (!this._shouldAnimate()) {
shellwm.completed_switch_workspace();
return;
}
@ -981,42 +679,27 @@ const WindowManager = new Lang.Class({
Main.ctrlAltTabManager.popup(backwards, binding.get_name(), binding.get_mask());
},
_toggleAppMenu : function(display, screen, window, event, binding) {
Main.panel.toggleAppMenu();
_openAppMenu : function(display, screen, window, event, binding) {
Main.panel.openAppMenu();
},
_showWorkspaceSwitcher : function(display, screen, window, binding) {
if (!Main.sessionMode.hasWorkspaces)
return;
if (screen.n_workspaces == 1)
return;
let [action,,,target] = binding.get_name().split('-');
let [action,,,direction] = binding.get_name().split('-');
let direction = Meta.MotionDirection[direction.toUpperCase()];
let newWs;
let direction;
if (isNaN(target)) {
direction = Meta.MotionDirection[target.toUpperCase()];
newWs = screen.get_active_workspace().get_neighbor(direction);
} else if (target > 0) {
target--;
newWs = screen.get_workspace_by_index(target);
if (screen.get_active_workspace().index() > target)
direction = Meta.MotionDirection.UP;
else
direction = Meta.MotionDirection.DOWN;
}
if (direction != Meta.MotionDirection.UP &&
direction != Meta.MotionDirection.DOWN)
return;
if (action == 'switch')
this.actionMoveWorkspace(newWs);
newWs = this.actionMoveWorkspace(direction);
else
this.actionMoveWindow(window, newWs);
newWs = this.actionMoveWindow(window, direction);
if (!Main.overview.visible) {
if (this._workspaceSwitcherPopup == null) {
@ -1029,31 +712,31 @@ const WindowManager = new Lang.Class({
}
},
actionMoveWorkspace: function(workspace) {
if (!Main.sessionMode.hasWorkspaces)
return;
actionMoveWorkspace: function(direction) {
let activeWorkspace = global.screen.get_active_workspace();
let toActivate = activeWorkspace.get_neighbor(direction);
if (activeWorkspace != workspace)
workspace.activate(global.get_current_time());
if (activeWorkspace != toActivate)
toActivate.activate(global.get_current_time());
return toActivate;
},
actionMoveWindow: function(window, workspace) {
if (!Main.sessionMode.hasWorkspaces)
return;
actionMoveWindow: function(window, direction) {
let activeWorkspace = global.screen.get_active_workspace();
let toActivate = activeWorkspace.get_neighbor(direction);
if (activeWorkspace != workspace) {
if (activeWorkspace != toActivate) {
// This won't have any effect for "always sticky" windows
// (like desktop windows or docks)
this._movingWindow = window;
window.change_workspace(workspace);
window.change_workspace(toActivate);
global.display.clear_mouse_mode();
workspace.activate_with_focus (window, global.get_current_time());
toActivate.activate_with_focus (window, global.get_current_time());
}
return toActivate;
},
});

View File

@ -127,7 +127,7 @@ const WindowClone = new Lang.Class({
if (this._stackAbove == null)
return null;
if (this.inDrag) {
if (this.inDrag || this._zooming) {
if (this._stackAbove._delegate)
return this._stackAbove._delegate.getActualStackAbove();
else
@ -453,10 +453,6 @@ const WindowOverlay = new Lang.Class({
metaWindow.delete(global.get_current_time());
},
_windowCanClose: function() {
return this._windowClone.metaWindow.can_close();
},
_onWindowAdded: function(workspace, win) {
let metaWindow = this._windowClone.metaWindow;
@ -492,14 +488,12 @@ const WindowOverlay = new Lang.Class({
_animateVisible: function() {
this._parentActor.raise_top();
if (this._windowCanClose()) {
this.closeButton.show();
this.closeButton.opacity = 0;
Tweener.addTween(this.closeButton,
{ opacity: 255,
time: CLOSE_BUTTON_FADE_TIME,
transition: 'easeOutQuad' });
}
this.closeButton.show();
this.closeButton.opacity = 0;
Tweener.addTween(this.closeButton,
{ opacity: 255,
time: CLOSE_BUTTON_FADE_TIME,
transition: 'easeOutQuad' });
this.border.show();
this.border.opacity = 0;
@ -759,6 +753,13 @@ const LayoutStrategy = new Lang.Class({
layout.space = space;
},
_getDistance: function (row, actor) {
let dist_x = actor.x - row.x;
let dist_y = actor.y - row.y;
return Math.sqrt(Math.pow(dist_x, 2) + Math.pow(dist_y, 2));
},
computeWindowSlots: function(layout, area) {
this._computeRowSizes(layout);
@ -766,36 +767,28 @@ const LayoutStrategy = new Lang.Class({
let slots = [];
// Do this in three parts.
let height = 0;
for (let i = 0; i < rows.length; i++) {
let row = rows[i];
height += row.height + this._rowSpacing;
}
height -= this._rowSpacing;
let y = 0;
for (let i = 0; i < rows.length; i++) {
let row = rows[i];
// If this window layout row doesn't fit in the actual
// geometry, then apply an additional scale to it.
row.additionalScale = Math.min(1, area.width / row.width, area.height / height);
row.x = area.x + (Math.max(area.width - row.width, 0) / 2) * row.additionalScale;
row.y = area.y + (y + Math.max(area.height - height, 0) / 2) * row.additionalScale;
row.x = area.x + (area.width - row.width) / 2;
row.y = area.y + y;
y += row.height + this._rowSpacing;
row.windows.sort(Lang.bind(this, function(a, b) {
return this._getDistance(row, a.realWindow) - this._getDistance(row, b.realWindow);
}));
}
let height = y - this._rowSpacing;
let baseY = (area.height - height) / 2;
for (let i = 0; i < rows.length; i++) {
let row = rows[i];
row.y += baseY;
let x = row.x;
for (let j = 0; j < row.windows.length; j++) {
let window = row.windows[j];
let s = scale * this._computeWindowScale(window) * row.additionalScale;
let s = scale * this._computeWindowScale(window);
let cellWidth = window.actor.width * s;
let cellHeight = window.actor.height * s;
@ -839,13 +832,6 @@ const UnalignedLayoutStrategy = new Lang.Class({
return false;
},
_sortRow: function(row) {
// Sort windows horizontally to minimize travel distance
row.windows.sort(function(a, b) {
return a.realWindow.x - b.realWindow.x;
});
},
computeLayout: function(windows, layout) {
let numRows = layout.numRows;
@ -876,7 +862,6 @@ const UnalignedLayoutStrategy = new Lang.Class({
row.windows.push(window);
row.fullWidth += width;
} else {
this._sortRow(row);
break;
}
}
@ -898,14 +883,6 @@ const UnalignedLayoutStrategy = new Lang.Class({
}
});
function padArea(area, padding) {
return {
x: area.x + padding.left,
y: area.y + padding.top,
width: area.width - padding.left - padding.right,
height: area.height - padding.top - padding.bottom,
};
}
/**
* @metaWorkspace: a #Meta.Workspace, or null
@ -917,19 +894,10 @@ const Workspace = new Lang.Class({
// When dragging a window, we use this slot for reserve space.
this._reservedSlot = null;
this.metaWorkspace = metaWorkspace;
// The full geometry is the geometry we should try and position
// windows for. The actual geometry we allocate may be less than
// this, like if the workspace switcher is slid out.
this._fullGeometry = null;
// The actual geometry is the geometry we need to arrange windows
// in. If this is a smaller area than the full geometry, we'll
// do some simple aspect ratio like math to fit the layout calculated
// for the full geometry into this area.
this._actualGeometry = null;
this._currentLayout = null;
this._x = 0;
this._y = 0;
this._width = 0;
this._height = 0;
this.monitorIndex = monitorIndex;
this._monitor = Main.layoutManager.monitors[this.monitorIndex];
@ -942,7 +910,7 @@ const Workspace = new Lang.Class({
this.actor.add_style_class_name('external-monitor');
this.actor.set_size(0, 0);
this._dropRect = new Clutter.Actor({ opacity: 0 });
this._dropRect = new Clutter.Rectangle({ opacity: 0 });
this._dropRect._delegate = this;
this.actor.add_actor(this._dropRect);
@ -979,29 +947,23 @@ const Workspace = new Lang.Class({
this._positionWindowsFlags = 0;
this._positionWindowsId = 0;
this._currentLayout = null;
},
setFullGeometry: function(geom) {
this._fullGeometry = geom;
this._recalculateWindowPositions(WindowPositionFlags.NONE);
},
setGeometry: function(x, y, width, height) {
this._x = x;
this._y = y;
this._width = width;
this._height = height;
setActualGeometry: function(geom) {
this._actualGeometry = geom;
if (this._actualGeometryLater)
return;
this._actualGeometryLater = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
let geom = this._actualGeometry;
this._dropRect.set_position(geom.x, geom.y);
this._dropRect.set_size(geom.width, geom.height);
this._updateWindowPositions(Main.overview.animationInProgress ? WindowPositionFlags.ANIMATE : WindowPositionFlags.NONE);
this._actualGeometryLater = 0;
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
this._dropRect.set_position(x, y);
this._dropRect.set_size(width, height);
return false;
}));
this.positionWindows(WindowPositionFlags.NONE);
},
_lookupIndex: function (metaWindow) {
@ -1029,32 +991,37 @@ const Workspace = new Lang.Class({
clone = null;
this._reservedSlot = clone;
this._recalculateWindowPositions(WindowPositionFlags.ANIMATE);
this._currentLayout = null;
this.positionWindows(WindowPositionFlags.ANIMATE);
},
_recalculateWindowPositions: function(flags) {
/**
* positionWindows:
* @flags:
* INITIAL - this is the initial positioning of the windows.
* ANIMATE - Indicates that we need animate changing position.
*/
positionWindows: function(flags) {
this._positionWindowsFlags |= flags;
if (this._positionWindowsId > 0)
return;
this._positionWindowsId = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
this._realRecalculateWindowPositions(this._positionWindowsFlags);
this._realPositionWindows(this._positionWindowsFlags);
this._positionWindowsFlags = 0;
this._positionWindowsId = 0;
return false;
}));
},
_realRecalculateWindowPositions: function(flags) {
_realPositionWindows : function(flags) {
if (this._repositionWindowsId > 0) {
Mainloop.source_remove(this._repositionWindowsId);
this._repositionWindowsId = 0;
}
let clones = this._windows.slice();
if (clones.length == 0)
return;
clones.sort(function(a, b) {
return a.metaWindow.get_stable_sequence() - b.metaWindow.get_stable_sequence();
@ -1063,25 +1030,11 @@ const Workspace = new Lang.Class({
if (this._reservedSlot)
clones.push(this._reservedSlot);
this._currentLayout = this._computeLayout(clones);
this._updateWindowPositions(flags);
},
_updateWindowPositions: function(flags) {
if (this._currentLayout == null) {
this._recalculateWindowPositions(flags);
return;
}
let initialPositioning = flags & WindowPositionFlags.INITIAL;
let animate = flags & WindowPositionFlags.ANIMATE;
let layout = this._currentLayout;
let strategy = layout.strategy;
let [, , padding] = this._getSpacingAndPadding();
let area = padArea(this._actualGeometry, padding);
let slots = strategy.computeWindowSlots(layout, area);
// Start the animations
let slots = this._computeAllWindowSlots(clones);
let currentWorkspace = global.screen.get_active_workspace();
let isOnCurrentWorkspace = this.metaWorkspace == null || this.metaWorkspace == currentWorkspace;
@ -1195,8 +1148,8 @@ const Workspace = new Lang.Class({
let [x, y, mask] = global.get_pointer();
let pointerHasMoved = (this._cursorX != x && this._cursorY != y);
let inWorkspace = (this._fullGeometry.x < x && x < this._fullGeometry.x + this._fullGeometry.width &&
this._fullGeometry.y < y && y < this._fullGeometry.y + this._fullGeometry.height);
let inWorkspace = (this._x < x && x < this._x + this._width &&
this._y < y && y < this._y + this._height);
if (pointerHasMoved && inWorkspace) {
// store current cursor position
@ -1211,7 +1164,7 @@ const Workspace = new Lang.Class({
return true;
}
this._recalculateWindowPositions(WindowPositionFlags.ANIMATE);
this.positionWindows(WindowPositionFlags.ANIMATE);
return false;
},
@ -1316,7 +1269,7 @@ const Workspace = new Lang.Class({
}
this._currentLayout = null;
this._recalculateWindowPositions(WindowPositionFlags.ANIMATE);
this.positionWindows(WindowPositionFlags.ANIMATE);
},
_windowAdded : function(metaWorkspace, metaWin) {
@ -1353,8 +1306,13 @@ const Workspace = new Lang.Class({
// Animate the full-screen to Overview transition.
zoomToOverview : function() {
this._currentLayout = null;
// Position and scale the windows.
this._recalculateWindowPositions(WindowPositionFlags.ANIMATE | WindowPositionFlags.INITIAL);
if (Main.overview.animationInProgress)
this.positionWindows(WindowPositionFlags.ANIMATE | WindowPositionFlags.INITIAL);
else
this.positionWindows(WindowPositionFlags.INITIAL);
},
// Animates the return from Overview mode
@ -1478,7 +1436,7 @@ const Workspace = new Lang.Class({
}));
clone.connect('size-changed',
Lang.bind(this, function() {
this._recalculateWindowPositions(WindowPositionFlags.NONE);
this.positionWindows(0);
}));
this.actor.add_actor(clone.actor);
@ -1527,14 +1485,12 @@ const Workspace = new Lang.Class({
}
},
_getBestLayout: function(windows, area, rowSpacing, columnSpacing) {
_computeLayout: function(windows, area, rowSpacing, columnSpacing) {
// We look for the largest scale that allows us to fit the
// largest row/tallest column on the workspace.
let lastLayout = {};
let strategy = new UnalignedLayoutStrategy(this._monitor, rowSpacing, columnSpacing);
for (let numRows = 1; ; numRows++) {
let numColumns = Math.ceil(windows.length / numRows);
@ -1544,6 +1500,8 @@ const Workspace = new Lang.Class({
if (numColumns == lastLayout.numColumns)
break;
let strategy = new UnalignedLayoutStrategy(this._monitor, rowSpacing, columnSpacing);
let layout = { area: area, strategy: strategy, numRows: numRows, numColumns: numColumns };
strategy.computeLayout(windows, layout);
strategy.computeScaleAndSpace(layout);
@ -1557,7 +1515,18 @@ const Workspace = new Lang.Class({
return lastLayout;
},
_getSpacingAndPadding: function() {
_rectEqual: function(one, two) {
if (one == two)
return true;
return (one.x == two.x &&
one.y == two.y &&
one.width == two.width &&
one.height == two.height);
},
_computeAllWindowSlots: function(windows) {
let totalWindows = windows.length;
let node = this.actor.get_theme_node();
// Window grid spacing
@ -1570,14 +1539,21 @@ const Workspace = new Lang.Class({
right: node.get_padding(St.Side.RIGHT),
};
if (!totalWindows)
return [];
let closeButtonHeight, captionHeight;
let leftBorder, rightBorder;
// All of the overlays have the same chrome sizes,
// so just pick the first one.
let overlay = this._windowOverlays[0];
[closeButtonHeight, captionHeight] = overlay.chromeHeights();
[leftBorder, rightBorder] = overlay.chromeWidths();
if (this._windowOverlays.length) {
// All of the overlays have the same chrome sizes,
// so just pick the first one.
let overlay = this._windowOverlays[0];
[closeButtonHeight, captionHeight] = overlay.chromeHeights();
[leftBorder, rightBorder] = overlay.chromeWidths();
} else {
[closeButtonHeight, captionHeight] = [0, 0];
[leftBorder, rightBorder] = [0, 0];
}
rowSpacing += captionHeight;
columnSpacing += (rightBorder + leftBorder) / 2;
@ -1586,13 +1562,25 @@ const Workspace = new Lang.Class({
padding.left += leftBorder;
padding.right += rightBorder;
return [rowSpacing, columnSpacing, padding];
},
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,
};
_computeLayout: function(windows) {
let [rowSpacing, columnSpacing, padding] = this._getSpacingAndPadding();
let area = padArea(this._fullGeometry, padding);
return this._getBestLayout(windows, area, rowSpacing, columnSpacing);
if (!this._currentLayout)
this._currentLayout = this._computeLayout(windows, area, rowSpacing, columnSpacing);
let layout = this._currentLayout;
let strategy = layout.strategy;
if (!this._rectEqual(area, layout.area)) {
layout.area = area;
strategy.computeScaleAndSpace(layout);
}
return strategy.computeWindowSlots(layout, area);
},
_onCloneSelected : function (clone, time) {

View File

@ -764,8 +764,8 @@ const ThumbnailsBox = new Lang.Class({
// to open its first window within some time, as tracked by Shell.WindowTracker.
// Here, we only add a very brief timeout to avoid the _immediate_ removal of the
// workspace while we wait for the startup sequence to load.
Main.wm.keepWorkspaceAlive(global.screen.get_workspace_by_index(newWorkspaceIndex),
WORKSPACE_KEEP_ALIVE_TIME);
Main.keepWorkspaceAlive(global.screen.get_workspace_by_index(newWorkspaceIndex),
WORKSPACE_KEEP_ALIVE_TIME);
}
// Start the animation on the workspace (which is actually

View File

@ -23,18 +23,6 @@ const MAX_WORKSPACES = 16;
const OVERRIDE_SCHEMA = 'org.gnome.shell.overrides';
function rectEqual(one, two) {
if (one == two)
return true;
if (!one || !two)
return false;
return (one.x == two.x &&
one.y == two.y &&
one.width == two.width &&
one.height == two.height);
}
const WorkspacesView = new Lang.Class({
Name: 'WorkspacesView',
@ -55,9 +43,10 @@ const WorkspacesView = new Lang.Class({
this._updateWorkspaceActors(false);
}));
this._fullGeometry = null;
this._actualGeometry = null;
this._width = 0;
this._height = 0;
this._x = 0;
this._y = 0;
this._spacing = 0;
this._animating = false; // tweening
this._scrolling = false; // swipe-scrolling
@ -96,8 +85,8 @@ const WorkspacesView = new Lang.Class({
this._overviewShownId =
Main.overview.connect('shown',
Lang.bind(this, function() {
this.actor.set_clip(this._fullGeometry.x, this._fullGeometry.y,
this._fullGeometry.width, this._fullGeometry.height);
this.actor.set_clip(this._x, this._y,
this._width, this._height);
}));
this.scrollAdjustment = new St.Adjustment({ value: activeWorkspaceIndex,
@ -135,9 +124,11 @@ const WorkspacesView = new Lang.Class({
continue;
let ws = new Workspace.Workspace(null, i);
ws.setFullGeometry(monitors[i]);
ws.setActualGeometry(monitors[i]);
Main.layoutManager.overviewGroup.add_actor(ws.actor);
ws.setGeometry(monitors[i].x,
monitors[i].y,
monitors[i].width,
monitors[i].height);
global.overlay_group.add_actor(ws.actor);
this._extraWorkspaces.push(ws);
}
},
@ -148,24 +139,18 @@ const WorkspacesView = new Lang.Class({
this._extraWorkspaces = [];
},
setFullGeometry: function(geom) {
if (rectEqual(this._fullGeometry, geom))
return;
setGeometry: function(x, y, width, height) {
if (this._x == x && this._y == y &&
this._width == width && this._height == height)
return;
this._fullGeometry = geom;
this._width = width;
this._height = height;
this._x = x;
this._y = y;
for (let i = 0; i < this._workspaces.length; i++)
this._workspaces[i].setFullGeometry(geom);
},
setActualGeometry: function(geom) {
if (rectEqual(this._actualGeometry, geom))
return;
this._actualGeometry = geom;
for (let i = 0; i < this._workspaces.length; i++)
this._workspaces[i].setActualGeometry(geom);
this._workspaces[i].setGeometry(x, y, width, height);
},
_lookupWorkspaceForMetaWindow: function (metaWindow) {
@ -225,7 +210,7 @@ const WorkspacesView = new Lang.Class({
Tweener.removeTweens(workspace.actor);
let y = (w - active) * (this._fullGeometry.height + this._spacing);
let y = (w - active) * (this._height + this._spacing);
if (showAnimation) {
let params = { y: y,
@ -296,9 +281,8 @@ const WorkspacesView = new Lang.Class({
if (newNumWorkspaces > oldNumWorkspaces) {
for (let w = oldNumWorkspaces; w < newNumWorkspaces; w++) {
this._workspaces[w].setFullGeometry(this._fullGeometry);
if (this._actualGeometry)
this._workspaces[w].setActualGeometry(this._actualGeometry);
this._workspaces[w].setGeometry(this._x, this._y,
this._width, this._height);
this.actor.add_actor(this._workspaces[w].actor);
}
@ -446,7 +430,7 @@ const WorkspacesDisplay = new Lang.Class({
_init: function() {
this.actor = new St.Widget({ clip_to_allocation: true });
this.actor.connect('notify::allocation', Lang.bind(this, this._updateWorkspacesActualGeometry));
this.actor.connect('notify::allocation', Lang.bind(this, this._updateWorkspacesGeometry));
this.actor.connect('parent-set', Lang.bind(this, this._parentSet));
let clickAction = new Clutter.ClickAction()
@ -500,8 +484,6 @@ const WorkspacesDisplay = new Lang.Class({
this._notifyOpacityId = 0;
this._scrollEventId = 0;
this._fullGeometry = null;
},
_onPan: function(action) {
@ -590,11 +572,10 @@ const WorkspacesDisplay = new Lang.Class({
this._workspacesViews.push(view);
}
this._updateWorkspacesFullGeometry();
this._updateWorkspacesActualGeometry();
this._updateWorkspacesGeometry();
for (let i = 0; i < this._workspacesViews.length; i++)
Main.layoutManager.overviewGroup.add_actor(this._workspacesViews[i].actor);
global.overlay_group.add_actor(this._workspacesViews[i].actor);
},
_scrollValueChanged: function() {
@ -638,7 +619,7 @@ const WorkspacesDisplay = new Lang.Class({
// This is kinda hackish - we want the primary view to
// appear as parent of this.actor, though in reality it
// is added directly to Main.layoutManager.overviewGroup
// is added directly to overlay_group
this._notifyOpacityId = newParent.connect('notify::opacity',
Lang.bind(this, function() {
let opacity = this.actor.get_parent().opacity;
@ -651,48 +632,31 @@ const WorkspacesDisplay = new Lang.Class({
}));
},
// This geometry should always be the fullest geometry
// the workspaces switcher can ever be allocated, as if
// the sliding controls were never slid in at all.
setWorkspacesFullGeometry: function(geom) {
this._fullGeometry = geom;
this._updateWorkspacesFullGeometry();
},
_updateWorkspacesFullGeometry: function() {
_updateWorkspacesGeometry: function() {
if (!this._workspacesViews.length)
return;
let monitors = Main.layoutManager.monitors;
let m = 0;
for (let i = 0; i < monitors.length; i++) {
if (i == this._primaryIndex) {
this._workspacesViews[m].setFullGeometry(this._fullGeometry);
m++;
} else if (!this._workspacesOnlyOnPrimary) {
this._workspacesViews[m].setFullGeometry(monitors[i]);
m++;
}
}
},
let fullWidth = this.actor.allocation.x2 - this.actor.allocation.x1;
let fullHeight = this.actor.allocation.y2 - this.actor.allocation.y1;
_updateWorkspacesActualGeometry: function() {
if (!this._workspacesViews.length)
return;
let width = fullWidth;
let height = fullHeight;
let [x, y] = this.actor.get_transformed_position();
let width = this.actor.allocation.x2 - this.actor.allocation.x1;
let height = this.actor.allocation.y2 - this.actor.allocation.y1;
let geometry = { x: x, y: y, width: width, height: height };
let rtl = (Clutter.get_default_text_direction () == Clutter.TextDirection.RTL);
let monitors = Main.layoutManager.monitors;
let m = 0;
for (let i = 0; i < monitors.length; i++) {
if (i == this._primaryIndex) {
this._workspacesViews[m].setActualGeometry(geometry);
this._workspacesViews[m].setGeometry(x, y, width, height);
m++;
} else if (!this._workspacesOnlyOnPrimary) {
this._workspacesViews[m].setActualGeometry(monitors[i]);
this._workspacesViews[m].setGeometry(monitors[i].x,
monitors[i].y,
monitors[i].width,
monitors[i].height);
m++;
}
}
@ -760,20 +724,15 @@ const WorkspacesDisplay = new Lang.Class({
_onScrollEvent: function(actor, event) {
if (!this.actor.mapped)
return false;
let activeWs = global.screen.get_active_workspace();
let ws;
switch (event.get_scroll_direction()) {
case Clutter.ScrollDirection.UP:
ws = activeWs.get_neighbor(Meta.MotionDirection.UP);
break;
Main.wm.actionMoveWorkspace(Meta.MotionDirection.UP);
return true;
case Clutter.ScrollDirection.DOWN:
ws = activeWs.get_neighbor(Meta.MotionDirection.DOWN);
break;
default:
return false;
Main.wm.actionMoveWorkspace(Meta.MotionDirection.DOWN);
return true;
}
Main.wm.actionMoveWorkspace(ws);
return true;
return false;
}
});
Signals.addSignalMethods(WorkspacesDisplay.prototype);

View File

@ -16,7 +16,7 @@ const XdndHandler = new Lang.Class({
this._cursorWindowClone = null;
// Used as a drag actor in case we don't have a cursor window clone
this._dummy = new Clutter.Actor({ width: 1, height: 1, opacity: 0 });
this._dummy = new Clutter.Rectangle({ width: 1, height: 1, opacity: 0 });
Main.uiGroup.add_actor(this._dummy);
Shell.util_set_hidden_from_pick(this._dummy, true);
this._dummy.hide();

View File

@ -125,12 +125,6 @@
<listitem><para>List possible modes and exit</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--clutter-display=<replaceable>DISPLAY</replaceable></option></term>
<listitem><para>Clutter the option display (otherwise ignored)</para></listitem>
</varlistentry>
</variablelist>
</refsect1>

View File

@ -28,7 +28,6 @@ gu
he
hi
hu
ia
id
it
ja

View File

@ -7,7 +7,6 @@ data/gnome-shell.desktop.in.in
data/gnome-shell-extension-prefs.desktop.in.in
data/org.gnome.shell.gschema.xml.in.in
js/extensionPrefs/main.js
js/gdm/authPrompt.js
js/gdm/loginDialog.js
js/gdm/powerMenu.js
js/gdm/util.js
@ -49,14 +48,13 @@ js/ui/status/keyboard.js
js/ui/status/lockScreenMenu.js
js/ui/status/network.js
js/ui/status/power.js
js/ui/status/system.js
js/ui/status/volume.js
js/ui/unlockDialog.js
js/ui/userMenu.js
js/ui/viewSelector.js
js/ui/wanda.js
js/ui/windowAttentionHandler.js
src/calendar-server/evolution-calendar.desktop.in.in
# Please do not remove this file from POTFILES.in. Run "git submodule init && git submodule update" to get it.
src/gvc/gvc-mixer-control.c
src/main.c
src/shell-app.c

777
po/bg.po

File diff suppressed because it is too large Load Diff

View File

@ -11,7 +11,7 @@ msgstr ""
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
"shell&keywords=I18N+L10N&component=general\n"
"POT-Creation-Date: 2013-03-11 21:29+0000\n"
"PO-Revision-Date: 2013-05-26 08:21+0200\n"
"PO-Revision-Date: 2013-03-11 00:03+0100\n"
"Last-Translator: Gil Forcada <gilforcada@guifi.net>\n"
"Language-Team: Catalan <tradgnome@softcatala.org>\n"
"Language: ca\n"
@ -1048,7 +1048,7 @@ msgstr "Obre els rellotges"
#: ../js/ui/dateMenu.js:105
msgid "Date & Time Settings"
msgstr "Configuració de la data i de l'hora"
msgstr "Configuració de la data i l'hora"
#. Translators: This is the date format to use when the calendar popup is
#. * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").

842
po/cs.po

File diff suppressed because it is too large Load Diff

926
po/de.po

File diff suppressed because it is too large Load Diff

239
po/el.po
View File

@ -5,8 +5,8 @@ msgstr ""
"Project-Id-Version: gnome-shell.po.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: 2013-04-19 02:48+0000\n"
"PO-Revision-Date: 2013-04-25 08:11+0300\n"
"POT-Creation-Date: 2013-03-02 23:02+0000\n"
"PO-Revision-Date: 2013-03-03 13:22+0300\n"
"Last-Translator: Dimitris Spingos (Δημήτρης Σπίγγος) <dmtrs32@gmail.com>\n"
"Language-Team: team@gnome.gr\n"
"Language: el\n"
@ -40,14 +40,10 @@ msgid "Focus the active notification"
msgstr "Εστίαση στην ενεργή ειδοποίηση"
#: ../data/50-gnome-shell-system.xml.in.h:4
msgid "Show the overview"
msgstr "Εμφάνιση της επισκόπησης"
#: ../data/50-gnome-shell-system.xml.in.h:5
msgid "Show all applications"
msgstr "Προβολή όλων των εφαρμογών"
#: ../data/50-gnome-shell-system.xml.in.h:6
#: ../data/50-gnome-shell-system.xml.in.h:5
msgid "Open the application menu"
msgstr "Άνοιγμα του μενού εφαρμογών"
@ -227,55 +223,45 @@ msgstr ""
"επισκόπησης ενεργειών."
#: ../data/org.gnome.shell.gschema.xml.in.in.h:25
#| msgid "Keybinding to open the \"Show Applications\" view"
msgid "Keybinding to open the overview"
msgstr "Συνδυασμός πλήκτρων για το άνοιγμα της επισκόπησης"
#: ../data/org.gnome.shell.gschema.xml.in.in.h:26
#| msgid "Keybinding to open the \"Show Applications\" view"
msgid "Keybinding to open the Activities Overview."
msgstr "Συνδυασμός πλήκτρων για το άνοιγμα της προβολής ενέργειες."
#: ../data/org.gnome.shell.gschema.xml.in.in.h:27
msgid "Keybinding to toggle the visibility of the message tray"
msgstr "Συνδυασμός πλήκτρων για την ορατότητα της περιοχής ειδοποιήσεων"
#: ../data/org.gnome.shell.gschema.xml.in.in.h:28
#: ../data/org.gnome.shell.gschema.xml.in.in.h:26
msgid "Keybinding to toggle the visibility of the message tray."
msgstr "Συνδυασμός πλήκτρων για την ορατότητα της περιοχής ειδοποιήσεων."
#: ../data/org.gnome.shell.gschema.xml.in.in.h:29
#: ../data/org.gnome.shell.gschema.xml.in.in.h:27
msgid "Keybinding to focus the active notification"
msgstr "Ο συνδυασμός πλήκτρων για εστίαση της ενεργής ειδοποίησης"
#: ../data/org.gnome.shell.gschema.xml.in.in.h:30
#: ../data/org.gnome.shell.gschema.xml.in.in.h:28
msgid "Keybinding to focus the active notification."
msgstr "Ο συνδυασμός πλήκτρων για εστίαση της ενεργής ειδοποίησης."
#: ../data/org.gnome.shell.gschema.xml.in.in.h:31
#: ../data/org.gnome.shell.gschema.xml.in.in.h:29
msgid "Keybinding to toggle the screen recorder"
msgstr "Συνδυασμός πλήκτρων για την εναλλαγή της μαγνητοσκόπησης οθόνης"
#: ../data/org.gnome.shell.gschema.xml.in.in.h:32
#: ../data/org.gnome.shell.gschema.xml.in.in.h:30
msgid "Keybinding to start/stop the builtin screen recorder."
msgstr ""
"Συνδυασμός πλήκτρων για το άνοιγμα/κλείσιμο της ενσωματωμένης "
"μαγνητοσκόπησης οθόνης."
#: ../data/org.gnome.shell.gschema.xml.in.in.h:33
#: ../data/org.gnome.shell.gschema.xml.in.in.h:31
msgid "Which keyboard to use"
msgstr "Ποιο πληκτρολόγιο θα χρησιμοποιηθεί"
#: ../data/org.gnome.shell.gschema.xml.in.in.h:34
#: ../data/org.gnome.shell.gschema.xml.in.in.h:32
msgid "The type of keyboard to use."
msgstr "Ο τύπος του πληκτρολογίου που θα χρησιμοποιηθεί."
#: ../data/org.gnome.shell.gschema.xml.in.in.h:35
#: ../data/org.gnome.shell.gschema.xml.in.in.h:33
msgid "Framerate used for recording screencasts."
msgstr ""
"Ο ρυθμός καρέ που θα χρησιμοποιηθεί για την καταγραφή του βίντεο οθόνης."
#: ../data/org.gnome.shell.gschema.xml.in.in.h:36
#: ../data/org.gnome.shell.gschema.xml.in.in.h:34
msgid ""
"The framerate of the resulting screencast recordered by GNOME Shell's "
"screencast recorder in frames-per-second."
@ -283,13 +269,13 @@ msgstr ""
"Ο ρυθμός καρέ του στιγμιότυπου που παράγεται από τον εγγραφέα βίντεο οθόνης "
"του GNOME Shell σε καρέ ανά δευτερόλεπτο."
#: ../data/org.gnome.shell.gschema.xml.in.in.h:37
#: ../data/org.gnome.shell.gschema.xml.in.in.h:35
msgid "The gstreamer pipeline used to encode the screencast"
msgstr ""
"Ο δίαυλος του gstreamer που χρησιμοποιήθηκε για την κωδικοποίηση του βίντεο "
"οθόνης"
#: ../data/org.gnome.shell.gschema.xml.in.in.h:39
#: ../data/org.gnome.shell.gschema.xml.in.in.h:37
#, no-c-format
msgid ""
"Sets the GStreamer pipeline used to encode recordings. It follows the syntax "
@ -317,12 +303,12 @@ msgstr ""
"χρησιμοποιείται ως παράδειγμα για το πιθανό βέλτιστο αριθμό πυρήνων του "
"συστήματος."
#: ../data/org.gnome.shell.gschema.xml.in.in.h:40
#: ../data/org.gnome.shell.gschema.xml.in.in.h:38
msgid "File extension used for storing the screencast"
msgstr ""
"Επέκταση αρχείου που θα χρησιμοποιηθεί για την αποθήκευση του βίντεο οθόνης"
#: ../data/org.gnome.shell.gschema.xml.in.in.h:41
#: ../data/org.gnome.shell.gschema.xml.in.in.h:39
msgid ""
"The filename for recorded screencasts will be a unique filename based on the "
"current date, and use this extension. It should be changed when recording to "
@ -332,11 +318,11 @@ msgstr ""
"βασισμένο στην τρέχουσα ημερομηνία και θα χρησιμοποιεί αυτή την επέκταση. Θα "
"πρέπει να αλλάζει όταν γίνεται εγγραφή σε διαφορετικό πρότυπο περιέκτη."
#: ../data/org.gnome.shell.gschema.xml.in.in.h:42
#: ../data/org.gnome.shell.gschema.xml.in.in.h:40
msgid "The application icon mode."
msgstr "Η κατάσταση εικονιδίου εφαρμογής."
#: ../data/org.gnome.shell.gschema.xml.in.in.h:43
#: ../data/org.gnome.shell.gschema.xml.in.in.h:41
msgid ""
"Configures how the windows are shown in the switcher. Valid possibilities "
"are 'thumbnail-only' (shows a thumbnail of the window), 'app-icon-"
@ -347,22 +333,22 @@ msgstr ""
"παραθύρου), 'app-icon-only' (εμφανίζει μόνο το εικονίδιο της εφαρμογής) ή "
"'both' - και τα δύο."
#: ../data/org.gnome.shell.gschema.xml.in.in.h:44
#: ../data/org.gnome.shell.gschema.xml.in.in.h:42
msgid "Attach modal dialog to the parent window"
msgstr "Προσάρτηση αναγκαστικού διαλόγου στο γονικό παράθυρο"
#: ../data/org.gnome.shell.gschema.xml.in.in.h:45
#: ../data/org.gnome.shell.gschema.xml.in.in.h:43
msgid ""
"This key overrides the key in org.gnome.mutter when running GNOME Shell."
msgstr ""
"Αυτό το κλειδί υπερισχύει του κλειδιού στο org.gnome.mutter όταν εκτελείται "
"το κέλυφος GNOME."
#: ../data/org.gnome.shell.gschema.xml.in.in.h:46
#: ../data/org.gnome.shell.gschema.xml.in.in.h:44
msgid "Arrangement of buttons on the titlebar"
msgstr "Διάταξη των κουμπιών στη γραμμή τίτλου"
#: ../data/org.gnome.shell.gschema.xml.in.in.h:47
#: ../data/org.gnome.shell.gschema.xml.in.in.h:45
msgid ""
"This key overrides the key in org.gnome.desktop.wm.preferences when running "
"GNOME Shell."
@ -370,17 +356,17 @@ msgstr ""
"Αυτό το κλειδί υπερισχύει του κλειδιού στο org.gnome.desktop.wm.preferences "
"όταν εκτελείται το κέλυφος GNOME."
#: ../data/org.gnome.shell.gschema.xml.in.in.h:48
#: ../data/org.gnome.shell.gschema.xml.in.in.h:46
msgid "Enable edge tiling when dropping windows on screen edges"
msgstr ""
"Ενεργοποίηση της παράθεσης άκρων όταν αποθέτετε παράθυρα στις άκρες της "
"οθόνης"
#: ../data/org.gnome.shell.gschema.xml.in.in.h:49
#: ../data/org.gnome.shell.gschema.xml.in.in.h:47
msgid "Workspaces are managed dynamically"
msgstr "Οι χώροι εργασίας διαχειρίζονται δυναμικά"
#: ../data/org.gnome.shell.gschema.xml.in.in.h:50
#: ../data/org.gnome.shell.gschema.xml.in.in.h:48
msgid "Workspaces only on primary monitor"
msgstr "Χώροι εργασίας μόνο στην κύρια οθόνη"
@ -402,42 +388,43 @@ msgstr ""
"πολλαπλών επιλογών."
#: ../js/gdm/loginDialog.js:405
#| msgid "Session..."
msgid "Session…"
msgstr "Συνεδρία…"
#. translators: this message is shown below the user list on the
#. login screen. It can be activated to reveal an entry for
#. manually entering the username.
#: ../js/gdm/loginDialog.js:630
#: ../js/gdm/loginDialog.js:629
msgid "Not listed?"
msgstr "Δεν είστε στη λίστα;"
#: ../js/gdm/loginDialog.js:787 ../js/ui/components/networkAgent.js:137
#: ../js/ui/components/polkitAgent.js:162 ../js/ui/endSessionDialog.js:376
#: ../js/gdm/loginDialog.js:783 ../js/ui/components/networkAgent.js:137
#: ../js/ui/components/polkitAgent.js:162 ../js/ui/endSessionDialog.js:375
#: ../js/ui/extensionDownloader.js:195 ../js/ui/shellMountOperation.js:399
#: ../js/ui/status/bluetooth.js:415 ../js/ui/unlockDialog.js:100
#: ../js/ui/userMenu.js:938
#: ../js/ui/status/bluetooth.js:415 ../js/ui/unlockDialog.js:126
#: ../js/ui/userMenu.js:932
msgid "Cancel"
msgstr "Ακύρωση"
#: ../js/gdm/loginDialog.js:803
#: ../js/gdm/loginDialog.js:799
msgctxt "button"
msgid "Sign In"
msgstr "Σύνδεση"
#: ../js/gdm/loginDialog.js:803
#: ../js/gdm/loginDialog.js:799
msgid "Next"
msgstr "Επόμενο"
#. TTLS and PEAP are actually much more complicated, but this complication
#. is not visible here since we only care about phase2 authentication
#. (and don't even care of which one)
#: ../js/gdm/loginDialog.js:918 ../js/ui/components/networkAgent.js:260
#: ../js/gdm/loginDialog.js:904 ../js/ui/components/networkAgent.js:260
#: ../js/ui/components/networkAgent.js:278
msgid "Username: "
msgstr "Όνομα χρήστη: "
#: ../js/gdm/loginDialog.js:1174
#: ../js/gdm/loginDialog.js:1157
msgid "Login Window"
msgstr "Παράθυρο σύνδεσης"
@ -446,8 +433,8 @@ msgstr "Παράθυρο σύνδεσης"
msgid "Power"
msgstr "Ενέργεια"
#: ../js/gdm/powerMenu.js:93 ../js/ui/userMenu.js:696 ../js/ui/userMenu.js:700
#: ../js/ui/userMenu.js:816
#: ../js/gdm/powerMenu.js:93 ../js/ui/userMenu.js:694 ../js/ui/userMenu.js:698
#: ../js/ui/userMenu.js:814
msgid "Suspend"
msgstr "Αναστολή"
@ -455,58 +442,58 @@ msgstr "Αναστολή"
msgid "Restart"
msgstr "Επανεκκίνηση"
#: ../js/gdm/powerMenu.js:103 ../js/ui/userMenu.js:698
#: ../js/ui/userMenu.js:700 ../js/ui/userMenu.js:815 ../js/ui/userMenu.js:942
#: ../js/gdm/powerMenu.js:103 ../js/ui/userMenu.js:696
#: ../js/ui/userMenu.js:698 ../js/ui/userMenu.js:813 ../js/ui/userMenu.js:936
msgid "Power Off"
msgstr "Απενεργοποίηση"
#: ../js/gdm/util.js:249
#: ../js/gdm/util.js:182
msgid "Authentication error"
msgstr "Σφάλμα πιστοποίησης"
#. Translators: this message is shown below the password entry field
#. to indicate the user can swipe their finger instead
#: ../js/gdm/util.js:366
#: ../js/gdm/util.js:299
msgid "(or swipe finger)"
msgstr "(ή περάστε το δάκτυλο σας)"
#: ../js/gdm/util.js:391
#: ../js/gdm/util.js:324
#, c-format
msgid "(e.g., user or %s)"
msgstr "(π.χ χρήστης ή %s)"
#: ../js/misc/util.js:97
#: ../js/misc/util.js:94
msgid "Command not found"
msgstr "Δε βρέθηκε η εντολή"
#. Replace "Error invoking GLib.shell_parse_argv: " with
#. something nicer
#: ../js/misc/util.js:130
#: ../js/misc/util.js:127
msgid "Could not parse command:"
msgstr "Δεν ήταν δυνατό να επεξεργαστεί η εντολή:"
#: ../js/misc/util.js:138
#: ../js/misc/util.js:135
#, c-format
msgid "Execution of '%s' failed:"
msgstr "Η εκτέλεση του '%s' απέτυχε:"
#: ../js/ui/appDisplay.js:349
#: ../js/ui/appDisplay.js:348
msgid "Frequent"
msgstr "Συχνό"
#: ../js/ui/appDisplay.js:356
#: ../js/ui/appDisplay.js:355
msgid "All"
msgstr "Όλα"
#: ../js/ui/appDisplay.js:914
#: ../js/ui/appDisplay.js:913
msgid "New Window"
msgstr "Νέο παράθυρο"
#: ../js/ui/appDisplay.js:917 ../js/ui/dash.js:284
#: ../js/ui/appDisplay.js:916 ../js/ui/dash.js:284
msgid "Remove from Favorites"
msgstr "Αφαίρεση από τα αγαπημένα"
#: ../js/ui/appDisplay.js:918
#: ../js/ui/appDisplay.js:917
msgid "Add to Favorites"
msgstr "Προσθήκη στα αγαπημένα"
@ -520,7 +507,7 @@ msgstr "Tο %s προστέθηκε στα αγαπημένα σας."
msgid "%s has been removed from your favorites."
msgstr "Tο %s αφαιρέθηκε από τα αγαπημένα σας."
#: ../js/ui/backgroundMenu.js:19 ../js/ui/userMenu.js:789
#: ../js/ui/backgroundMenu.js:19 ../js/ui/userMenu.js:787
msgid "Settings"
msgstr "Ρυθμίσεις"
@ -543,7 +530,7 @@ msgctxt "event list time"
msgid "%H\\u2236%M"
msgstr "%Ω\\u2236%Λ"
#. Translators: Shown in calendar event list, if 12h format,
#. Transators: Shown in calendar event list, if 12h format,
#. \u2236 is a ratio character, similar to : and \u2009 is
#. a thin space
#: ../js/ui/calendar.js:77
@ -645,35 +632,35 @@ msgid "S"
msgstr "Σ"
#. Translators: Text to show if there are no events
#: ../js/ui/calendar.js:720
#: ../js/ui/calendar.js:692
msgid "Nothing Scheduled"
msgstr "Τίποτα προγραμματισμένο"
#. Translators: Shown on calendar heading when selected day occurs on current year
#: ../js/ui/calendar.js:736
#: ../js/ui/calendar.js:708
msgctxt "calendar heading"
msgid "%A, %B %d"
msgstr "%A, %B %d"
#. Translators: Shown on calendar heading when selected day occurs on different year
#: ../js/ui/calendar.js:739
#: ../js/ui/calendar.js:711
msgctxt "calendar heading"
msgid "%A, %B %d, %Y"
msgstr "%A, %B %d, %Y"
#: ../js/ui/calendar.js:749
#: ../js/ui/calendar.js:721
msgid "Today"
msgstr "Σήμερα"
#: ../js/ui/calendar.js:753
#: ../js/ui/calendar.js:725
msgid "Tomorrow"
msgstr "Αύριο"
#: ../js/ui/calendar.js:764
#: ../js/ui/calendar.js:736
msgid "This week"
msgstr "Αυτή η εβδομάδα"
#: ../js/ui/calendar.js:772
#: ../js/ui/calendar.js:744
msgid "Next week"
msgstr "Επόμενη εβδομάδα"
@ -689,12 +676,12 @@ msgstr "Αποσυνδέθηκε εξωτερικός δίσκος"
msgid "Removable Devices"
msgstr "Αφαιρούμενες συσκευές"
#: ../js/ui/components/autorunManager.js:594
#: ../js/ui/components/autorunManager.js:593
#, c-format
msgid "Open with %s"
msgstr "Άνοιγμα με %s"
#: ../js/ui/components/autorunManager.js:620
#: ../js/ui/components/autorunManager.js:619
msgid "Eject"
msgstr "Εξαγωγή"
@ -803,7 +790,7 @@ msgid "Sorry, that didn't work. Please try again."
msgstr "Συγνώμη, αυτό δεν λειτούργησε. Παρακαλώ προσπαθήστε ξανά."
#. Translators: this is a filename used for screencast recording
#: ../js/ui/components/recorder.js:47
#: ../js/ui/components/recorder.js:48
#, no-c-format
msgid "Screencast from %d %t"
msgstr "Βίντεο οθόνης από %d %t"
@ -1050,7 +1037,7 @@ msgstr "Προβολή λογαριασμού"
msgid "Unknown reason"
msgstr "Άγνωστος λόγος"
#: ../js/ui/ctrlAltTab.js:29 ../js/ui/viewSelector.js:96
#: ../js/ui/ctrlAltTab.js:29 ../js/ui/viewSelector.js:97
msgid "Windows"
msgstr "Παράθυρα"
@ -1079,7 +1066,7 @@ msgstr "Ρυθμίσεις ημερομηνίας & ώρας"
#. Translators: This is the date format to use when the calendar popup is
#. * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
#.
#: ../js/ui/dateMenu.js:215
#: ../js/ui/dateMenu.js:205
msgid "%A %B %e, %Y"
msgstr "%A %B %e, %Y"
@ -1248,6 +1235,7 @@ msgid "Remove"
msgstr "Αφαίρεση"
#: ../js/ui/messageTray.js:1501
#| msgid "No Messages"
msgid "Clear Messages"
msgstr "Καθαρισμός μηνυμάτων"
@ -1255,15 +1243,15 @@ msgstr "Καθαρισμός μηνυμάτων"
msgid "Notification Settings"
msgstr "Ρυθμίσεις ειδοποιήσεων"
#: ../js/ui/messageTray.js:1709
#: ../js/ui/messageTray.js:1707
msgid "No Messages"
msgstr "Κανένα μήνυμα"
#: ../js/ui/messageTray.js:1785
#: ../js/ui/messageTray.js:1787
msgid "Message Tray"
msgstr "Περιοχή ειδοποιήσεων"
#: ../js/ui/messageTray.js:2813
#: ../js/ui/messageTray.js:2864
msgid "System Information"
msgstr "Πληροφορίες συστήματος"
@ -1272,14 +1260,14 @@ msgctxt "program"
msgid "Unknown"
msgstr "Άγνωστο"
#: ../js/ui/overviewControls.js:463 ../js/ui/screenShield.js:149
#: ../js/ui/overviewControls.js:460 ../js/ui/screenShield.js:153
#, c-format
msgid "%d new message"
msgid_plural "%d new messages"
msgstr[0] "%d νέο μήνυμα"
msgstr[1] "%d νέα μηνύματα"
#: ../js/ui/overview.js:84
#: ../js/ui/overview.js:82
msgid "Undo"
msgstr "Αναίρεση"
@ -1291,21 +1279,22 @@ msgstr "Επισκόπηση"
#. in the search entry when no search is
#. active; it should not exceed ~30
#. characters.
#: ../js/ui/overview.js:271
#: ../js/ui/overview.js:284
#| msgid "Type to search..."
msgid "Type to search…"
msgstr "Πληκτρολογήστε για αναζήτηση…"
#: ../js/ui/panel.js:633
#: ../js/ui/panel.js:613
msgid "Quit"
msgstr "Έξοδος"
#. Translators: If there is no suitable word for "Activities"
#. in your language, you can use the word for "Overview".
#: ../js/ui/panel.js:657
#: ../js/ui/panel.js:642
msgid "Activities"
msgstr "Δραστηριότητες"
#: ../js/ui/panel.js:954
#: ../js/ui/panel.js:983
msgid "Top Bar"
msgstr "Πάνω μπάρα"
@ -1318,32 +1307,32 @@ msgstr "Πάνω μπάρα"
msgid "toggle-switch-us"
msgstr "toggle-switch-intl"
#: ../js/ui/runDialog.js:74
#: ../js/ui/runDialog.js:205
msgid "Enter a Command"
msgstr "Εισαγωγή εντολής"
#: ../js/ui/runDialog.js:110
#: ../js/ui/runDialog.js:241
msgid "Close"
msgstr "Κλείσιμο"
#. Translators: This is a time format for a date in
#. long format
#: ../js/ui/screenShield.js:86
#: ../js/ui/screenShield.js:90
msgid "%A, %B %d"
msgstr "%A, %B %d"
#: ../js/ui/screenShield.js:151
#: ../js/ui/screenShield.js:155
#, c-format
msgid "%d new notification"
msgid_plural "%d new notifications"
msgstr[0] "%d νέα ειδοποίηση"
msgstr[1] "%d νέες ειδοποιήσεις"
#: ../js/ui/screenShield.js:438 ../js/ui/userMenu.js:807
#: ../js/ui/screenShield.js:442 ../js/ui/userMenu.js:805
msgid "Lock"
msgstr "Κλείδωμα"
#: ../js/ui/screenShield.js:641
#: ../js/ui/screenShield.js:639
msgid "GNOME needs to lock the screen"
msgstr "Το GNOME χρειάζεται να κλειδώσει την οθόνη"
@ -1354,19 +1343,21 @@ msgstr "Το GNOME χρειάζεται να κλειδώσει την οθόν
#.
#. XXX: another option is to kick the user into the gdm login
#. screen, where we're not affected by grabs
#: ../js/ui/screenShield.js:762 ../js/ui/screenShield.js:1198
#: ../js/ui/screenShield.js:758 ../js/ui/screenShield.js:1169
#| msgid "Unable to connect to %s"
msgid "Unable to lock"
msgstr "Αδυναμία κλειδώματος"
#: ../js/ui/screenShield.js:763 ../js/ui/screenShield.js:1199
#: ../js/ui/screenShield.js:759 ../js/ui/screenShield.js:1170
msgid "Lock was blocked by an application"
msgstr "Το κλείδωμα εμποδίστηκε από μια εφαρμογή"
#: ../js/ui/searchDisplay.js:453
#: ../js/ui/searchDisplay.js:431
#| msgid "Searching..."
msgid "Searching…"
msgstr "Αναζήτηση…"
#: ../js/ui/searchDisplay.js:497
#: ../js/ui/searchDisplay.js:475
msgid "No results."
msgstr "Δε βρέθηκαν αποτελέσματα."
@ -1378,11 +1369,11 @@ msgstr "Αντιγραφή"
msgid "Paste"
msgstr "Επικόλληση"
#: ../js/ui/shellEntry.js:101
#: ../js/ui/shellEntry.js:105
msgid "Show Text"
msgstr "Εμφάνιση κειμένου"
#: ../js/ui/shellEntry.js:103
#: ../js/ui/shellEntry.js:107
msgid "Hide Text"
msgstr "Απόκρυψη κειμένου"
@ -1394,7 +1385,7 @@ msgstr "Κωδικός"
msgid "Remember Password"
msgstr "Απομνημόνευση κωδικού"
#: ../js/ui/shellMountOperation.js:403 ../js/ui/unlockDialog.js:114
#: ../js/ui/shellMountOperation.js:403 ../js/ui/unlockDialog.js:140
msgid "Unlock"
msgstr "Ξεκλείδωμα"
@ -1458,10 +1449,12 @@ msgid "Visibility"
msgstr "Ορατότητα"
#: ../js/ui/status/bluetooth.js:59
#| msgid "Send Files to Device..."
msgid "Send Files to Device…"
msgstr "Αποστολή αρχείων σε συσκευή…"
#: ../js/ui/status/bluetooth.js:60
#| msgid "Set Up a New Device..."
msgid "Set Up a New Device…"
msgstr "Ρύθμιση νέας συσκευής…"
@ -1488,6 +1481,7 @@ msgid "connecting..."
msgstr "σύνδεση..."
#: ../js/ui/status/bluetooth.js:239
#| msgid "Send Files..."
msgid "Send Files…"
msgstr "Αποστολή αρχείων…"
@ -1700,6 +1694,7 @@ msgstr "Ρυθμίσεις τροφοδοσίας"
#. 0 is reported when UPower does not have enough data
#. to estimate battery life
#: ../js/ui/status/power.js:99
#| msgid "Estimating..."
msgid "Estimating…"
msgstr "Υπολογισμός…"
@ -1799,59 +1794,59 @@ msgstr "Ένταση ήχου"
msgid "Microphone"
msgstr "Μικρόφωνο"
#: ../js/ui/unlockDialog.js:125
#: ../js/ui/unlockDialog.js:151
msgid "Log in as another user"
msgstr "Είσοδος ως άλλος χρήστης"
#: ../js/ui/unlockDialog.js:146
#: ../js/ui/unlockDialog.js:177
msgid "Unlock Window"
msgstr "Ξεκλείδωμα παραθύρου"
#: ../js/ui/userMenu.js:193
#: ../js/ui/userMenu.js:192
msgid "Available"
msgstr "Διαθέσιμος-η"
#: ../js/ui/userMenu.js:196
#: ../js/ui/userMenu.js:195
msgid "Busy"
msgstr "Απασχολημένος-η"
#: ../js/ui/userMenu.js:199
#: ../js/ui/userMenu.js:198
msgid "Invisible"
msgstr "Αόρατος-η"
#: ../js/ui/userMenu.js:202
#: ../js/ui/userMenu.js:201
msgid "Away"
msgstr "Απουσιάζει"
#: ../js/ui/userMenu.js:205
#: ../js/ui/userMenu.js:204
msgid "Idle"
msgstr "Αδρανής"
#: ../js/ui/userMenu.js:208
#: ../js/ui/userMenu.js:207
msgid "Offline"
msgstr "Εκτός σύνδεσης"
#: ../js/ui/userMenu.js:781
#: ../js/ui/userMenu.js:779
msgid "Notifications"
msgstr "Ειδοποιήσεις"
#: ../js/ui/userMenu.js:797
#: ../js/ui/userMenu.js:795
msgid "Switch User"
msgstr "Αλλαγή χρήστη"
#: ../js/ui/userMenu.js:802
#: ../js/ui/userMenu.js:800
msgid "Log Out"
msgstr "Αποσύνδεση"
#: ../js/ui/userMenu.js:822
#: ../js/ui/userMenu.js:820
msgid "Install Updates & Restart"
msgstr "Εγκατάσταση ενημερώσεων & επανεκκίνηση"
#: ../js/ui/userMenu.js:840
#: ../js/ui/userMenu.js:838
msgid "Your chat status will be set to busy"
msgstr "Η κατάσταση συνομιλίας σας θα ορισθεί σε απασχολημένος"
#: ../js/ui/userMenu.js:841
#: ../js/ui/userMenu.js:839
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."
@ -1860,36 +1855,34 @@ msgstr ""
"κατάσταση σας έχει ορισθεί έτσι ώστε να γίνεται γνωστό ότι πιθανόν να μην "
"δείτε τα μηνύματα τους."
#: ../js/ui/userMenu.js:888
#: ../js/ui/userMenu.js:885
msgid "Other users are logged in."
msgstr "Και άλλοι χρήστες είναι συνδεμένοι."
#: ../js/ui/userMenu.js:893
#: ../js/ui/userMenu.js:890
msgid "Shutting down might cause them to lose unsaved work."
msgstr ""
"Κλείνοντας μπορεί να τους προκαλέσετε την απώλεια αναποθήκευτης εργασίας."
#. Translators: Remote here refers to a remote session, like a ssh login
#: ../js/ui/userMenu.js:921
#: ../js/ui/userMenu.js:916
#, c-format
msgid "%s (remote)"
msgstr "%s (απομακρυσμένο)"
#. Translators: Console here refers to a tty like a VT console
#: ../js/ui/userMenu.js:924
#: ../js/ui/userMenu.js:918
#, c-format
msgid "%s (console)"
msgstr "%s (κονσόλα)"
#: ../js/ui/viewSelector.js:100
#: ../js/ui/viewSelector.js:101
msgid "Applications"
msgstr "Εφαρμογές"
#: ../js/ui/viewSelector.js:104
#: ../js/ui/viewSelector.js:105
msgid "Search"
msgstr "Αναζήτηση"
#: ../js/ui/wanda.js:77
#: ../js/ui/wanda.js:92
#, c-format
msgid ""
"Sorry, no wisdom for you today:\n"
@ -1898,7 +1891,7 @@ msgstr ""
"Συγγνώμη, κανένα απόφθεγμα για εσάς σήμερα:\n"
"%s"
#: ../js/ui/wanda.js:81
#: ../js/ui/wanda.js:96
#, c-format
msgid "%s the Oracle says"
msgstr "%s ο Προφήτης λέει"

922
po/es.po

File diff suppressed because it is too large Load Diff

649
po/gl.po

File diff suppressed because it is too large Load Diff

1950
po/ia.po

File diff suppressed because it is too large Load Diff

588
po/id.po

File diff suppressed because it is too large Load Diff

615
po/it.po

File diff suppressed because it is too large Load Diff

573
po/ja.po

File diff suppressed because it is too large Load Diff

554
po/kk.po

File diff suppressed because it is too large Load Diff

View File

@ -461,7 +461,7 @@ msgstr "Nepavyko įvykdyti „%s“:"
#: ../js/ui/appDisplay.js:349
msgid "Frequent"
msgstr "Dažnai naudojamos"
msgstr "Dažniausios"
#: ../js/ui/appDisplay.js:356
msgid "All"
@ -1262,7 +1262,7 @@ msgstr "Apžvalga"
#. characters.
#: ../js/ui/overview.js:271
msgid "Type to search…"
msgstr "Rašykite, ko ieškote…"
msgstr "Rašykite ko ieškote…"
#: ../js/ui/panel.js:612
msgid "Quit"
@ -1299,7 +1299,7 @@ msgstr "Užverti"
#. long format
#: ../js/ui/screenShield.js:86
msgid "%A, %B %d"
msgstr "%A, %B %d d."
msgstr "%A, %B %d"
#: ../js/ui/screenShield.js:151
#, c-format

320
po/ml.po
View File

@ -10,9 +10,9 @@ 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: 2013-04-24 18:23+0000\n"
"PO-Revision-Date: 2013-04-25 00:32+0530\n"
"Last-Translator: Balasankar C <c.balasankar@gmail.com>\n"
"POT-Creation-Date: 2013-03-23 11:49+0000\n"
"PO-Revision-Date: 2013-03-25 14:57+0530\n"
"Last-Translator: Ani Peter <peter.ani@gmail.com>\n"
"Language-Team: American English <kde-i18n-doc@kde.org>\n"
"Language: ml\n"
"MIME-Version: 1.0\n"
@ -44,14 +44,10 @@ msgid "Focus the active notification"
msgstr "സജീവമായ അറിയിപ്പിനെ കേന്ദ്രീകരിക്കുക"
#: ../data/50-gnome-shell-system.xml.in.h:4
msgid "Show the overview"
msgstr "പൊതുവായ അവലോകനം കാണിക്കുക"
#: ../data/50-gnome-shell-system.xml.in.h:5
msgid "Show all applications"
msgstr "എല്ലാ പ്രയോഗങ്ങളും കാണിയ്ക്കുക"
#: ../data/50-gnome-shell-system.xml.in.h:6
#: ../data/50-gnome-shell-system.xml.in.h:5
msgid "Open the application menu"
msgstr "പ്രയോഗത്തിന്റെ മെനു തുറക്കുക"
@ -75,7 +71,8 @@ msgstr "ഗ്നോം ഷെല്‍ എക്സ്റ്റെന്‍ഷ
#: ../data/org.gnome.shell.gschema.xml.in.in.h:1
msgid "Enable internal tools useful for developers and testers from Alt-F2"
msgstr ""
"Alt-F2-ല്‍ ഡവലപ്പര്‍മാര്‍ക്കും ടെസ്റ്റേര്‍സിനും പ്രയോജനകരമായ ആന്തരിക പ്രയോഗങ്ങള്‍ പ്രവര്‍ത്തന "
"Alt-F2-ല്‍ ഡവലപ്പര്‍മാര്‍ക്കും ടെസ്റ്റേര്‍സിനും പ്രയോജനകരമായ ആന്തരിക "
"പ്രയോഗങ്ങള്‍ പ്രവര്‍ത്തന "
"സജ്ജമാക്കുന്നു"
#: ../data/org.gnome.shell.gschema.xml.in.in.h:2
@ -83,7 +80,8 @@ msgid ""
"Allows access to internal debugging and monitoring tools using the Alt-F2 "
"dialog."
msgstr ""
"Alt-F2 ഡയലോഗ് ഉപയോഗിച്ചു് ആന്തരിക ഡീബഗ്ഗിലേക്കും നീരീക്ഷണ പ്രയോഗങ്ങളിലേക്കും പ്രവേശനം "
"Alt-F2 ഡയലോഗ് ഉപയോഗിച്ചു് ആന്തരിക ഡീബഗ്ഗിലേക്കും നീരീക്ഷണ പ്രയോഗങ്ങളിലേക്കും "
"പ്രവേശനം "
"അനുവദിയ്ക്കുക."
#: ../data/org.gnome.shell.gschema.xml.in.in.h:3
@ -97,14 +95,19 @@ msgid ""
"list. You can also manipulate this list with the EnableExtension and "
"DisableExtension DBus methods on org.gnome.Shell."
msgstr ""
"ഗ്നോം ഷെല്‍ എക്സ്റ്റെന്‍ഷനുകള്‍ക്കു് ഒരു യുയുഐഡി വിശേഷതയുണ്ടു്; ലഭ്യമാക്കേണ്ട എക്സ്റ്റെന്‍ഷനുകള്‍ ഈ കീ പട്ടിക "
"ലഭ്യമാക്കുന്നു. ലഭ്യമാക്കേണ്ട ഏതു് എക്സ്റ്റെന്‍ഷനും ഈ പട്ടികയിലുണ്ടാവണം. org.gnome.Shell-ല്‍ "
"നിങ്ങള്‍ക്കു് EnableExtension, DisableExtension എന്നീ ഡീബസ് രീതികളിലൂടെ ഈ പട്ടിക "
"ഗ്നോം ഷെല്‍ എക്സ്റ്റെന്‍ഷനുകള്‍ക്കു് ഒരു യുയുഐഡി വിശേഷതയുണ്ടു്; ലഭ്യമാക്കേണ്ട "
"എക്സ്റ്റെന്‍ഷനുകള്‍ ഈ കീ പട്ടിക "
"ലഭ്യമാക്കുന്നു. ലഭ്യമാക്കേണ്ട ഏതു് എക്സ്റ്റെന്‍ഷനും ഈ പട്ടികയിലുണ്ടാവണം. "
"org.gnome.Shell-ല്‍ "
"നിങ്ങള്‍ക്കു് EnableExtension, DisableExtension എന്നീ ഡീബസ് രീതികളിലൂടെ ഈ "
"പട്ടിക "
"കൈകാര്യം ചെയ്യുവാനും സാധിയ്ക്കുന്നു."
#: ../data/org.gnome.shell.gschema.xml.in.in.h:5
msgid "Whether to collect stats about applications usage"
msgstr "പ്രയോഗങ്ങളുടെ ഉപയോഗത്തെപ്പറ്റിയുള്ള സ്ഥിതിവിവരക്കണക്കുകള്‍ ശേഖരിയ്ക്കണമോ എന്നു്"
msgstr ""
"പ്രയോഗങ്ങളുടെ ഉപയോഗത്തെപ്പറ്റിയുള്ള സ്ഥിതിവിവരക്കണക്കുകള്‍ ശേഖരിയ്ക്കണമോ "
"എന്നു്"
#: ../data/org.gnome.shell.gschema.xml.in.in.h:6
msgid ""
@ -113,9 +116,12 @@ msgid ""
"want to disable this for privacy reasons. Please note that doing so won't "
"remove already saved data."
msgstr ""
"ഏറ്റവും കൂടുതല്‍ തവണ ഉപയോഗിയ്ക്കുന്ന പ്രയോഗങ്ങള്‍ ലഭ്യമാക്കുന്നതിനായി ഷെല്‍ സാധാരണയായി സജീവമായ "
"പ്രയോഗങ്ങളെ നിരീക്ഷിയ്ക്കുന്നു. (ഉദാഹരണത്തിനു്, ലോഞ്ചേര്‍സ്). ഈ ഡേറ്റാ സ്വകാര്യമായി "
"സൂക്ഷിയ്ക്കുന്നെങ്കിലും, ചില കാരണങ്ങളാല്‍ ഇതു് പ്രവര്‍ത്തന രഹിതമാക്കേണ്ടതുണ്ടു്. ഇങ്ങനെ ചെയ്യുന്നതു് "
"ഏറ്റവും കൂടുതല്‍ തവണ ഉപയോഗിയ്ക്കുന്ന പ്രയോഗങ്ങള്‍ ലഭ്യമാക്കുന്നതിനായി ഷെല്‍ "
"സാധാരണയായി സജീവമായ "
"പ്രയോഗങ്ങളെ നിരീക്ഷിയ്ക്കുന്നു. (ഉദാഹരണത്തിനു്, ലോഞ്ചേര്‍സ്). ഈ ഡേറ്റാ "
"സ്വകാര്യമായി "
"സൂക്ഷിയ്ക്കുന്നെങ്കിലും, ചില കാരണങ്ങളാല്‍ ഇതു് പ്രവര്‍ത്തന "
"രഹിതമാക്കേണ്ടതുണ്ടു്. ഇങ്ങനെ ചെയ്യുന്നതു് "
"നിങ്ങള്‍ സൂക്ഷിച്ച ഡേറ്റയെ ബാധിയ്ക്കുന്നതല്ല."
#: ../data/org.gnome.shell.gschema.xml.in.in.h:7
@ -126,7 +132,8 @@ msgstr "ഇഷ്ടമുള്ള പ്രയോഗങ്ങള്‍ക്
msgid ""
"The applications corresponding to these identifiers will be displayed in the "
"favorites area."
msgstr "ഈ ഐഡന്റിഫയറുകള്‍ക്കുള്ള പ്രയോഗങ്ങള്‍ ഉചിതമായ സ്ഥലങ്ങളില്‍ കാണിയ്ക്കുന്നു."
msgstr ""
"ഈ ഐഡന്റിഫയറുകള്‍ക്കുള്ള പ്രയോഗങ്ങള്‍ ഉചിതമായ സ്ഥലങ്ങളില്‍ കാണിയ്ക്കുന്നു."
#: ../data/org.gnome.shell.gschema.xml.in.in.h:9
msgid "List of categories that should be displayed as folders"
@ -137,8 +144,8 @@ msgid ""
"Each category name in this list will be represented as folder in the "
"application view, rather than being displayed inline in the main view."
msgstr ""
"ഈ പട്ടികയിലുള്ള ഓരോ വിഭാഗത്തിന്റെ പേരും, പ്രധാന കാഴ്ചയില്‍ ഓരോ വരിയായി കാണിയ്ക്കുന്നതിനു് "
"പകരം പ്രയോഗങ്ങളുടെ കാഴ്ചയില്‍ ഫോള്‍ഡറായി കാണിയ്ക്കുന്നു. "
"ഈ പട്ടികയിലുള്ള ഓരോ വിഭാഗത്തിന്റെ പേരും, പ്രധാന കാഴ്ചയില്‍ ഓരോ വരിയായി "
"കാണിയ്ക്കുന്നതിനു് പകരം പ്രയോഗങ്ങളുടെ കാഴ്ചയില്‍ ഫോള്‍ഡറായി കാണിയ്ക്കുന്നു. "
#: ../data/org.gnome.shell.gschema.xml.in.in.h:11
msgid "History for command (Alt-F2) dialog"
@ -153,7 +160,8 @@ msgid ""
"Internally used to store the last IM presence explicitly set by the user. "
"The value here is from the TpConnectionPresenceType enumeration."
msgstr ""
"ഉപയോക്താവു് സജ്ജമാക്കിയ അവസാന ഐഎം ആന്തരികമായി സൂക്ഷിയ്ക്കുന്നതിനു് ഉപയോഗിയ്ക്കുന്നു. മൂല്യം "
"ഉപയോക്താവു് സജ്ജമാക്കിയ അവസാന ഐഎം ആന്തരികമായി സൂക്ഷിയ്ക്കുന്നതിനു് "
"ഉപയോഗിയ്ക്കുന്നു. മൂല്യം "
"TpConnectionPresenceType തരത്തിലുള്ളതാകുന്നു."
#: ../data/org.gnome.shell.gschema.xml.in.in.h:14
@ -161,7 +169,8 @@ msgid ""
"Internally used to store the last session presence status for the user. The "
"value here is from the GsmPresenceStatus enumeration."
msgstr ""
"ഉപയോക്താവിനുള്ള അവസാന സെഷന്‍ അവസ്ഥ ആന്തരികമായി സൂക്ഷിയ്ക്കുന്നതിനു് ഉപയോഗിയ്ക്കുന്നു. മൂല്യം "
"ഉപയോക്താവിനുള്ള അവസാന സെഷന്‍ അവസ്ഥ ആന്തരികമായി സൂക്ഷിയ്ക്കുന്നതിനു് "
"ഉപയോഗിയ്ക്കുന്നു. മൂല്യം "
"GsmPresenceStatus തരത്തിലുള്ളതാകുന്നു."
#: ../data/org.gnome.shell.gschema.xml.in.in.h:15
@ -173,13 +182,15 @@ msgid ""
"This key overrides the automatic hiding of the 'Log out' menuitem in single-"
"user, single-session situations."
msgstr ""
"സിംഗിള്‍ യൂസര്‍, സിംഗിള്‍ സെഷനില്‍ 'ലോഗൌട്ട്' മെനുവസ്തു അദൃശ്യമാക്കുന്നതിനായി ഈ കീ ഉപയോഗിയ്ക്കുന്നു"
"സിംഗിള്‍ യൂസര്‍, സിംഗിള്‍ സെഷനില്‍ 'ലോഗൌട്ട്' മെനുവസ്തു അദൃശ്യമാക്കുന്നതിനായി "
"ഈ കീ ഉപയോഗിയ്ക്കുന്നു"
#: ../data/org.gnome.shell.gschema.xml.in.in.h:17
msgid ""
"Whether to remember password for mounting encrypted or remote filesystems"
msgstr ""
"എന്‍ക്രിപ്റ്റ് ചെയ്തതോ വിദൂരമോ ആയ ഫയല്‍സിസ്റ്റങ്ങള്‍ മൌണ്ട് ചെയ്യുമ്പോഴുള്ള രഹസ്യവാക്ക് ഓര്‍മ്മിക്കണോ എന്ന്"
"എന്‍ക്രിപ്റ്റ് ചെയ്തതോ വിദൂരമോ ആയ ഫയല്‍സിസ്റ്റങ്ങള്‍ മൌണ്ട് ചെയ്യുമ്പോഴുള്ള "
"രഹസ്യവാക്ക് ഓര്‍മ്മിക്കണോ എന്ന്"
#: ../data/org.gnome.shell.gschema.xml.in.in.h:18
msgid ""
@ -188,9 +199,10 @@ msgid ""
"'Remember Password' checkbox will be present. This key sets the default "
"state of the checkbox."
msgstr ""
"ഒരു എന്‍ക്രിപ്റ്റ് ചെയ്ത ഡിവൈസ് അല്ലെങ്കില്‍ വിദൂര ഫയല്‍സിസ്റ്റം മൌണ്ട് ചെയ്യുമ്പോള്‍ ഷെല്‍ ഒരു രഹസ്യവാക്ക് "
"ആവശ്യപ്പെടുന്നു. രഹസ്യവാക്ക് സൂക്ഷിയ്ക്കുവാന്‍ സാധ്യമെങ്കില്‍, 'രഹസ്യവാക്ക് ഓര്‍ത്തു്വയ്ക്കുക' ചെക്ക്ബോക്സ് "
"കാണാം. ഈ കീ ചെക്ക്ബോക്സിന്റെ സ്വതവേയുള്ള അവസ്ഥ സജ്ജമാക്കുന്നു."
"ഒരു എന്‍ക്രിപ്റ്റ് ചെയ്ത ഡിവൈസ് അല്ലെങ്കില്‍ വിദൂര ഫയല്‍സിസ്റ്റം മൌണ്ട് "
"ചെയ്യുമ്പോള്‍ ഷെല്‍ ഒരു രഹസ്യവാക്ക് ആവശ്യപ്പെടുന്നു. രഹസ്യവാക്ക് "
"സൂക്ഷിയ്ക്കുവാന്‍ സാധ്യമെങ്കില്‍, 'രഹസ്യവാക്ക് ഓര്‍ത്തു്വയ്ക്കുക' "
"ചെക്ക്ബോക്സ് കാണാം. ഈ കീ ചെക്ക്ബോക്സിന്റെ സ്വതവേയുള്ള അവസ്ഥ സജ്ജമാക്കുന്നു."
#: ../data/org.gnome.shell.gschema.xml.in.in.h:19
msgid "Show the week date in the calendar"
@ -216,65 +228,61 @@ msgstr "\"പ്രയോഗങ്ങള്‍ കാണിയ്ക്കുക
msgid ""
"Keybinding to open the \"Show Applications\" view of the Activities Overview."
msgstr ""
"പ്രവര്‍ത്തികളുടെ അവലോകനത്തിന്റെ \"പ്രയോഗങ്ങള്‍ കാണിയ്ക്കുക\" എന്ന കാഴ്ച തുറക്കുന്നതിനുള്ള കീബൈന്‍ഡിങ്"
"പ്രവര്‍ത്തികളുടെ അവലോകനത്തിന്റെ \"പ്രയോഗങ്ങള്‍ കാണിയ്ക്കുക\" എന്ന കാഴ്ച "
"തുറക്കുന്നതിനുള്ള കീബൈന്‍ഡിങ്"
#: ../data/org.gnome.shell.gschema.xml.in.in.h:25
msgid "Keybinding to open the overview"
msgstr "പൊതുവായ അവലോകനം തുറക്കുന്നതിനുള്ള കീബൈന്‍ഡിങ്"
#: ../data/org.gnome.shell.gschema.xml.in.in.h:26
msgid "Keybinding to open the Activities Overview."
msgstr "പ്രയോഗങ്ങളുടെ പൊതുവായ അവലോകനം എന്ന കാഴ്ച തുറക്കുന്നതിനുള്ള കീബൈന്‍ഡിങ്"
#: ../data/org.gnome.shell.gschema.xml.in.in.h:27
msgid "Keybinding to toggle the visibility of the message tray"
msgstr "സന്ദേശ ട്രേയുടെ ദൃശ്യത ടൊഗ്ഗിള്‍ ചെയ്യുന്നതിനുള്ള കീക്കൂട്ടം"
#: ../data/org.gnome.shell.gschema.xml.in.in.h:28
#: ../data/org.gnome.shell.gschema.xml.in.in.h:26
msgid "Keybinding to toggle the visibility of the message tray."
msgstr "സന്ദേശ ട്രേയുടെ ദൃശ്യത ടൊഗ്ഗിള്‍ ചെയ്യുന്നതിനുള്ള കീക്കൂട്ടം."
#: ../data/org.gnome.shell.gschema.xml.in.in.h:29
#: ../data/org.gnome.shell.gschema.xml.in.in.h:27
msgid "Keybinding to focus the active notification"
msgstr "സജീവമായ അറിയിപ്പിനുള്ള കീബൈന്‍ഡിങ്"
#: ../data/org.gnome.shell.gschema.xml.in.in.h:30
#: ../data/org.gnome.shell.gschema.xml.in.in.h:28
msgid "Keybinding to focus the active notification."
msgstr "സജീവമായ അറിയിപ്പിനുള്ള കീബൈന്‍ഡിങ്."
#: ../data/org.gnome.shell.gschema.xml.in.in.h:31
#: ../data/org.gnome.shell.gschema.xml.in.in.h:29
msgid "Keybinding to toggle the screen recorder"
msgstr "സ്ക്രീന്‍ റിക്കോര്‍ഡര്‍ ടൊഗ്ഗിള്‍ ചെയ്യുന്നതിനുള്ള കീക്കൂട്ടം"
#: ../data/org.gnome.shell.gschema.xml.in.in.h:32
#: ../data/org.gnome.shell.gschema.xml.in.in.h:30
msgid "Keybinding to start/stop the builtin screen recorder."
msgstr "ബിള്‍ട്ടിന്‍ സ്ക്രീന്‍ റിക്കോര്‍ഡര്‍ തുടങ്ങുവാന്‍/നിര്‍ത്തുന്നതിനുള്ള കീക്കൂട്ടം."
msgstr ""
"ബിള്‍ട്ടിന്‍ സ്ക്രീന്‍ റിക്കോര്‍ഡര്‍ തുടങ്ങുവാന്‍/നിര്‍ത്തുന്നതിനുള്ള "
"കീക്കൂട്ടം."
#: ../data/org.gnome.shell.gschema.xml.in.in.h:33
#: ../data/org.gnome.shell.gschema.xml.in.in.h:31
msgid "Which keyboard to use"
msgstr "ഏതു് കീബോര്‍ഡ് ഉപയോഗിയ്ക്കണം"
#: ../data/org.gnome.shell.gschema.xml.in.in.h:34
#: ../data/org.gnome.shell.gschema.xml.in.in.h:32
msgid "The type of keyboard to use."
msgstr "ഏതു് തരം കീബോര്‍ഡ് ഉപയോഗിയ്ക്കണം."
#: ../data/org.gnome.shell.gschema.xml.in.in.h:35
#: ../data/org.gnome.shell.gschema.xml.in.in.h:33
msgid "Framerate used for recording screencasts."
msgstr "സ്ക്രീന്‍കാസ്റ്റുകള്‍ റിക്കോര്‍ഡ് ചെയ്യുന്നതിനുള്ള ഫ്രെയിം റേറ്റ്."
#: ../data/org.gnome.shell.gschema.xml.in.in.h:36
#: ../data/org.gnome.shell.gschema.xml.in.in.h:34
msgid ""
"The framerate of the resulting screencast recordered by GNOME Shell's "
"screencast recorder in frames-per-second."
msgstr ""
"ഗ്നോം ഷെല്ലിന്റെ സ്ക്രീന്‍കാസ്റ്റ് റിക്കോര്‍ഡര്‍ റീക്കോര്‍ഡ് ചെയ്തിട്ടുള്ള സ്ക്രീന്‍കാസ്റ്റിന്റെ "
"ഗ്നോം ഷെല്ലിന്റെ സ്ക്രീന്‍കാസ്റ്റ് റിക്കോര്‍ഡര്‍ റീക്കോര്‍ഡ് ചെയ്തിട്ടുള്ള "
"സ്ക്രീന്‍കാസ്റ്റിന്റെ "
"ഫ്രെയിംറേറ്റ്, ഒരു സെക്കന്‍ഡില്‍ ഒരു ഫ്രെയിം."
#: ../data/org.gnome.shell.gschema.xml.in.in.h:37
#: ../data/org.gnome.shell.gschema.xml.in.in.h:35
msgid "The gstreamer pipeline used to encode the screencast"
msgstr "സ്ക്രീന്‍കാസ്റ്റ് എന്‍കോഡ് ചെയ്യുന്നതിനുള്ള gstreamer പൈപ്പ്‌ലൈന്‍"
#: ../data/org.gnome.shell.gschema.xml.in.in.h:39
#: ../data/org.gnome.shell.gschema.xml.in.in.h:37
#, no-c-format
msgid ""
"Sets the GStreamer pipeline used to encode recordings. It follows the syntax "
@ -288,68 +296,78 @@ msgid ""
"threads=%T ! queue ! webmmux' and records to WEBM using the VP8 codec. %T is "
"used as a placeholder for a guess at the optimal thread count on the system."
msgstr ""
"റിക്കോര്‍ഡിങുകള്‍ എന്‍കോഡ് ചെയ്യുന്നതിനായി GStreamer പൈപ്പ് ലൈന്‍ ഉപയോഗിയ്ക്കുന്നു. gst-launch-"
"നുള്ള സിന്റാക്സ് ഉപയോഗിയ്ക്കുന്നു. കാലിയായി സജ്ജമാക്കുമ്പോള്‍ കാലിയാകുന്നു.ഇതു് നിലവില്‍ 'vp8enc "
"റിക്കോര്‍ഡിങുകള്‍ എന്‍കോഡ് ചെയ്യുന്നതിനായി GStreamer പൈപ്പ് ലൈന്‍ "
"ഉപയോഗിയ്ക്കുന്നു. gst-launch-"
"നുള്ള സിന്റാക്സ് ഉപയോഗിയ്ക്കുന്നു. കാലിയായി സജ്ജമാക്കുമ്പോള്‍ "
"കാലിയാകുന്നു.ഇതു് നിലവില്‍ 'vp8enc "
"min_quantizer=13 max_quantizer=13 cpu-used=5 deadline=1000000 threads=%T ! "
"queue ! webmmux' ആകുന്നുസ WEBM VP8 കോഡ് ഉപയോഗിച്ചു് റിക്കോര്‍ഡ് ചെയ്യുന്നു."
#: ../data/org.gnome.shell.gschema.xml.in.in.h:40
#: ../data/org.gnome.shell.gschema.xml.in.in.h:38
msgid "File extension used for storing the screencast"
msgstr "സ്ക്രീന്‍കാസ്റ്റ് സൂക്ഷിയ്ക്കുന്നതിനുള്ള ഫയല്‍ എക്സ്റ്റെന്‍ഷന്‍"
#: ../data/org.gnome.shell.gschema.xml.in.in.h:41
#: ../data/org.gnome.shell.gschema.xml.in.in.h:39
msgid ""
"The filename for recorded screencasts will be a unique filename based on the "
"current date, and use this extension. It should be changed when recording to "
"a different container format."
msgstr ""
"റിക്കോര്‍ഡ് ചെയ്ത സ്ക്രീന്‍കാസ്റ്റുകള്‍ക്കുള്ള ഫയല്‍നാമം നിലവിലുള്ള തീയതി, എക്സ്റ്റെന്‍ഷന്‍ എന്നിവ "
"അനുസരിച്ചാകുന്നു. മറ്റൊരു ശൈലിയിലേക്കു് റിക്കോര്‍ഡ് ചെയ്യുമ്പോള്‍ ഇതു് മാറ്റണം."
"റിക്കോര്‍ഡ് ചെയ്ത സ്ക്രീന്‍കാസ്റ്റുകള്‍ക്കുള്ള ഫയല്‍നാമം നിലവിലുള്ള തീയതി, "
"എക്സ്റ്റെന്‍ഷന്‍ എന്നിവ "
"അനുസരിച്ചാകുന്നു. മറ്റൊരു ശൈലിയിലേക്കു് റിക്കോര്‍ഡ് ചെയ്യുമ്പോള്‍ ഇതു് "
"മാറ്റണം."
#: ../data/org.gnome.shell.gschema.xml.in.in.h:42
#: ../data/org.gnome.shell.gschema.xml.in.in.h:40
msgid "The application icon mode."
msgstr "പ്രയോഗത്തിന്റെ ഐക്കണ്‍ മോഡ്."
#: ../data/org.gnome.shell.gschema.xml.in.in.h:43
#: ../data/org.gnome.shell.gschema.xml.in.in.h:41
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 ""
"സ്വിച്ചറില്‍ ജാലകങ്ങള്‍ എങ്ങനെ കാണിയ്ക്കുന്നു എന്നു് ക്രമീകരിയ്ക്കുന്നു. ശരിയായ സാധ്യതകള്‍: "
"'thumbnail-only' (ജാലകത്തിന്റെ പ്രതിരൂപം കാണിയ്ക്കുന്നു), 'app-icon-"
"സ്വിച്ചറില്‍ ജാലകങ്ങള്‍ എങ്ങനെ കാണിയ്ക്കുന്നു എന്നു് ക്രമീകരിയ്ക്കുന്നു. "
"ശരിയായ "
"സാധ്യതകള്‍: 'thumbnail-only' (ജാലകത്തിന്റെ പ്രതിരൂപം കാണിയ്ക്കുന്നു), "
"'app-icon-"
"only' (പ്രയോഗത്തിന്റെ പ്രതിരൂപം കാണിയ്ക്കുന്നു) അല്ലെങ്കില്‍ 'both'."
#: ../data/org.gnome.shell.gschema.xml.in.in.h:44
#: ../data/org.gnome.shell.gschema.xml.in.in.h:42
msgid "Attach modal dialog to the parent window"
msgstr "പേരന്റ് ജാലകത്തിലേക്കു് ഡയലോഗ് ചേര്‍ക്കുക"
#: ../data/org.gnome.shell.gschema.xml.in.in.h:45
#: ../data/org.gnome.shell.gschema.xml.in.in.h:43
msgid ""
"This key overrides the key in org.gnome.mutter when running GNOME Shell."
msgstr "ഗ്നോം ഷെല്‍ പ്രവര്‍ത്തിയ്ക്കുമ്പോള്‍ org.gnome.mutter-ലുള്ള കീ ഈ കീ തിരുത്തിയെഴുതുന്നു."
msgstr ""
"ഗ്നോം ഷെല്‍ പ്രവര്‍ത്തിയ്ക്കുമ്പോള്‍ org.gnome.mutter-ലുള്ള കീ ഈ കീ "
"തിരുത്തിയെഴുതുന്നു."
#: ../data/org.gnome.shell.gschema.xml.in.in.h:46
#: ../data/org.gnome.shell.gschema.xml.in.in.h:44
msgid "Arrangement of buttons on the titlebar"
msgstr "തലക്കെട്ടിനുള്ള ബാറില്‍ ബട്ടണുകളുടെ ക്രമീകരണം"
#: ../data/org.gnome.shell.gschema.xml.in.in.h:47
#: ../data/org.gnome.shell.gschema.xml.in.in.h:45
msgid ""
"This key overrides the key in org.gnome.desktop.wm.preferences when running "
"GNOME Shell."
msgstr ""
"ഗ്നോം ഷെല്‍ പ്രവര്‍ത്തിയ്ക്കുമ്പോള്‍ org.gnome.desktop.wm.preferences-ലുള്ള കീ ഈ കീ "
"തിരുത്തിയെഴുതുന്നു."
"ഗ്നോം ഷെല്‍ പ്രവര്‍ത്തിയ്ക്കുമ്പോള്‍ org.gnome.desktop.wm.preferences-ലുള്ള "
"കീ ഈ കീ തിരുത്തിയെഴുതുന്നു."
#: ../data/org.gnome.shell.gschema.xml.in.in.h:48
#: ../data/org.gnome.shell.gschema.xml.in.in.h:46
msgid "Enable edge tiling when dropping windows on screen edges"
msgstr "സ്ക്രീന്‍ കോണുകളില്‍ ജാലകങ്ങള്‍ എത്തിയ്ക്കുമ്പോള്‍ കോണ്‍ ചരിയ്ക്കുന്നതിനായി പ്രവര്‍ത്തന സജ്ജമാക്കുക"
msgstr ""
"സ്ക്രീന്‍ കോണുകളില്‍ ജാലകങ്ങള്‍ എത്തിയ്ക്കുമ്പോള്‍ കോണ്‍ ചരിയ്ക്കുന്നതിനായി "
"പ്രവര്‍ത്തന സജ്ജമാക്കുക"
#: ../data/org.gnome.shell.gschema.xml.in.in.h:49
#: ../data/org.gnome.shell.gschema.xml.in.in.h:47
msgid "Workspaces are managed dynamically"
msgstr "പണിയിടങ്ങള്‍ ഡയനാമിക്കായി കൈകാര്യം ചെയ്തിരിക്കുന്നു"
#: ../data/org.gnome.shell.gschema.xml.in.in.h:50
#: ../data/org.gnome.shell.gschema.xml.in.in.h:48
msgid "Workspaces only on primary monitor"
msgstr "പ്രധാന മോണിറ്ററില്‍ മാത്രം പണിയിടങ്ങള്‍"
@ -364,9 +382,12 @@ msgstr "എക്സ്റ്റെന്‍ഷന്‍"
#: ../js/extensionPrefs/main.js:189
msgid "Select an extension to configure using the combobox above."
msgstr "മുകളിലുള്ള കോമ്പോ ബോക്സ് ഉപയോഗിച്ചു് ക്രമീകരിയ്ക്കുന്നതിനുള്ളൊരു എക്സ്റ്റെന്‍ഷന്‍ തെര‍ഞ്ഞെടുക്കുക."
msgstr ""
"മുകളിലുള്ള കോമ്പോ ബോക്സ് ഉപയോഗിച്ചു് ക്രമീകരിയ്ക്കുന്നതിനുള്ളൊരു "
"എക്സ്റ്റെന്‍ഷന്‍ തെര‍ഞ്ഞെടുക്കുക."
#: ../js/gdm/loginDialog.js:405
#| msgid "Session..."
msgid "Session…"
msgstr "പ്രവര്‍ത്തനവേള..."
@ -377,32 +398,32 @@ msgstr "പ്രവര്‍ത്തനവേള..."
msgid "Not listed?"
msgstr "ലഭ്യമല്ലേ?"
#: ../js/gdm/loginDialog.js:787 ../js/ui/components/networkAgent.js:137
#: ../js/ui/components/polkitAgent.js:162 ../js/ui/endSessionDialog.js:376
#: ../js/gdm/loginDialog.js:786 ../js/ui/components/networkAgent.js:137
#: ../js/ui/components/polkitAgent.js:162 ../js/ui/endSessionDialog.js:375
#: ../js/ui/extensionDownloader.js:195 ../js/ui/shellMountOperation.js:399
#: ../js/ui/status/bluetooth.js:415 ../js/ui/unlockDialog.js:100
#: ../js/ui/status/bluetooth.js:415 ../js/ui/unlockDialog.js:99
#: ../js/ui/userMenu.js:938
msgid "Cancel"
msgstr "വേണ്ട"
#: ../js/gdm/loginDialog.js:803
#: ../js/gdm/loginDialog.js:802
msgctxt "button"
msgid "Sign In"
msgstr "അകത്തുകയറുക"
#: ../js/gdm/loginDialog.js:803
#: ../js/gdm/loginDialog.js:802
msgid "Next"
msgstr "അടുത്തത്"
#. TTLS and PEAP are actually much more complicated, but this complication
#. is not visible here since we only care about phase2 authentication
#. (and don't even care of which one)
#: ../js/gdm/loginDialog.js:918 ../js/ui/components/networkAgent.js:260
#: ../js/gdm/loginDialog.js:917 ../js/ui/components/networkAgent.js:260
#: ../js/ui/components/networkAgent.js:278
msgid "Username: "
msgstr "ഉപയോക്തൃ നാമം: "
#: ../js/gdm/loginDialog.js:1174
#: ../js/gdm/loginDialog.js:1173
msgid "Login Window"
msgstr "പ്രവേശന ജാലകം"
@ -455,23 +476,23 @@ msgstr "ആജ്ഞ പ്രാവര്‍ത്തികമാക്കാ
msgid "Execution of '%s' failed:"
msgstr "'%s' നടപ്പിലാക്കുന്നതില്‍ പരാജയപ്പെട്ടു:"
#: ../js/ui/appDisplay.js:351
#: ../js/ui/appDisplay.js:349
msgid "Frequent"
msgstr "ഇടയ്ക്കിടെ"
#: ../js/ui/appDisplay.js:358
#: ../js/ui/appDisplay.js:356
msgid "All"
msgstr "എല്ലാം"
#: ../js/ui/appDisplay.js:916
#: ../js/ui/appDisplay.js:914
msgid "New Window"
msgstr "പുതിയ വിന്‍ഡോ"
#: ../js/ui/appDisplay.js:919 ../js/ui/dash.js:284
#: ../js/ui/appDisplay.js:917 ../js/ui/dash.js:284
msgid "Remove from Favorites"
msgstr "ഇഷ്ടപ്പെട്ടവയില്‍ നിന്നും നീക്കം ചെയ്യുക"
#: ../js/ui/appDisplay.js:920
#: ../js/ui/appDisplay.js:918
msgid "Add to Favorites"
msgstr "ഇഷ്ടപ്പെട്ടവയിലേക്ക് ചേര്‍ക്കുക"
@ -508,7 +529,7 @@ msgctxt "event list time"
msgid "%H\\u2236%M"
msgstr "%H\\u2236%M"
#. Translators: Shown in calendar event list, if 12h format,
#. Transators: Shown in calendar event list, if 12h format,
#. \u2236 is a ratio character, similar to : and \u2009 is
#. a thin space
#: ../js/ui/calendar.js:77
@ -711,7 +732,8 @@ msgid ""
"Passwords or encryption keys are required to access the wireless network "
"'%s'."
msgstr ""
"വയര്‍ലെസ് നെറ്റ്‌വര്‍ക്ക് '%s'-ലേക്ക് പ്രവേശിക്കുന്നതിനായി രഹസ്യവാക്കുകള്‍ അല്ലെങ്കില്‍ എന്‍ക്രിപ്ഷന്‍ കീകള്‍ "
"വയര്‍ലെസ് നെറ്റ്‌വര്‍ക്ക് '%s'-ലേക്ക് പ്രവേശിക്കുന്നതിനായി രഹസ്യവാക്കുകള്‍ "
"അല്ലെങ്കില്‍ എന്‍ക്രിപ്ഷന്‍ കീകള്‍ "
"ആവശ്യമുണ്ടു്."
#: ../js/ui/components/networkAgent.js:314
@ -768,7 +790,7 @@ msgid "Sorry, that didn't work. Please try again."
msgstr "ക്ഷമിക്കണം, അതു ശരിയല്ല. ദയവായി വീണ്ടും ശ്രമിക്കുക."
#. Translators: this is a filename used for screencast recording
#: ../js/ui/components/recorder.js:47
#: ../js/ui/components/recorder.js:48
#, no-c-format
msgid "Screencast from %d %t"
msgstr "%d-ല്‍ നിന്നുള്ള സ്ക്രീന്‍കാസ്റ്റ്, %t-ല്‍"
@ -891,7 +913,8 @@ msgstr "%s നിങ്ങള്‍ക്കു് %s അയച്ചിരി
#: ../js/ui/components/telepathyClient.js:1206
#, c-format
msgid "%s would like permission to see when you are online"
msgstr "നിങ്ങള്‍ ഓണ്‍ലൈന്‍ ആകുമ്പോള്‍ കാണുന്നതിനുള്ള അനുമതി %s-നു് ആവശ്യമുണ്ടു്"
msgstr ""
"നിങ്ങള്‍ ഓണ്‍ലൈന്‍ ആകുമ്പോള്‍ കാണുന്നതിനുള്ള അനുമതി %s-നു് ആവശ്യമുണ്ടു്"
#: ../js/ui/components/telepathyClient.js:1298
msgid "Network error"
@ -964,7 +987,9 @@ msgstr "ഈ അക്കൌണ്ട് നിലവില്‍ സര്‍വ
#: ../js/ui/components/telepathyClient.js:1332
msgid ""
"Connection has been replaced by a new connection using the same resource"
msgstr "അതേ ശ്രോതസ്സ് ഉപയോഗിച്ചു് ഒരു പുതിയ കണക്ഷന്‍ ഉപയോഗിച്ചു് ഈ കണക്ഷന്‍ മാറ്റിസ്ഥാപിയ്ക്കുന്നു"
msgstr ""
"അതേ ശ്രോതസ്സ് ഉപയോഗിച്ചു് ഒരു പുതിയ കണക്ഷന്‍ ഉപയോഗിച്ചു് ഈ കണക്ഷന്‍ "
"മാറ്റിസ്ഥാപിയ്ക്കുന്നു"
#: ../js/ui/components/telepathyClient.js:1334
msgid "The account already exists on the server"
@ -981,14 +1006,17 @@ msgstr "സമ്മതപത്രം വീണ്ടും ആവശ്യപ
#: ../js/ui/components/telepathyClient.js:1340
msgid ""
"Certificate uses an insecure cipher algorithm or is cryptographically weak"
msgstr "സമ്മതപത്രം സുരക്ഷിതമല്ലാത്തൊരു സിഫര്‍ ആല്‍ഗോരിഥം ഉപയോഗിയ്ക്കുന്നു അല്ലെങ്കില്‍ ഉചിതമല്ല"
msgstr ""
"സമ്മതപത്രം സുരക്ഷിതമല്ലാത്തൊരു സിഫര്‍ ആല്‍ഗോരിഥം ഉപയോഗിയ്ക്കുന്നു "
"അല്ലെങ്കില്‍ ഉചിതമല്ല"
#: ../js/ui/components/telepathyClient.js:1342
msgid ""
"The length of the server certificate, or the depth of the server certificate "
"chain, exceed the limits imposed by the cryptography library"
msgstr ""
"സര്‍വറിന്റെ സമ്മതപത്രത്തിന്റെ വ്യാപ്തി, അല്ലെങ്കില്‍ സര്‍വര്‍ സമ്മതപത്ര ചെയിന്റെ വ്യാപ്തി, എന്നിവ "
"സര്‍വറിന്റെ സമ്മതപത്രത്തിന്റെ വ്യാപ്തി, അല്ലെങ്കില്‍ സര്‍വര്‍ സമ്മതപത്ര "
"ചെയിന്റെ വ്യാപ്തി, എന്നിവ "
"പരിധിയില്‍ കൂടുന്നു"
#: ../js/ui/components/telepathyClient.js:1344
@ -1057,7 +1085,8 @@ msgstr "പുറത്ത് കടക്കുക"
#: ../js/ui/endSessionDialog.js:65
msgid "Click Log Out to quit these applications and log out of the system."
msgstr ""
"ഈ പ്രയോഗങ്ങളില്‍ നിന്നും പുറത്തു് കടക്കുന്നതിനായി പുറത്തു കടക്കുക ക്ലിക്ക് ചെയ്തു് സിസ്റ്റത്തില്‍ നിന്നും "
"ഈ പ്രയോഗങ്ങളില്‍ നിന്നും പുറത്തു് കടക്കുന്നതിനായി പുറത്തു കടക്കുക ക്ലിക്ക് "
"ചെയ്തു് സിസ്റ്റത്തില്‍ നിന്നും "
"പുറത്തു് കടക്കുക."
#: ../js/ui/endSessionDialog.js:67
@ -1091,7 +1120,8 @@ msgstr "നിര്‍ത്തുക"
#: ../js/ui/endSessionDialog.js:84
msgid "Click Power Off to quit these applications and power off the system."
msgstr ""
"ഈ പ്രയോഗങ്ങളില്‍ നിന്നും പുറത്തു് കടക്കുന്നതിനായി പവര്‍ ഓഫ് ചെയ്യുക ക്ലിക്ക് ചെയ്തു സിസ്റ്റിന്റെ പവര്‍ "
"ഈ പ്രയോഗങ്ങളില്‍ നിന്നും പുറത്തു് കടക്കുന്നതിനായി പവര്‍ ഓഫ് ചെയ്യുക ക്ലിക്ക് "
"ചെയ്തു സിസ്റ്റിന്റെ പവര്‍ "
"ഓഫ് ചെയ്യുക."
#: ../js/ui/endSessionDialog.js:86
@ -1122,7 +1152,8 @@ msgstr "പുനരാരംഭിക്കുക"
#: ../js/ui/endSessionDialog.js:101
msgid "Click Restart to quit these applications and restart the system."
msgstr "ഈ പ്രയോഗങ്ങള്‍ നിറുത്തി സിസ്റ്റം പുനരാരംഭിക്കുവാന്‍ പുനരാരംഭിക്കൂ അമര്‍ത്തുക"
msgstr ""
"ഈ പ്രയോഗങ്ങള്‍ നിറുത്തി സിസ്റ്റം പുനരാരംഭിക്കുവാന്‍ പുനരാരംഭിക്കൂ അമര്‍ത്തുക"
#: ../js/ui/endSessionDialog.js:103
#, c-format
@ -1142,7 +1173,9 @@ msgstr "ഇന്‍സ്റ്റോള്‍"
#: ../js/ui/extensionDownloader.js:204
#, c-format
msgid "Download and install '%s' from extensions.gnome.org?"
msgstr "extensions.gnome.org ഇല്‍ നിന്നും '%s' ഡൗണ്‍ലോട് ചെയ്ത് ഇന്‍സ്റ്റോള്‍ ചെയ്യേണമോ?"
msgstr ""
"extensions.gnome.org ഇല്‍ നിന്നും '%s' ഡൗണ്‍ലോട് ചെയ്ത് ഇന്‍സ്റ്റോള്‍ "
"ചെയ്യേണമോ?"
#: ../js/ui/keyboard.js:619 ../js/ui/status/keyboard.js:314
#: ../js/ui/status/power.js:211
@ -1171,9 +1204,7 @@ msgstr "പിശകുകള്‍ കാണിക്കുക"
msgid "Enabled"
msgstr "പ്രവര്‍ത്തനക്ഷമമാക്കി"
#. translators:
#. * The device has been disabled
#: ../js/ui/lookingGlass.js:769 ../src/gvc/gvc-mixer-control.c:1830
#: ../js/ui/lookingGlass.js:769
msgid "Disabled"
msgstr "പ്രവര്‍ത്തനരഹിതമാക്കി"
@ -1206,6 +1237,7 @@ msgid "Remove"
msgstr "നീക്കം ചെയ്യുക"
#: ../js/ui/messageTray.js:1501
#| msgid "No Messages"
msgid "Clear Messages"
msgstr "സന്ദേശങ്ങള്‍ വെടിപ്പാക്കുക"
@ -1213,35 +1245,35 @@ msgstr "സന്ദേശങ്ങള്‍ വെടിപ്പാക്ക
msgid "Notification Settings"
msgstr "അറിയിപ്പു് ക്രമീകരണങ്ങള്‍"
#: ../js/ui/messageTray.js:1710
#: ../js/ui/messageTray.js:1709
msgid "No Messages"
msgstr "സന്ദേശങ്ങളില്ല"
#: ../js/ui/messageTray.js:1783
#: ../js/ui/messageTray.js:1782
msgid "Message Tray"
msgstr "സന്ദേശത്തിന്റെ ട്രേ"
#: ../js/ui/messageTray.js:2801
#: ../js/ui/messageTray.js:2810
msgid "System Information"
msgstr "സിസ്റ്റത്തെക്കുറിച്ചുള്ള വിവരം"
#: ../js/ui/notificationDaemon.js:629 ../src/shell-app.c:378
#: ../js/ui/notificationDaemon.js:629 ../src/shell-app.c:374
msgctxt "program"
msgid "Unknown"
msgstr "അജ്ഞാതം"
#: ../js/ui/overviewControls.js:472 ../js/ui/screenShield.js:149
#: ../js/ui/overviewControls.js:463 ../js/ui/screenShield.js:149
#, c-format
msgid "%d new message"
msgid_plural "%d new messages"
msgstr[0] "%d പുതിയ സന്ദേശം"
msgstr[1] "%d പുതിയ സന്ദേശങ്ങള്‍"
#: ../js/ui/overview.js:82
#: ../js/ui/overview.js:84
msgid "Undo"
msgstr "വേണ്ട"
#: ../js/ui/overview.js:127
#: ../js/ui/overview.js:129
msgid "Overview"
msgstr "അവലോകനം"
@ -1249,21 +1281,22 @@ msgstr "അവലോകനം"
#. in the search entry when no search is
#. active; it should not exceed ~30
#. characters.
#: ../js/ui/overview.js:260
#: ../js/ui/overview.js:271
#| msgid "Type to search..."
msgid "Type to search…"
msgstr "തെരയുന്നതിനായി ടൈപ്പ് ചെയ്യുക..."
#: ../js/ui/panel.js:641
#: ../js/ui/panel.js:612
msgid "Quit"
msgstr "നിര്‍ത്തുക"
#. Translators: If there is no suitable word for "Activities"
#. in your language, you can use the word for "Overview".
#: ../js/ui/panel.js:692
#: ../js/ui/panel.js:636
msgid "Activities"
msgstr "പ്രവര്‍ത്തനങ്ങള്‍"
#: ../js/ui/panel.js:989
#: ../js/ui/panel.js:933
msgid "Top Bar"
msgstr "മുകളിലുള്ള ബാര്‍"
@ -1272,15 +1305,15 @@ 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:718
#: ../js/ui/popupMenu.js:727
msgid "toggle-switch-us"
msgstr "toggle-switch-us"
#: ../js/ui/runDialog.js:74
#: ../js/ui/runDialog.js:73
msgid "Enter a Command"
msgstr "ഒരു കമാന്‍ഡ് നല്‍കുക"
#: ../js/ui/runDialog.js:110
#: ../js/ui/runDialog.js:109
msgid "Close"
msgstr "അടക്കുക"
@ -1301,7 +1334,7 @@ msgstr[1] "%d പുതിയ അറിയിപ്പുകള്‍"
msgid "Lock"
msgstr "പൂട്ടുക"
#: ../js/ui/screenShield.js:641
#: ../js/ui/screenShield.js:637
msgid "GNOME needs to lock the screen"
msgstr "ഗ്നോമിന് സ്ക്രീന്‍ പൂട്ടണം"
@ -1312,15 +1345,17 @@ msgstr "ഗ്നോമിന് സ്ക്രീന്‍ പൂട്ടണ
#.
#. XXX: another option is to kick the user into the gdm login
#. screen, where we're not affected by grabs
#: ../js/ui/screenShield.js:762 ../js/ui/screenShield.js:1198
#: ../js/ui/screenShield.js:758 ../js/ui/screenShield.js:1194
#| msgid "Unable to connect to %s"
msgid "Unable to lock"
msgstr "പൂട്ടുവാന്‍ സാധ്യമല്ല"
#: ../js/ui/screenShield.js:763 ../js/ui/screenShield.js:1199
#: ../js/ui/screenShield.js:759 ../js/ui/screenShield.js:1195
msgid "Lock was blocked by an application"
msgstr "പൂട്ടുന്ന സംവിധാനം ഒരു പ്രയോഗം തടസ്സപ്പെടുത്തിയിരിയ്ക്കുന്നു"
#: ../js/ui/searchDisplay.js:453
#| msgid "Searching..."
msgid "Searching…"
msgstr "തെരയുന്നു..."
@ -1336,11 +1371,11 @@ msgstr "പകര്‍ത്തുക"
msgid "Paste"
msgstr "ഒട്ടിയ്ക്കുക"
#: ../js/ui/shellEntry.js:101
#: ../js/ui/shellEntry.js:106
msgid "Show Text"
msgstr "പദാവലി കാണിക്കുക"
#: ../js/ui/shellEntry.js:103
#: ../js/ui/shellEntry.js:108
msgid "Hide Text"
msgstr "പദാവലി മറക്കുക"
@ -1352,7 +1387,7 @@ msgstr "രഹസ്യവാക്ക്"
msgid "Remember Password"
msgstr "രഹസ്യവാക്ക് ഓര്‍ത്തു് വയ്ക്കുക"
#: ../js/ui/shellMountOperation.js:403 ../js/ui/unlockDialog.js:114
#: ../js/ui/shellMountOperation.js:403 ../js/ui/unlockDialog.js:113
msgid "Unlock"
msgstr "പൂട്ട് തുറക്കുക"
@ -1416,10 +1451,12 @@ msgid "Visibility"
msgstr "കാഴ്ച"
#: ../js/ui/status/bluetooth.js:59
#| msgid "Send Files to Device..."
msgid "Send Files to Device…"
msgstr "ഡിവൈസിലേക്കു് ഫയലുകള്‍ അയയ്ക്കുക..."
#: ../js/ui/status/bluetooth.js:60
#| msgid "Set Up a New Device..."
msgid "Set Up a New Device…"
msgstr "പുതിയൊരു ഡിവൈസ് സജ്ജമാക്കുക..."
@ -1446,6 +1483,7 @@ msgid "connecting..."
msgstr "ബന്ധിപ്പിയ്ക്കുന്നു...."
#: ../js/ui/status/bluetooth.js:239
#| msgid "Send Files..."
msgid "Send Files…"
msgstr "ഫയലുകള്‍ അയയ്ക്കുക..."
@ -1658,6 +1696,7 @@ msgstr "ഊര്‍ജ്ജ ക്രമീകരണങ്ങള്‍"
#. 0 is reported when UPower does not have enough data
#. to estimate battery life
#: ../js/ui/status/power.js:99
#| msgid "Estimating..."
msgid "Estimating…"
msgstr "കണക്കുകൂട്ടുന്നു..."
@ -1757,11 +1796,11 @@ msgstr "ഒച്ച"
msgid "Microphone"
msgstr "മൈക്രോഫോണ്‍"
#: ../js/ui/unlockDialog.js:125
#: ../js/ui/unlockDialog.js:124
msgid "Log in as another user"
msgstr "മറ്റൊരു ഉപയോക്താവായി പ്രവേശിയ്ക്കുക"
#: ../js/ui/unlockDialog.js:146
#: ../js/ui/unlockDialog.js:145
msgid "Unlock Window"
msgstr "ജാലകത്തിന്റെ പൂട്ടു തുറക്കുക"
@ -1814,8 +1853,10 @@ 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."
msgstr ""
"ചാറ്റ് സന്ദേശങ്ങള്‍ എന്ന പോലെ അറിയിപ്പുകള്‍ പ്രവര്‍ത്തന രഹിതമാക്കുന്നു. മറ്റുള്ളവരുടെ ചാറ്റ് സന്ദേശങ്ങള്‍ "
"നിങ്ങള്‍ക്കു് കാണുവാന്‍ സാധ്യമല്ല എന്നു് നിങ്ങളുടെ ഓണ്‍ലൈന്‍ അവസ്ഥയില്‍ വ്യക്തമാക്കുന്നു."
"ചാറ്റ് സന്ദേശങ്ങള്‍ എന്ന പോലെ അറിയിപ്പുകള്‍ പ്രവര്‍ത്തന രഹിതമാക്കുന്നു. "
"മറ്റുള്ളവരുടെ ചാറ്റ് സന്ദേശങ്ങള്‍ "
"നിങ്ങള്‍ക്കു് കാണുവാന്‍ സാധ്യമല്ല എന്നു് നിങ്ങളുടെ ഓണ്‍ലൈന്‍ അവസ്ഥയില്‍ "
"വ്യക്തമാക്കുന്നു."
#: ../js/ui/userMenu.js:888
msgid "Other users are logged in."
@ -1868,28 +1909,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] "%u ഔട്ട്പുട്ട്"
msgstr[1] "%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] "%u ഇന്‍പുട്ട്"
msgstr[1] "%u ഇന്‍പുട്ടുകള്‍"
#: ../src/gvc/gvc-mixer-control.c:2371
msgid "System Sounds"
msgstr "സിസ്റ്റം ശബ്ദങ്ങള്‍"
#: ../src/main.c:347
msgid "Print version"
msgstr "പ്രിന്റ് ചെയ്യുവാന്‍ സാധിയ്ക്കുന്ന പതിപ്പു്"
@ -1906,7 +1925,7 @@ msgstr "ഒരു പ്രത്യേക മോഡ് ഉപയോഗിയ്
msgid "List possible modes"
msgstr "സാധ്യമായ മോഡുകള്‍ ലഭ്യമാക്കുക"
#: ../src/shell-app.c:626
#: ../src/shell-app.c:622
#, c-format
msgid "Failed to launch '%s'"
msgstr "'%s' ലഭ്യമാക്കുന്നതില്‍ പരാജയം"
@ -1936,6 +1955,19 @@ msgstr "ഉപയോക്താവു് ആധികാരികത ഉറപ
#~ msgid "More..."
#~ msgstr "കൂടുതല്‍..."
#~ msgid "%u Output"
#~ msgid_plural "%u Outputs"
#~ msgstr[0] "%u ഔട്ട്പുട്ട്"
#~ msgstr[1] "%u ഔട്ട്പുട്ടുകള്‍"
#~ msgid "%u Input"
#~ msgid_plural "%u Inputs"
#~ msgstr[0] "%u ഇന്‍പുട്ട്"
#~ msgstr[1] "%u ഇന്‍പുട്ടുകള്‍"
#~ msgid "System Sounds"
#~ msgstr "സിസ്റ്റം ശബ്ദങ്ങള്‍"
#~ msgctxt "event list time"
#~ msgid "%H:%M"
#~ msgstr "%H:%M"

550
po/nb.po

File diff suppressed because it is too large Load Diff

628
po/sk.po

File diff suppressed because it is too large Load Diff

407
po/sl.po

File diff suppressed because it is too large Load Diff

163
po/sr.po
View File

@ -8,7 +8,7 @@ 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: 2013-03-31 01:48+0000\n"
"POT-Creation-Date: 2013-03-04 08:38+0000\n"
"PO-Revision-Date: 2013-03-08 20:26+0200\n"
"Last-Translator: Мирослав Николић <miroslavnikolic@rocketmail.com>\n"
"Language-Team: Serbian <gnom@prevod.org>\n"
@ -16,8 +16,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=4; plural=n==1? 3 : n%10==1 && n%100!=11 ? 0 : n"
"%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
"Plural-Forms: nplurals=4; plural=n==1? 3 : n%10==1 && n%100!=11 ? 0 : "
"n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
"X-Project-Style: gnome\n"
#: ../data/50-gnome-shell-screenshot.xml.in.h:1
@ -365,52 +365,53 @@ msgid "Select an extension to configure using the combobox above."
msgstr "Изаберите проширење за подешавање користећи прозорче за избор."
#: ../js/gdm/loginDialog.js:405
#| msgid "Session..."
msgid "Session…"
msgstr "Сесија…"
#. translators: this message is shown below the user list on the
#. login screen. It can be activated to reveal an entry for
#. manually entering the username.
#: ../js/gdm/loginDialog.js:630
#: ../js/gdm/loginDialog.js:629
msgid "Not listed?"
msgstr "Није на списку?"
#: ../js/gdm/loginDialog.js:786 ../js/ui/components/networkAgent.js:137
#: ../js/gdm/loginDialog.js:783 ../js/ui/components/networkAgent.js:137
#: ../js/ui/components/polkitAgent.js:162 ../js/ui/endSessionDialog.js:375
#: ../js/ui/extensionDownloader.js:195 ../js/ui/shellMountOperation.js:399
#: ../js/ui/status/bluetooth.js:415 ../js/ui/unlockDialog.js:99
#: ../js/ui/userMenu.js:938
#: ../js/ui/status/bluetooth.js:415 ../js/ui/unlockDialog.js:126
#: ../js/ui/userMenu.js:934
msgid "Cancel"
msgstr "Откажи"
#: ../js/gdm/loginDialog.js:802
#: ../js/gdm/loginDialog.js:799
msgctxt "button"
msgid "Sign In"
msgstr "Пријави ме"
#: ../js/gdm/loginDialog.js:802
#: ../js/gdm/loginDialog.js:799
msgid "Next"
msgstr "Даље"
#. TTLS and PEAP are actually much more complicated, but this complication
#. is not visible here since we only care about phase2 authentication
#. (and don't even care of which one)
#: ../js/gdm/loginDialog.js:917 ../js/ui/components/networkAgent.js:260
#: ../js/gdm/loginDialog.js:904 ../js/ui/components/networkAgent.js:260
#: ../js/ui/components/networkAgent.js:278
msgid "Username: "
msgstr "Корисничко име: "
#: ../js/gdm/loginDialog.js:1173
#: ../js/gdm/loginDialog.js:1157
msgid "Login Window"
msgstr "Прозор за пријављивање"
#. Translators: accessible name of the power menu in the login screen
#: ../js/gdm/powerMenu.js:36
msgid "Power"
msgstr "Напајање"
msgstr "Угаси"
#: ../js/gdm/powerMenu.js:93 ../js/ui/userMenu.js:696 ../js/ui/userMenu.js:700
#: ../js/ui/userMenu.js:816
#: ../js/gdm/powerMenu.js:93 ../js/ui/userMenu.js:695 ../js/ui/userMenu.js:699
#: ../js/ui/userMenu.js:815
msgid "Suspend"
msgstr "Обустави"
@ -418,58 +419,58 @@ msgstr "Обустави"
msgid "Restart"
msgstr "Поново покрени"
#: ../js/gdm/powerMenu.js:103 ../js/ui/userMenu.js:698
#: ../js/ui/userMenu.js:700 ../js/ui/userMenu.js:815 ../js/ui/userMenu.js:942
#: ../js/gdm/powerMenu.js:103 ../js/ui/userMenu.js:697
#: ../js/ui/userMenu.js:699 ../js/ui/userMenu.js:814 ../js/ui/userMenu.js:938
msgid "Power Off"
msgstr "Искључи"
msgstr "Угаси"
#: ../js/gdm/util.js:249
#: ../js/gdm/util.js:182
msgid "Authentication error"
msgstr "Грешка потврђивања идентитета"
#. Translators: this message is shown below the password entry field
#. to indicate the user can swipe their finger instead
#: ../js/gdm/util.js:366
#: ../js/gdm/util.js:299
msgid "(or swipe finger)"
msgstr "(или лупите прст)"
#: ../js/gdm/util.js:391
#: ../js/gdm/util.js:324
#, c-format
msgid "(e.g., user or %s)"
msgstr "(нпр., корисник или %s)"
#: ../js/misc/util.js:97
#: ../js/misc/util.js:94
msgid "Command not found"
msgstr "Наредба није нађена"
#. Replace "Error invoking GLib.shell_parse_argv: " with
#. something nicer
#: ../js/misc/util.js:130
#: ../js/misc/util.js:127
msgid "Could not parse command:"
msgstr "Не могу да обрадим наредбу:"
#: ../js/misc/util.js:138
#: ../js/misc/util.js:135
#, c-format
msgid "Execution of '%s' failed:"
msgstr "Није успело покретање „%s“:"
#: ../js/ui/appDisplay.js:349
#: ../js/ui/appDisplay.js:348
msgid "Frequent"
msgstr "Често"
#: ../js/ui/appDisplay.js:356
#: ../js/ui/appDisplay.js:355
msgid "All"
msgstr "Све"
#: ../js/ui/appDisplay.js:914
#: ../js/ui/appDisplay.js:913
msgid "New Window"
msgstr "Нови прозор"
#: ../js/ui/appDisplay.js:917 ../js/ui/dash.js:284
#: ../js/ui/appDisplay.js:916 ../js/ui/dash.js:284
msgid "Remove from Favorites"
msgstr "Уклони из омиљених"
#: ../js/ui/appDisplay.js:918
#: ../js/ui/appDisplay.js:917
msgid "Add to Favorites"
msgstr "Додај у омиљене"
@ -483,7 +484,7 @@ msgstr "„%s“ је додат међу омиљене."
msgid "%s has been removed from your favorites."
msgstr "„%s“ је уклоњен из омиљених."
#: ../js/ui/backgroundMenu.js:19 ../js/ui/userMenu.js:789
#: ../js/ui/backgroundMenu.js:19 ../js/ui/userMenu.js:788
msgid "Settings"
msgstr "Подешавања"
@ -506,7 +507,7 @@ msgctxt "event list time"
msgid "%H\\u2236%M"
msgstr "%H\\u2236%M"
#. Translators: Shown in calendar event list, if 12h format,
#. Transators: Shown in calendar event list, if 12h format,
#. \u2236 is a ratio character, similar to : and \u2009 is
#. a thin space
#: ../js/ui/calendar.js:77
@ -608,35 +609,35 @@ msgid "S"
msgstr "Суб"
#. Translators: Text to show if there are no events
#: ../js/ui/calendar.js:720
#: ../js/ui/calendar.js:692
msgid "Nothing Scheduled"
msgstr "Ништа планирано"
#. Translators: Shown on calendar heading when selected day occurs on current year
#: ../js/ui/calendar.js:736
#: ../js/ui/calendar.js:708
msgctxt "calendar heading"
msgid "%A, %B %d"
msgstr "%A, %B %d"
#. Translators: Shown on calendar heading when selected day occurs on different year
#: ../js/ui/calendar.js:739
#: ../js/ui/calendar.js:711
msgctxt "calendar heading"
msgid "%A, %B %d, %Y"
msgstr "%A, %B %d, %Y"
#: ../js/ui/calendar.js:749
#: ../js/ui/calendar.js:721
msgid "Today"
msgstr "Данас"
#: ../js/ui/calendar.js:753
#: ../js/ui/calendar.js:725
msgid "Tomorrow"
msgstr "Сутра"
#: ../js/ui/calendar.js:764
#: ../js/ui/calendar.js:736
msgid "This week"
msgstr "Ове недеље"
#: ../js/ui/calendar.js:772
#: ../js/ui/calendar.js:744
msgid "Next week"
msgstr "Следеће недеље"
@ -652,12 +653,12 @@ msgstr "Спољни уређај је искључен"
msgid "Removable Devices"
msgstr "Уклоњиви уређаји"
#: ../js/ui/components/autorunManager.js:594
#: ../js/ui/components/autorunManager.js:593
#, c-format
msgid "Open with %s"
msgstr "Отвори програмом %s"
#: ../js/ui/components/autorunManager.js:620
#: ../js/ui/components/autorunManager.js:619
msgid "Eject"
msgstr "Избаци"
@ -1037,7 +1038,7 @@ msgstr "Подешавања датума и времена"
#. Translators: This is the date format to use when the calendar popup is
#. * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
#.
#: ../js/ui/dateMenu.js:215
#: ../js/ui/dateMenu.js:205
msgid "%A %B %e, %Y"
msgstr "%A, %e. %b., %R"
@ -1212,6 +1213,7 @@ msgid "Remove"
msgstr "Уклони"
#: ../js/ui/messageTray.js:1501
#| msgid "No Messages"
msgid "Clear Messages"
msgstr "Очисти поруке"
@ -1219,15 +1221,15 @@ msgstr "Очисти поруке"
msgid "Notification Settings"
msgstr "Поставке обавештења"
#: ../js/ui/messageTray.js:1709
#: ../js/ui/messageTray.js:1707
msgid "No Messages"
msgstr "Нема порука"
#: ../js/ui/messageTray.js:1782
#: ../js/ui/messageTray.js:1787
msgid "Message Tray"
msgstr "Фиока порука"
#: ../js/ui/messageTray.js:2810
#: ../js/ui/messageTray.js:2864
msgid "System Information"
msgstr "Подаци о систему"
@ -1236,7 +1238,7 @@ msgctxt "program"
msgid "Unknown"
msgstr "Непознат"
#: ../js/ui/overviewControls.js:463 ../js/ui/screenShield.js:149
#: ../js/ui/overviewControls.js:460 ../js/ui/screenShield.js:153
#, c-format
msgid "%d new message"
msgid_plural "%d new messages"
@ -1245,7 +1247,7 @@ msgstr[1] "%d нове поруке"
msgstr[2] "%d нових порука"
msgstr[3] "Једна нова порука"
#: ../js/ui/overview.js:84
#: ../js/ui/overview.js:82
msgid "Undo"
msgstr "Опозови"
@ -1257,21 +1259,22 @@ msgstr "Преглед"
#. in the search entry when no search is
#. active; it should not exceed ~30
#. characters.
#: ../js/ui/overview.js:271
#: ../js/ui/overview.js:284
#| msgid "Type to search..."
msgid "Type to search…"
msgstr "Упишите текст за претрагу…"
#: ../js/ui/panel.js:612
#: ../js/ui/panel.js:613
msgid "Quit"
msgstr "Изађи"
#. Translators: If there is no suitable word for "Activities"
#. in your language, you can use the word for "Overview".
#: ../js/ui/panel.js:636
#: ../js/ui/panel.js:642
msgid "Activities"
msgstr "Активности"
#: ../js/ui/panel.js:933
#: ../js/ui/panel.js:983
msgid "Top Bar"
msgstr "Горња трака"
@ -1284,21 +1287,21 @@ msgstr "Горња трака"
msgid "toggle-switch-us"
msgstr "toggle-switch-intl"
#: ../js/ui/runDialog.js:73
#: ../js/ui/runDialog.js:205
msgid "Enter a Command"
msgstr "Унесите наредбу"
#: ../js/ui/runDialog.js:109
#: ../js/ui/runDialog.js:241
msgid "Close"
msgstr "Затвори"
#. Translators: This is a time format for a date in
#. long format
#: ../js/ui/screenShield.js:86
#: ../js/ui/screenShield.js:90
msgid "%A, %B %d"
msgstr "%A, %d. %B"
#: ../js/ui/screenShield.js:151
#: ../js/ui/screenShield.js:155
#, c-format
msgid "%d new notification"
msgid_plural "%d new notifications"
@ -1307,11 +1310,11 @@ msgstr[1] "%d нова обавештења"
msgstr[2] "%d нових обавештења"
msgstr[3] "Једно ново обавештење"
#: ../js/ui/screenShield.js:438 ../js/ui/userMenu.js:807
#: ../js/ui/screenShield.js:442 ../js/ui/userMenu.js:806
msgid "Lock"
msgstr "Закључај"
#: ../js/ui/screenShield.js:640
#: ../js/ui/screenShield.js:639
msgid "GNOME needs to lock the screen"
msgstr "Гном мора да закључа екран"
@ -1322,19 +1325,21 @@ msgstr "Гном мора да закључа екран"
#.
#. XXX: another option is to kick the user into the gdm login
#. screen, where we're not affected by grabs
#: ../js/ui/screenShield.js:761 ../js/ui/screenShield.js:1197
#: ../js/ui/screenShield.js:758 ../js/ui/screenShield.js:1169
#| msgid "Unable to connect to %s"
msgid "Unable to lock"
msgstr "Не могу да закључам"
#: ../js/ui/screenShield.js:762 ../js/ui/screenShield.js:1198
#: ../js/ui/screenShield.js:759 ../js/ui/screenShield.js:1170
msgid "Lock was blocked by an application"
msgstr "Неки програм је блокирао закључавање"
#: ../js/ui/searchDisplay.js:453
#: ../js/ui/searchDisplay.js:431
#| msgid "Searching..."
msgid "Searching…"
msgstr "Тражим…"
#: ../js/ui/searchDisplay.js:497
#: ../js/ui/searchDisplay.js:475
msgid "No results."
msgstr "Нема одговарајућих резултата."
@ -1346,11 +1351,11 @@ msgstr "Умножи"
msgid "Paste"
msgstr "Убаци"
#: ../js/ui/shellEntry.js:106
#: ../js/ui/shellEntry.js:105
msgid "Show Text"
msgstr "Прикажи текст"
#: ../js/ui/shellEntry.js:108
#: ../js/ui/shellEntry.js:107
msgid "Hide Text"
msgstr "Сакриј текст"
@ -1362,7 +1367,7 @@ msgstr "Лозинка"
msgid "Remember Password"
msgstr "Запамти лозинку"
#: ../js/ui/shellMountOperation.js:403 ../js/ui/unlockDialog.js:113
#: ../js/ui/shellMountOperation.js:403 ../js/ui/unlockDialog.js:140
msgid "Unlock"
msgstr "Откључај"
@ -1426,10 +1431,12 @@ msgid "Visibility"
msgstr "Видљивост"
#: ../js/ui/status/bluetooth.js:59
#| msgid "Send Files to Device..."
msgid "Send Files to Device…"
msgstr "Пошаљи датотеке на уређај…"
#: ../js/ui/status/bluetooth.js:60
#| msgid "Set Up a New Device..."
msgid "Set Up a New Device…"
msgstr "Подеси нови уређај…"
@ -1456,6 +1463,7 @@ msgid "connecting..."
msgstr "повезујем се..."
#: ../js/ui/status/bluetooth.js:239
#| msgid "Send Files..."
msgid "Send Files…"
msgstr "Пошаљи датотеке…"
@ -1668,6 +1676,7 @@ msgstr "Подешавања напајања"
#. 0 is reported when UPower does not have enough data
#. to estimate battery life
#: ../js/ui/status/power.js:99
#| msgid "Estimating..."
msgid "Estimating…"
msgstr "Приближно…"
@ -1775,11 +1784,11 @@ msgstr "Јачина звука"
msgid "Microphone"
msgstr "Микрофон"
#: ../js/ui/unlockDialog.js:124
#: ../js/ui/unlockDialog.js:151
msgid "Log in as another user"
msgstr "Пријавите се као други корсник"
#: ../js/ui/unlockDialog.js:145
#: ../js/ui/unlockDialog.js:177
msgid "Unlock Window"
msgstr "Откључај прозор"
@ -1807,27 +1816,27 @@ msgstr "Мирује"
msgid "Offline"
msgstr "Ван мреже"
#: ../js/ui/userMenu.js:781
#: ../js/ui/userMenu.js:780
msgid "Notifications"
msgstr "Обавештења"
#: ../js/ui/userMenu.js:797
#: ../js/ui/userMenu.js:796
msgid "Switch User"
msgstr "Промени корисника"
#: ../js/ui/userMenu.js:802
#: ../js/ui/userMenu.js:801
msgid "Log Out"
msgstr "Одјави ме"
#: ../js/ui/userMenu.js:822
#: ../js/ui/userMenu.js:821
msgid "Install Updates & Restart"
msgstr "Инсталирај ажурирања и поново покрени"
#: ../js/ui/userMenu.js:840
#: ../js/ui/userMenu.js:839
msgid "Your chat status will be set to busy"
msgstr "Ваше стање ћаскања ће бити постављено на заузето"
#: ../js/ui/userMenu.js:841
#: ../js/ui/userMenu.js:840
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."
@ -1836,22 +1845,20 @@ msgstr ""
"мрежи је подешено тако да ће остали знати да ви не можете видети њихове "
"поруке."
#: ../js/ui/userMenu.js:888
#: ../js/ui/userMenu.js:886
msgid "Other users are logged in."
msgstr "Други корисници су пријављени."
#: ../js/ui/userMenu.js:893
#: ../js/ui/userMenu.js:891
msgid "Shutting down might cause them to lose unsaved work."
msgstr "Искључивање може да доведе до тога да изгубе несачувани рад."
#. Translators: Remote here refers to a remote session, like a ssh login
#: ../js/ui/userMenu.js:921
#: ../js/ui/userMenu.js:918
#, c-format
msgid "%s (remote)"
msgstr "%s (удаљено)"
#. Translators: Console here refers to a tty like a VT console
#: ../js/ui/userMenu.js:924
#: ../js/ui/userMenu.js:920
#, c-format
msgid "%s (console)"
msgstr "%s (љуска)"
@ -1864,7 +1871,7 @@ msgstr "Програми"
msgid "Search"
msgstr "Тражи"
#: ../js/ui/wanda.js:77
#: ../js/ui/wanda.js:92
#, c-format
msgid ""
"Sorry, no wisdom for you today:\n"
@ -1873,7 +1880,7 @@ msgstr ""
"Извините, данас за вас нема мудрости:\n"
"%s"
#: ../js/ui/wanda.js:81
#: ../js/ui/wanda.js:96
#, c-format
msgid "%s the Oracle says"
msgstr "Пророк је рекао %s"

View File

@ -8,7 +8,7 @@ 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: 2013-03-31 01:48+0000\n"
"POT-Creation-Date: 2013-03-04 08:38+0000\n"
"PO-Revision-Date: 2013-03-08 20:26+0200\n"
"Last-Translator: Miroslav Nikolić <miroslavnikolic@rocketmail.com>\n"
"Language-Team: Serbian <gnom@prevod.org>\n"
@ -16,8 +16,8 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=4; plural=n==1? 3 : n%10==1 && n%100!=11 ? 0 : n"
"%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
"Plural-Forms: nplurals=4; plural=n==1? 3 : n%10==1 && n%100!=11 ? 0 : "
"n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
"X-Project-Style: gnome\n"
#: ../data/50-gnome-shell-screenshot.xml.in.h:1
@ -365,52 +365,53 @@ msgid "Select an extension to configure using the combobox above."
msgstr "Izaberite proširenje za podešavanje koristeći prozorče za izbor."
#: ../js/gdm/loginDialog.js:405
#| msgid "Session..."
msgid "Session…"
msgstr "Sesija…"
#. translators: this message is shown below the user list on the
#. login screen. It can be activated to reveal an entry for
#. manually entering the username.
#: ../js/gdm/loginDialog.js:630
#: ../js/gdm/loginDialog.js:629
msgid "Not listed?"
msgstr "Nije na spisku?"
#: ../js/gdm/loginDialog.js:786 ../js/ui/components/networkAgent.js:137
#: ../js/gdm/loginDialog.js:783 ../js/ui/components/networkAgent.js:137
#: ../js/ui/components/polkitAgent.js:162 ../js/ui/endSessionDialog.js:375
#: ../js/ui/extensionDownloader.js:195 ../js/ui/shellMountOperation.js:399
#: ../js/ui/status/bluetooth.js:415 ../js/ui/unlockDialog.js:99
#: ../js/ui/userMenu.js:938
#: ../js/ui/status/bluetooth.js:415 ../js/ui/unlockDialog.js:126
#: ../js/ui/userMenu.js:934
msgid "Cancel"
msgstr "Otkaži"
#: ../js/gdm/loginDialog.js:802
#: ../js/gdm/loginDialog.js:799
msgctxt "button"
msgid "Sign In"
msgstr "Prijavi me"
#: ../js/gdm/loginDialog.js:802
#: ../js/gdm/loginDialog.js:799
msgid "Next"
msgstr "Dalje"
#. TTLS and PEAP are actually much more complicated, but this complication
#. is not visible here since we only care about phase2 authentication
#. (and don't even care of which one)
#: ../js/gdm/loginDialog.js:917 ../js/ui/components/networkAgent.js:260
#: ../js/gdm/loginDialog.js:904 ../js/ui/components/networkAgent.js:260
#: ../js/ui/components/networkAgent.js:278
msgid "Username: "
msgstr "Korisničko ime: "
#: ../js/gdm/loginDialog.js:1173
#: ../js/gdm/loginDialog.js:1157
msgid "Login Window"
msgstr "Prozor za prijavljivanje"
#. Translators: accessible name of the power menu in the login screen
#: ../js/gdm/powerMenu.js:36
msgid "Power"
msgstr "Napajanje"
msgstr "Ugasi"
#: ../js/gdm/powerMenu.js:93 ../js/ui/userMenu.js:696 ../js/ui/userMenu.js:700
#: ../js/ui/userMenu.js:816
#: ../js/gdm/powerMenu.js:93 ../js/ui/userMenu.js:695 ../js/ui/userMenu.js:699
#: ../js/ui/userMenu.js:815
msgid "Suspend"
msgstr "Obustavi"
@ -418,58 +419,58 @@ msgstr "Obustavi"
msgid "Restart"
msgstr "Ponovo pokreni"
#: ../js/gdm/powerMenu.js:103 ../js/ui/userMenu.js:698
#: ../js/ui/userMenu.js:700 ../js/ui/userMenu.js:815 ../js/ui/userMenu.js:942
#: ../js/gdm/powerMenu.js:103 ../js/ui/userMenu.js:697
#: ../js/ui/userMenu.js:699 ../js/ui/userMenu.js:814 ../js/ui/userMenu.js:938
msgid "Power Off"
msgstr "Isključi"
msgstr "Ugasi"
#: ../js/gdm/util.js:249
#: ../js/gdm/util.js:182
msgid "Authentication error"
msgstr "Greška potvrđivanja identiteta"
#. Translators: this message is shown below the password entry field
#. to indicate the user can swipe their finger instead
#: ../js/gdm/util.js:366
#: ../js/gdm/util.js:299
msgid "(or swipe finger)"
msgstr "(ili lupite prst)"
#: ../js/gdm/util.js:391
#: ../js/gdm/util.js:324
#, c-format
msgid "(e.g., user or %s)"
msgstr "(npr., korisnik ili %s)"
#: ../js/misc/util.js:97
#: ../js/misc/util.js:94
msgid "Command not found"
msgstr "Naredba nije nađena"
#. Replace "Error invoking GLib.shell_parse_argv: " with
#. something nicer
#: ../js/misc/util.js:130
#: ../js/misc/util.js:127
msgid "Could not parse command:"
msgstr "Ne mogu da obradim naredbu:"
#: ../js/misc/util.js:138
#: ../js/misc/util.js:135
#, c-format
msgid "Execution of '%s' failed:"
msgstr "Nije uspelo pokretanje „%s“:"
#: ../js/ui/appDisplay.js:349
#: ../js/ui/appDisplay.js:348
msgid "Frequent"
msgstr "Često"
#: ../js/ui/appDisplay.js:356
#: ../js/ui/appDisplay.js:355
msgid "All"
msgstr "Sve"
#: ../js/ui/appDisplay.js:914
#: ../js/ui/appDisplay.js:913
msgid "New Window"
msgstr "Novi prozor"
#: ../js/ui/appDisplay.js:917 ../js/ui/dash.js:284
#: ../js/ui/appDisplay.js:916 ../js/ui/dash.js:284
msgid "Remove from Favorites"
msgstr "Ukloni iz omiljenih"
#: ../js/ui/appDisplay.js:918
#: ../js/ui/appDisplay.js:917
msgid "Add to Favorites"
msgstr "Dodaj u omiljene"
@ -483,7 +484,7 @@ msgstr "„%s“ je dodat među omiljene."
msgid "%s has been removed from your favorites."
msgstr "„%s“ je uklonjen iz omiljenih."
#: ../js/ui/backgroundMenu.js:19 ../js/ui/userMenu.js:789
#: ../js/ui/backgroundMenu.js:19 ../js/ui/userMenu.js:788
msgid "Settings"
msgstr "Podešavanja"
@ -506,7 +507,7 @@ msgctxt "event list time"
msgid "%H\\u2236%M"
msgstr "%H\\u2236%M"
#. Translators: Shown in calendar event list, if 12h format,
#. Transators: Shown in calendar event list, if 12h format,
#. \u2236 is a ratio character, similar to : and \u2009 is
#. a thin space
#: ../js/ui/calendar.js:77
@ -608,35 +609,35 @@ msgid "S"
msgstr "Sub"
#. Translators: Text to show if there are no events
#: ../js/ui/calendar.js:720
#: ../js/ui/calendar.js:692
msgid "Nothing Scheduled"
msgstr "Ništa planirano"
#. Translators: Shown on calendar heading when selected day occurs on current year
#: ../js/ui/calendar.js:736
#: ../js/ui/calendar.js:708
msgctxt "calendar heading"
msgid "%A, %B %d"
msgstr "%A, %B %d"
#. Translators: Shown on calendar heading when selected day occurs on different year
#: ../js/ui/calendar.js:739
#: ../js/ui/calendar.js:711
msgctxt "calendar heading"
msgid "%A, %B %d, %Y"
msgstr "%A, %B %d, %Y"
#: ../js/ui/calendar.js:749
#: ../js/ui/calendar.js:721
msgid "Today"
msgstr "Danas"
#: ../js/ui/calendar.js:753
#: ../js/ui/calendar.js:725
msgid "Tomorrow"
msgstr "Sutra"
#: ../js/ui/calendar.js:764
#: ../js/ui/calendar.js:736
msgid "This week"
msgstr "Ove nedelje"
#: ../js/ui/calendar.js:772
#: ../js/ui/calendar.js:744
msgid "Next week"
msgstr "Sledeće nedelje"
@ -652,12 +653,12 @@ msgstr "Spoljni uređaj je isključen"
msgid "Removable Devices"
msgstr "Uklonjivi uređaji"
#: ../js/ui/components/autorunManager.js:594
#: ../js/ui/components/autorunManager.js:593
#, c-format
msgid "Open with %s"
msgstr "Otvori programom %s"
#: ../js/ui/components/autorunManager.js:620
#: ../js/ui/components/autorunManager.js:619
msgid "Eject"
msgstr "Izbaci"
@ -1037,7 +1038,7 @@ msgstr "Podešavanja datuma i vremena"
#. Translators: This is the date format to use when the calendar popup is
#. * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
#.
#: ../js/ui/dateMenu.js:215
#: ../js/ui/dateMenu.js:205
msgid "%A %B %e, %Y"
msgstr "%A, %e. %b., %R"
@ -1212,6 +1213,7 @@ msgid "Remove"
msgstr "Ukloni"
#: ../js/ui/messageTray.js:1501
#| msgid "No Messages"
msgid "Clear Messages"
msgstr "Očisti poruke"
@ -1219,15 +1221,15 @@ msgstr "Očisti poruke"
msgid "Notification Settings"
msgstr "Postavke obaveštenja"
#: ../js/ui/messageTray.js:1709
#: ../js/ui/messageTray.js:1707
msgid "No Messages"
msgstr "Nema poruka"
#: ../js/ui/messageTray.js:1782
#: ../js/ui/messageTray.js:1787
msgid "Message Tray"
msgstr "Fioka poruka"
#: ../js/ui/messageTray.js:2810
#: ../js/ui/messageTray.js:2864
msgid "System Information"
msgstr "Podaci o sistemu"
@ -1236,7 +1238,7 @@ msgctxt "program"
msgid "Unknown"
msgstr "Nepoznat"
#: ../js/ui/overviewControls.js:463 ../js/ui/screenShield.js:149
#: ../js/ui/overviewControls.js:460 ../js/ui/screenShield.js:153
#, c-format
msgid "%d new message"
msgid_plural "%d new messages"
@ -1245,7 +1247,7 @@ msgstr[1] "%d nove poruke"
msgstr[2] "%d novih poruka"
msgstr[3] "Jedna nova poruka"
#: ../js/ui/overview.js:84
#: ../js/ui/overview.js:82
msgid "Undo"
msgstr "Opozovi"
@ -1257,21 +1259,22 @@ msgstr "Pregled"
#. in the search entry when no search is
#. active; it should not exceed ~30
#. characters.
#: ../js/ui/overview.js:271
#: ../js/ui/overview.js:284
#| msgid "Type to search..."
msgid "Type to search…"
msgstr "Upišite tekst za pretragu…"
#: ../js/ui/panel.js:612
#: ../js/ui/panel.js:613
msgid "Quit"
msgstr "Izađi"
#. Translators: If there is no suitable word for "Activities"
#. in your language, you can use the word for "Overview".
#: ../js/ui/panel.js:636
#: ../js/ui/panel.js:642
msgid "Activities"
msgstr "Aktivnosti"
#: ../js/ui/panel.js:933
#: ../js/ui/panel.js:983
msgid "Top Bar"
msgstr "Gornja traka"
@ -1284,21 +1287,21 @@ msgstr "Gornja traka"
msgid "toggle-switch-us"
msgstr "toggle-switch-intl"
#: ../js/ui/runDialog.js:73
#: ../js/ui/runDialog.js:205
msgid "Enter a Command"
msgstr "Unesite naredbu"
#: ../js/ui/runDialog.js:109
#: ../js/ui/runDialog.js:241
msgid "Close"
msgstr "Zatvori"
#. Translators: This is a time format for a date in
#. long format
#: ../js/ui/screenShield.js:86
#: ../js/ui/screenShield.js:90
msgid "%A, %B %d"
msgstr "%A, %d. %B"
#: ../js/ui/screenShield.js:151
#: ../js/ui/screenShield.js:155
#, c-format
msgid "%d new notification"
msgid_plural "%d new notifications"
@ -1307,11 +1310,11 @@ msgstr[1] "%d nova obaveštenja"
msgstr[2] "%d novih obaveštenja"
msgstr[3] "Jedno novo obaveštenje"
#: ../js/ui/screenShield.js:438 ../js/ui/userMenu.js:807
#: ../js/ui/screenShield.js:442 ../js/ui/userMenu.js:806
msgid "Lock"
msgstr "Zaključaj"
#: ../js/ui/screenShield.js:640
#: ../js/ui/screenShield.js:639
msgid "GNOME needs to lock the screen"
msgstr "Gnom mora da zaključa ekran"
@ -1322,19 +1325,21 @@ msgstr "Gnom mora da zaključa ekran"
#.
#. XXX: another option is to kick the user into the gdm login
#. screen, where we're not affected by grabs
#: ../js/ui/screenShield.js:761 ../js/ui/screenShield.js:1197
#: ../js/ui/screenShield.js:758 ../js/ui/screenShield.js:1169
#| msgid "Unable to connect to %s"
msgid "Unable to lock"
msgstr "Ne mogu da zaključam"
#: ../js/ui/screenShield.js:762 ../js/ui/screenShield.js:1198
#: ../js/ui/screenShield.js:759 ../js/ui/screenShield.js:1170
msgid "Lock was blocked by an application"
msgstr "Neki program je blokirao zaključavanje"
#: ../js/ui/searchDisplay.js:453
#: ../js/ui/searchDisplay.js:431
#| msgid "Searching..."
msgid "Searching…"
msgstr "Tražim…"
#: ../js/ui/searchDisplay.js:497
#: ../js/ui/searchDisplay.js:475
msgid "No results."
msgstr "Nema odgovarajućih rezultata."
@ -1346,11 +1351,11 @@ msgstr "Umnoži"
msgid "Paste"
msgstr "Ubaci"
#: ../js/ui/shellEntry.js:106
#: ../js/ui/shellEntry.js:105
msgid "Show Text"
msgstr "Prikaži tekst"
#: ../js/ui/shellEntry.js:108
#: ../js/ui/shellEntry.js:107
msgid "Hide Text"
msgstr "Sakrij tekst"
@ -1362,7 +1367,7 @@ msgstr "Lozinka"
msgid "Remember Password"
msgstr "Zapamti lozinku"
#: ../js/ui/shellMountOperation.js:403 ../js/ui/unlockDialog.js:113
#: ../js/ui/shellMountOperation.js:403 ../js/ui/unlockDialog.js:140
msgid "Unlock"
msgstr "Otključaj"
@ -1426,10 +1431,12 @@ msgid "Visibility"
msgstr "Vidljivost"
#: ../js/ui/status/bluetooth.js:59
#| msgid "Send Files to Device..."
msgid "Send Files to Device…"
msgstr "Pošalji datoteke na uređaj…"
#: ../js/ui/status/bluetooth.js:60
#| msgid "Set Up a New Device..."
msgid "Set Up a New Device…"
msgstr "Podesi novi uređaj…"
@ -1456,6 +1463,7 @@ msgid "connecting..."
msgstr "povezujem se..."
#: ../js/ui/status/bluetooth.js:239
#| msgid "Send Files..."
msgid "Send Files…"
msgstr "Pošalji datoteke…"
@ -1668,6 +1676,7 @@ msgstr "Podešavanja napajanja"
#. 0 is reported when UPower does not have enough data
#. to estimate battery life
#: ../js/ui/status/power.js:99
#| msgid "Estimating..."
msgid "Estimating…"
msgstr "Približno…"
@ -1775,11 +1784,11 @@ msgstr "Jačina zvuka"
msgid "Microphone"
msgstr "Mikrofon"
#: ../js/ui/unlockDialog.js:124
#: ../js/ui/unlockDialog.js:151
msgid "Log in as another user"
msgstr "Prijavite se kao drugi korsnik"
#: ../js/ui/unlockDialog.js:145
#: ../js/ui/unlockDialog.js:177
msgid "Unlock Window"
msgstr "Otključaj prozor"
@ -1807,27 +1816,27 @@ msgstr "Miruje"
msgid "Offline"
msgstr "Van mreže"
#: ../js/ui/userMenu.js:781
#: ../js/ui/userMenu.js:780
msgid "Notifications"
msgstr "Obaveštenja"
#: ../js/ui/userMenu.js:797
#: ../js/ui/userMenu.js:796
msgid "Switch User"
msgstr "Promeni korisnika"
#: ../js/ui/userMenu.js:802
#: ../js/ui/userMenu.js:801
msgid "Log Out"
msgstr "Odjavi me"
#: ../js/ui/userMenu.js:822
#: ../js/ui/userMenu.js:821
msgid "Install Updates & Restart"
msgstr "Instaliraj ažuriranja i ponovo pokreni"
#: ../js/ui/userMenu.js:840
#: ../js/ui/userMenu.js:839
msgid "Your chat status will be set to busy"
msgstr "Vaše stanje ćaskanja će biti postavljeno na zauzeto"
#: ../js/ui/userMenu.js:841
#: ../js/ui/userMenu.js:840
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."
@ -1836,22 +1845,20 @@ msgstr ""
"mreži je podešeno tako da će ostali znati da vi ne možete videti njihove "
"poruke."
#: ../js/ui/userMenu.js:888
#: ../js/ui/userMenu.js:886
msgid "Other users are logged in."
msgstr "Drugi korisnici su prijavljeni."
#: ../js/ui/userMenu.js:893
#: ../js/ui/userMenu.js:891
msgid "Shutting down might cause them to lose unsaved work."
msgstr "Isključivanje može da dovede do toga da izgube nesačuvani rad."
#. Translators: Remote here refers to a remote session, like a ssh login
#: ../js/ui/userMenu.js:921
#: ../js/ui/userMenu.js:918
#, c-format
msgid "%s (remote)"
msgstr "%s (udaljeno)"
#. Translators: Console here refers to a tty like a VT console
#: ../js/ui/userMenu.js:924
#: ../js/ui/userMenu.js:920
#, c-format
msgid "%s (console)"
msgstr "%s (ljuska)"
@ -1864,7 +1871,7 @@ msgstr "Programi"
msgid "Search"
msgstr "Traži"
#: ../js/ui/wanda.js:77
#: ../js/ui/wanda.js:92
#, c-format
msgid ""
"Sorry, no wisdom for you today:\n"
@ -1873,7 +1880,7 @@ msgstr ""
"Izvinite, danas za vas nema mudrosti:\n"
"%s"
#: ../js/ui/wanda.js:81
#: ../js/ui/wanda.js:96
#, c-format
msgid "%s the Oracle says"
msgstr "Prorok je rekao %s"

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