Compare commits

..

2 Commits

Author SHA1 Message Date
Ray Strode
36c11009f5 loginDialog: make spinner and session menu button share position
They never need to be shown at the same time, and the design has
the UI fade between them.

This commit implements that.

https://bugzilla.gnome.org/show_bug.cgi?id=702818
2013-06-26 10:05:32 -04:00
Jasper St. Pierre
df6f6b7368 loginDialog: Move the session list to a PopupMenu
There are some issues with the existing session menu. First, it looks
kinda bad. It seems like it's hanging around there, but it doesn't really know
what to do with itself.

Second, when it expands down it requires that the buttons below move
down with it. This kind of movement is awkward and looks a bit weird.

Third, it's current position makes the "dialog" tall and unwieldy when
you add things like messages for finger print readers or authentication errors.

This commit moves the session list to a menu behind a button to address
the above problems.

Some updates to patch by Ray Strode.

https://bugzilla.gnome.org/show_bug.cgi?id=702818
2013-06-26 10:05:04 -04:00
84 changed files with 7619 additions and 7170 deletions

29
NEWS
View File

@@ -1,32 +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 3.9.3
===== =====
* Don't push window thumbs when workspace switcher is hidden [Jasper; #701167] * Don't push window thumbs when workspace switcher is hidden [Jasper; #701167]

View File

@@ -1,5 +1,5 @@
AC_PREREQ(2.63) 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.9.3],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
AC_CONFIG_HEADERS([config.h]) AC_CONFIG_HEADERS([config.h])
AC_CONFIG_SRCDIR([src/shell-global.c]) AC_CONFIG_SRCDIR([src/shell-global.c])
@@ -63,7 +63,7 @@ AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
CLUTTER_MIN_VERSION=1.13.4 CLUTTER_MIN_VERSION=1.13.4
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1 GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
GJS_MIN_VERSION=1.35.4 GJS_MIN_VERSION=1.35.4
MUTTER_MIN_VERSION=3.9.4 MUTTER_MIN_VERSION=3.9.3
GTK_MIN_VERSION=3.7.9 GTK_MIN_VERSION=3.7.9
GIO_MIN_VERSION=2.37.0 GIO_MIN_VERSION=2.37.0
LIBECAL_MIN_VERSION=3.5.3 LIBECAL_MIN_VERSION=3.5.3

View File

@@ -186,19 +186,6 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
</key> </key>
</schema> </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"> <enum id="org.gnome.shell.window-switcher.AppIconMode">
<value value="1" nick="thumbnail-only"/> <value value="1" nick="thumbnail-only"/>
<value value="2" nick="app-icon-only"/> <value value="2" nick="app-icon-only"/>

View File

@@ -189,6 +189,13 @@ StScrollBar StButton#vhandle:active {
border-width: 0px; 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 /* 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. * override .popup-menu.font-size, everything else will scale with it.
*/ */
@@ -212,6 +219,10 @@ StScrollBar StButton#vhandle:active {
.popup-image-menu-item { .popup-image-menu-item {
} }
.popup-combobox-item {
spacing: 1em;
}
.popup-separator-menu-item { .popup-separator-menu-item {
-gradient-height: 1px; -gradient-height: 1px;
-gradient-start: rgba(255,255,255,0.0); -gradient-start: rgba(255,255,255,0.0);
@@ -225,6 +236,10 @@ StScrollBar StButton#vhandle:active {
font-weight: bold; font-weight: bold;
} }
.popup-device-menu-item {
spacing: .5em;
}
.popup-status-menu-item { .popup-status-menu-item {
font-weight: normal; font-weight: normal;
color: #999; color: #999;
@@ -234,10 +249,19 @@ StScrollBar StButton#vhandle:active {
color: white; color: white;
} }
.popup-subtitle-menu-item, .popup-subtitle-menu-item:insensitive {
font-weight: bold;
color: white;
}
.popup-menu-icon { .popup-menu-icon {
icon-size: 1.09em; icon-size: 1.09em;
} }
.popup-battery-percentage {
padding-left: 24px;
}
/* Switches */ /* Switches */
.toggle-switch { .toggle-switch {
width: 65px; width: 65px;
@@ -262,51 +286,10 @@ StScrollBar StButton#vhandle:active {
background-size: contain; background-size: contain;
} }
/* Network */ .nm-menu-item-icons {
.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 {
spacing: .5em; spacing: .5em;
} }
.nm-dialog-icon {
icon-size: 16px;
}
/* Buttons */ /* Buttons */
.candidate-page-button, .candidate-page-button,
@@ -344,6 +327,10 @@ StScrollBar StButton#vhandle:active {
border-width: 2px; border-width: 2px;
} }
.app-view-control:focus {
padding: 3px;
}
.app-view-control:first-child:ltr:focus, .app-view-control:first-child:ltr:focus,
.app-view-control:last-child:rtl:focus { .app-view-control:last-child:rtl:focus {
border-right-width: 1px; border-right-width: 1px;
@@ -378,8 +365,7 @@ StScrollBar StButton#vhandle:active {
.modal-dialog-button, .modal-dialog-button,
.notification-button, .notification-button,
.hotplug-notification-item, .hotplug-notification-item,
.app-view-controls, .app-view-controls {
#screenShieldNotifications {
border-radius: 18px; border-radius: 18px;
} }
@@ -488,6 +474,10 @@ StScrollBar StButton#vhandle:active {
height: 1.86em; height: 1.86em;
} }
#panel.lock-screen {
background-color: rgba(0,0,0,0.3);
}
#panel.unlock-screen, #panel.unlock-screen,
#panel.login-screen { #panel.login-screen {
background-color: transparent; background-color: transparent;
@@ -622,30 +612,58 @@ StScrollBar StButton#vhandle:active {
spacing: 8px; 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 { .system-status-icon {
icon-size: 1.09em; 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 */
#overview { #overview {
@@ -893,11 +911,6 @@ StScrollBar StButton#vhandle:active {
padding: 4px 32px; padding: 4px 32px;
} }
.app-view-control:focus {
padding: 3px 31px;
}
.search-display > StBoxLayout, .search-display > StBoxLayout,
.all-apps > StBoxLayout, .all-apps > StBoxLayout,
.frequent-apps > StBoxLayout { .frequent-apps > StBoxLayout {
@@ -1119,7 +1132,7 @@ StScrollBar StButton#vhandle:active {
padding: 4px; padding: 4px;
} }
.lg-extensions-list { .lg-extension-list {
padding: 4px; padding: 4px;
spacing: 6px; spacing: 6px;
} }
@@ -1284,7 +1297,7 @@ StScrollBar StButton#vhandle:active {
} }
.events-table { .events-table {
width: 320px; min-width: 320px;
spacing-columns: 6pt; spacing-columns: 6pt;
padding: 0 1.4em; padding: 0 1.4em;
} }
@@ -2196,18 +2209,6 @@ StScrollBar StButton#vhandle:active {
/* Login Dialog */ /* 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 { .login-dialog-banner {
font-size: 10pt; font-size: 10pt;
font-weight: bold; font-weight: bold;
@@ -2237,10 +2238,6 @@ StScrollBar StButton#vhandle:active {
min-width: 350px; min-width: 350px;
} }
.login-dialog-button-box {
spacing: 5px;
}
.login-dialog-prompt-login-hint-message { .login-dialog-prompt-login-hint-message {
font-size: 10.5pt; font-size: 10.5pt;
} }
@@ -2322,9 +2319,6 @@ StScrollBar StButton#vhandle:active {
font-weight: bold; font-weight: bold;
color: #666666; color: #666666;
padding-top: 1em; padding-top: 1em;
}
.login-dialog-user-selection-box .login-dialog-not-listed-label {
padding-left: 2px; padding-left: 2px;
} }
@@ -2345,7 +2339,6 @@ StScrollBar StButton#vhandle:active {
padding-top: 24px; padding-top: 24px;
padding-bottom: 12px; padding-bottom: 12px;
spacing: 8px; spacing: 8px;
width: 23em;
} }
.login-dialog-prompt-label { .login-dialog-prompt-label {
@@ -2353,6 +2346,10 @@ StScrollBar StButton#vhandle:active {
font-size: 14px; font-size: 14px;
} }
.login-dialog-prompt-entry {
width: 15em;
}
.login-dialog-session-list-button StIcon { .login-dialog-session-list-button StIcon {
icon-size: 1.25em; icon-size: 1.25em;
} }
@@ -2430,11 +2427,6 @@ StScrollBar StButton#vhandle:active {
/* Screen shield */ /* Screen shield */
#panel.lock-screen,
#screenShieldNotifications {
background-color: rgba(0,0,0,0.3);
}
.screen-shield-background { .screen-shield-background {
background: black; background: black;
box-shadow: 0px 4px 8px rgba(0,0,0,0.9); box-shadow: 0px 4px 8px rgba(0,0,0,0.9);
@@ -2479,27 +2471,33 @@ StScrollBar StButton#vhandle:active {
} }
#screenShieldNotifications { #screenShieldNotifications {
border-radius: 8px;
background-color: rgba(0.0, 0.0, 0.0, 0.9);
border: 2px solid #868686;
max-height: 500px; max-height: 500px;
padding: 12px; padding: 18px 0;
box-shadow: .5em .5em 20px rgba(0, 0, 0, 0.5);
} }
.screen-shield-notifications-box { .screen-shield-notifications-box {
spacing: 12px; spacing: 18px;
width: 30em; max-width: 34em;
} }
.screen-shield-notification-source { .screen-shield-notification-source {
padding: 3px 6px; padding: 13px 24px;
spacing: 5px; spacing: 5px;
} }
.screen-shield-notification-label { .screen-shield-notification-label {
font-size: 1.2em;
font-weight: bold; font-weight: bold;
padding: 0px 0px 0px 12px; padding: 0px 18px;
color: #babdb6;
} }
.screen-shield-notification-count-text { .screen-shield-notification-count-text {
padding: 0px 0px 0px 12px; padding: 0px 18px;
} }
/* Remove background from notifications, otherwise /* Remove background from notifications, otherwise
@@ -2517,31 +2515,6 @@ StScrollBar StButton#vhandle:active {
padding-bottom: 0px; 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 { .input-source-switcher-symbol {
font-size: 34pt; font-size: 34pt;
width: 96px; width: 96px;

View File

@@ -17,7 +17,6 @@ misc/config.js: misc/config.js.in Makefile
jsdir = $(pkgdatadir)/js jsdir = $(pkgdatadir)/js
nobase_dist_js_DATA = \ nobase_dist_js_DATA = \
gdm/authPrompt.js \
gdm/batch.js \ gdm/batch.js \
gdm/fingerprint.js \ gdm/fingerprint.js \
gdm/loginDialog.js \ gdm/loginDialog.js \
@@ -34,9 +33,7 @@ nobase_dist_js_DATA = \
misc/jsParse.js \ misc/jsParse.js \
misc/loginManager.js \ misc/loginManager.js \
misc/modemManager.js \ misc/modemManager.js \
misc/objectManager.js \
misc/params.js \ misc/params.js \
misc/smartcardManager.js \
misc/util.js \ misc/util.js \
perf/core.js \ perf/core.js \
ui/altTab.js \ ui/altTab.js \
@@ -98,10 +95,10 @@ nobase_dist_js_DATA = \
ui/status/power.js \ ui/status/power.js \
ui/status/volume.js \ ui/status/volume.js \
ui/status/bluetooth.js \ ui/status/bluetooth.js \
ui/status/system.js \
ui/switcherPopup.js \ ui/switcherPopup.js \
ui/tweener.js \ ui/tweener.js \
ui/unlockDialog.js \ ui/unlockDialog.js \
ui/userMenu.js \
ui/userWidget.js \ ui/userWidget.js \
ui/viewSelector.js \ ui/viewSelector.js \
ui/wanda.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

@@ -32,7 +32,7 @@ const Shell = imports.gi.Shell;
const Signals = imports.signals; const Signals = imports.signals;
const St = imports.gi.St; const St = imports.gi.St;
const AuthPrompt = imports.gdm.authPrompt; const Animation = imports.ui.animation;
const Batch = imports.gdm.batch; const Batch = imports.gdm.batch;
const BoxPointer = imports.ui.boxpointer; const BoxPointer = imports.ui.boxpointer;
const CtrlAltTab = imports.ui.ctrlAltTab; const CtrlAltTab = imports.ui.ctrlAltTab;
@@ -46,6 +46,9 @@ const UserWidget = imports.ui.userWidget;
const _FADE_ANIMATION_TIME = 0.25; const _FADE_ANIMATION_TIME = 0.25;
const _SCROLL_ANIMATION_TIME = 0.5; const _SCROLL_ANIMATION_TIME = 0.5;
const _WORK_SPINNER_ICON_SIZE = 24;
const _WORK_SPINNER_ANIMATION_DELAY = 1.0;
const _WORK_SPINNER_ANIMATION_TIME = 0.3;
const _TIMED_LOGIN_IDLE_THRESHOLD = 5.0; const _TIMED_LOGIN_IDLE_THRESHOLD = 5.0;
const _LOGO_ICON_HEIGHT = 48; const _LOGO_ICON_HEIGHT = 48;
@@ -287,8 +290,8 @@ const UserList = new Lang.Class({
}); });
Signals.addSignalMethods(UserList.prototype); Signals.addSignalMethods(UserList.prototype);
const SessionMenuButton = new Lang.Class({ const SessionList = new Lang.Class({
Name: 'SessionMenuButton', Name: 'SessionList',
_init: function() { _init: function() {
let gearIcon = new St.Icon({ icon_name: 'emblem-system-symbolic' }); let gearIcon = new St.Icon({ icon_name: 'emblem-system-symbolic' });
@@ -386,7 +389,7 @@ const SessionMenuButton = new Lang.Class({
} }
} }
}); });
Signals.addSignalMethods(SessionMenuButton.prototype); Signals.addSignalMethods(SessionList.prototype);
const LoginDialog = new Lang.Class({ const LoginDialog = new Lang.Class({
Name: 'LoginDialog', Name: 'LoginDialog',
@@ -401,10 +404,10 @@ const LoginDialog = new Lang.Class({
parentActor.add_child(this.actor); parentActor.add_child(this.actor);
this._userManager = AccountsService.UserManager.get_default() this._userManager = AccountsService.UserManager.get_default()
let gdmClient = new Gdm.Client(); this._greeterClient = new Gdm.Client();
if (GLib.getenv('GDM_GREETER_TEST') != '1') { 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', this._greeter.connect('default-session-name-changed',
Lang.bind(this, this._onDefaultSessionChanged)); Lang.bind(this, this._onDefaultSessionChanged));
@@ -415,6 +418,15 @@ const LoginDialog = new Lang.Class({
Lang.bind(this, this._onTimedLoginRequested)); 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 = new Gio.Settings({ schema: GdmUtil.LOGIN_SCREEN_SCHEMA });
this._settings.connect('changed::' + GdmUtil.BANNER_MESSAGE_KEY, this._settings.connect('changed::' + GdmUtil.BANNER_MESSAGE_KEY,
@@ -431,8 +443,7 @@ const LoginDialog = new Lang.Class({
Lang.bind(this, this._updateLogoTexture)); Lang.bind(this, this._updateLogoTexture));
this._userSelectionBox = new St.BoxLayout({ style_class: 'login-dialog-user-selection-box', this._userSelectionBox = new St.BoxLayout({ style_class: 'login-dialog-user-selection-box',
vertical: true, vertical: true });
visible: false });
this._userSelectionBox.add_constraint(new Clutter.AlignConstraint({ source: this.actor, this._userSelectionBox.add_constraint(new Clutter.AlignConstraint({ source: this.actor,
align_axis: Clutter.AlignAxis.BOTH, align_axis: Clutter.AlignAxis.BOTH,
factor: 0.5 })); factor: 0.5 }));
@@ -449,18 +460,61 @@ const LoginDialog = new Lang.Class({
x_fill: true, x_fill: true,
y_fill: true }); y_fill: true });
this._authPrompt = new AuthPrompt.AuthPrompt(gdmClient, AuthPrompt.AuthPromptMode.UNLOCK_OR_LOG_IN); this._promptBox = new St.BoxLayout({ style_class: 'login-dialog-prompt-layout',
this._authPrompt.connect('prompted', Lang.bind(this, this._onPrompted)); vertical: true });
this._authPrompt.connect('reset', Lang.bind(this, this._onReset));
this._authPrompt.hide();
this._authPrompt.actor.add_constraint(new Clutter.AlignConstraint({ source: this.actor, this._promptBox.connect('button-press-event',
Lang.bind(this, function(actor, event) {
if (event.get_key_symbol() == Clutter.KEY_Escape) {
this.cancel();
}
}));
this._promptBox.add_constraint(new Clutter.AlignConstraint({ source: this.actor,
align_axis: Clutter.AlignAxis.BOTH, align_axis: Clutter.AlignAxis.BOTH,
factor: 0.5 })); factor: 0.5 }));
this.actor.add_child(this._promptBox);
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._promptBox.add(this._promptLabel,
this._userList.actor.add_constraint(new Clutter.BindConstraint({ source: this._authPrompt.actor, { expand: true,
coordinate: Clutter.BindCoordinate.WIDTH })); 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._buttonBox = new St.BoxLayout({ style_class: 'modal-dialog-button-box',
vertical: false });
this._promptBox.add(this._buttonBox,
{ expand: true,
x_align: St.Align.MIDDLE,
y_align: St.Align.END });
this._cancelButton = null;
this._signInButton = null;
this._promptBox.hide();
// translators: this message is shown below the user list on the // translators: this message is shown below the user list on the
// login screen. It can be activated to reveal an entry for // login screen. It can be activated to reveal an entry for
@@ -475,13 +529,7 @@ const LoginDialog = new Lang.Class({
x_align: St.Align.START, x_align: St.Align.START,
x_fill: true }); x_fill: true });
this._notListedButton.connect('clicked', this._notListedButton.connect('clicked', Lang.bind(this, this._hideUserListAndLogIn));
Lang.bind(this, function() {
this._authPrompt.cancelButton.show();
this._hideUserListAskForUsernameAndBeginVerification();
}));
this._notListedButton.hide();
this._userSelectionBox.add(this._notListedButton, this._userSelectionBox.add(this._notListedButton,
{ expand: false, { expand: false,
@@ -516,26 +564,40 @@ const LoginDialog = new Lang.Class({
this._onUserListActivated(item); this._onUserListActivated(item);
})); }));
this._defaultButtonWell = new St.Widget();
this._sessionMenuButton = new SessionMenuButton(); this._sessionList = new SessionList();
this._sessionMenuButton.connect('session-activated', this._sessionList.connect('session-activated',
Lang.bind(this, function(list, sessionId) { Lang.bind(this, function(list, sessionId) {
this._greeter.call_select_session_sync (sessionId, null); this._greeter.call_select_session_sync (sessionId, null);
})); }));
this._sessionMenuButton.actor.opacity = 0; this._sessionList.actor.opacity = 0;
this._sessionMenuButton.actor.show(); this._sessionList.actor.show();
this._authPrompt.addActorToDefaultButtonWell(this._sessionMenuButton.actor); this._defaultButtonWell.add_child(this._sessionList.actor);
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._defaultButtonWell.add_child(this._workSpinner.actor);
this._sessionList.actor.add_constraint(new Clutter.AlignConstraint({ source: this._workSpinner.actor,
align_axis: Clutter.AlignAxis.BOTH,
factor: 0.5 }));
}, },
_updateDisableUserList: function() { _updateDisableUserList: function() {
let disableUserList = this._settings.get_boolean(GdmUtil.DISABLE_USER_LIST_KEY); 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) { if (disableUserList != this._disableUserList) {
this._disableUserList = disableUserList; this._disableUserList = disableUserList;
if (this._authPrompt.verificationStatus == AuthPrompt.AuthPromptStatus.NOT_VERIFYING) if (!this._verifyingUser)
this._authPrompt.reset(); this._reset();
} }
}, },
@@ -569,38 +631,103 @@ const LoginDialog = new Lang.Class({
this._updateLogoTexture(this._textureCache, this._logoFileUri); this._updateLogoTexture(this._textureCache, this._logoFileUri);
}, },
_onPrompted: function() { _reset: function() {
this._sessionMenuButton.updateSensitivity(true); this._userVerifier.clear();
if (this._shouldShowSessionMenuButton()) this._updateSensitivity(true);
this._authPrompt.setActorInDefaultButtonWell(this._sessionMenuButton.actor); this._promptMessage.hide();
this._showPrompt(); this._user = null;
this._verifyingUser = false;
if (this._disableUserList)
this._hideUserListAndLogIn();
else
this._showUserList();
}, },
_onReset: function(authPrompt, beginRequest) { _setWorking: function(working) {
this._sessionMenuButton.updateSensitivity(true); if (!this._workSpinner)
return;
this._user = null; Tweener.removeTweens(this._workSpinner.actor);
if (working) {
if (this._sessionList.actor.opacity > 0)
Tweener.addTween(this._sessionList.actor,
{ opacity: 0,
delay: _WORK_SPINNER_ANIMATION_DELAY,
time: _WORK_SPINNER_ANIMATION_TIME,
transition: 'linear'
});
if (beginRequest == AuthPrompt.BeginRequestType.PROVIDE_USERNAME) { this._workSpinner.play();
if (this._disableUserList) { Tweener.addTween(this._workSpinner.actor,
this._authPrompt.cancelButton.hide(); { opacity: 255,
this._hideUserListAskForUsernameAndBeginVerification(); delay: _WORK_SPINNER_ANIMATION_DELAY,
time: _WORK_SPINNER_ANIMATION_TIME,
transition: 'linear'
});
} else { } else {
this._showUserList(); if (this._sessionList.actor.opacity == 0 && this._shouldShowSessionList())
Tweener.addTween(this._sessionList.actor,
{ opacity: 255,
delay: _WORK_SPINNER_ANIMATION_DELAY,
time: _WORK_SPINNER_ANIMATION_TIME,
transition: 'linear'
});
Tweener.addTween(this._workSpinner.actor,
{ opacity: 0,
time: _WORK_SPINNER_ANIMATION_TIME,
transition: 'linear',
onCompleteScope: this,
onComplete: function() {
if (this._workSpinner)
this._workSpinner.stop();
} }
} else { });
this._authPrompt.cancelButton.hide();
this._hideUserListAndBeginVerification();
} }
}, },
_verificationFailed: function() {
this._promptEntry.text = '';
this._updateSensitivity(true);
this._setWorking(false);
},
_onDefaultSessionChanged: function(client, sessionId) { _onDefaultSessionChanged: function(client, sessionId) {
this._sessionMenuButton.setActiveSession(sessionId); this._sessionList.setActiveSession(sessionId);
}, },
_shouldShowSessionMenuButton: function() { _showMessage: function(userVerifier, message, styleClass) {
if (this._authPrompt.verifyingUser) if (message) {
this._promptMessage.text = message;
this._promptMessage.styleClass = styleClass;
this._promptMessage.show();
} else {
this._promptMessage.hide();
}
},
_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();
},
_shouldShowSessionList: function() {
if (this._verifyingUser)
return true; return true;
if (!this._user) if (!this._user)
@@ -612,15 +739,159 @@ const LoginDialog = new Lang.Class({
return true; return true;
}, },
_showPrompt: function() { _showPrompt: function(forSecret) {
if (this._authPrompt.actor.visible) this._promptLabel.show();
return; this._promptEntry.show();
this._authPrompt.actor.opacity = 0; this._promptLoginHint.opacity = 0;
this._authPrompt.actor.show(); this._promptLoginHint.show();
Tweener.addTween(this._authPrompt.actor, this._promptBox.opacity = 0;
this._promptBox.show();
Tweener.addTween(this._promptBox,
{ opacity: 255, { opacity: 255,
time: _FADE_ANIMATION_TIME, time: _FADE_ANIMATION_TIME,
transition: 'easeOutQuad' }); transition: 'easeOutQuad' });
if (this._shouldShowSessionList()) {
this._sessionList.actor.opacity = 255;
} else {
this._sessionList.actor.opacity = 0;
}
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();
},
_prepareDialog: function(forSecret, hold) {
this._buttonBox.visible = true;
this._buttonBox.remove_all_children();
if (!this._disableUserList || this._verifyingUser) {
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._signInButton = new St.Button({ style_class: 'modal-dialog-button',
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
reactive: true,
can_focus: true,
label: forSecret ? C_("button", "Sign In") : _("Next") });
this._signInButton.connect('clicked',
Lang.bind(this, function() {
hold.release();
}));
this._signInButton.add_style_pseudo_class('default');
this._buttonBox.add(this._signInButton,
{ expand: false,
x_fill: false,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.END });
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() {
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._buttonBox.remove_all_children();
this._signInButton = null;
this._cancelButton = null;
},
_askQuestion: function(verifier, serviceName, question, passwordChar) {
this._promptLabel.set_text(question);
this._updateSensitivity(true);
this._promptEntry.set_text('');
this._promptEntry.clutter_text.set_password_char(passwordChar);
let tasks = [function() {
return this._showPrompt(!!passwordChar);
},
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();
}, },
_showRealmLoginHint: function(realmManager, hint) { _showRealmLoginHint: function(realmManager, hint) {
@@ -633,32 +904,34 @@ const LoginDialog = new Lang.Class({
// Translators: this message is shown below the username entry field // Translators: this message is shown below the username entry field
// to clue the user in on how to login to the local network realm // to clue the user in on how to login to the local network realm
this._authPrompt.setHint(_("(e.g., user or %s)").format(hint)); this._showLoginHint(null, _("(e.g., user or %s)").format(hint));
}, },
_askForUsernameAndBeginVerification: function() { _askForUsernameAndLogIn: function() {
this._authPrompt.setPasswordChar(''); this._promptLabel.set_text(_("Username: "));
this._authPrompt.setQuestion(_("Username: ")); this._promptEntry.set_text('');
this._promptEntry.clutter_text.set_password_char('');
let realmManager = new Realmd.Manager(); let realmManager = new Realmd.Manager();
let realmSignalId = realmManager.connect('login-format-changed', let signalId = realmManager.connect('login-format-changed',
Lang.bind(this, this._showRealmLoginHint)); Lang.bind(this, this._showRealmLoginHint));
this._showRealmLoginHint(realmManager.loginFormat); this._showRealmLoginHint(realmManager.loginFormat);
let nextSignalId = this._authPrompt.connect('next', let tasks = [this._showPrompt,
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 });
realmManager.disconnect(realmSignalId) function() {
let userName = this._promptEntry.get_text();
this._promptEntry.reactive = false;
return this._beginVerificationForUser(userName);
},
function() {
realmManager.disconnect(signalId)
realmManager.release(); realmManager.release();
})); }];
this._showPrompt();
let batch = new Batch.ConsecutiveBatch(this, tasks);
return batch.run();
}, },
_startSession: function(serviceName) { _startSession: function(serviceName) {
@@ -685,9 +958,15 @@ const LoginDialog = new Lang.Class({
}, },
_onSessionOpened: function(client, serviceName) { _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); this._startSession(serviceName);
})); }));
}
}, },
_waitForItemForUser: function(userName) { _waitForItemForUser: function(userName) {
@@ -819,42 +1098,38 @@ const LoginDialog = new Lang.Class({
this._userSelectionBox.visible = expanded; this._userSelectionBox.visible = expanded;
}, },
_hideUserList: function() { _hideUserListAndLogIn: function() {
this._setUserListExpanded(false); this._setUserListExpanded(false);
if (this._userSelectionBox.visible)
GdmUtil.cloneAndFadeOutActor(this._userSelectionBox); GdmUtil.cloneAndFadeOutActor(this._userSelectionBox);
}, this._askForUsernameAndLogIn();
_hideUserListAskForUsernameAndBeginVerification: function() {
this._hideUserList();
this._askForUsernameAndBeginVerification();
},
_hideUserListAndBeginVerification: function() {
this._hideUserList();
this._authPrompt.begin();
}, },
_showUserList: function() { _showUserList: function() {
this._authPrompt.hide(); this._hidePrompt();
this._sessionMenuButton.close();
this._authPrompt.cancelButton.show();
this._setUserListExpanded(true); this._setUserListExpanded(true);
this._notListedButton.show();
this._userList.actor.grab_key_focus(); this._userList.actor.grab_key_focus();
}, },
_beginVerificationForItem: function(item) { _beginVerificationForUser: function(userName) {
this._authPrompt.setUser(item.user);
let userName = item.user.get_user_name();
let hold = new Batch.Hold(); let hold = new Batch.Hold();
this._authPrompt.begin({ userName: userName, this._userVerifier.begin(userName, hold);
hold: hold }); this._verifyingUser = true;
return hold; return hold;
}, },
_beginVerificationForItem: function(item) {
let userWidget = new UserWidget.UserWidget(item.user);
this._promptUser.set_child(userWidget.actor);
let tasks = [function() {
let userName = item.user.get_user_name();
return this._beginVerificationForUser(userName);
}];
let batch = new Batch.ConsecutiveBatch(this, tasks);
return batch.run();
},
_onUserListActivated: function(activatedItem) { _onUserListActivated: function(activatedItem) {
let tasks = [function() { let tasks = [function() {
return GdmUtil.cloneAndFadeOutActor(this._userSelectionBox); return GdmUtil.cloneAndFadeOutActor(this._userSelectionBox);
@@ -913,7 +1188,7 @@ const LoginDialog = new Lang.Class({
}, },
addCharacter: function(unichar) { addCharacter: function(unichar) {
this._authPrompt.addCharacter(unichar); this._promptEntry.clutter_text.insert_unichar(unichar);
}, },
}); });
Signals.addSignalMethods(LoginDialog.prototype); Signals.addSignalMethods(LoginDialog.prototype);

View File

@@ -6,26 +6,20 @@ const GLib = imports.gi.GLib;
const Lang = imports.lang; const Lang = imports.lang;
const Mainloop = imports.mainloop; const Mainloop = imports.mainloop;
const Signals = imports.signals; const Signals = imports.signals;
const St = imports.gi.St;
const Batch = imports.gdm.batch; const Batch = imports.gdm.batch;
const Fprint = imports.gdm.fingerprint; const Fprint = imports.gdm.fingerprint;
const Main = imports.ui.main; const Main = imports.ui.main;
const Params = imports.misc.params; const Params = imports.misc.params;
const ShellEntry = imports.ui.shellEntry;
const SmartcardManager = imports.misc.smartcardManager;
const Tweener = imports.ui.tweener; const Tweener = imports.ui.tweener;
const PASSWORD_SERVICE_NAME = 'gdm-password'; const PASSWORD_SERVICE_NAME = 'gdm-password';
const FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint'; const FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint';
const SMARTCARD_SERVICE_NAME = 'gdm-smartcard';
const FADE_ANIMATION_TIME = 0.16; const FADE_ANIMATION_TIME = 0.16;
const CLONE_FADE_ANIMATION_TIME = 0.25; const CLONE_FADE_ANIMATION_TIME = 0.25;
const LOGIN_SCREEN_SCHEMA = 'org.gnome.login-screen'; const LOGIN_SCREEN_SCHEMA = 'org.gnome.login-screen';
const PASSWORD_AUTHENTICATION_KEY = 'enable-password-authentication';
const FINGERPRINT_AUTHENTICATION_KEY = 'enable-fingerprint-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_KEY = 'banner-message-enable';
const BANNER_MESSAGE_TEXT_KEY = 'banner-message-text'; const BANNER_MESSAGE_TEXT_KEY = 'banner-message-text';
const ALLOWED_FAILURES_KEY = 'allowed-failures'; const ALLOWED_FAILURES_KEY = 'allowed-failures';
@@ -120,34 +114,11 @@ const ShellUserVerifier = new Lang.Class({
this._client = client; this._client = client;
this._settings = new Gio.Settings({ schema: LOGIN_SCREEN_SCHEMA }); 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._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._messageQueue = []; this._messageQueue = [];
this._messageQueueTimeoutId = 0; this._messageQueueTimeoutId = 0;
this.hasPendingMessages = false; this.hasPendingMessages = false;
this.reauthenticating = false;
this._failCounter = 0; this._failCounter = 0;
}, },
@@ -156,7 +127,6 @@ const ShellUserVerifier = new Lang.Class({
this._cancellable = new Gio.Cancellable(); this._cancellable = new Gio.Cancellable();
this._hold = hold; this._hold = hold;
this._userName = userName; this._userName = userName;
this.reauthenticating = false;
this._checkForFingerprintReader(); this._checkForFingerprintReader();
@@ -174,10 +144,8 @@ const ShellUserVerifier = new Lang.Class({
if (this._cancellable) if (this._cancellable)
this._cancellable.cancel(); this._cancellable.cancel();
if (this._userVerifier) { if (this._userVerifier)
this._userVerifier.call_cancel_sync(null); this._userVerifier.call_cancel_sync(null);
this.clear();
}
}, },
clear: function() { clear: function() {
@@ -195,12 +163,13 @@ const ShellUserVerifier = new Lang.Class({
}, },
answerQuery: function(serviceName, answer) { answerQuery: function(serviceName, answer) {
if (!this.hasPendingMessages) { if (!this._userVerifier.hasPendingMessages) {
this._clearMessageQueue();
this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null); this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
} else { } else {
let signalId = this.connect('no-more-messages', let signalId = this._userVerifier.connect('no-more-messages',
Lang.bind(this, function() { Lang.bind(this, function() {
this.disconnect(signalId); this._userVerifier.disconnect(signalId);
this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null); this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
})); }));
} }
@@ -272,28 +241,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) { _reportInitError: function(where, error) {
logError(error, where); logError(error, where);
this._hold.release(); this._hold.release();
@@ -319,7 +266,6 @@ const ShellUserVerifier = new Lang.Class({
return; return;
} }
this.reauthenticating = true;
this._connectSignals(); this._connectSignals();
this._beginVerification(); this._beginVerification();
this._hold.release(); this._hold.release();
@@ -350,35 +296,11 @@ const ShellUserVerifier = new Lang.Class({
this._userVerifier.connect('verification-complete', Lang.bind(this, this._onVerificationComplete)); 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() { _beginVerification: function() {
this._hold.acquire(); this._hold.acquire();
if (this._userName) { 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._userName,
this._cancellable, this._cancellable,
Lang.bind(this, function(obj, result) { Lang.bind(this, function(obj, result) {
@@ -394,7 +316,7 @@ const ShellUserVerifier = new Lang.Class({
this._hold.release(); this._hold.release();
})); }));
if (this._haveFingerprintReader && !this.serviceIsForeground(FINGERPRINT_SERVICE_NAME)) { if (this._haveFingerprintReader) {
this._hold.acquire(); this._hold.acquire();
this._userVerifier.call_begin_verification_for_user(FINGERPRINT_SERVICE_NAME, this._userVerifier.call_begin_verification_for_user(FINGERPRINT_SERVICE_NAME,
@@ -414,7 +336,7 @@ const ShellUserVerifier = new Lang.Class({
})); }));
} }
} else { } else {
this._userVerifier.call_begin_verification(this._getForegroundService(), this._userVerifier.call_begin_verification(PASSWORD_SERVICE_NAME,
this._cancellable, this._cancellable,
Lang.bind(this, function(obj, result) { Lang.bind(this, function(obj, result) {
try { try {
@@ -432,36 +354,39 @@ const ShellUserVerifier = new Lang.Class({
}, },
_onInfo: function(client, serviceName, info) { _onInfo: function(client, serviceName, info) {
if (this.serviceIsForeground(serviceName)) { // We don't display fingerprint messages, because they
this._queueMessage(info, 'login-dialog-message-info'); // have words like UPEK in them. Instead we use the messages
} else 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. // as a cue to display our own message.
if (serviceName == FINGERPRINT_SERVICE_NAME &&
this._haveFingerprintReader) {
// Translators: this message is shown below the password entry field // Translators: this message is shown below the password entry field
// to indicate the user can swipe their finger instead // to indicate the user can swipe their finger instead
this.emit('show-login-hint', _("(or swipe finger)")); 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) { _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; return;
this._queueMessage(problem, 'login-dialog-message-warning'); this._queueMessage(problem, 'login-dialog-message-warning');
}, },
_onInfoQuery: function(client, serviceName, question) { _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; return;
this.emit('ask-question', serviceName, question, ''); this.emit('ask-question', serviceName, question, '');
}, },
_onSecretInfoQuery: function(client, serviceName, secretQuestion) { _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; return;
this.emit('ask-question', serviceName, secretQuestion, '\u25cf'); this.emit('ask-question', serviceName, secretQuestion, '\u25cf');
@@ -470,7 +395,6 @@ const ShellUserVerifier = new Lang.Class({
_onReset: function() { _onReset: function() {
// Clear previous attempts to authenticate // Clear previous attempts to authenticate
this._failCounter = 0; this._failCounter = 0;
this._updateDefaultService();
this.emit('reset'); this.emit('reset');
}, },
@@ -499,22 +423,22 @@ const ShellUserVerifier = new Lang.Class({
this._failCounter < this._settings.get_int(ALLOWED_FAILURES_KEY); this._failCounter < this._settings.get_int(ALLOWED_FAILURES_KEY);
if (canRetry) { if (canRetry) {
if (!this.hasPendingMessages) { if (!this._userVerifier.hasPendingMessages) {
this._retry(); this._retry();
} else { } else {
let signalId = this.connect('no-more-messages', let signalId = this._userVerifier.connect('no-more-messages',
Lang.bind(this, function() { Lang.bind(this, function() {
this.disconnect(signalId); this._userVerifier.disconnect(signalId);
this._retry(); this._retry();
})); }));
} }
} else { } else {
if (!this.hasPendingMessages) { if (!this._userVerifier.hasPendingMessages) {
this._cancelAndReset(); this._cancelAndReset();
} else { } else {
let signalId = this.connect('no-more-messages', let signalId = this._userVerifier.connect('no-more-messages',
Lang.bind(this, function() { Lang.bind(this, function() {
this.disconnect(signalId); this._userVerifier.disconnect(signalId);
this._cancelAndReset(); this._cancelAndReset();
})); }));
} }
@@ -527,7 +451,7 @@ const ShellUserVerifier = new Lang.Class({
// if the password service fails, then cancel everything. // if the password service fails, then cancel everything.
// But if, e.g., fingerprint fails, still give // But if, e.g., fingerprint fails, still give
// password authentication a chance to succeed // password authentication a chance to succeed
if (this.serviceIsForeground(serviceName)) { if (serviceName == PASSWORD_SERVICE_NAME) {
this._verificationFailed(true); this._verificationFailed(true);
} }

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

@@ -436,11 +436,8 @@ const AppSwitcher = new Lang.Class({
this._arrows = []; this._arrows = [];
let windowTracker = Shell.WindowTracker.get_default(); 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, let allWindows = global.display.get_tab_list(Meta.TabList.NORMAL,
global.screen, workspace); global.screen, null);
// Construct the AppIcons, add to the popup // Construct the AppIcons, add to the popup
for (let i = 0; i < apps.length; i++) { for (let i = 0; i < apps.length; i++) {
@@ -450,10 +447,8 @@ const AppSwitcher = new Lang.Class({
appIcon.cachedWindows = allWindows.filter(function(w) { appIcon.cachedWindows = allWindows.filter(function(w) {
return windowTracker.get_window_app (w) == appIcon.app; return windowTracker.get_window_app (w) == appIcon.app;
}); });
if (workspace == null || appIcon.cachedWindows.length > 0) {
this._addIcon(appIcon); this._addIcon(appIcon);
} }
}
this._curApp = -1; this._curApp = -1;
this._iconSize = 0; this._iconSize = 0;

View File

@@ -358,6 +358,17 @@ const ControlsBoxLayout = Lang.Class({
let totalSpacing = this.spacing * (childrenCount - 1); let totalSpacing = this.spacing * (childrenCount - 1);
return [maxMinWidth * childrenCount + totalSpacing, return [maxMinWidth * childrenCount + totalSpacing,
maxNaturalWidth * childrenCount + totalSpacing]; maxNaturalWidth * childrenCount + totalSpacing];
},
vfunc_set_container: function(container) {
if(this._styleChangedId) {
this._container.disconnect(this._styleChangedId);
this._styleChangedId = 0;
}
if(container != null)
this._styleChangedId = container.connect('style-changed', Lang.bind(this,
function() { this.spacing = this._container.get_theme_node().get_length('spacing'); }));
this._container = container;
} }
}); });
@@ -405,9 +416,8 @@ const AppDisplay = new Lang.Class({
this.actor.add(this._viewStack, { expand: true }); this.actor.add(this._viewStack, { expand: true });
let layout = new ControlsBoxLayout({ homogeneous: true }); let layout = new ControlsBoxLayout({ homogeneous: true });
this._controls = new St.Widget({ style_class: 'app-view-controls', this._controls = new St.Widget({ style_class: 'app-view-controls' });
layout_manager: layout }); this._controls.set_layout_manager(layout);
layout.hookup_style(this._controls);
this.actor.add(new St.Bin({ child: this._controls })); this.actor.add(new St.Bin({ child: this._controls }));

View File

@@ -142,20 +142,23 @@ const BackgroundCache = new Lang.Class({
cancellable: null, cancellable: null,
onFinished: null }); onFinished: null });
let fileLoad = { filename: params.filename, for (let i = 0; i < this._pendingFileLoads.length; i++) {
style: params.style, if (this._pendingFileLoads[i].filename == params.filename &&
shouldCopy: false, this._pendingFileLoads[i].style == params.style) {
this._pendingFileLoads[i].callers.push({ shouldCopy: true,
monitorIndex: params.monitorIndex, monitorIndex: params.monitorIndex,
effects: params.effects, effects: params.effects,
onFinished: params.onFinished, onFinished: params.onFinished });
cancellable: new Gio.Cancellable(), }; return;
this._pendingFileLoads.push(fileLoad);
if (params.cancellable) {
params.cancellable.connect(Lang.bind(this, function(c) {
fileLoad.cancellable.cancel();
}));
} }
}
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, let content = new Meta.Background({ meta_screen: global.screen,
monitor: params.monitorIndex, monitor: params.monitorIndex,
@@ -163,19 +166,9 @@ const BackgroundCache = new Lang.Class({
content.load_file_async(params.filename, content.load_file_async(params.filename,
params.style, params.style,
fileLoad.cancellable, params.cancellable,
Lang.bind(this, Lang.bind(this,
function(object, result) { 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 { try {
content.load_file_finish(result); content.load_file_finish(result);
@@ -185,25 +178,22 @@ const BackgroundCache = new Lang.Class({
content = null; content = null;
} }
let needsCopy = false;
for (let i = 0; i < this._pendingFileLoads.length; i++) { for (let i = 0; i < this._pendingFileLoads.length; i++) {
let pendingLoad = this._pendingFileLoads[i]; let pendingLoad = this._pendingFileLoads[i];
if (pendingLoad.filename != params.filename || if (pendingLoad.filename != params.filename ||
pendingLoad.style != params.style) pendingLoad.style != params.style)
continue; continue;
if (pendingLoad.cancellable.is_cancelled()) for (let j = 0; j < pendingLoad.callers.length; j++) {
continue; 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);
} }
needsCopy = true; pendingLoad.callers[j].onFinished(content);
pendingLoad.onFinished(content); }
} }
this._pendingFileLoads.splice(i, 1); this._pendingFileLoads.splice(i, 1);
@@ -211,15 +201,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) { getImageContent: function(params) {
params = Params.parse(params, { monitorIndex: 0, params = Params.parse(params, { monitorIndex: 0,
style: null, style: null,

View File

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

View File

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

View File

@@ -58,10 +58,14 @@ const CtrlAltTabManager = new Lang.Class({
}, },
focusGroup: function(item, timestamp) { focusGroup: function(item, timestamp) {
if (item.focusCallback) if (item.focusCallback) {
item.focusCallback(timestamp); item.focusCallback(timestamp);
else } else {
if (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); item.root.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
}
}, },
// Sort the items into a consistent order; panel first, tray last, // Sort the items into a consistent order; panel first, tray last,
@@ -132,6 +136,8 @@ const CtrlAltTabManager = new Lang.Class({
}, },
_focusWindows: function(timestamp) { _focusWindows: function(timestamp) {
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
global.stage.key_focus = null;
global.screen.focus_default_window(timestamp); global.screen.focus_default_window(timestamp);
} }
}); });

View File

@@ -1,7 +1,6 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk; const Gtk = imports.gi.Gtk;
const St = imports.gi.St; const St = imports.gi.St;
const Lang = imports.lang; const Lang = imports.lang;
@@ -359,12 +358,25 @@ const _Draggable = new Lang.Class({
return true; return true;
}, },
_updateDragHover : function () { _updateDragPosition : function (event) {
let [stageX, stageY] = event.get_coords();
this._dragX = stageX;
this._dragY = stageY;
// 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, let target = this._dragActor.get_stage().get_actor_at_pos(Clutter.PickMode.ALL,
this._dragX, this._dragY); 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 = { let dragEvent = {
x: this._dragX, x: stageX,
y: this._dragY, y: stageY,
dragActor: this._dragActor, dragActor: this._dragActor,
source: this.actor._delegate, source: this.actor._delegate,
targetActor: target targetActor: target
@@ -375,14 +387,13 @@ const _Draggable = new Lang.Class({
let result = motionFunc(dragEvent); let result = motionFunc(dragEvent);
if (result != DragMotionResult.CONTINUE) { if (result != DragMotionResult.CONTINUE) {
global.set_cursor(DRAG_CURSOR_MAP[result]); global.set_cursor(DRAG_CURSOR_MAP[result]);
return false; return true;
} }
} }
} }
while (target) { while (target) {
if (target._delegate && target._delegate.handleDragOver) { if (target._delegate && target._delegate.handleDragOver) {
let [r, targX, targY] = target.transform_stage_point(this._dragX, this._dragY); 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 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 // 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. // to continue checking the parents.
@@ -390,34 +401,17 @@ const _Draggable = new Lang.Class({
this._dragActor, this._dragActor,
targX, targX,
targY, targY,
0); event.get_time());
if (result != DragMotionResult.CONTINUE) { if (result != DragMotionResult.CONTINUE) {
global.set_cursor(DRAG_CURSOR_MAP[result]); global.set_cursor(DRAG_CURSOR_MAP[result]);
return false; return true;
} }
} }
target = target.get_parent(); target = target.get_parent();
} }
global.set_cursor(Shell.Cursor.DND_IN_DRAG); 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();
return true; return true;
}, },
@@ -517,11 +511,6 @@ const _Draggable = new Lang.Class({
}, },
_cancelDrag: function(eventTime) { _cancelDrag: function(eventTime) {
if (this._updateHoverId) {
GLib.source_remove(this._updateHoverId);
this._updateHoverId = 0;
}
this.emit('drag-cancelled', eventTime); this.emit('drag-cancelled', eventTime);
this._dragInProgress = false; this._dragInProgress = false;
let [snapBackX, snapBackY, snapBackScale] = this._getRestoreLocation(); let [snapBackX, snapBackY, snapBackScale] = this._getRestoreLocation();

View File

@@ -10,7 +10,6 @@ const Clutter = imports.gi.Clutter;;
const Gettext = imports.gettext; const Gettext = imports.gettext;
const GLib = imports.gi.GLib; const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk; const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const St = imports.gi.St; 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) { function _makeLoggingFunc(func) {
return function() { return function() {
return func([].join.call(arguments, ', ')); return func([].join.call(arguments, ', '));
@@ -77,12 +60,6 @@ function init() {
_patchContainerClass(St.BoxLayout); _patchContainerClass(St.BoxLayout);
_patchContainerClass(St.Table); _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() { Clutter.Actor.prototype.toString = function() {
return St.describe_actor(this); return St.describe_actor(this);
}; };

View File

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

@@ -528,10 +528,17 @@ const LayoutManager = new Lang.Class({
get focusIndex() { get focusIndex() {
let i = Main.layoutManager.primaryIndex; let i = Main.layoutManager.primaryIndex;
if (global.stage.key_focus != null) if (global.stage_input_mode == Shell.StageInputMode.FOCUSED ||
i = this.findIndexForActor(global.stage.key_focus); global.stage_input_mode == Shell.StageInputMode.FULLSCREEN) {
else if (global.display.focus_window != null) let focusActor = global.stage.key_focus;
i = global.display.focus_window.get_monitor(); if (focusActor)
i = this.findIndexForActor(focusActor);
} else {
let focusWindow = global.display.focus_window;
if (focusWindow)
i = focusWindow.get_monitor();
}
return i; return i;
}, },
@@ -589,13 +596,9 @@ const LayoutManager = new Lang.Class({
// screen. So, we set no_clear_hint at the end of the animation. // screen. So, we set no_clear_hint at the end of the animation.
_prepareStartupAnimation: function() { _prepareStartupAnimation: function() {
// During the initial transition, add a simple actor to block all events, // Set ourselves to FULLSCREEN input mode while the animation is running
// so they don't get delivered to X11 windows that have been transformed. // so events don't get delivered to X11 windows (which are distorted by the animation)
this._coverPane = new Clutter.Actor({ opacity: 0, global.stage_input_mode = Shell.StageInputMode.FULLSCREEN;
width: global.screen_width,
height: global.screen_height,
reactive: true });
this.addChrome(this._coverPane);
if (Main.sessionMode.isGreeter) { if (Main.sessionMode.isGreeter) {
this.panelBox.translation_y = -this.panelBox.height; this.panelBox.translation_y = -this.panelBox.height;
@@ -665,8 +668,7 @@ const LayoutManager = new Lang.Class({
// we no longer need to clear the stage // we no longer need to clear the stage
global.stage.no_clear_hint = true; global.stage.no_clear_hint = true;
this._coverPane.destroy(); global.stage_input_mode = Shell.StageInputMode.NORMAL;
this._coverPane = null;
this._systemBackground.actor.destroy(); this._systemBackground.actor.destroy();
this._systemBackground = null; this._systemBackground = null;

View File

@@ -849,9 +849,8 @@ const LookingGlass = new Lang.Class({
this._updateFont(); this._updateFont();
// We want it to appear to slide out from underneath the panel // We want it to appear to slide out from underneath the panel
Main.uiGroup.add_actor(this.actor); Main.layoutManager.panelBox.add_actor(this.actor);
Main.uiGroup.set_child_below_sibling(this.actor, this.actor.lower_bottom();
Main.layoutManager.panelBox);
Main.layoutManager.panelBox.connect('allocation-changed', Main.layoutManager.panelBox.connect('allocation-changed',
Lang.bind(this, this._queueResize)); Lang.bind(this, this._queueResize));
Main.layoutManager.keyboardBox.connect('allocation-changed', Main.layoutManager.keyboardBox.connect('allocation-changed',
@@ -1073,7 +1072,7 @@ const LookingGlass = new Lang.Class({
let availableHeight = primary.height - Main.layoutManager.keyboardBox.height; let availableHeight = primary.height - Main.layoutManager.keyboardBox.height;
let myHeight = Math.min(primary.height * 0.7, availableHeight * 0.9); let myHeight = Math.min(primary.height * 0.7, availableHeight * 0.9);
this.actor.x = (primary.width - myWidth) / 2; 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._targetY = this._hiddenY + myHeight;
this.actor.y = this._hiddenY; this.actor.y = this._hiddenY;
this.actor.width = myWidth; this.actor.width = myWidth;

View File

@@ -356,6 +356,8 @@ function pushModal(actor, params) {
Meta.disable_unredirect_for_screen(global.screen); Meta.disable_unredirect_for_screen(global.screen);
} }
global.set_stage_input_mode(Shell.StageInputMode.FULLSCREEN);
modalCount += 1; modalCount += 1;
let actorDestroyId = actor.connect('destroy', function() { let actorDestroyId = actor.connect('destroy', function() {
let index = _findModal(actor); let index = _findModal(actor);
@@ -404,6 +406,7 @@ function popModal(actor, timestamp) {
if (focusIndex < 0) { if (focusIndex < 0) {
global.stage.set_key_focus(null); global.stage.set_key_focus(null);
global.end_modal(timestamp); global.end_modal(timestamp);
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
keybindingMode = Shell.KeyBindingMode.NORMAL; keybindingMode = Shell.KeyBindingMode.NORMAL;
throw new Error('incorrect pop'); throw new Error('incorrect pop');
@@ -451,6 +454,7 @@ function popModal(actor, timestamp) {
return; return;
global.end_modal(timestamp); global.end_modal(timestamp);
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
Meta.enable_unredirect_for_screen(global.screen); Meta.enable_unredirect_for_screen(global.screen);
keybindingMode = Shell.KeyBindingMode.NORMAL; keybindingMode = Shell.KeyBindingMode.NORMAL;
} }

View File

@@ -97,65 +97,6 @@ function _fixMarkup(text, allowMarkup) {
return GLib.markup_escape_text(text, -1); 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({ const URLHighlighter = new Lang.Class({
Name: 'URLHighlighter', Name: 'URLHighlighter',
@@ -1424,10 +1365,6 @@ const SummaryItem = new Lang.Class({
this.notificationStackWidget.add_actor(this.notificationStackView); this.notificationStackWidget.add_actor(this.notificationStackView);
this.closeButton = Util.makeCloseButton(); 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.notificationStackWidget.add_actor(this.closeButton);
this._stackedNotifications = []; this._stackedNotifications = [];
@@ -1637,7 +1574,6 @@ const MessageTray = new Lang.Class({
this._notificationBin.set_y_align(Clutter.ActorAlign.START); this._notificationBin.set_y_align(Clutter.ActorAlign.START);
this._notificationWidget.add_actor(this._notificationBin); this._notificationWidget.add_actor(this._notificationBin);
this._notificationWidget.hide(); this._notificationWidget.hide();
this._notificationFocusGrabber = new FocusGrabber(this._notificationWidget);
this._notificationQueue = []; this._notificationQueue = [];
this._notification = null; this._notification = null;
this._notificationClickedId = 0; this._notificationClickedId = 0;
@@ -1687,6 +1623,7 @@ const MessageTray = new Lang.Class({
{ keybindingMode: Shell.KeyBindingMode.MESSAGE_TRAY }); { keybindingMode: Shell.KeyBindingMode.MESSAGE_TRAY });
this._grabHelper.addActor(this._summaryBoxPointer.actor); this._grabHelper.addActor(this._summaryBoxPointer.actor);
this._grabHelper.addActor(this.actor); this._grabHelper.addActor(this.actor);
this._grabHelper.addActor(this._notificationWidget);
Main.layoutManager.connect('keyboard-visible-changed', Lang.bind(this, this._onKeyboardVisibleChanged)); Main.layoutManager.connect('keyboard-visible-changed', Lang.bind(this, this._onKeyboardVisibleChanged));
@@ -1812,6 +1749,7 @@ const MessageTray = new Lang.Class({
let [x, y, mask] = global.get_pointer(); let [x, y, mask] = global.get_pointer();
this._contextMenu.setPosition(Math.round(x), Math.round(y)); this._contextMenu.setPosition(Math.round(x), Math.round(y));
this._grabHelper.grab({ actor: this._contextMenu.actor, this._grabHelper.grab({ actor: this._contextMenu.actor,
modal: true,
onUngrab: Lang.bind(this, function () { onUngrab: Lang.bind(this, function () {
this._contextMenu.close(BoxPointer.PopupAnimation.FULL); this._contextMenu.close(BoxPointer.PopupAnimation.FULL);
}) })
@@ -1919,8 +1857,9 @@ const MessageTray = new Lang.Class({
_closeNotification: function() { _closeNotification: function() {
if (this._notificationState == State.SHOWN) { if (this._notificationState == State.SHOWN) {
this._closeButton.hide(); this._closeButton.hide();
this._notification.emit('done-displaying'); this._notificationClosed = true;
this._notification.destroy(); this._updateState();
this._notificationClosed = false;
} }
}, },
@@ -2377,6 +2316,7 @@ const MessageTray = new Lang.Class({
_showTray: function() { _showTray: function() {
if (!this._grabHelper.grab({ actor: this.actor, if (!this._grabHelper.grab({ actor: this.actor,
modal: true,
onUngrab: Lang.bind(this, this._escapeTray) })) { onUngrab: Lang.bind(this, this._escapeTray) })) {
this._traySummoned = false; this._traySummoned = false;
return false; return false;
@@ -2580,7 +2520,19 @@ const MessageTray = new Lang.Class({
}, },
_hideNotification: function() { _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) { if (this._notificationExpandedId) {
this._notification.disconnect(this._notificationExpandedId); this._notification.disconnect(this._notificationExpandedId);
@@ -2678,7 +2630,8 @@ const MessageTray = new Lang.Class({
}, },
_ensureNotificationFocused: function() { _ensureNotificationFocused: function() {
this._notificationFocusGrabber.grabFocus(); this._grabHelper.grab({ actor: this._notification.actor,
grabFocus: true });
}, },
_onSourceDoneDisplayingContent: function(source, closeTray) { _onSourceDoneDisplayingContent: function(source, closeTray) {
@@ -2701,9 +2654,11 @@ const MessageTray = new Lang.Class({
let closeButton = summaryItem.closeButton; let closeButton = summaryItem.closeButton;
closeButton.show(); closeButton.show();
this._summaryBoxPointerCloseClickedId = closeButton.connect('clicked', Lang.bind(this, this._hideSummaryBoxPointer));
summaryItem.prepareNotificationStackForShowing(); summaryItem.prepareNotificationStackForShowing();
} else if (this._clickedSummaryItemMouseButton == 3) { } else if (this._clickedSummaryItemMouseButton == 3) {
child = summaryItem.rightClickMenu; child = summaryItem.rightClickMenu;
this._summaryBoxPointerCloseClickedId = 0;
} }
// If the user clicked the middle mouse button, or the item // If the user clicked the middle mouse button, or the item
@@ -2719,6 +2674,7 @@ const MessageTray = new Lang.Class({
this._summaryBoxPointer.bin.child = child; this._summaryBoxPointer.bin.child = child;
this._grabHelper.grab({ actor: this._summaryBoxPointer.bin.child, this._grabHelper.grab({ actor: this._summaryBoxPointer.bin.child,
modal: true,
onUngrab: Lang.bind(this, this._onSummaryBoxPointerUngrabbed) }); onUngrab: Lang.bind(this, this._onSummaryBoxPointerUngrabbed) });
this._summaryBoxPointer.actor.opacity = 0; this._summaryBoxPointer.actor.opacity = 0;
@@ -2797,7 +2753,10 @@ const MessageTray = new Lang.Class({
this._summaryBoxPointerItem.disconnect(this._summaryBoxPointerContentUpdatedId); this._summaryBoxPointerItem.disconnect(this._summaryBoxPointerContentUpdatedId);
this._summaryBoxPointerContentUpdatedId = 0; this._summaryBoxPointerContentUpdatedId = 0;
} }
if (this._summaryBoxPointerCloseClickedId != 0) {
this._summaryBoxPointerItem.closeButton.disconnect(this._summaryBoxPointerCloseClickedId);
this._summaryBoxPointerCloseClickedId = 0;
}
if (this._sourceDoneDisplayingId) { if (this._sourceDoneDisplayingId) {
this._summaryBoxPointerItem.source.disconnect(this._sourceDoneDisplayingId); this._summaryBoxPointerItem.source.disconnect(this._sourceDoneDisplayingId);
this._sourceDoneDisplayingId = 0; this._sourceDoneDisplayingId = 0;

View File

@@ -79,8 +79,9 @@ const ModalDialog = new Lang.Class({
this.dialogLayout = new St.BoxLayout({ style_class: 'modal-dialog', this.dialogLayout = new St.BoxLayout({ style_class: 'modal-dialog',
vertical: true }); vertical: true });
if (params.styleClass != null) if (params.styleClass != null) {
this.dialogLayout.add_style_class_name(params.styleClass); this.dialogLayout.add_style_class_name(params.styleClass);
}
if (!this._shellReactive) { if (!this._shellReactive) {
this._lightbox = new Lightbox.Lightbox(this._group, this._lightbox = new Lightbox.Lightbox(this._group,
@@ -356,9 +357,8 @@ const ModalDialog = new Lang.Class({
if (this._savedKeyFocus) { if (this._savedKeyFocus) {
this._savedKeyFocus.grab_key_focus(); this._savedKeyFocus.grab_key_focus();
this._savedKeyFocus = null; this._savedKeyFocus = null;
} else { } else
this._initialKeyFocus.grab_key_focus(); this._initialKeyFocus.grab_key_focus();
}
if (!this._shellReactive) if (!this._shellReactive)
this._eventBlocker.lower_bottom(); this._eventBlocker.lower_bottom();

View File

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

View File

@@ -148,7 +148,7 @@ const Overview = new Lang.Class({
// Dash elements, or mouseover handlers in the workspaces. // Dash elements, or mouseover handlers in the workspaces.
this._coverPane = new Clutter.Actor({ opacity: 0, this._coverPane = new Clutter.Actor({ opacity: 0,
reactive: true }); reactive: true });
this._stack.add_actor(this._coverPane); this._overview.add_actor(this._coverPane);
this._coverPane.connect('event', Lang.bind(this, function (actor, event) { return true; })); this._coverPane.connect('event', Lang.bind(this, function (actor, event) { return true; }));
this._stack.add_actor(this._overview); this._stack.add_actor(this._overview);

View File

@@ -297,16 +297,13 @@ const AppMenuButton = new Lang.Class({
this._updateIconBoxClip(); 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() { _onIconThemeChanged: function() {
if (this._iconBox.child == null) if (this._iconBox.child == null)
return; 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() { _updateIconBoxClip: function() {
@@ -452,7 +449,7 @@ const AppMenuButton = new Lang.Class({
// If the app has just lost focus to the panel, pretend // If the app has just lost focus to the panel, pretend
// nothing happened; otherwise you can't keynav to the // nothing happened; otherwise you can't keynav to the
// app menu. // app menu.
if (global.stage.key_focus != null) if (global.stage_input_mode == Shell.StageInputMode.FOCUSED)
return; return;
} }
this._sync(); this._sync();
@@ -529,10 +526,12 @@ const AppMenuButton = new Lang.Class({
} }
this._targetApp = targetApp; this._targetApp = targetApp;
let icon = targetApp.get_faded_icon(2 * PANEL_ICON_SIZE);
this._label.setText(targetApp.get_name()); this._label.setText(targetApp.get_name());
this.setName(targetApp.get_name()); this.setName(targetApp.get_name());
this._syncIcon(); this._iconBox.set_child(icon);
this._iconBox.show(); this._iconBox.show();
if (targetApp.get_state() == Shell.AppState.STARTING || if (targetApp.get_state() == Shell.AppState.STARTING ||
@@ -858,7 +857,7 @@ const PANEL_ITEM_IMPLEMENTATIONS = {
'lockScreen': imports.ui.status.lockScreenMenu.Indicator, 'lockScreen': imports.ui.status.lockScreenMenu.Indicator,
'keyboard': imports.ui.status.keyboard.InputSourceIndicator, 'keyboard': imports.ui.status.keyboard.InputSourceIndicator,
'powerMenu': imports.gdm.powerMenu.PowerMenuButton, 'powerMenu': imports.gdm.powerMenu.PowerMenuButton,
'system': imports.ui.status.system.Indicator, 'userMenu': imports.ui.userMenu.UserMenuButton
}; };
if (Config.HAVE_BLUETOOTH) if (Config.HAVE_BLUETOOTH)

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('button-press-event', Lang.bind(this, this._onButtonPress));
this.actor.connect('key-press-event', Lang.bind(this, this._onSourceKeyPress)); this.actor.connect('key-press-event', Lang.bind(this, this._onSourceKeyPress));
this.actor.connect('notify::visible', Lang.bind(this, this._onVisibilityChanged));
if (dontCreateMenu) if (dontCreateMenu)
this.menu = new PopupMenu.PopupDummyMenu(this.actor); this.menu = new PopupMenu.PopupDummyMenu(this.actor);
@@ -184,14 +183,6 @@ const Button = new Lang.Class({
return false; return false;
}, },
_onVisibilityChanged: function() {
if (!this.menu)
return;
if (!this.actor.visible)
this.menu.close();
},
_onMenuKeyPress: function(actor, event) { _onMenuKeyPress: function(actor, event) {
if (global.focus_manager.navigate_from_event(event)) if (global.focus_manager.navigate_from_event(event))
return true; return true;

File diff suppressed because it is too large Load Diff

View File

@@ -7,6 +7,7 @@ const Lang = imports.lang;
const St = imports.gi.St; const St = imports.gi.St;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const FileUtils = imports.misc.fileUtils;
const Search = imports.ui.search; const Search = imports.ui.search;
const KEY_FILE_GROUP = 'Shell Search Provider'; const KEY_FILE_GROUP = 'Shell Search Provider';
@@ -59,10 +60,17 @@ var SearchProviderProxy = Gio.DBusProxy.makeProxyWrapper(SearchProviderIface);
var SearchProvider2Proxy = Gio.DBusProxy.makeProxyWrapper(SearchProvider2Iface); var SearchProvider2Proxy = Gio.DBusProxy.makeProxyWrapper(SearchProvider2Iface);
function loadRemoteSearchProviders(addProviderCallback) { function loadRemoteSearchProviders(addProviderCallback) {
let objectPaths = {}; let data = { loadedProviders: [],
let loadedProviders = []; objectPaths: {},
addProviderCallback: addProviderCallback };
FileUtils.collectFromDatadirsAsync('search-providers',
{ loadedCallback: remoteProvidersLoaded,
processFile: loadRemoteSearchProvider,
data: data
});
}
function loadRemoteSearchProvider(file) { function loadRemoteSearchProvider(file, info, data) {
let keyfile = new GLib.KeyFile(); let keyfile = new GLib.KeyFile();
let path = file.get_path(); let path = file.get_path();
@@ -81,7 +89,7 @@ function loadRemoteSearchProviders(addProviderCallback) {
let busName = keyfile.get_string(group, 'BusName'); let busName = keyfile.get_string(group, 'BusName');
let objectPath = keyfile.get_string(group, 'ObjectPath'); let objectPath = keyfile.get_string(group, 'ObjectPath');
if (objectPaths[objectPath]) if (data.objectPaths[objectPath])
return; return;
let appInfo = null; let appInfo = null;
@@ -105,38 +113,22 @@ function loadRemoteSearchProviders(addProviderCallback) {
else else
remoteProvider = new RemoteSearchProvider(appInfo, busName, objectPath); remoteProvider = new RemoteSearchProvider(appInfo, busName, objectPath);
objectPaths[objectPath] = remoteProvider; data.objectPaths[objectPath] = remoteProvider;
loadedProviders.push(remoteProvider); data.loadedProviders.push(remoteProvider);
} catch(e) { } catch(e) {
log('Failed to add search provider %s: %s'.format(path, e.toString())); log('Failed to add search provider %s: %s'.format(path, e.toString()));
} }
} }
let dataDirs = GLib.get_system_data_dirs(); function remoteProvidersLoaded(loadState) {
dataDirs.forEach(function(dataDir) {
let path = GLib.build_filenamev([dataDir, 'gnome-shell', 'search-providers']);
let dir = Gio.File.new_for_path(path);
let fileEnum;
try {
fileEnum = dir.enumerate_children('standard::name,standard::type',
Gio.FileQueryInfoFlags.NONE, null);
} catch (e) {
fileEnum = null;
}
if (fileEnum != null) {
let info;
while ((info = fileEnum.next_file(null)))
loadRemoteSearchProvider(fileEnum.get_child(info));
}
});
let searchSettings = new Gio.Settings({ schema: Search.SEARCH_PROVIDERS_SCHEMA }); let searchSettings = new Gio.Settings({ schema: Search.SEARCH_PROVIDERS_SCHEMA });
let sortOrder = searchSettings.get_strv('sort-order'); let sortOrder = searchSettings.get_strv('sort-order');
// Special case gnome-control-center to be always active and always first // Special case gnome-control-center to be always active and always first
sortOrder.unshift('gnome-control-center.desktop'); sortOrder.unshift('gnome-control-center.desktop');
loadedProviders.sort(function(providerA, providerB) { loadState.loadedProviders.sort(
function(providerA, providerB) {
let idxA, idxB; let idxA, idxB;
let appIdA, appIdB; let appIdA, appIdB;
@@ -166,7 +158,10 @@ function loadRemoteSearchProviders(addProviderCallback) {
return (idxA - idxB); return (idxA - idxB);
}); });
loadedProviders.forEach(addProviderCallback); loadState.loadedProviders.forEach(
function(provider) {
loadState.addProviderCallback(provider);
});
} }
const RemoteSearchProvider = new Lang.Class({ const RemoteSearchProvider = new Lang.Class({

View File

@@ -23,7 +23,6 @@ const Main = imports.ui.main;
const Overview = imports.ui.overview; const Overview = imports.ui.overview;
const MessageTray = imports.ui.messageTray; const MessageTray = imports.ui.messageTray;
const ShellDBus = imports.ui.shellDBus; const ShellDBus = imports.ui.shellDBus;
const SmartcardManager = imports.misc.smartcardManager;
const Tweener = imports.ui.tweener; const Tweener = imports.ui.tweener;
const Util = imports.misc.util; const Util = imports.misc.util;
@@ -517,13 +516,6 @@ const ScreenShield = new Lang.Class({
this._screenSaverDBus = new ShellDBus.ScreenSaverDBus(this); 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._inhibitor = null;
this._aboutToSuspend = false; this._aboutToSuspend = false;
this._loginManager = LoginManager.getLoginManager(); this._loginManager = LoginManager.getLoginManager();
@@ -728,8 +720,6 @@ const ScreenShield = new Lang.Class({
}, },
_onDragEnd: function(action, actor, eventX, eventY, modifiers) { _onDragEnd: function(action, actor, eventX, eventY, modifiers) {
if (this._lockScreenState != MessageTray.State.HIDING)
return;
if (this._lockScreenGroup.y < -(ARROW_DRAG_THRESHOLD * global.stage.height)) { if (this._lockScreenGroup.y < -(ARROW_DRAG_THRESHOLD * global.stage.height)) {
// Complete motion automatically // Complete motion automatically
let [velocity, velocityX, velocityY] = this._dragAction.get_velocity(0); let [velocity, velocityX, velocityY] = this._dragAction.get_velocity(0);
@@ -772,19 +762,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()) { if (!this._becomeModal()) {
// We could not become modal, so we can't activate the // We could not become modal, so we can't activate the
// screenshield. The user is probably very upset at this // screenshield. The user is probably very upset at this
@@ -798,6 +775,19 @@ const ScreenShield = new Lang.Class({
return; 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(); this._lightbox.show();
if (this._activationTime == 0) if (this._activationTime == 0)
@@ -935,6 +925,7 @@ const ScreenShield = new Lang.Class({
} }
this._dialog.connect('failed', Lang.bind(this, this._onUnlockFailed)); this._dialog.connect('failed', Lang.bind(this, this._onUnlockFailed));
this._dialog.connect('unlocked', Lang.bind(this, this._onUnlockSucceded));
} }
this._dialog.allowCancel = allowCancel; this._dialog.allowCancel = allowCancel;
@@ -944,6 +935,10 @@ const ScreenShield = new Lang.Class({
this._resetLockScreen(true, false); this._resetLockScreen(true, false);
}, },
_onUnlockSucceded: function() {
this.deactivate(true);
},
_resetLockScreen: function(animateLockScreen, animateLockDialog) { _resetLockScreen: function(animateLockScreen, animateLockDialog) {
// Don't reset the lock screen unless it is completely hidden // Don't reset the lock screen unless it is completely hidden
// This prevents the shield going down if the lock-delay timeout // This prevents the shield going down if the lock-delay timeout
@@ -1129,12 +1124,6 @@ const ScreenShield = new Lang.Class({
}, },
deactivate: function(animate) { deactivate: function(animate) {
this._dialog.finish(Lang.bind(this, function() {
this._finishDeactivate(animate);
}));
},
_finishDeactivate: function(animate) {
this._hideLockScreen(animate, 0); this._hideLockScreen(animate, 0);
if (this._hasLockScreen) if (this._hasLockScreen)

View File

@@ -31,7 +31,7 @@ const SearchSystem = new Lang.Class({
let remoteIndex = this._remoteProviders.indexOf(provider); let remoteIndex = this._remoteProviders.indexOf(provider);
if (remoteIndex != -1) if (remoteIndex != -1)
this._remoteProviders.splice(remoteIndex, 1); this._remoteProviders.splice(index, 1);
}, },
getProviders: function() { getProviders: function() {

View File

@@ -60,7 +60,7 @@ const _modes = {
unlockDialog: undefined, unlockDialog: undefined,
components: ['polkitAgent', 'telepathyClient'], components: ['polkitAgent', 'telepathyClient'],
panel: { panel: {
left: [], left: ['userMenu'],
center: [], center: [],
right: ['lockScreen'] right: ['lockScreen']
}, },
@@ -72,7 +72,7 @@ const _modes = {
unlockDialog: undefined, unlockDialog: undefined,
components: ['polkitAgent', 'telepathyClient'], components: ['polkitAgent', 'telepathyClient'],
panel: { panel: {
left: [], left: ['userMenu'],
center: [], center: [],
right: ['a11y', 'keyboard', 'lockScreen'] right: ['a11y', 'keyboard', 'lockScreen']
}, },
@@ -97,7 +97,7 @@ const _modes = {
left: ['activities', 'appMenu'], left: ['activities', 'appMenu'],
center: ['dateMenu'], center: ['dateMenu'],
right: ['a11y', 'keyboard', 'volume', 'bluetooth', right: ['a11y', 'keyboard', 'volume', 'bluetooth',
'network', 'battery', 'system'] 'network', 'battery', 'userMenu']
} }
} }
}; };

View File

@@ -41,7 +41,6 @@ const GnomeShellIface = <interface name="org.gnome.Shell">
<signal name="AcceleratorActivated"> <signal name="AcceleratorActivated">
<arg name="action" type="u" /> <arg name="action" type="u" />
<arg name="deviceid" type="u" /> <arg name="deviceid" type="u" />
<arg name="timestamp" type="u" />
</signal> </signal>
<property name="Mode" type="s" access="read" /> <property name="Mode" type="s" access="read" />
<property name="OverviewActive" type="b" access="readwrite" /> <property name="OverviewActive" type="b" access="readwrite" />
@@ -80,8 +79,8 @@ const GnomeShell = new Lang.Class({
this._grabbers = new Hash.Map(); this._grabbers = new Hash.Map();
global.display.connect('accelerator-activated', Lang.bind(this, global.display.connect('accelerator-activated', Lang.bind(this,
function(display, action, deviceid, timestamp) { function(display, action, deviceid) {
this._emitAcceleratorActivated(action, deviceid, timestamp); this._emitAcceleratorActivated(action, deviceid);
})); }));
}, },
@@ -167,7 +166,7 @@ const GnomeShell = new Lang.Class({
return invocation.return_value(GLib.Variant.new('(b)', [ungrabSucceeded])); return invocation.return_value(GLib.Variant.new('(b)', [ungrabSucceeded]));
}, },
_emitAcceleratorActivated: function(action, deviceid, timestamp) { _emitAcceleratorActivated: function(action, deviceid) {
let destination = this._grabbedAccelerators.get(action); let destination = this._grabbedAccelerators.get(action);
if (!destination) if (!destination)
return; return;
@@ -178,7 +177,7 @@ const GnomeShell = new Lang.Class({
this._dbusImpl.get_object_path(), this._dbusImpl.get_object_path(),
info ? info.name : null, info ? info.name : null,
'AcceleratorActivated', 'AcceleratorActivated',
GLib.Variant.new('(uuu)', [action, deviceid, timestamp])); GLib.Variant.new('(uu)', [action, deviceid]));
}, },
_grabAcceleratorForSender: function(accelerator, flags, sender) { _grabAcceleratorForSender: function(accelerator, flags, sender) {

View File

@@ -98,24 +98,21 @@ const Slider = new Lang.Class({
}, },
_startDragging: function(actor, event) { _startDragging: function(actor, event) {
this.startDragging(event); if (this._dragging) // don't allow two drags at the same time
},
startDragging: function(event) {
if (this._dragging)
return false; return false;
this._dragging = true; this._dragging = true;
let device = event.get_device(); // FIXME: we should only grab the specific device that originated
device.grab(this.actor); // the event, but for some weird reason events are still delivered
this._grabbedDevice = device; // outside the slider if using clutter_grab_pointer_for_device
Clutter.grab_pointer(this.actor);
this._releaseId = this.actor.connect('button-release-event', Lang.bind(this, this._endDragging)); 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)); this._motionId = this.actor.connect('motion-event', Lang.bind(this, this._motionEvent));
let absX, absY; let absX, absY;
[absX, absY] = event.get_coords(); [absX, absY] = event.get_coords();
this._moveHandle(absX, absY); this._moveHandle(absX, absY);
return true; return true;
}, },
@@ -124,8 +121,7 @@ const Slider = new Lang.Class({
this.actor.disconnect(this._releaseId); this.actor.disconnect(this._releaseId);
this.actor.disconnect(this._motionId); this.actor.disconnect(this._motionId);
this._grabbedDevice.ungrab(); Clutter.ungrab_pointer();
this._grabbedDevice = null;
this._dragging = false; this._dragging = false;
this.emit('drag-end'); this.emit('drag-end');

View File

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

View File

@@ -13,6 +13,13 @@ const NotificationDaemon = imports.ui.notificationDaemon;
const PanelMenu = imports.ui.panelMenu; const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu; const PopupMenu = imports.ui.popupMenu;
const ConnectionState = {
DISCONNECTED: 0,
CONNECTED: 1,
DISCONNECTING: 2,
CONNECTING: 3
}
const Indicator = new Lang.Class({ const Indicator = new Lang.Class({
Name: 'BTIndicator', Name: 'BTIndicator',
Extends: PanelMenu.SystemStatusButton, Extends: PanelMenu.SystemStatusButton,
@@ -20,18 +27,61 @@ const Indicator = new Lang.Class({
_init: function() { _init: function() {
this.parent('bluetooth-disabled-symbolic', _("Bluetooth")); 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 = 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('pincode-request', Lang.bind(this, this._pinRequest));
this._applet.connect('confirm-request', Lang.bind(this, this._confirmRequest)); this._applet.connect('confirm-request', Lang.bind(this, this._confirmRequest));
@@ -40,18 +90,199 @@ const Indicator = new Lang.Class({
this._applet.connect('cancel-request', Lang.bind(this, this._cancelRequest)); this._applet.connect('cancel-request', Lang.bind(this, this._cancelRequest));
}, },
_sync: function() { _updateKillswitch: function() {
let connectedDevices = this._applet.get_devices().filter(function(device) { let current_state = this._applet.killswitch_state;
return device.connected; 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;
this._killswitch.setToggleState(on);
if (can_toggle)
this._killswitch.setStatus(null);
else
/* TRANSLATORS: this means that bluetooth was disabled by hardware rfkill */
this._killswitch.setStatus(_("hardware disabled"));
this.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);
}); });
let nDevices = connectedDevices.length; } 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);
});
}
}));
let on = nDevices > 0; item.menu.addMenuItem(menuitem);
this.mainIcon.visible = on; }
this.actor.visible = on;
if (on) if (device.capabilities & GnomeBluetoothApplet.Capabilities.OBEX_PUSH) {
this._item.status.text = ngettext("%d Connected Device", "%d Connected Devices").format(nDevices); 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() { _ensureSource: function() {

View File

@@ -398,6 +398,8 @@ const InputSourceIndicator = new Lang.Class({
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated)); Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
this._sessionUpdated(); this._sessionUpdated();
this.menu.addSettingsAction(_("Region & Language Settings"), 'gnome-region-panel.desktop');
this._sourcesPerWindow = false; this._sourcesPerWindow = false;
this._focusWindowNotifyId = 0; this._focusWindowNotifyId = 0;
this._overviewShowingId = 0; this._overviewShowingId = 0;

File diff suppressed because it is too large Load Diff

View File

@@ -2,15 +2,39 @@
const Gio = imports.gi.Gio; const Gio = imports.gi.Gio;
const Lang = imports.lang; 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 PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu; const PopupMenu = imports.ui.popupMenu;
const BUS_NAME = 'org.gnome.SettingsDaemon.Power'; const BUS_NAME = 'org.gnome.SettingsDaemon.Power';
const OBJECT_PATH = '/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"> const PowerManagerInterface = <interface name="org.gnome.SettingsDaemon.Power">
<method name="GetDevices"> <method name="GetDevices">
<arg type="a(susdut)" direction="out" /> <arg type="a(susdut)" direction="out" />
@@ -37,67 +61,90 @@ const Indicator = new Lang.Class({
return; return;
} }
this._proxy.connect('g-properties-changed', this._proxy.connect('g-properties-changed',
Lang.bind(this, this._sync)); Lang.bind(this, this._devicesChanged));
this._sync(); this._devicesChanged();
})); }));
this._item = new PopupMenu.PopupSubMenuMenuItem(_("Battery"), true); this._deviceItems = [ ];
this._item.menu.addSettingsAction(_("Power Settings"), 'gnome-power-panel.desktop'); this._hasPrimary = false;
this.menu.addMenuItem(this._item); this._primaryDeviceId = null;
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated)); this._batteryItem = new PopupMenu.PopupMenuItem('', { reactive: false });
this._sessionUpdated(); 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() { _readPrimaryDevice: function() {
let sensitive = !Main.sessionMode.isLocked && !Main.sessionMode.isGreeter; this._proxy.GetPrimaryDeviceRemote(Lang.bind(this, function(result, error) {
this.menu.setSensitive(sensitive); if (error) {
}, this._hasPrimary = false;
this._primaryDeviceId = null;
_statusForDevice: function(device) { this._batteryItem.actor.hide();
let [device_id, device_type, icon, percentage, state, seconds] = device; return;
}
if (state == UPower.DeviceState.FULLY_CHARGED) let [[device_id, device_type, icon, percentage, state, seconds]] = result;
return _("Fully Charged"); if (device_type == UPDeviceType.BATTERY) {
this._hasPrimary = true;
let time = Math.round(seconds / 60); let time = Math.round(seconds / 60);
if (time == 0) { if (time == 0) {
// 0 is reported when UPower does not have enough data // 0 is reported when UPower does not have enough data
// to estimate battery life // to estimate battery life
return _("Estimating…"); this._batteryItem.label.text = _("Estimating…");
} } else {
let minutes = time % 60; let minutes = time % 60;
let hours = Math.floor(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");
if (state == UPower.DeviceState.DISCHARGING) { timestring = template.format (hours, ngettext("hour", "hours", hours), minutes, ngettext("minute", "minutes", minutes));
// Translators: this is <hours>:<minutes> Remaining (<percentage>) }
return _("%d\u2236%d Remaining (%d%%)".format(hours, minutes, percentage)); } 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();
} }
if (state == UPower.DeviceState.CHARGING) { this._primaryDeviceId = device_id;
// 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() { _readOtherDevices: function() {
this._proxy.GetPrimaryDeviceRemote(Lang.bind(this, function(result, error) { this._proxy.GetDevicesRemote(Lang.bind(this, function(result, error) {
this._deviceItems.forEach(function(i) { i.destroy(); });
this._deviceItems = [];
if (error) { if (error) {
this._item.actor.hide();
return; return;
} }
let [device] = result; let position = 0;
let [device_id, device_type] = device; let [devices] = result;
if (device_type == UPower.DeviceKind.BATTERY) { for (let i = 0; i < devices.length; i++) {
this._item.status.text = this._statusForDevice(device); let [device_id, device_type] = devices[i];
this._item.actor.show(); if (device_type == UPDeviceType.AC_POWER || device_id == this._primaryDeviceId)
} else { continue;
this._item.actor.hide();
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) { if (icon) {
let gicon = Gio.icon_new_for_string(icon); let gicon = Gio.icon_new_for_string(icon);
this.setGIcon(gicon); this.setGIcon(gicon);
this._item.icon.gicon = gicon;
hasIcon = true; hasIcon = true;
} }
this.mainIcon.visible = hasIcon; this.mainIcon.visible = hasIcon;
this.actor.visible = hasIcon; this.actor.visible = hasIcon;
}, },
_sync: function() { _devicesChanged: function() {
this._syncIcon(); 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,6 @@ const Signals = imports.signals;
const PanelMenu = imports.ui.panelMenu; const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu; const PopupMenu = imports.ui.popupMenu;
const Slider = imports.ui.slider;
const VOLUME_NOTIFY_ID = 1; const VOLUME_NOTIFY_ID = 1;
@@ -29,23 +28,18 @@ function getMixerControl() {
const StreamSlider = new Lang.Class({ const StreamSlider = new Lang.Class({
Name: 'StreamSlider', Name: 'StreamSlider',
_init: function(control) { _init: function(control, title) {
this._control = control; 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('value-changed', Lang.bind(this, this._sliderChanged));
this._slider.connect('drag-end', Lang.bind(this, this._notifyVolumeChange)); this._slider.connect('drag-end', Lang.bind(this, this._notifyVolumeChange));
this._icon = new St.Icon({ style_class: 'popup-menu-icon' }); this.item.addMenuItem(this._title);
this.item.addMenuItem(this._slider);
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._stream = null; this._stream = null;
}, },
@@ -89,7 +83,8 @@ const StreamSlider = new Lang.Class({
_updateVisibility: function() { _updateVisibility: function() {
let visible = this._shouldBeVisible(); let visible = this._shouldBeVisible();
this.item.actor.visible = visible; this._title.actor.visible = visible;
this._slider.actor.visible = visible;
}, },
scroll: function(event) { scroll: function(event) {
@@ -183,17 +178,11 @@ const OutputStreamSlider = new Lang.Class({
this._portChangedId = 0; this._portChangedId = 0;
}, },
_updateSliderIcon: function() {
this._icon.icon_name = (this._hasHeadphones ?
'audio-headphones-symbolic' :
'audio-speakers-symbolic');
},
_portChanged: function() { _portChanged: function() {
let hasHeadphones = this._findHeadphones(this._stream); let hasHeadphones = this._findHeadphones(this._stream);
if (hasHeadphones != this._hasHeadphones) { if (hasHeadphones != this._hasHeadphones) {
this._hasHeadphones = hasHeadphones; this._hasHeadphones = hasHeadphones;
this._updateSliderIcon(); this.emit('headphones-changed', this._hasHeadphones);
} }
} }
}); });
@@ -202,11 +191,10 @@ const InputStreamSlider = new Lang.Class({
Name: 'InputStreamSlider', Name: 'InputStreamSlider',
Extends: StreamSlider, Extends: StreamSlider,
_init: function(control) { _init: function(control, title) {
this.parent(control); this.parent(control, title);
this._control.connect('stream-added', Lang.bind(this, this._maybeShowInput)); this._control.connect('stream-added', Lang.bind(this, this._maybeShowInput));
this._control.connect('stream-removed', 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) { _connectStream: function(stream) {
@@ -254,13 +242,17 @@ const VolumeMenu = new Lang.Class({
this._control.connect('default-sink-changed', Lang.bind(this, this._readOutput)); this._control.connect('default-sink-changed', Lang.bind(this, this._readOutput));
this._control.connect('default-source-changed', Lang.bind(this, this._readInput)); this._control.connect('default-source-changed', Lang.bind(this, this._readInput));
this._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._output.connect('stream-updated', Lang.bind(this, function() {
this.emit('icon-changed'); 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.addMenuItem(this._output.item);
this._input = new InputStreamSlider(this._control); this._input = new InputStreamSlider(this._control, _("Microphone"));
this.addMenuItem(this._input.item); this.addMenuItem(this._input.item);
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
@@ -308,9 +300,18 @@ const Indicator = new Lang.Class({
this.actor.visible = (icon != null); this.actor.visible = (icon != null);
this.setIcon(icon); 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: 'audio-headphones-symbolic' }));
this._headphoneIcon.visible = false;
this.menu.addMenuItem(this._volumeMenu); 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)); 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 -*- // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const AccountsService = imports.gi.AccountsService; const AccountsService = imports.gi.AccountsService;
const Atk = imports.gi.Atk;
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const Gdm = imports.gi.Gdm; const Gdm = imports.gi.Gdm;
const Gio = imports.gi.Gio; const Gio = imports.gi.Gio;
@@ -13,13 +12,13 @@ const Signals = imports.signals;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const St = imports.gi.St; const St = imports.gi.St;
const Layout = imports.ui.layout;
const Main = imports.ui.main; const Main = imports.ui.main;
const ModalDialog = imports.ui.modalDialog;
const Panel = imports.ui.panel; const Panel = imports.ui.panel;
const ShellEntry = imports.ui.shellEntry;
const Tweener = imports.ui.tweener; const Tweener = imports.ui.tweener;
const UserWidget = imports.ui.userWidget; const UserWidget = imports.ui.userWidget;
const AuthPrompt = imports.gdm.authPrompt;
const Batch = imports.gdm.batch; const Batch = imports.gdm.batch;
const GdmUtil = imports.gdm.util; const GdmUtil = imports.gdm.util;
const LoginDialog = imports.gdm.loginDialog; const LoginDialog = imports.gdm.loginDialog;
@@ -29,35 +28,91 @@ const IDLE_TIMEOUT = 2 * 60;
const UnlockDialog = new Lang.Class({ const UnlockDialog = new Lang.Class({
Name: 'UnlockDialog', Name: 'UnlockDialog',
Extends: ModalDialog.ModalDialog,
_init: function(parentActor) { _init: function(parentActor) {
this.actor = new St.Widget({ accessible_role: Atk.Role.WINDOW, this.parent({ shellReactive: true,
style_class: 'login-dialog', styleClass: 'login-dialog',
visible: false }); keybindingMode: Shell.KeyBindingMode.UNLOCK_SCREEN,
parentActor: parentActor
this.actor.add_constraint(new Layout.MonitorConstraint({ primary: true })); });
parentActor.add_child(this.actor);
this._userManager = AccountsService.UserManager.get_default(); this._userManager = AccountsService.UserManager.get_default();
this._userName = GLib.get_user_name(); this._userName = GLib.get_user_name();
this._user = this._userManager.get_user(this._userName); this._user = this._userManager.get_user(this._userName);
this._promptBox = new St.BoxLayout({ vertical: true }); this._failCounter = 0;
this.actor.add_child(this._promptBox); this._firstQuestion = true;
this._promptBox.add_constraint(new Clutter.AlignConstraint({ source: this.actor,
align_axis: Clutter.AlignAxis.BOTH,
factor: 0.5 }));
this._authPrompt = new AuthPrompt.AuthPrompt(new Gdm.Client(), AuthPrompt.AuthPromptMode.UNLOCK_ONLY); this._greeterClient = new Gdm.Client();
this._authPrompt.connect('failed', Lang.bind(this, this._fail)); this._userVerifier = new GdmUtil.ShellUserVerifier(this._greeterClient, { reauthenticationOnly: true });
this._authPrompt.connect('cancelled', Lang.bind(this, this._fail)); this._userVerified = false;
this._authPrompt.connect('reset', Lang.bind(this, this._onReset));
this._authPrompt.setPasswordChar('\u25cf');
this._authPrompt.nextButton.label = _("Unlock");
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.allowCancel = false; 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.placeSpinner({ 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' }); let screenSaverSettings = new Gio.Settings({ schema: 'org.gnome.desktop.screensaver' });
if (screenSaverSettings.get_boolean('user-switch-enabled')) { if (screenSaverSettings.get_boolean('user-switch-enabled')) {
@@ -70,100 +125,174 @@ const UnlockDialog = new Lang.Class({
x_align: St.Align.START, x_align: St.Align.START,
x_fill: true }); x_fill: true });
this._otherUserButton.connect('clicked', Lang.bind(this, this._otherUserClicked)); 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 { } else {
this._otherUserButton = null; this._otherUserButton = null;
} }
this._authPrompt.reset();
this._updateSensitivity(true); 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._idleMonitor = new GnomeDesktop.IdleMonitor();
this._idleWatchId = this._idleMonitor.add_idle_watch(IDLE_TIMEOUT * 1000, Lang.bind(this, this._escape)); this._idleWatchId = this._idleMonitor.add_idle_watch(IDLE_TIMEOUT * 1000, Lang.bind(this, this._escape));
}, },
_updateSensitivity: function(sensitive) { _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) { if (this._otherUserButton) {
this._otherUserButton.reactive = sensitive; this._otherUserButton.reactive = sensitive;
this._otherUserButton.can_focus = sensitive; this._otherUserButton.can_focus = sensitive;
} }
}, },
_fail: function() { _updateOkButtonSensitivity: function(sensitive) {
this.emit('failed'); this._okButton.reactive = sensitive;
this._okButton.can_focus = sensitive;
}, },
_onReset: function(authPrompt, beginRequest) { _showMessage: function(userVerifier, message, styleClass) {
let userName; if (message) {
if (beginRequest == AuthPrompt.BeginRequestType.PROVIDE_USERNAME) { this._promptMessage.text = message;
this._authPrompt.setUser(this._user); this._promptMessage.styleClass = styleClass;
userName = this._userName; GdmUtil.fadeInActor(this._promptMessage);
} else { } else {
userName = null; 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() { _escape: function() {
if (this.allowCancel) if (this.allowCancel) {
this._authPrompt.cancel(); this._userVerifier.cancel();
this.emit('failed');
}
}, },
_otherUserClicked: function(button, event) { _otherUserClicked: function(button, event) {
Gdm.goto_login_session_sync(null); Gdm.goto_login_session_sync(null);
this._authPrompt.cancel(); this._userVerifier.cancel();
this.emit('failed');
}, },
destroy: function() { destroy: function() {
this.popModal(); this._userVerifier.clear();
this.actor.destroy();
if (this._idleWatchId) { if (this._idleWatchId) {
this._idleMonitor.remove_watch(this._idleWatchId); this._idleMonitor.remove_watch(this._idleWatchId);
this._idleWatchId = 0; this._idleWatchId = 0;
} }
this.parent();
}, },
cancel: function() { cancel: function() {
this._authPrompt.cancel(); this._userVerifier.cancel(null);
this.destroy(); this.destroy();
}, },
addCharacter: function(unichar) { 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);

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

@@ -0,0 +1,944 @@
// -*- 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 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 SHOW_FULL_NAME_IN_TOP_BAR_KEY = 'show-full-name-in-top-bar';
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);
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 UserWidget.Avatar(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.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);
this.menu.addSettingsAction(_("Settings"), 'gnome-control-center.desktop');
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();
},
_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 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();
},
_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

@@ -24,7 +24,7 @@ const Avatar = new Lang.Class({
this._user = user; this._user = user;
params = Params.parse(params, { reactive: false, params = Params.parse(params, { reactive: false,
iconSize: AVATAR_ICON_SIZE, iconSize: AVATAR_ICON_SIZE,
styleClass: 'framed-user-icon' }); styleClass: 'status-chooser-user-icon' });
this._iconSize = params.iconSize; this._iconSize = params.iconSize;
this.actor = new St.Bin({ style_class: params.styleClass, this.actor = new St.Bin({ style_class: params.styleClass,

View File

@@ -473,20 +473,12 @@ const WindowManager = new Lang.Class({
this._dimWindow(this._dimmedWindows[i]); this._dimWindow(this._dimmedWindows[i]);
})); }));
if (Main.sessionMode.hasWorkspaces)
this._workspaceTracker = new WorkspaceTracker(this); this._workspaceTracker = new WorkspaceTracker(this);
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT, global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT,
false, -1, 1); false, -1, 1);
}, },
keepWorkspaceAlive: function(workspace, duration) {
if (!this._workspaceTracker)
return;
this._workspaceTracker.keepWorkspaceAlive(workspace, duration);
},
setCustomKeybindingHandler: function(name, modes, handler) { setCustomKeybindingHandler: function(name, modes, handler) {
if (Meta.keybindings_set_custom_handler(name, handler)) if (Meta.keybindings_set_custom_handler(name, handler))
this.allowKeybinding(name, modes); this.allowKeybinding(name, modes);
@@ -833,7 +825,7 @@ const WindowManager = new Lang.Class({
}, },
_switchWorkspace : function(shellwm, from, to, direction) { _switchWorkspace : function(shellwm, from, to, direction) {
if (!Main.sessionMode.hasWorkspaces || !this._shouldAnimate()) { if (!this._shouldAnimate()) {
shellwm.completed_switch_workspace(); shellwm.completed_switch_workspace();
return; return;
} }
@@ -986,9 +978,6 @@ const WindowManager = new Lang.Class({
}, },
_showWorkspaceSwitcher : function(display, screen, window, binding) { _showWorkspaceSwitcher : function(display, screen, window, binding) {
if (!Main.sessionMode.hasWorkspaces)
return;
if (screen.n_workspaces == 1) if (screen.n_workspaces == 1)
return; return;
@@ -1030,19 +1019,14 @@ const WindowManager = new Lang.Class({
}, },
actionMoveWorkspace: function(workspace) { actionMoveWorkspace: function(workspace) {
if (!Main.sessionMode.hasWorkspaces)
return;
let activeWorkspace = global.screen.get_active_workspace(); let activeWorkspace = global.screen.get_active_workspace();
if (activeWorkspace != workspace) if (activeWorkspace != workspace)
workspace.activate(global.get_current_time()); workspace.activate(global.get_current_time());
}, },
actionMoveWindow: function(window, workspace) { actionMoveWindow: function(window, workspace) {
if (!Main.sessionMode.hasWorkspaces)
return;
let activeWorkspace = global.screen.get_active_workspace(); let activeWorkspace = global.screen.get_active_workspace();
if (activeWorkspace != workspace) { if (activeWorkspace != workspace) {
@@ -1055,5 +1039,6 @@ const WindowManager = new Lang.Class({
global.display.clear_mouse_mode(); global.display.clear_mouse_mode();
workspace.activate_with_focus (window, global.get_current_time()); workspace.activate_with_focus (window, global.get_current_time());
} }
}, },
}); });

View File

@@ -127,7 +127,7 @@ const WindowClone = new Lang.Class({
if (this._stackAbove == null) if (this._stackAbove == null)
return null; return null;
if (this.inDrag) { if (this.inDrag || this._zooming) {
if (this._stackAbove._delegate) if (this._stackAbove._delegate)
return this._stackAbove._delegate.getActualStackAbove(); return this._stackAbove._delegate.getActualStackAbove();
else else
@@ -997,7 +997,7 @@ const Workspace = new Lang.Class({
this._dropRect.set_position(geom.x, geom.y); this._dropRect.set_position(geom.x, geom.y);
this._dropRect.set_size(geom.width, geom.height); this._dropRect.set_size(geom.width, geom.height);
this._updateWindowPositions(Main.overview.animationInProgress ? WindowPositionFlags.ANIMATE : WindowPositionFlags.NONE); this._updateWindowPositions(WindowPositionFlags.NONE);
this._actualGeometryLater = 0; this._actualGeometryLater = 0;
return false; return false;

View File

@@ -764,7 +764,7 @@ const ThumbnailsBox = new Lang.Class({
// to open its first window within some time, as tracked by Shell.WindowTracker. // 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 // 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. // workspace while we wait for the startup sequence to load.
Main.wm.keepWorkspaceAlive(global.screen.get_workspace_by_index(newWorkspaceIndex), Main.keepWorkspaceAlive(global.screen.get_workspace_by_index(newWorkspaceIndex),
WORKSPACE_KEEP_ALIVE_TIME); WORKSPACE_KEEP_ALIVE_TIME);
} }

View File

@@ -446,7 +446,7 @@ const WorkspacesDisplay = new Lang.Class({
_init: function() { _init: function() {
this.actor = new St.Widget({ clip_to_allocation: true }); 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._allocationChanged));
this.actor.connect('parent-set', Lang.bind(this, this._parentSet)); this.actor.connect('parent-set', Lang.bind(this, this._parentSet));
let clickAction = new Clutter.ClickAction() let clickAction = new Clutter.ClickAction()
@@ -676,6 +676,12 @@ const WorkspacesDisplay = new Lang.Class({
} }
}, },
_allocationChanged: function() {
if (Main.overview.animationInProgress)
return;
this._updateWorkspacesActualGeometry();
},
_updateWorkspacesActualGeometry: function() { _updateWorkspacesActualGeometry: function() {
if (!this._workspacesViews.length) if (!this._workspacesViews.length)
return; return;

View File

@@ -7,7 +7,6 @@ data/gnome-shell.desktop.in.in
data/gnome-shell-extension-prefs.desktop.in.in data/gnome-shell-extension-prefs.desktop.in.in
data/org.gnome.shell.gschema.xml.in.in data/org.gnome.shell.gschema.xml.in.in
js/extensionPrefs/main.js js/extensionPrefs/main.js
js/gdm/authPrompt.js
js/gdm/loginDialog.js js/gdm/loginDialog.js
js/gdm/powerMenu.js js/gdm/powerMenu.js
js/gdm/util.js js/gdm/util.js
@@ -49,9 +48,9 @@ js/ui/status/keyboard.js
js/ui/status/lockScreenMenu.js js/ui/status/lockScreenMenu.js
js/ui/status/network.js js/ui/status/network.js
js/ui/status/power.js js/ui/status/power.js
js/ui/status/system.js
js/ui/status/volume.js js/ui/status/volume.js
js/ui/unlockDialog.js js/ui/unlockDialog.js
js/ui/userMenu.js
js/ui/viewSelector.js js/ui/viewSelector.js
js/ui/wanda.js js/ui/wanda.js
js/ui/windowAttentionHandler.js js/ui/windowAttentionHandler.js

620
po/cs.po

File diff suppressed because it is too large Load Diff

691
po/de.po

File diff suppressed because it is too large Load Diff

577
po/es.po

File diff suppressed because it is too large Load Diff

343
po/gl.po
View File

@@ -11,8 +11,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: gnome-shell master\n" "Project-Id-Version: gnome-shell master\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-06-28 01:33+0200\n" "POT-Creation-Date: 2013-05-31 01:03+0200\n"
"PO-Revision-Date: 2013-06-28 01:35+0200\n" "PO-Revision-Date: 2013-05-31 01:04+0200\n"
"Last-Translator: Fran Dieguez <frandieguez@gnome.org>\n" "Last-Translator: Fran Dieguez <frandieguez@gnome.org>\n"
"Language-Team: gnome-l10n-gl@gnome.org\n" "Language-Team: gnome-l10n-gl@gnome.org\n"
"Language: gl\n" "Language: gl\n"
@@ -369,41 +369,37 @@ msgid "Select an extension to configure using the combobox above."
msgstr "" msgstr ""
"Seleccione unha extensión que configurar usando a caixa combinada de arriba." "Seleccione unha extensión que configurar usando a caixa combinada de arriba."
#: ../js/gdm/loginDialog.js:308 #: ../js/gdm/loginDialog.js:371
msgid "Choose Session" msgid "Session"
msgstr "Escolla unha sesión" msgstr "Sesión"
#: ../js/gdm/loginDialog.js:326
msgid "Session"
msgstr "Sesión"
#. translators: this message is shown below the user list on the #. translators: this message is shown below the user list on the
#. login screen. It can be activated to reveal an entry for #. login screen. It can be activated to reveal an entry for
#. manually entering the username. #. manually entering the username.
#: ../js/gdm/loginDialog.js:528 #: ../js/gdm/loginDialog.js:601
msgid "Not listed?" msgid "Not listed?"
msgstr "Non está na lista?" msgstr "Non está na lista?"
#: ../js/gdm/loginDialog.js:810 ../js/ui/components/networkAgent.js:137 #: ../js/gdm/loginDialog.js:776 ../js/ui/components/networkAgent.js:137
#: ../js/ui/components/polkitAgent.js:161 ../js/ui/endSessionDialog.js:376 #: ../js/ui/components/polkitAgent.js:161 ../js/ui/endSessionDialog.js:376
#: ../js/ui/extensionDownloader.js:195 ../js/ui/shellMountOperation.js:399 #: ../js/ui/extensionDownloader.js:195 ../js/ui/shellMountOperation.js:399
#: ../js/ui/status/bluetooth.js:449 ../js/ui/unlockDialog.js:95 #: ../js/ui/status/bluetooth.js:415 ../js/ui/unlockDialog.js:96
#: ../js/ui/userMenu.js:884 #: ../js/ui/userMenu.js:938
msgid "Cancel" msgid "Cancel"
msgstr "Cancelar" msgstr "Cancelar"
#: ../js/gdm/loginDialog.js:833 #: ../js/gdm/loginDialog.js:791
msgctxt "button" msgctxt "button"
msgid "Sign In" msgid "Sign In"
msgstr "Iniciar sesión" msgstr "Iniciar sesión"
#: ../js/gdm/loginDialog.js:833 #: ../js/gdm/loginDialog.js:791
msgid "Next" msgid "Next"
msgstr "Seguinte" msgstr "Seguinte"
#. Translators: this message is shown below the username entry field #. Translators: this message is shown below the username entry field
#. to clue the user in on how to login to the local network realm #. to clue the user in on how to login to the local network realm
#: ../js/gdm/loginDialog.js:934 #: ../js/gdm/loginDialog.js:888
#, c-format #, c-format
msgid "(e.g., user or %s)" msgid "(e.g., user or %s)"
msgstr "(p.ex., usuario ou %s)" msgstr "(p.ex., usuario ou %s)"
@@ -411,12 +407,12 @@ msgstr "(p.ex., usuario ou %s)"
#. TTLS and PEAP are actually much more complicated, but this complication #. TTLS and PEAP are actually much more complicated, but this complication
#. is not visible here since we only care about phase2 authentication #. is not visible here since we only care about phase2 authentication
#. (and don't even care of which one) #. (and don't even care of which one)
#: ../js/gdm/loginDialog.js:938 ../js/ui/components/networkAgent.js:260 #: ../js/gdm/loginDialog.js:892 ../js/ui/components/networkAgent.js:260
#: ../js/ui/components/networkAgent.js:278 #: ../js/ui/components/networkAgent.js:278
msgid "Username: " msgid "Username: "
msgstr "Nome de usuario: " msgstr "Nome de usuario: "
#: ../js/gdm/loginDialog.js:1205 #: ../js/gdm/loginDialog.js:1158
msgid "Login Window" msgid "Login Window"
msgstr "Xanela de inicio de sesión" msgstr "Xanela de inicio de sesión"
@@ -425,8 +421,8 @@ msgstr "Xanela de inicio de sesión"
msgid "Power" msgid "Power"
msgstr "Apagar" msgstr "Apagar"
#: ../js/gdm/powerMenu.js:93 ../js/ui/userMenu.js:651 ../js/ui/userMenu.js:655 #: ../js/gdm/powerMenu.js:93 ../js/ui/userMenu.js:696 ../js/ui/userMenu.js:700
#: ../js/ui/userMenu.js:768 #: ../js/ui/userMenu.js:816
msgid "Suspend" msgid "Suspend"
msgstr "Suspender" msgstr "Suspender"
@@ -434,18 +430,18 @@ msgstr "Suspender"
msgid "Restart" msgid "Restart"
msgstr "Reiniciar" msgstr "Reiniciar"
#: ../js/gdm/powerMenu.js:103 ../js/ui/userMenu.js:653 #: ../js/gdm/powerMenu.js:103 ../js/ui/userMenu.js:698
#: ../js/ui/userMenu.js:655 ../js/ui/userMenu.js:767 ../js/ui/userMenu.js:888 #: ../js/ui/userMenu.js:700 ../js/ui/userMenu.js:815 ../js/ui/userMenu.js:942
msgid "Power Off" msgid "Power Off"
msgstr "Apagar" msgstr "Apagar"
#: ../js/gdm/util.js:248 #: ../js/gdm/util.js:247
msgid "Authentication error" msgid "Authentication error"
msgstr "Erro de autenticación" msgstr "Erro de autenticación"
#. Translators: this message is shown below the password entry field #. Translators: this message is shown below the password entry field
#. to indicate the user can swipe their finger instead #. to indicate the user can swipe their finger instead
#: ../js/gdm/util.js:365 #: ../js/gdm/util.js:364
msgid "(or swipe finger)" msgid "(or swipe finger)"
msgstr "(ou pase o dedo)" msgstr "(ou pase o dedo)"
@@ -464,23 +460,23 @@ msgstr "Non foi posíbel analizar a orde:"
msgid "Execution of '%s' failed:" msgid "Execution of '%s' failed:"
msgstr "Produciuse un fallo na execución de «%s»:" msgstr "Produciuse un fallo na execución de «%s»:"
#: ../js/ui/appDisplay.js:397 #: ../js/ui/appDisplay.js:361
msgid "Frequent" msgid "Frequent"
msgstr "Frecuentes" msgstr "Frecuentes"
#: ../js/ui/appDisplay.js:404 #: ../js/ui/appDisplay.js:368
msgid "All" msgid "All"
msgstr "Todos" msgstr "Todos"
#: ../js/ui/appDisplay.js:996 #: ../js/ui/appDisplay.js:960
msgid "New Window" msgid "New Window"
msgstr "Xanela nova" msgstr "Xanela nova"
#: ../js/ui/appDisplay.js:999 ../js/ui/dash.js:284 #: ../js/ui/appDisplay.js:963 ../js/ui/dash.js:284
msgid "Remove from Favorites" msgid "Remove from Favorites"
msgstr "Retirar dos marcadores" msgstr "Retirar dos marcadores"
#: ../js/ui/appDisplay.js:1000 #: ../js/ui/appDisplay.js:964
msgid "Add to Favorites" msgid "Add to Favorites"
msgstr "Engadir aos favoritos" msgstr "Engadir aos favoritos"
@@ -494,7 +490,7 @@ msgstr "%s foi engadido aos seus favoritos."
msgid "%s has been removed from your favorites." msgid "%s has been removed from your favorites."
msgstr "%s retirouse dos seus marcadores." msgstr "%s retirouse dos seus marcadores."
#: ../js/ui/backgroundMenu.js:19 ../js/ui/userMenu.js:744 #: ../js/ui/backgroundMenu.js:19 ../js/ui/userMenu.js:789
msgid "Settings" msgid "Settings"
msgstr "Preferencias" msgstr "Preferencias"
@@ -619,35 +615,35 @@ msgid "S"
msgstr "S" msgstr "S"
#. Translators: Text to show if there are no events #. Translators: Text to show if there are no events
#: ../js/ui/calendar.js:750 #: ../js/ui/calendar.js:735
msgid "Nothing Scheduled" msgid "Nothing Scheduled"
msgstr "Nada programado" msgstr "Nada programado"
#. Translators: Shown on calendar heading when selected day occurs on current year #. Translators: Shown on calendar heading when selected day occurs on current year
#: ../js/ui/calendar.js:768 #: ../js/ui/calendar.js:751
msgctxt "calendar heading" msgctxt "calendar heading"
msgid "%A, %B %d" msgid "%A, %B %d"
msgstr "%A, %d de %B" msgstr "%A, %d de %B"
#. Translators: Shown on calendar heading when selected day occurs on different year #. Translators: Shown on calendar heading when selected day occurs on different year
#: ../js/ui/calendar.js:771 #: ../js/ui/calendar.js:754
msgctxt "calendar heading" msgctxt "calendar heading"
msgid "%A, %B %d, %Y" msgid "%A, %B %d, %Y"
msgstr "%A, %d de %B de %Y" msgstr "%A, %d de %B de %Y"
#: ../js/ui/calendar.js:782 #: ../js/ui/calendar.js:764
msgid "Today" msgid "Today"
msgstr "Hoxe" msgstr "Hoxe"
#: ../js/ui/calendar.js:786 #: ../js/ui/calendar.js:768
msgid "Tomorrow" msgid "Tomorrow"
msgstr "Mañá" msgstr "Mañá"
#: ../js/ui/calendar.js:797 #: ../js/ui/calendar.js:779
msgid "This week" msgid "This week"
msgstr "Esta semana" msgstr "Esta semana"
#: ../js/ui/calendar.js:805 #: ../js/ui/calendar.js:787
msgid "Next week" msgid "Next week"
msgstr "A vindeira semana" msgstr "A vindeira semana"
@@ -836,14 +832,14 @@ msgstr "<b>%d</b> de <b>%B</b> de <b>%Y</b>, <b>%H:%M</b> "
#. Translators: this is the other person changing their old IM name to their new #. Translators: this is the other person changing their old IM name to their new
#. IM name. #. IM name.
#: ../js/ui/components/telepathyClient.js:986 #: ../js/ui/components/telepathyClient.js:985
#, c-format #, c-format
msgid "%s is now known as %s" msgid "%s is now known as %s"
msgstr "Agora %s chámase %s" msgstr "Agora %s chámase %s"
#. translators: argument is a room name like #. translators: argument is a room name like
#. * room@jabber.org for example. #. * room@jabber.org for example.
#: ../js/ui/components/telepathyClient.js:1089 #: ../js/ui/components/telepathyClient.js:1088
#, c-format #, c-format
msgid "Invitation to %s" msgid "Invitation to %s"
msgstr "Convite a %s" msgstr "Convite a %s"
@@ -851,38 +847,38 @@ msgstr "Convite a %s"
#. translators: first argument is the name of a contact and the second #. translators: first argument is the name of a contact and the second
#. * one the name of a room. "Alice is inviting you to join room@jabber.org #. * one the name of a room. "Alice is inviting you to join room@jabber.org
#. * for example. #. * for example.
#: ../js/ui/components/telepathyClient.js:1097 #: ../js/ui/components/telepathyClient.js:1096
#, c-format #, c-format
msgid "%s is inviting you to join %s" msgid "%s is inviting you to join %s"
msgstr "%s estalle convidando a unirse a %s" msgstr "%s estalle convidando a unirse a %s"
#: ../js/ui/components/telepathyClient.js:1099 #: ../js/ui/components/telepathyClient.js:1098
#: ../js/ui/components/telepathyClient.js:1138 #: ../js/ui/components/telepathyClient.js:1137
#: ../js/ui/components/telepathyClient.js:1178 #: ../js/ui/components/telepathyClient.js:1177
#: ../js/ui/components/telepathyClient.js:1241 #: ../js/ui/components/telepathyClient.js:1240
msgid "Decline" msgid "Decline"
msgstr "Rexeitar" msgstr "Rexeitar"
#: ../js/ui/components/telepathyClient.js:1100 #: ../js/ui/components/telepathyClient.js:1099
#: ../js/ui/components/telepathyClient.js:1179 #: ../js/ui/components/telepathyClient.js:1178
#: ../js/ui/components/telepathyClient.js:1242 #: ../js/ui/components/telepathyClient.js:1241
msgid "Accept" msgid "Accept"
msgstr "Aceptar" msgstr "Aceptar"
#. translators: argument is a contact name like Alice for example. #. translators: argument is a contact name like Alice for example.
#: ../js/ui/components/telepathyClient.js:1130 #: ../js/ui/components/telepathyClient.js:1129
#, c-format #, c-format
msgid "Video call from %s" msgid "Video call from %s"
msgstr "Videochamada de %s" msgstr "Videochamada de %s"
#. translators: argument is a contact name like Alice for example. #. translators: argument is a contact name like Alice for example.
#: ../js/ui/components/telepathyClient.js:1133 #: ../js/ui/components/telepathyClient.js:1132
#, c-format #, c-format
msgid "Call from %s" msgid "Call from %s"
msgstr "Chamada de %s" msgstr "Chamada de %s"
#. translators: this is a button label (verb), not a noun #. translators: this is a button label (verb), not a noun
#: ../js/ui/components/telepathyClient.js:1140 #: ../js/ui/components/telepathyClient.js:1139
msgid "Answer" msgid "Answer"
msgstr "Responder" msgstr "Responder"
@@ -891,112 +887,112 @@ msgstr "Responder"
#. * file name. The string will be something #. * file name. The string will be something
#. * like: "Alice is sending you test.ogg" #. * like: "Alice is sending you test.ogg"
#. #.
#: ../js/ui/components/telepathyClient.js:1172 #: ../js/ui/components/telepathyClient.js:1171
#, c-format #, c-format
msgid "%s is sending you %s" msgid "%s is sending you %s"
msgstr "%s esta enviándolle %s" msgstr "%s esta enviándolle %s"
#. To translators: The parameter is the contact's alias #. To translators: The parameter is the contact's alias
#: ../js/ui/components/telepathyClient.js:1207 #: ../js/ui/components/telepathyClient.js:1206
#, c-format #, c-format
msgid "%s would like permission to see when you are online" msgid "%s would like permission to see when you are online"
msgstr "%s solicítalle permiso para ver cando está en liña" msgstr "%s solicítalle permiso para ver cando está en liña"
#: ../js/ui/components/telepathyClient.js:1299 #: ../js/ui/components/telepathyClient.js:1298
msgid "Network error" msgid "Network error"
msgstr "Erro da rede" msgstr "Erro da rede"
#: ../js/ui/components/telepathyClient.js:1301 #: ../js/ui/components/telepathyClient.js:1300
msgid "Authentication failed" msgid "Authentication failed"
msgstr "Fallou a autenticación" msgstr "Fallou a autenticación"
#: ../js/ui/components/telepathyClient.js:1303 #: ../js/ui/components/telepathyClient.js:1302
msgid "Encryption error" msgid "Encryption error"
msgstr "Erro de cifrado" msgstr "Erro de cifrado"
#: ../js/ui/components/telepathyClient.js:1305 #: ../js/ui/components/telepathyClient.js:1304
msgid "Certificate not provided" msgid "Certificate not provided"
msgstr "Certificado non fornecido" msgstr "Certificado non fornecido"
#: ../js/ui/components/telepathyClient.js:1307 #: ../js/ui/components/telepathyClient.js:1306
msgid "Certificate untrusted" msgid "Certificate untrusted"
msgstr "Non se confía no certificado" msgstr "Non se confía no certificado"
#: ../js/ui/components/telepathyClient.js:1309 #: ../js/ui/components/telepathyClient.js:1308
msgid "Certificate expired" msgid "Certificate expired"
msgstr "Certificado caducado" msgstr "Certificado caducado"
#: ../js/ui/components/telepathyClient.js:1311 #: ../js/ui/components/telepathyClient.js:1310
msgid "Certificate not activated" msgid "Certificate not activated"
msgstr "Certificado non activado" msgstr "Certificado non activado"
#: ../js/ui/components/telepathyClient.js:1313 #: ../js/ui/components/telepathyClient.js:1312
msgid "Certificate hostname mismatch" msgid "Certificate hostname mismatch"
msgstr "O nome do servidor do certificado non coincide" msgstr "O nome do servidor do certificado non coincide"
#: ../js/ui/components/telepathyClient.js:1315 #: ../js/ui/components/telepathyClient.js:1314
msgid "Certificate fingerprint mismatch" msgid "Certificate fingerprint mismatch"
msgstr "A pegada do certificado non coincide" msgstr "A pegada do certificado non coincide"
#: ../js/ui/components/telepathyClient.js:1317 #: ../js/ui/components/telepathyClient.js:1316
msgid "Certificate self-signed" msgid "Certificate self-signed"
msgstr "Certificado autoasinado" msgstr "Certificado autoasinado"
#: ../js/ui/components/telepathyClient.js:1319 #: ../js/ui/components/telepathyClient.js:1318
msgid "Status is set to offline" msgid "Status is set to offline"
msgstr "O estado está definido a «desconectado»" msgstr "O estado está definido a «desconectado»"
#: ../js/ui/components/telepathyClient.js:1321 #: ../js/ui/components/telepathyClient.js:1320
msgid "Encryption is not available" msgid "Encryption is not available"
msgstr "O cifrado non está dispoñíbel" msgstr "O cifrado non está dispoñíbel"
#: ../js/ui/components/telepathyClient.js:1323 #: ../js/ui/components/telepathyClient.js:1322
msgid "Certificate is invalid" msgid "Certificate is invalid"
msgstr "O certificado non é válido" msgstr "O certificado non é válido"
#: ../js/ui/components/telepathyClient.js:1325 #: ../js/ui/components/telepathyClient.js:1324
msgid "Connection has been refused" msgid "Connection has been refused"
msgstr "Rexeitouse a conexión" msgstr "Rexeitouse a conexión"
#: ../js/ui/components/telepathyClient.js:1327 #: ../js/ui/components/telepathyClient.js:1326
msgid "Connection can't be established" msgid "Connection can't be established"
msgstr "Non é posíbel estabelecer a conexión" msgstr "Non é posíbel estabelecer a conexión"
#: ../js/ui/components/telepathyClient.js:1329 #: ../js/ui/components/telepathyClient.js:1328
msgid "Connection has been lost" msgid "Connection has been lost"
msgstr "Perdeuse a conexión" msgstr "Perdeuse a conexión"
#: ../js/ui/components/telepathyClient.js:1331 #: ../js/ui/components/telepathyClient.js:1330
msgid "This account is already connected to the server" msgid "This account is already connected to the server"
msgstr "Esta cuenta xa está conectada ao servidor" msgstr "Esta cuenta xa está conectada ao servidor"
#: ../js/ui/components/telepathyClient.js:1333 #: ../js/ui/components/telepathyClient.js:1332
msgid "" msgid ""
"Connection has been replaced by a new connection using the same resource" "Connection has been replaced by a new connection using the same resource"
msgstr "" msgstr ""
"Substituíuse a conexión por unha nova conexión empregando o mesmo recurso" "Substituíuse a conexión por unha nova conexión empregando o mesmo recurso"
#: ../js/ui/components/telepathyClient.js:1335 #: ../js/ui/components/telepathyClient.js:1334
msgid "The account already exists on the server" msgid "The account already exists on the server"
msgstr "Esta conta xa existe no servidor" msgstr "Esta conta xa existe no servidor"
#: ../js/ui/components/telepathyClient.js:1337 #: ../js/ui/components/telepathyClient.js:1336
msgid "Server is currently too busy to handle the connection" msgid "Server is currently too busy to handle the connection"
msgstr "" msgstr ""
"Nestes intres o servidor está moi ocupado tentando xestionar a conexión" "Nestes intres o servidor está moi ocupado tentando xestionar a conexión"
#: ../js/ui/components/telepathyClient.js:1339 #: ../js/ui/components/telepathyClient.js:1338
msgid "Certificate has been revoked" msgid "Certificate has been revoked"
msgstr "Revogouse o certificado" msgstr "Revogouse o certificado"
#: ../js/ui/components/telepathyClient.js:1341 #: ../js/ui/components/telepathyClient.js:1340
msgid "" msgid ""
"Certificate uses an insecure cipher algorithm or is cryptographically weak" "Certificate uses an insecure cipher algorithm or is cryptographically weak"
msgstr "" msgstr ""
"O certificado usa un algoritmo de cifrado inseguro ou é criptográficamente " "O certificado usa un algoritmo de cifrado inseguro ou é criptográficamente "
"débil" "débil"
#: ../js/ui/components/telepathyClient.js:1343 #: ../js/ui/components/telepathyClient.js:1342
msgid "" msgid ""
"The length of the server certificate, or the depth of the server certificate " "The length of the server certificate, or the depth of the server certificate "
"chain, exceed the limits imposed by the cryptography library" "chain, exceed the limits imposed by the cryptography library"
@@ -1005,22 +1001,22 @@ msgstr ""
"certificado do servidor excede os límites impostos pola biblioteca de " "certificado do servidor excede os límites impostos pola biblioteca de "
"criptografía." "criptografía."
#: ../js/ui/components/telepathyClient.js:1345 #: ../js/ui/components/telepathyClient.js:1344
msgid "Internal error" msgid "Internal error"
msgstr "Erro interno" msgstr "Erro interno"
#. translators: argument is the account name, like #. translators: argument is the account name, like
#. * name@jabber.org for example. #. * name@jabber.org for example.
#: ../js/ui/components/telepathyClient.js:1355 #: ../js/ui/components/telepathyClient.js:1354
#, c-format #, c-format
msgid "Unable to connect to %s" msgid "Unable to connect to %s"
msgstr "Non foi posíbel conectarse a %s" msgstr "Non foi posíbel conectarse a %s"
#: ../js/ui/components/telepathyClient.js:1360 #: ../js/ui/components/telepathyClient.js:1359
msgid "View account" msgid "View account"
msgstr "Ver conta" msgstr "Ver conta"
#: ../js/ui/components/telepathyClient.js:1399 #: ../js/ui/components/telepathyClient.js:1398
msgid "Unknown reason" msgid "Unknown reason"
msgstr "Razón descoñecida" msgstr "Razón descoñecida"
@@ -1034,26 +1030,26 @@ msgstr "Mostrar aplicativos"
#. Translators: this is the name of the dock/favorites area on #. Translators: this is the name of the dock/favorites area on
#. the left of the overview #. the left of the overview
#: ../js/ui/dash.js:439 #: ../js/ui/dash.js:435
msgid "Dash" msgid "Dash"
msgstr "Taboleiro" msgstr "Taboleiro"
#: ../js/ui/dateMenu.js:85 #: ../js/ui/dateMenu.js:86
msgid "Open Calendar" msgid "Open Calendar"
msgstr "Abrir Calendario" msgstr "Abrir Calendario"
#: ../js/ui/dateMenu.js:89 #: ../js/ui/dateMenu.js:90
msgid "Open Clocks" msgid "Open Clocks"
msgstr "Abrir Reloxos" msgstr "Abrir Reloxos"
#: ../js/ui/dateMenu.js:96 #: ../js/ui/dateMenu.js:97
msgid "Date & Time Settings" msgid "Date & Time Settings"
msgstr "Preferencias de data e hora" msgstr "Preferencias de data e hora"
#. Translators: This is the date format to use when the calendar popup is #. 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"). #. * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
#. #.
#: ../js/ui/dateMenu.js:201 #: ../js/ui/dateMenu.js:208
msgid "%A %B %e, %Y" msgid "%A %B %e, %Y"
msgstr "%a, %e de %B, %Y" msgstr "%a, %e de %B, %Y"
@@ -1265,17 +1261,17 @@ msgstr "Vista xeral"
msgid "Type to search…" msgid "Type to search…"
msgstr "Escriba para buscar…" msgstr "Escriba para buscar…"
#: ../js/ui/panel.js:567 #: ../js/ui/panel.js:642
msgid "Quit" msgid "Quit"
msgstr "Saír" msgstr "Saír"
#. Translators: If there is no suitable word for "Activities" #. Translators: If there is no suitable word for "Activities"
#. in your language, you can use the word for "Overview". #. in your language, you can use the word for "Overview".
#: ../js/ui/panel.js:618 #: ../js/ui/panel.js:693
msgid "Activities" msgid "Activities"
msgstr "Actividades" msgstr "Actividades"
#: ../js/ui/panel.js:914 #: ../js/ui/panel.js:989
msgid "Top Bar" msgid "Top Bar"
msgstr "Barra superior" msgstr "Barra superior"
@@ -1284,7 +1280,7 @@ msgstr "Barra superior"
#. "ON" and "OFF") or "toggle-switch-intl" (for toggle #. "ON" and "OFF") or "toggle-switch-intl" (for toggle
#. switches containing "◯" and "|"). Other values will #. switches containing "◯" and "|"). Other values will
#. simply result in invisible toggle switches. #. simply result in invisible toggle switches.
#: ../js/ui/popupMenu.js:549 #: ../js/ui/popupMenu.js:738
msgid "toggle-switch-us" msgid "toggle-switch-us"
msgstr "toggle-switch-intl" msgstr "toggle-switch-intl"
@@ -1309,7 +1305,7 @@ msgid_plural "%d new notifications"
msgstr[0] "%d notificación nova" msgstr[0] "%d notificación nova"
msgstr[1] "%d notificacións novas" msgstr[1] "%d notificacións novas"
#: ../js/ui/screenShield.js:449 ../js/ui/userMenu.js:759 #: ../js/ui/screenShield.js:449 ../js/ui/userMenu.js:807
msgid "Lock" msgid "Lock"
msgstr "Bloquear" msgstr "Bloquear"
@@ -1324,11 +1320,11 @@ msgstr "GNOME precisa bloquear a pantalla"
#. #.
#. XXX: another option is to kick the user into the gdm login #. XXX: another option is to kick the user into the gdm login
#. screen, where we're not affected by grabs #. screen, where we're not affected by grabs
#: ../js/ui/screenShield.js:775 ../js/ui/screenShield.js:1215 #: ../js/ui/screenShield.js:773 ../js/ui/screenShield.js:1213
msgid "Unable to lock" msgid "Unable to lock"
msgstr "Non foi posíbel bloquear" msgstr "Non foi posíbel bloquear"
#: ../js/ui/screenShield.js:776 ../js/ui/screenShield.js:1216 #: ../js/ui/screenShield.js:774 ../js/ui/screenShield.js:1214
msgid "Lock was blocked by an application" msgid "Lock was blocked by an application"
msgstr "Un aplicativo impediu o bloqueo" msgstr "Un aplicativo impediu o bloqueo"
@@ -1364,7 +1360,7 @@ msgstr "Contrasinal"
msgid "Remember Password" msgid "Remember Password"
msgstr "Lembrar contrasinal" msgstr "Lembrar contrasinal"
#: ../js/ui/shellMountOperation.js:403 ../js/ui/unlockDialog.js:108 #: ../js/ui/shellMountOperation.js:403 ../js/ui/unlockDialog.js:109
msgid "Unlock" msgid "Unlock"
msgstr "Desbloquear" msgstr "Desbloquear"
@@ -1417,9 +1413,9 @@ msgid "Large Text"
msgstr "Texto grande" msgstr "Texto grande"
#: ../js/ui/status/bluetooth.js:28 ../js/ui/status/bluetooth.js:32 #: ../js/ui/status/bluetooth.js:28 ../js/ui/status/bluetooth.js:32
#: ../js/ui/status/bluetooth.js:290 ../js/ui/status/bluetooth.js:327 #: ../js/ui/status/bluetooth.js:289 ../js/ui/status/bluetooth.js:321
#: ../js/ui/status/bluetooth.js:355 ../js/ui/status/bluetooth.js:391 #: ../js/ui/status/bluetooth.js:357 ../js/ui/status/bluetooth.js:388
#: ../js/ui/status/bluetooth.js:422 ../js/ui/status/network.js:713 #: ../js/ui/status/network.js:739
msgid "Bluetooth" msgid "Bluetooth"
msgstr "Bluetooth" msgstr "Bluetooth"
@@ -1440,82 +1436,73 @@ msgid "Bluetooth Settings"
msgstr "Preferencias do Bluetooth" msgstr "Preferencias do Bluetooth"
#. TRANSLATORS: this means that bluetooth was disabled by hardware rfkill #. TRANSLATORS: this means that bluetooth was disabled by hardware rfkill
#: ../js/ui/status/bluetooth.js:105 ../js/ui/status/network.js:140 #: ../js/ui/status/bluetooth.js:104 ../js/ui/status/network.js:142
msgid "hardware disabled" msgid "hardware disabled"
msgstr "hardware desactivado" msgstr "hardware desactivado"
#: ../js/ui/status/bluetooth.js:198 #: ../js/ui/status/bluetooth.js:197
msgid "Connection" msgid "Connection"
msgstr "Conexión" msgstr "Conexión"
#: ../js/ui/status/bluetooth.js:209 ../js/ui/status/network.js:399 #: ../js/ui/status/bluetooth.js:208 ../js/ui/status/network.js:404
msgid "disconnecting..." msgid "disconnecting..."
msgstr "desconectando…" msgstr "desconectando…"
#: ../js/ui/status/bluetooth.js:222 ../js/ui/status/network.js:405 #: ../js/ui/status/bluetooth.js:221 ../js/ui/status/network.js:410
#: ../js/ui/status/network.js:1298 #: ../js/ui/status/network.js:1343
msgid "connecting..." msgid "connecting..."
msgstr "conectando…" msgstr "conectando…"
#: ../js/ui/status/bluetooth.js:240 #: ../js/ui/status/bluetooth.js:239
msgid "Send Files…" msgid "Send Files…"
msgstr "Enviar ficheiros…" msgstr "Enviar ficheiros…"
#: ../js/ui/status/bluetooth.js:247 #: ../js/ui/status/bluetooth.js:246
msgid "Keyboard Settings" msgid "Keyboard Settings"
msgstr "Preferencias do teclado" msgstr "Preferencias do teclado"
#: ../js/ui/status/bluetooth.js:250 #: ../js/ui/status/bluetooth.js:249
msgid "Mouse Settings" msgid "Mouse Settings"
msgstr "Preferencias do rato" msgstr "Preferencias do rato"
#: ../js/ui/status/bluetooth.js:255 ../js/ui/status/volume.js:313 #: ../js/ui/status/bluetooth.js:254 ../js/ui/status/volume.js:316
msgid "Sound Settings" msgid "Sound Settings"
msgstr "Preferencias do son" msgstr "Preferencias do son"
#: ../js/ui/status/bluetooth.js:328 ../js/ui/status/bluetooth.js:356 #: ../js/ui/status/bluetooth.js:322
#, c-format #, c-format
msgid "Authorization request from %s" msgid "Authorization request from %s"
msgstr "Solicitude de autorización de %s" msgstr "Solicitude de autorización de %s"
#: ../js/ui/status/bluetooth.js:334 ../js/ui/status/bluetooth.js:399 #: ../js/ui/status/bluetooth.js:328
#: ../js/ui/status/bluetooth.js:430
#, c-format
msgid "Device %s wants to pair with this computer"
msgstr "O dispositivo «%s» quere emparellarse con este equipo"
#: ../js/ui/status/bluetooth.js:336
msgid "Allow"
msgstr "Permitir"
#: ../js/ui/status/bluetooth.js:337
msgid "Deny"
msgstr "Denegar"
#: ../js/ui/status/bluetooth.js:362
#, c-format #, c-format
msgid "Device %s wants access to the service '%s'" msgid "Device %s wants access to the service '%s'"
msgstr "O dispositivo %s quere acceder ao servizo «%s»" msgstr "O dispositivo %s quere acceder ao servizo «%s»"
#: ../js/ui/status/bluetooth.js:364 #: ../js/ui/status/bluetooth.js:330
msgid "Always grant access" msgid "Always grant access"
msgstr "Conceder acceso sempre" msgstr "Conceder acceso sempre"
#: ../js/ui/status/bluetooth.js:365 #: ../js/ui/status/bluetooth.js:331
msgid "Grant this time only" msgid "Grant this time only"
msgstr "Conceder só esta vez" msgstr "Conceder só esta vez"
#: ../js/ui/status/bluetooth.js:366 #: ../js/ui/status/bluetooth.js:332
msgid "Reject" msgid "Reject"
msgstr "Rexeitar" msgstr "Rexeitar"
#. Translators: argument is the device short name #. Translators: argument is the device short name
#: ../js/ui/status/bluetooth.js:393 #: ../js/ui/status/bluetooth.js:359
#, c-format #, c-format
msgid "Pairing confirmation for %s" msgid "Pairing confirmation for %s"
msgstr "Confirmación de emparellado para «%s»" msgstr "Confirmación de emparellado para «%s»"
#: ../js/ui/status/bluetooth.js:400 #: ../js/ui/status/bluetooth.js:365 ../js/ui/status/bluetooth.js:396
#, c-format
msgid "Device %s wants to pair with this computer"
msgstr "O dispositivo «%s» quere emparellarse con este equipo"
#: ../js/ui/status/bluetooth.js:366
#, c-format #, c-format
msgid "" msgid ""
"Please confirm whether the Passkey '%06d' matches the one on the device." "Please confirm whether the Passkey '%06d' matches the one on the device."
@@ -1523,24 +1510,24 @@ msgstr ""
"Confirme que a frase de paso «%06d» coincide coa mostrada no dispositivo." "Confirme que a frase de paso «%06d» coincide coa mostrada no dispositivo."
#. Translators: this is the verb, not the noun #. Translators: this is the verb, not the noun
#: ../js/ui/status/bluetooth.js:403 #: ../js/ui/status/bluetooth.js:369
msgid "Matches" msgid "Matches"
msgstr "Coincide" msgstr "Coincide"
#: ../js/ui/status/bluetooth.js:404 #: ../js/ui/status/bluetooth.js:370
msgid "Does not match" msgid "Does not match"
msgstr "Non coincide" msgstr "Non coincide"
#: ../js/ui/status/bluetooth.js:423 #: ../js/ui/status/bluetooth.js:389
#, c-format #, c-format
msgid "Pairing request for %s" msgid "Pairing request for %s"
msgstr "Solicitude de emparellamento para «%s»" msgstr "Solicitude de emparellamento para «%s»"
#: ../js/ui/status/bluetooth.js:431 #: ../js/ui/status/bluetooth.js:397
msgid "Please enter the PIN mentioned on the device." msgid "Please enter the PIN mentioned on the device."
msgstr "Escriba o PIN mencionado no dispositivo." msgstr "Escriba o PIN mencionado no dispositivo."
#: ../js/ui/status/bluetooth.js:448 #: ../js/ui/status/bluetooth.js:414
msgid "OK" msgid "OK"
msgstr "Aceptar" msgstr "Aceptar"
@@ -1560,81 +1547,87 @@ msgstr "Volume, rede, batería"
msgid "<unknown>" msgid "<unknown>"
msgstr "<descoñecido>" msgstr "<descoñecido>"
#: ../js/ui/status/network.js:125 #: ../js/ui/status/network.js:127
msgid "Wi-Fi" msgid "Wi-Fi"
msgstr "Wifi" msgstr "Wifi"
#. Translators: this indicates that wireless or wwan is disabled by hardware killswitch #. Translators: this indicates that wireless or wwan is disabled by hardware killswitch
#: ../js/ui/status/network.js:162 #: ../js/ui/status/network.js:164
msgid "disabled" msgid "disabled"
msgstr "desactivada" msgstr "desactivada"
#. Translators: this is for network devices that are physically present but are not #. Translators: this is for network devices that are physically present but are not
#. under NetworkManager's control (and thus cannot be used in the menu) #. under NetworkManager's control (and thus cannot be used in the menu)
#: ../js/ui/status/network.js:397 #: ../js/ui/status/network.js:402
msgid "unmanaged" msgid "unmanaged"
msgstr "non xestionada" msgstr "non xestionada"
#. Translators: this is for network connections that require some kind of key or password #. Translators: this is for network connections that require some kind of key or password
#: ../js/ui/status/network.js:408 ../js/ui/status/network.js:1301 #: ../js/ui/status/network.js:413 ../js/ui/status/network.js:1346
msgid "authentication required" msgid "authentication required"
msgstr "requírese autenticación" msgstr "requírese autenticación"
#. Translators: this is for devices that require some kind of firmware or kernel #. Translators: this is for devices that require some kind of firmware or kernel
#. module, which is missing #. module, which is missing
#: ../js/ui/status/network.js:419 #: ../js/ui/status/network.js:423
msgid "firmware missing" msgid "firmware missing"
msgstr "falta o «firmware»" msgstr "falta o «firmware»"
#. Translators: this is for wired network devices that are physically disconnected
#: ../js/ui/status/network.js:430
msgid "cable unplugged"
msgstr "cable desconectado"
#. Translators: this is for a network device that cannot be activated (for example it #. Translators: this is for a network device that cannot be activated (for example it
#. is disabled by rfkill, or it has no coverage #. is disabled by rfkill, or it has no coverage
#: ../js/ui/status/network.js:423 #: ../js/ui/status/network.js:435
msgid "unavailable" msgid "unavailable"
msgstr "non dispoñíbel" msgstr "non dispoñíbel"
#: ../js/ui/status/network.js:425 ../js/ui/status/network.js:1303 #: ../js/ui/status/network.js:437 ../js/ui/status/network.js:1348
msgid "connection failed" msgid "connection failed"
msgstr "conexión fallada" msgstr "conexión fallada"
#: ../js/ui/status/network.js:478 ../js/ui/status/network.js:1190 #: ../js/ui/status/network.js:490 ../js/ui/status/network.js:1236
#: ../js/ui/status/network.js:1424
msgid "More…" msgid "More…"
msgstr "Máis…" msgstr "Máis…"
#. TRANSLATORS: this is the indication that a connection for another logged in user is active, #. TRANSLATORS: this is the indication that a connection for another logged in user is active,
#. and we cannot access its settings (including the name) #. and we cannot access its settings (including the name)
#: ../js/ui/status/network.js:506 ../js/ui/status/network.js:1142 #: ../js/ui/status/network.js:518 ../js/ui/status/network.js:1191
msgid "Connected (private)" msgid "Connected (private)"
msgstr "Conectada (privada)" msgstr "Conectada (privada)"
#: ../js/ui/status/network.js:572 #: ../js/ui/status/network.js:597
msgid "Wired" msgid "Wired"
msgstr "Con fíos" msgstr "Con fíos"
#: ../js/ui/status/network.js:592 #: ../js/ui/status/network.js:611
msgid "Mobile broadband" msgid "Mobile broadband"
msgstr "Banda larga móbil" msgstr "Banda larga móbil"
#: ../js/ui/status/network.js:1474 #: ../js/ui/status/network.js:1522
msgid "Enable networking" msgid "Enable networking"
msgstr "Activar rede" msgstr "Activar rede"
#: ../js/ui/status/network.js:1522 #: ../js/ui/status/network.js:1583
msgid "Network Settings" msgid "Network Settings"
msgstr "Preferencias da rede" msgstr "Preferencias da rede"
#: ../js/ui/status/network.js:1539 #: ../js/ui/status/network.js:1600
msgid "Network Manager" msgid "Network Manager"
msgstr "Xestor da rede" msgstr "Xestor da rede"
#: ../js/ui/status/network.js:1623 #: ../js/ui/status/network.js:1690
msgid "Connection failed" msgid "Connection failed"
msgstr "Produciuse un fallo na conexión" msgstr "Produciuse un fallo na conexión"
#: ../js/ui/status/network.js:1624 #: ../js/ui/status/network.js:1691
msgid "Activation of network connection failed" msgid "Activation of network connection failed"
msgstr "Produciuse un fallo na activación da conexión de rede" msgstr "Produciuse un fallo na activación da conexión de rede"
#: ../js/ui/status/network.js:1937 #: ../js/ui/status/network.js:2047
msgid "Networking is disabled" msgid "Networking is disabled"
msgstr "A rede está desactivada" msgstr "A rede está desactivada"
@@ -1735,72 +1728,72 @@ msgctxt "device"
msgid "Unknown" msgid "Unknown"
msgstr "Descoñecido" msgstr "Descoñecido"
#: ../js/ui/status/volume.js:121 #: ../js/ui/status/volume.js:124
msgid "Volume changed" msgid "Volume changed"
msgstr "Volume cambiado" msgstr "Volume cambiado"
#. Translators: This is the label for audio volume #. Translators: This is the label for audio volume
#: ../js/ui/status/volume.js:246 ../js/ui/status/volume.js:294 #: ../js/ui/status/volume.js:249 ../js/ui/status/volume.js:297
msgid "Volume" msgid "Volume"
msgstr "Volume" msgstr "Volume"
#: ../js/ui/status/volume.js:255 #: ../js/ui/status/volume.js:258
msgid "Microphone" msgid "Microphone"
msgstr "Micrófono" msgstr "Micrófono"
#: ../js/ui/unlockDialog.js:119 #: ../js/ui/unlockDialog.js:120
msgid "Log in as another user" msgid "Log in as another user"
msgstr "Iniciar sesión como outro usuario" msgstr "Iniciar sesión como outro usuario"
#: ../js/ui/unlockDialog.js:140 #: ../js/ui/unlockDialog.js:141
msgid "Unlock Window" msgid "Unlock Window"
msgstr "Desbloquear xanela" msgstr "Desbloquear xanela"
#: ../js/ui/userMenu.js:149 #: ../js/ui/userMenu.js:193
msgid "Available" msgid "Available"
msgstr "Dispoñíbel" msgstr "Dispoñíbel"
#: ../js/ui/userMenu.js:152 #: ../js/ui/userMenu.js:196
msgid "Busy" msgid "Busy"
msgstr "Ocupado" msgstr "Ocupado"
#: ../js/ui/userMenu.js:155 #: ../js/ui/userMenu.js:199
msgid "Invisible" msgid "Invisible"
msgstr "Invisíbel" msgstr "Invisíbel"
#: ../js/ui/userMenu.js:158 #: ../js/ui/userMenu.js:202
msgid "Away" msgid "Away"
msgstr "Ausente" msgstr "Ausente"
#: ../js/ui/userMenu.js:161 #: ../js/ui/userMenu.js:205
msgid "Idle" msgid "Idle"
msgstr "Inactivo" msgstr "Inactivo"
#: ../js/ui/userMenu.js:164 #: ../js/ui/userMenu.js:208
msgid "Offline" msgid "Offline"
msgstr "Desconectado" msgstr "Desconectado"
#: ../js/ui/userMenu.js:736 #: ../js/ui/userMenu.js:781
msgid "Notifications" msgid "Notifications"
msgstr "Notificacións" msgstr "Notificacións"
#: ../js/ui/userMenu.js:749 #: ../js/ui/userMenu.js:797
msgid "Switch User" msgid "Switch User"
msgstr "Cambiar de usuario" msgstr "Cambiar de usuario"
#: ../js/ui/userMenu.js:754 #: ../js/ui/userMenu.js:802
msgid "Log Out" msgid "Log Out"
msgstr "Saír da sesión" msgstr "Saír da sesión"
#: ../js/ui/userMenu.js:774 #: ../js/ui/userMenu.js:822
msgid "Install Updates & Restart" msgid "Install Updates & Restart"
msgstr "Instalar actualizacións e reiniciar" msgstr "Instalar actualizacións e reiniciar"
#: ../js/ui/userMenu.js:792 #: ../js/ui/userMenu.js:840
msgid "Your chat status will be set to busy" msgid "Your chat status will be set to busy"
msgstr "O seu estado de conversa estabelecerase a «ocupado»" msgstr "O seu estado de conversa estabelecerase a «ocupado»"
#: ../js/ui/userMenu.js:793 #: ../js/ui/userMenu.js:841
msgid "" msgid ""
"Notifications are now disabled, including chat messages. Your online status " "Notifications are now disabled, including chat messages. Your online status "
"has been adjusted to let others know that you might not see their messages." "has been adjusted to let others know that you might not see their messages."
@@ -1809,22 +1802,22 @@ msgstr ""
"conversa. O seu estado de conexión axustouse para que outros saiban que non " "conversa. O seu estado de conexión axustouse para que outros saiban que non "
"quere ver as súas mensaxes." "quere ver as súas mensaxes."
#: ../js/ui/userMenu.js:834 #: ../js/ui/userMenu.js:888
msgid "Other users are logged in." msgid "Other users are logged in."
msgstr "Hai outros usuarios conectados." msgstr "Hai outros usuarios conectados."
#: ../js/ui/userMenu.js:839 #: ../js/ui/userMenu.js:893
msgid "Shutting down might cause them to lose unsaved work." msgid "Shutting down might cause them to lose unsaved work."
msgstr "Se apaga o computador pode perder o traballo que non gardou." msgstr "Se apaga o computador pode perder o traballo que non gardou."
#. Translators: Remote here refers to a remote session, like a ssh login #. Translators: Remote here refers to a remote session, like a ssh login
#: ../js/ui/userMenu.js:867 #: ../js/ui/userMenu.js:921
#, c-format #, c-format
msgid "%s (remote)" msgid "%s (remote)"
msgstr "%s (remoto)" msgstr "%s (remoto)"
#. Translators: Console here refers to a tty like a VT console #. Translators: Console here refers to a tty like a VT console
#: ../js/ui/userMenu.js:870 #: ../js/ui/userMenu.js:924
#, c-format #, c-format
msgid "%s (console)" msgid "%s (console)"
msgstr "%s (consola)" msgstr "%s (consola)"
@@ -1882,21 +1875,21 @@ msgstr[1] "%u entradas"
msgid "System Sounds" msgid "System Sounds"
msgstr "Sons do sistema" msgstr "Sons do sistema"
#: ../src/main.c:353 #: ../src/main.c:372
msgid "Print version" msgid "Print version"
msgstr "Imprimir versión" msgstr "Imprimir versión"
#: ../src/main.c:359 #: ../src/main.c:378
msgid "Mode used by GDM for login screen" msgid "Mode used by GDM for login screen"
msgstr "Modo usado por GDM para a pantalla de inicio" msgstr "Modo usado por GDM para a pantalla de inicio"
#: ../src/main.c:365 #: ../src/main.c:384
msgid "Use a specific mode, e.g. \"gdm\" for login screen" msgid "Use a specific mode, e.g. \"gdm\" for login screen"
msgstr "" msgstr ""
"Usar un modo específico, por exemplo, «gdm» para a pantalla de inicio de " "Usar un modo específico, por exemplo, «gdm» para a pantalla de inicio de "
"sesión" "sesión"
#: ../src/main.c:371 #: ../src/main.c:390
msgid "List possible modes" msgid "List possible modes"
msgstr "Listar os modos posíbeis" msgstr "Listar os modos posíbeis"
@@ -1917,9 +1910,6 @@ msgstr "O contrasinal non pode estar baleiro"
msgid "Authentication dialog was dismissed by the user" msgid "Authentication dialog was dismissed by the user"
msgstr "O usuario rexeitou o diálogo de autenticación" msgstr "O usuario rexeitou o diálogo de autenticación"
#~ msgid "cable unplugged"
#~ msgstr "cable desconectado"
#~ msgid "Whether to collect stats about applications usage" #~ msgid "Whether to collect stats about applications usage"
#~ msgstr "" #~ msgstr ""
#~ "Indica se se deben recolectar estatísticas sobre o uso dos aplicativos" #~ "Indica se se deben recolectar estatísticas sobre o uso dos aplicativos"
@@ -2037,6 +2027,9 @@ msgstr "O usuario rexeitou o diálogo de autenticación"
#~ msgid "System Settings" #~ msgid "System Settings"
#~ msgstr "Preferencias do sistema" #~ msgstr "Preferencias do sistema"
#~ msgid "Switch Session"
#~ msgstr "Cambiar de sesión"
#~ msgid "disabled OpenSearch providers" #~ msgid "disabled OpenSearch providers"
#~ msgstr "fornecedores OpenSearch desactivados" #~ msgstr "fornecedores OpenSearch desactivados"

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

142
po/nb.po
View File

@@ -8,8 +8,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: gnome-shell 3.9.x\n" "Project-Id-Version: gnome-shell 3.9.x\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-07-18 15:29+0200\n" "POT-Creation-Date: 2013-06-17 12:28+0200\n"
"PO-Revision-Date: 2013-07-04 11:09+0200\n" "PO-Revision-Date: 2013-06-17 12:29+0200\n"
"Last-Translator: Kjartan Maraas <kmaraas@gnome.org>\n" "Last-Translator: Kjartan Maraas <kmaraas@gnome.org>\n"
"Language-Team: Norwegian bokmål <i18n-nb@lister.ping.uio.no>\n" "Language-Team: Norwegian bokmål <i18n-nb@lister.ping.uio.no>\n"
"Language: \n" "Language: \n"
@@ -331,22 +331,18 @@ msgstr "Utvidelse"
msgid "Select an extension to configure using the combobox above." msgid "Select an extension to configure using the combobox above."
msgstr "Velg en utvidelse som skal konfigureres med komboboksen over." msgstr "Velg en utvidelse som skal konfigureres med komboboksen over."
#: ../js/gdm/loginDialog.js:308 #: ../js/gdm/loginDialog.js:370
msgid "Choose Session" msgid "Session"
msgstr "Velg økt" msgstr "Økt …"
#: ../js/gdm/loginDialog.js:326
msgid "Session"
msgstr "Økt"
#. translators: this message is shown below the user list on the #. translators: this message is shown below the user list on the
#. login screen. It can be activated to reveal an entry for #. login screen. It can be activated to reveal an entry for
#. manually entering the username. #. manually entering the username.
#: ../js/gdm/loginDialog.js:533 #: ../js/gdm/loginDialog.js:600
msgid "Not listed?" msgid "Not listed?"
msgstr "Ikke listet?" msgstr "Ikke listet?"
#: ../js/gdm/loginDialog.js:810 ../js/ui/components/networkAgent.js:137 #: ../js/gdm/loginDialog.js:775 ../js/ui/components/networkAgent.js:137
#: ../js/ui/components/polkitAgent.js:161 ../js/ui/endSessionDialog.js:376 #: ../js/ui/components/polkitAgent.js:161 ../js/ui/endSessionDialog.js:376
#: ../js/ui/extensionDownloader.js:195 ../js/ui/shellMountOperation.js:399 #: ../js/ui/extensionDownloader.js:195 ../js/ui/shellMountOperation.js:399
#: ../js/ui/status/bluetooth.js:449 ../js/ui/unlockDialog.js:95 #: ../js/ui/status/bluetooth.js:449 ../js/ui/unlockDialog.js:95
@@ -354,18 +350,18 @@ msgstr "Ikke listet?"
msgid "Cancel" msgid "Cancel"
msgstr "Avbryt" msgstr "Avbryt"
#: ../js/gdm/loginDialog.js:833 #: ../js/gdm/loginDialog.js:790
msgctxt "button" msgctxt "button"
msgid "Sign In" msgid "Sign In"
msgstr "Logg inn" msgstr "Logg inn"
#: ../js/gdm/loginDialog.js:833 #: ../js/gdm/loginDialog.js:790
msgid "Next" msgid "Next"
msgstr "Neste" msgstr "Neste"
#. Translators: this message is shown below the username entry field #. Translators: this message is shown below the username entry field
#. to clue the user in on how to login to the local network realm #. to clue the user in on how to login to the local network realm
#: ../js/gdm/loginDialog.js:934 #: ../js/gdm/loginDialog.js:887
#, c-format #, c-format
msgid "(e.g., user or %s)" msgid "(e.g., user or %s)"
msgstr "(f.eks. bruker eller %s)" msgstr "(f.eks. bruker eller %s)"
@@ -373,12 +369,12 @@ msgstr "(f.eks. bruker eller %s)"
#. TTLS and PEAP are actually much more complicated, but this complication #. TTLS and PEAP are actually much more complicated, but this complication
#. is not visible here since we only care about phase2 authentication #. is not visible here since we only care about phase2 authentication
#. (and don't even care of which one) #. (and don't even care of which one)
#: ../js/gdm/loginDialog.js:938 ../js/ui/components/networkAgent.js:260 #: ../js/gdm/loginDialog.js:891 ../js/ui/components/networkAgent.js:260
#: ../js/ui/components/networkAgent.js:278 #: ../js/ui/components/networkAgent.js:278
msgid "Username: " msgid "Username: "
msgstr "Brukernavn: " msgstr "Brukernavn: "
#: ../js/gdm/loginDialog.js:1205 #: ../js/gdm/loginDialog.js:1157
msgid "Login Window" msgid "Login Window"
msgstr "Innloggingsvindu" msgstr "Innloggingsvindu"
@@ -798,14 +794,14 @@ msgstr "<b>%d</b> <b>%B</b> <b>%Y</b>, <b>%H.%M</b> "
#. Translators: this is the other person changing their old IM name to their new #. Translators: this is the other person changing their old IM name to their new
#. IM name. #. IM name.
#: ../js/ui/components/telepathyClient.js:986 #: ../js/ui/components/telepathyClient.js:985
#, c-format #, c-format
msgid "%s is now known as %s" msgid "%s is now known as %s"
msgstr "%s er nå kjent som %s" msgstr "%s er nå kjent som %s"
#. translators: argument is a room name like #. translators: argument is a room name like
#. * room@jabber.org for example. #. * room@jabber.org for example.
#: ../js/ui/components/telepathyClient.js:1089 #: ../js/ui/components/telepathyClient.js:1088
#, c-format #, c-format
msgid "Invitation to %s" msgid "Invitation to %s"
msgstr "Invitasjon til %s" msgstr "Invitasjon til %s"
@@ -813,38 +809,38 @@ msgstr "Invitasjon til %s"
#. translators: first argument is the name of a contact and the second #. translators: first argument is the name of a contact and the second
#. * one the name of a room. "Alice is inviting you to join room@jabber.org #. * one the name of a room. "Alice is inviting you to join room@jabber.org
#. * for example. #. * for example.
#: ../js/ui/components/telepathyClient.js:1097 #: ../js/ui/components/telepathyClient.js:1096
#, c-format #, c-format
msgid "%s is inviting you to join %s" msgid "%s is inviting you to join %s"
msgstr "%s inviterer deg til å bli med i %s" msgstr "%s inviterer deg til å bli med i %s"
#: ../js/ui/components/telepathyClient.js:1099 #: ../js/ui/components/telepathyClient.js:1098
#: ../js/ui/components/telepathyClient.js:1138 #: ../js/ui/components/telepathyClient.js:1137
#: ../js/ui/components/telepathyClient.js:1178 #: ../js/ui/components/telepathyClient.js:1177
#: ../js/ui/components/telepathyClient.js:1241 #: ../js/ui/components/telepathyClient.js:1240
msgid "Decline" msgid "Decline"
msgstr "Avslå" msgstr "Avslå"
#: ../js/ui/components/telepathyClient.js:1100 #: ../js/ui/components/telepathyClient.js:1099
#: ../js/ui/components/telepathyClient.js:1179 #: ../js/ui/components/telepathyClient.js:1178
#: ../js/ui/components/telepathyClient.js:1242 #: ../js/ui/components/telepathyClient.js:1241
msgid "Accept" msgid "Accept"
msgstr "Godta" msgstr "Godta"
#. translators: argument is a contact name like Alice for example. #. translators: argument is a contact name like Alice for example.
#: ../js/ui/components/telepathyClient.js:1130 #: ../js/ui/components/telepathyClient.js:1129
#, c-format #, c-format
msgid "Video call from %s" msgid "Video call from %s"
msgstr "Videosamtale fra %s" msgstr "Videosamtale fra %s"
#. translators: argument is a contact name like Alice for example. #. translators: argument is a contact name like Alice for example.
#: ../js/ui/components/telepathyClient.js:1133 #: ../js/ui/components/telepathyClient.js:1132
#, c-format #, c-format
msgid "Call from %s" msgid "Call from %s"
msgstr "Samtale fra %s" msgstr "Samtale fra %s"
#. translators: this is a button label (verb), not a noun #. translators: this is a button label (verb), not a noun
#: ../js/ui/components/telepathyClient.js:1140 #: ../js/ui/components/telepathyClient.js:1139
msgid "Answer" msgid "Answer"
msgstr "Svar" msgstr "Svar"
@@ -853,110 +849,110 @@ msgstr "Svar"
#. * file name. The string will be something #. * file name. The string will be something
#. * like: "Alice is sending you test.ogg" #. * like: "Alice is sending you test.ogg"
#. #.
#: ../js/ui/components/telepathyClient.js:1172 #: ../js/ui/components/telepathyClient.js:1171
#, c-format #, c-format
msgid "%s is sending you %s" msgid "%s is sending you %s"
msgstr "%s sender deg %s" msgstr "%s sender deg %s"
#. To translators: The parameter is the contact's alias #. To translators: The parameter is the contact's alias
#: ../js/ui/components/telepathyClient.js:1207 #: ../js/ui/components/telepathyClient.js:1206
#, c-format #, c-format
msgid "%s would like permission to see when you are online" msgid "%s would like permission to see when you are online"
msgstr "%s vil ha rettigheter til å se når du er tilkoblet" msgstr "%s vil ha rettigheter til å se når du er tilkoblet"
#: ../js/ui/components/telepathyClient.js:1299 #: ../js/ui/components/telepathyClient.js:1298
msgid "Network error" msgid "Network error"
msgstr "Nettverksfeil" msgstr "Nettverksfeil"
#: ../js/ui/components/telepathyClient.js:1301 #: ../js/ui/components/telepathyClient.js:1300
msgid "Authentication failed" msgid "Authentication failed"
msgstr "Autentisering feilet" msgstr "Autentisering feilet"
#: ../js/ui/components/telepathyClient.js:1303 #: ../js/ui/components/telepathyClient.js:1302
msgid "Encryption error" msgid "Encryption error"
msgstr "Feil ved kryptering" msgstr "Feil ved kryptering"
#: ../js/ui/components/telepathyClient.js:1305 #: ../js/ui/components/telepathyClient.js:1304
msgid "Certificate not provided" msgid "Certificate not provided"
msgstr "Sertifikat ikke oppgitt" msgstr "Sertifikat ikke oppgitt"
#: ../js/ui/components/telepathyClient.js:1307 #: ../js/ui/components/telepathyClient.js:1306
msgid "Certificate untrusted" msgid "Certificate untrusted"
msgstr "Stoler ikke på sertifikatet" msgstr "Stoler ikke på sertifikatet"
#: ../js/ui/components/telepathyClient.js:1309 #: ../js/ui/components/telepathyClient.js:1308
msgid "Certificate expired" msgid "Certificate expired"
msgstr "Sertifikatet er utløpt" msgstr "Sertifikatet er utløpt"
#: ../js/ui/components/telepathyClient.js:1311 #: ../js/ui/components/telepathyClient.js:1310
msgid "Certificate not activated" msgid "Certificate not activated"
msgstr "Sertifikatet er ikke aktivert" msgstr "Sertifikatet er ikke aktivert"
#: ../js/ui/components/telepathyClient.js:1313 #: ../js/ui/components/telepathyClient.js:1312
msgid "Certificate hostname mismatch" msgid "Certificate hostname mismatch"
msgstr "Feil vertsnavn for sertifikat" msgstr "Feil vertsnavn for sertifikat"
#: ../js/ui/components/telepathyClient.js:1315 #: ../js/ui/components/telepathyClient.js:1314
msgid "Certificate fingerprint mismatch" msgid "Certificate fingerprint mismatch"
msgstr "Feil fingeravtrykk for sertifikat" msgstr "Feil fingeravtrykk for sertifikat"
#: ../js/ui/components/telepathyClient.js:1317 #: ../js/ui/components/telepathyClient.js:1316
msgid "Certificate self-signed" msgid "Certificate self-signed"
msgstr "Sertifikatet er selvsignert" msgstr "Sertifikatet er selvsignert"
#: ../js/ui/components/telepathyClient.js:1319 #: ../js/ui/components/telepathyClient.js:1318
msgid "Status is set to offline" msgid "Status is set to offline"
msgstr "Status er satt til frakoblet" msgstr "Status er satt til frakoblet"
#: ../js/ui/components/telepathyClient.js:1321 #: ../js/ui/components/telepathyClient.js:1320
msgid "Encryption is not available" msgid "Encryption is not available"
msgstr "Kryptering er ikke tilgjengelig" msgstr "Kryptering er ikke tilgjengelig"
#: ../js/ui/components/telepathyClient.js:1323 #: ../js/ui/components/telepathyClient.js:1322
msgid "Certificate is invalid" msgid "Certificate is invalid"
msgstr "Sertifikatet er ugyldig" msgstr "Sertifikatet er ugyldig"
#: ../js/ui/components/telepathyClient.js:1325 #: ../js/ui/components/telepathyClient.js:1324
msgid "Connection has been refused" msgid "Connection has been refused"
msgstr "Tilkobling ble nektet" msgstr "Tilkobling ble nektet"
#: ../js/ui/components/telepathyClient.js:1327 #: ../js/ui/components/telepathyClient.js:1326
msgid "Connection can't be established" msgid "Connection can't be established"
msgstr "Tilkobling kan ikke etableres" msgstr "Tilkobling kan ikke etableres"
#: ../js/ui/components/telepathyClient.js:1329 #: ../js/ui/components/telepathyClient.js:1328
msgid "Connection has been lost" msgid "Connection has been lost"
msgstr "Tilkobling tapt" msgstr "Tilkobling tapt"
#: ../js/ui/components/telepathyClient.js:1331 #: ../js/ui/components/telepathyClient.js:1330
msgid "This account is already connected to the server" msgid "This account is already connected to the server"
msgstr "Denne kontoen er allerede koblet til tjeneren" msgstr "Denne kontoen er allerede koblet til tjeneren"
#: ../js/ui/components/telepathyClient.js:1333 #: ../js/ui/components/telepathyClient.js:1332
msgid "" msgid ""
"Connection has been replaced by a new connection using the same resource" "Connection has been replaced by a new connection using the same resource"
msgstr "" msgstr ""
"Tilkoblingen har blitt erstattet av en ny tilkobling som bruker samme ressurs" "Tilkoblingen har blitt erstattet av en ny tilkobling som bruker samme ressurs"
#: ../js/ui/components/telepathyClient.js:1335 #: ../js/ui/components/telepathyClient.js:1334
msgid "The account already exists on the server" msgid "The account already exists on the server"
msgstr "Kontoen eksisterer allerede på tjeneren" msgstr "Kontoen eksisterer allerede på tjeneren"
#: ../js/ui/components/telepathyClient.js:1337 #: ../js/ui/components/telepathyClient.js:1336
msgid "Server is currently too busy to handle the connection" msgid "Server is currently too busy to handle the connection"
msgstr "Tjener er for opptatt til å håndtere tilkoblingen" msgstr "Tjener er for opptatt til å håndtere tilkoblingen"
#: ../js/ui/components/telepathyClient.js:1339 #: ../js/ui/components/telepathyClient.js:1338
msgid "Certificate has been revoked" msgid "Certificate has been revoked"
msgstr "Sertifikatet er tilbaketrukket" msgstr "Sertifikatet er tilbaketrukket"
#: ../js/ui/components/telepathyClient.js:1341 #: ../js/ui/components/telepathyClient.js:1340
msgid "" msgid ""
"Certificate uses an insecure cipher algorithm or is cryptographically weak" "Certificate uses an insecure cipher algorithm or is cryptographically weak"
msgstr "" msgstr ""
"Sertifikatet bruker en usikker sifferalgoritme eller er krytografisk svakt" "Sertifikatet bruker en usikker sifferalgoritme eller er krytografisk svakt"
#: ../js/ui/components/telepathyClient.js:1343 #: ../js/ui/components/telepathyClient.js:1342
msgid "" msgid ""
"The length of the server certificate, or the depth of the server certificate " "The length of the server certificate, or the depth of the server certificate "
"chain, exceed the limits imposed by the cryptography library" "chain, exceed the limits imposed by the cryptography library"
@@ -964,22 +960,22 @@ msgstr ""
"Lengden eller dybden på tjenersertifikatet oversteg grensen som er satt i " "Lengden eller dybden på tjenersertifikatet oversteg grensen som er satt i "
"kryptografibiblioteket" "kryptografibiblioteket"
#: ../js/ui/components/telepathyClient.js:1345 #: ../js/ui/components/telepathyClient.js:1344
msgid "Internal error" msgid "Internal error"
msgstr "Intern feil" msgstr "Intern feil"
#. translators: argument is the account name, like #. translators: argument is the account name, like
#. * name@jabber.org for example. #. * name@jabber.org for example.
#: ../js/ui/components/telepathyClient.js:1355 #: ../js/ui/components/telepathyClient.js:1354
#, c-format #, c-format
msgid "Unable to connect to %s" msgid "Unable to connect to %s"
msgstr "Kan ikke koble til %s" msgstr "Kan ikke koble til %s"
#: ../js/ui/components/telepathyClient.js:1360 #: ../js/ui/components/telepathyClient.js:1359
msgid "View account" msgid "View account"
msgstr "Vis konto" msgstr "Vis konto"
#: ../js/ui/components/telepathyClient.js:1399 #: ../js/ui/components/telepathyClient.js:1398
msgid "Unknown reason" msgid "Unknown reason"
msgstr "Ukjent årsak" msgstr "Ukjent årsak"
@@ -1012,7 +1008,7 @@ msgstr "Innstillinger for dato og klokkeslett"
#. Translators: This is the date format to use when the calendar popup is #. 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"). #. * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
#. #.
#: ../js/ui/dateMenu.js:201 #: ../js/ui/dateMenu.js:202
msgid "%A %B %e, %Y" msgid "%A %B %e, %Y"
msgstr "%a %e %B, %Y" msgstr "%a %e %B, %Y"
@@ -1170,31 +1166,31 @@ msgstr "Vis kildekode"
msgid "Web Page" msgid "Web Page"
msgstr "Nettside" msgstr "Nettside"
#: ../js/ui/messageTray.js:1241 #: ../js/ui/messageTray.js:1182
msgid "Open" msgid "Open"
msgstr "Åpne" msgstr "Åpne"
#: ../js/ui/messageTray.js:1248 #: ../js/ui/messageTray.js:1189
msgid "Remove" msgid "Remove"
msgstr "Fjern" msgstr "Fjern"
#: ../js/ui/messageTray.js:1560 #: ../js/ui/messageTray.js:1501
msgid "Clear Messages" msgid "Clear Messages"
msgstr "Tøm meldinger" msgstr "Tøm meldinger"
#: ../js/ui/messageTray.js:1587 #: ../js/ui/messageTray.js:1528
msgid "Notification Settings" msgid "Notification Settings"
msgstr "Innstillinger for varsling" msgstr "Innstillinger for varsling"
#: ../js/ui/messageTray.js:1770 #: ../js/ui/messageTray.js:1711
msgid "No Messages" msgid "No Messages"
msgstr "Ingen meldinger" msgstr "Ingen meldinger"
#: ../js/ui/messageTray.js:1842 #: ../js/ui/messageTray.js:1784
msgid "Message Tray" msgid "Message Tray"
msgstr "Meldingstrau" msgstr "Meldingstrau"
#: ../js/ui/messageTray.js:2854 #: ../js/ui/messageTray.js:2811
msgid "System Information" msgid "System Information"
msgstr "Systeminformasjon" msgstr "Systeminformasjon"
@@ -1226,17 +1222,17 @@ msgstr "Oversikt"
msgid "Type to search…" msgid "Type to search…"
msgstr "Skriv for å søke …" msgstr "Skriv for å søke …"
#: ../js/ui/panel.js:567 #: ../js/ui/panel.js:642
msgid "Quit" msgid "Quit"
msgstr "Avslutt" msgstr "Avslutt"
#. Translators: If there is no suitable word for "Activities" #. Translators: If there is no suitable word for "Activities"
#. in your language, you can use the word for "Overview". #. in your language, you can use the word for "Overview".
#: ../js/ui/panel.js:618 #: ../js/ui/panel.js:693
msgid "Activities" msgid "Activities"
msgstr "Aktiviteter" msgstr "Aktiviteter"
#: ../js/ui/panel.js:914 #: ../js/ui/panel.js:989
msgid "Top Bar" msgid "Top Bar"
msgstr "Topp-panel" msgstr "Topp-panel"
@@ -1245,7 +1241,7 @@ msgstr "Topp-panel"
#. "ON" and "OFF") or "toggle-switch-intl" (for toggle #. "ON" and "OFF") or "toggle-switch-intl" (for toggle
#. switches containing "◯" and "|"). Other values will #. switches containing "◯" and "|"). Other values will
#. simply result in invisible toggle switches. #. simply result in invisible toggle switches.
#: ../js/ui/popupMenu.js:517 #: ../js/ui/popupMenu.js:545
msgid "toggle-switch-us" msgid "toggle-switch-us"
msgstr "toggle-switch-intl" msgstr "toggle-switch-intl"
@@ -1285,11 +1281,11 @@ msgstr "GNOME må låse skjermen"
#. #.
#. XXX: another option is to kick the user into the gdm login #. XXX: another option is to kick the user into the gdm login
#. screen, where we're not affected by grabs #. screen, where we're not affected by grabs
#: ../js/ui/screenShield.js:788 ../js/ui/screenShield.js:1216 #: ../js/ui/screenShield.js:773 ../js/ui/screenShield.js:1213
msgid "Unable to lock" msgid "Unable to lock"
msgstr "Kan ikke låse" msgstr "Kan ikke låse"
#: ../js/ui/screenShield.js:789 ../js/ui/screenShield.js:1217 #: ../js/ui/screenShield.js:774 ../js/ui/screenShield.js:1214
msgid "Lock was blocked by an application" msgid "Lock was blocked by an application"
msgstr "Låsing ble stoppet av et program" msgstr "Låsing ble stoppet av et program"
@@ -1594,7 +1590,7 @@ msgstr "Tilkobling feilet"
msgid "Activation of network connection failed" msgid "Activation of network connection failed"
msgstr "Aktivering av nettverkstilkobling feilet" msgstr "Aktivering av nettverkstilkobling feilet"
#: ../js/ui/status/network.js:1938 #: ../js/ui/status/network.js:1937
msgid "Networking is disabled" msgid "Networking is disabled"
msgstr "Nettverk er slått av" msgstr "Nettverk er slått av"

369
po/sk.po
View File

@@ -10,8 +10,8 @@ msgstr ""
"Project-Id-Version: gnome-shell\n" "Project-Id-Version: gnome-shell\n"
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
"shell&keywords=I18N+L10N&component=general\n" "shell&keywords=I18N+L10N&component=general\n"
"POT-Creation-Date: 2013-07-13 07:31+0000\n" "POT-Creation-Date: 2013-05-24 21:21+0000\n"
"PO-Revision-Date: 2013-07-13 20:32+0200\n" "PO-Revision-Date: 2013-05-24 23:24+0100\n"
"Last-Translator: Dušan Kazik <prescott66@gmail.com>\n" "Last-Translator: Dušan Kazik <prescott66@gmail.com>\n"
"Language-Team: Slovak <gnome-sk-list@gnome.org>\n" "Language-Team: Slovak <gnome-sk-list@gnome.org>\n"
"Language: sk\n" "Language: sk\n"
@@ -384,43 +384,38 @@ msgstr "Rozšírenie"
msgid "Select an extension to configure using the combobox above." msgid "Select an extension to configure using the combobox above."
msgstr "Použitím ponuky vyberte rozšírenie na nastavenie" msgstr "Použitím ponuky vyberte rozšírenie na nastavenie"
# button #: ../js/gdm/loginDialog.js:371
#: ../js/gdm/loginDialog.js:308 msgid "Session…"
msgid "Choose Session" msgstr "Relácia…"
msgstr "Vybrať reláciu"
#: ../js/gdm/loginDialog.js:326
msgid "Session"
msgstr "Relácia"
# https://bugzilla.gnome.org/show_bug.cgi?id=659972 # https://bugzilla.gnome.org/show_bug.cgi?id=659972
#. translators: this message is shown below the user list on the #. translators: this message is shown below the user list on the
#. login screen. It can be activated to reveal an entry for #. login screen. It can be activated to reveal an entry for
#. manually entering the username. #. manually entering the username.
#: ../js/gdm/loginDialog.js:533 #: ../js/gdm/loginDialog.js:601
msgid "Not listed?" msgid "Not listed?"
msgstr "Nie ste v zozname?" msgstr "Nie ste v zozname?"
#: ../js/gdm/loginDialog.js:810 ../js/ui/components/networkAgent.js:137 #: ../js/gdm/loginDialog.js:776 ../js/ui/components/networkAgent.js:137
#: ../js/ui/components/polkitAgent.js:161 ../js/ui/endSessionDialog.js:376 #: ../js/ui/components/polkitAgent.js:161 ../js/ui/endSessionDialog.js:376
#: ../js/ui/extensionDownloader.js:195 ../js/ui/shellMountOperation.js:399 #: ../js/ui/extensionDownloader.js:195 ../js/ui/shellMountOperation.js:399
#: ../js/ui/status/bluetooth.js:449 ../js/ui/unlockDialog.js:95 #: ../js/ui/status/bluetooth.js:415 ../js/ui/unlockDialog.js:96
#: ../js/ui/userMenu.js:884 #: ../js/ui/userMenu.js:938
msgid "Cancel" msgid "Cancel"
msgstr "Zrušiť" msgstr "Zrušiť"
#: ../js/gdm/loginDialog.js:833 #: ../js/gdm/loginDialog.js:791
msgctxt "button" msgctxt "button"
msgid "Sign In" msgid "Sign In"
msgstr "Prihlásiť sa" msgstr "Prihlásiť sa"
#: ../js/gdm/loginDialog.js:833 #: ../js/gdm/loginDialog.js:791
msgid "Next" msgid "Next"
msgstr "Ďalej" msgstr "Ďalej"
#. Translators: this message is shown below the username entry field #. Translators: this message is shown below the username entry field
#. to clue the user in on how to login to the local network realm #. to clue the user in on how to login to the local network realm
#: ../js/gdm/loginDialog.js:934 #: ../js/gdm/loginDialog.js:888
#, c-format #, c-format
msgid "(e.g., user or %s)" msgid "(e.g., user or %s)"
msgstr "(napr., používateľ alebo %s)" msgstr "(napr., používateľ alebo %s)"
@@ -428,12 +423,12 @@ msgstr "(napr., používateľ alebo %s)"
#. TTLS and PEAP are actually much more complicated, but this complication #. TTLS and PEAP are actually much more complicated, but this complication
#. is not visible here since we only care about phase2 authentication #. is not visible here since we only care about phase2 authentication
#. (and don't even care of which one) #. (and don't even care of which one)
#: ../js/gdm/loginDialog.js:938 ../js/ui/components/networkAgent.js:260 #: ../js/gdm/loginDialog.js:892 ../js/ui/components/networkAgent.js:260
#: ../js/ui/components/networkAgent.js:278 #: ../js/ui/components/networkAgent.js:278
msgid "Username: " msgid "Username: "
msgstr "Používateľské meno: " msgstr "Používateľské meno: "
#: ../js/gdm/loginDialog.js:1205 #: ../js/gdm/loginDialog.js:1158
msgid "Login Window" msgid "Login Window"
msgstr "Prihlasovacie okno" msgstr "Prihlasovacie okno"
@@ -442,8 +437,8 @@ msgstr "Prihlasovacie okno"
msgid "Power" msgid "Power"
msgstr "Napájanie" msgstr "Napájanie"
#: ../js/gdm/powerMenu.js:93 ../js/ui/userMenu.js:651 ../js/ui/userMenu.js:655 #: ../js/gdm/powerMenu.js:93 ../js/ui/userMenu.js:696 ../js/ui/userMenu.js:700
#: ../js/ui/userMenu.js:768 #: ../js/ui/userMenu.js:816
msgid "Suspend" msgid "Suspend"
msgstr "Uspať" msgstr "Uspať"
@@ -451,18 +446,18 @@ msgstr "Uspať"
msgid "Restart" msgid "Restart"
msgstr "Reštartovať" msgstr "Reštartovať"
#: ../js/gdm/powerMenu.js:103 ../js/ui/userMenu.js:653 #: ../js/gdm/powerMenu.js:103 ../js/ui/userMenu.js:698
#: ../js/ui/userMenu.js:655 ../js/ui/userMenu.js:767 ../js/ui/userMenu.js:888 #: ../js/ui/userMenu.js:700 ../js/ui/userMenu.js:815 ../js/ui/userMenu.js:942
msgid "Power Off" msgid "Power Off"
msgstr "Vypnúť" msgstr "Vypnúť"
#: ../js/gdm/util.js:248 #: ../js/gdm/util.js:247
msgid "Authentication error" msgid "Authentication error"
msgstr "Chyba pri overovaní totožnosti" msgstr "Chyba pri overovaní totožnosti"
#. Translators: this message is shown below the password entry field #. Translators: this message is shown below the password entry field
#. to indicate the user can swipe their finger instead #. to indicate the user can swipe their finger instead
#: ../js/gdm/util.js:365 #: ../js/gdm/util.js:364
msgid "(or swipe finger)" msgid "(or swipe finger)"
msgstr "(alebo prejdite prstom)" msgstr "(alebo prejdite prstom)"
@@ -481,23 +476,23 @@ msgstr "Nepodarilo sa analyzovať príkaz:"
msgid "Execution of '%s' failed:" msgid "Execution of '%s' failed:"
msgstr "Spustenie „%s“ zlyhalo:" msgstr "Spustenie „%s“ zlyhalo:"
#: ../js/ui/appDisplay.js:397 #: ../js/ui/appDisplay.js:361
msgid "Frequent" msgid "Frequent"
msgstr "Často používané" msgstr "Často používané"
#: ../js/ui/appDisplay.js:404 #: ../js/ui/appDisplay.js:368
msgid "All" msgid "All"
msgstr "Všetky" msgstr "Všetky"
#: ../js/ui/appDisplay.js:996 #: ../js/ui/appDisplay.js:960
msgid "New Window" msgid "New Window"
msgstr "Nové okno" msgstr "Nové okno"
#: ../js/ui/appDisplay.js:999 ../js/ui/dash.js:284 #: ../js/ui/appDisplay.js:963 ../js/ui/dash.js:284
msgid "Remove from Favorites" msgid "Remove from Favorites"
msgstr "Odstrániť z obľúbených" msgstr "Odstrániť z obľúbených"
#: ../js/ui/appDisplay.js:1000 #: ../js/ui/appDisplay.js:964
msgid "Add to Favorites" msgid "Add to Favorites"
msgstr "Pridať do obľúbených" msgstr "Pridať do obľúbených"
@@ -511,7 +506,7 @@ msgstr "Program %s bol pridaný medzi obľúbené."
msgid "%s has been removed from your favorites." msgid "%s has been removed from your favorites."
msgstr "Program %s bol odstránený z obľúbených." msgstr "Program %s bol odstránený z obľúbených."
#: ../js/ui/backgroundMenu.js:19 ../js/ui/userMenu.js:744 #: ../js/ui/backgroundMenu.js:19 ../js/ui/userMenu.js:789
msgid "Settings" msgid "Settings"
msgstr "Nastavenia" msgstr "Nastavenia"
@@ -636,35 +631,35 @@ msgid "S"
msgstr "So" msgstr "So"
#. Translators: Text to show if there are no events #. Translators: Text to show if there are no events
#: ../js/ui/calendar.js:750 #: ../js/ui/calendar.js:735
msgid "Nothing Scheduled" msgid "Nothing Scheduled"
msgstr "Žiadne naplánované udalosti" msgstr "Žiadne naplánované udalosti"
#. Translators: Shown on calendar heading when selected day occurs on current year #. Translators: Shown on calendar heading when selected day occurs on current year
#: ../js/ui/calendar.js:768 #: ../js/ui/calendar.js:751
msgctxt "calendar heading" msgctxt "calendar heading"
msgid "%A, %B %d" msgid "%A, %B %d"
msgstr "%A, %e. %B" msgstr "%A, %e. %B"
#. Translators: Shown on calendar heading when selected day occurs on different year #. Translators: Shown on calendar heading when selected day occurs on different year
#: ../js/ui/calendar.js:771 #: ../js/ui/calendar.js:754
msgctxt "calendar heading" msgctxt "calendar heading"
msgid "%A, %B %d, %Y" msgid "%A, %B %d, %Y"
msgstr "%A, %e. %B %Y" msgstr "%A, %e. %B %Y"
#: ../js/ui/calendar.js:782 #: ../js/ui/calendar.js:764
msgid "Today" msgid "Today"
msgstr "Dnes" msgstr "Dnes"
#: ../js/ui/calendar.js:786 #: ../js/ui/calendar.js:768
msgid "Tomorrow" msgid "Tomorrow"
msgstr "Zajtra" msgstr "Zajtra"
#: ../js/ui/calendar.js:797 #: ../js/ui/calendar.js:779
msgid "This week" msgid "This week"
msgstr "Tento týždeň" msgstr "Tento týždeň"
#: ../js/ui/calendar.js:805 #: ../js/ui/calendar.js:787
msgid "Next week" msgid "Next week"
msgstr "Ďalší týždeň" msgstr "Ďalší týždeň"
@@ -856,14 +851,14 @@ msgstr "<b>%e.</b> <b>%B</b> <b>%Y</b> o <b>%H:%M</b>"
#. Translators: this is the other person changing their old IM name to their new #. Translators: this is the other person changing their old IM name to their new
#. IM name. #. IM name.
#: ../js/ui/components/telepathyClient.js:986 #: ../js/ui/components/telepathyClient.js:985
#, c-format #, c-format
msgid "%s is now known as %s" msgid "%s is now known as %s"
msgstr "Kontakt %s odteraz vystupuje ako %s" msgstr "Kontakt %s odteraz vystupuje ako %s"
#. translators: argument is a room name like #. translators: argument is a room name like
#. * room@jabber.org for example. #. * room@jabber.org for example.
#: ../js/ui/components/telepathyClient.js:1089 #: ../js/ui/components/telepathyClient.js:1088
#, c-format #, c-format
msgid "Invitation to %s" msgid "Invitation to %s"
msgstr "Pozvánka do %s" msgstr "Pozvánka do %s"
@@ -871,38 +866,38 @@ msgstr "Pozvánka do %s"
#. translators: first argument is the name of a contact and the second #. translators: first argument is the name of a contact and the second
#. * one the name of a room. "Alice is inviting you to join room@jabber.org #. * one the name of a room. "Alice is inviting you to join room@jabber.org
#. * for example. #. * for example.
#: ../js/ui/components/telepathyClient.js:1097 #: ../js/ui/components/telepathyClient.js:1096
#, c-format #, c-format
msgid "%s is inviting you to join %s" msgid "%s is inviting you to join %s"
msgstr "Kontakt %s vás pozýva aby ste sa pridali do %s" msgstr "Kontakt %s vás pozýva aby ste sa pridali do %s"
#: ../js/ui/components/telepathyClient.js:1099 #: ../js/ui/components/telepathyClient.js:1098
#: ../js/ui/components/telepathyClient.js:1138 #: ../js/ui/components/telepathyClient.js:1137
#: ../js/ui/components/telepathyClient.js:1178 #: ../js/ui/components/telepathyClient.js:1177
#: ../js/ui/components/telepathyClient.js:1241 #: ../js/ui/components/telepathyClient.js:1240
msgid "Decline" msgid "Decline"
msgstr "Odmietnuť" msgstr "Odmietnuť"
#: ../js/ui/components/telepathyClient.js:1100 #: ../js/ui/components/telepathyClient.js:1099
#: ../js/ui/components/telepathyClient.js:1179 #: ../js/ui/components/telepathyClient.js:1178
#: ../js/ui/components/telepathyClient.js:1242 #: ../js/ui/components/telepathyClient.js:1241
msgid "Accept" msgid "Accept"
msgstr "Prijať" msgstr "Prijať"
#. translators: argument is a contact name like Alice for example. #. translators: argument is a contact name like Alice for example.
#: ../js/ui/components/telepathyClient.js:1130 #: ../js/ui/components/telepathyClient.js:1129
#, c-format #, c-format
msgid "Video call from %s" msgid "Video call from %s"
msgstr "Videohovor od kontaktu %s" msgstr "Videohovor od kontaktu %s"
#. translators: argument is a contact name like Alice for example. #. translators: argument is a contact name like Alice for example.
#: ../js/ui/components/telepathyClient.js:1133 #: ../js/ui/components/telepathyClient.js:1132
#, c-format #, c-format
msgid "Call from %s" msgid "Call from %s"
msgstr "Hovor od kontaktu %s" msgstr "Hovor od kontaktu %s"
#. translators: this is a button label (verb), not a noun #. translators: this is a button label (verb), not a noun
#: ../js/ui/components/telepathyClient.js:1140 #: ../js/ui/components/telepathyClient.js:1139
msgid "Answer" msgid "Answer"
msgstr "Prijať hovor" msgstr "Prijať hovor"
@@ -911,110 +906,110 @@ msgstr "Prijať hovor"
#. * file name. The string will be something #. * file name. The string will be something
#. * like: "Alice is sending you test.ogg" #. * like: "Alice is sending you test.ogg"
#. #.
#: ../js/ui/components/telepathyClient.js:1172 #: ../js/ui/components/telepathyClient.js:1171
#, c-format #, c-format
msgid "%s is sending you %s" msgid "%s is sending you %s"
msgstr "Kontakt %s vám posiela %s" msgstr "Kontakt %s vám posiela %s"
#. To translators: The parameter is the contact's alias #. To translators: The parameter is the contact's alias
#: ../js/ui/components/telepathyClient.js:1207 #: ../js/ui/components/telepathyClient.js:1206
#, c-format #, c-format
msgid "%s would like permission to see when you are online" msgid "%s would like permission to see when you are online"
msgstr "Kontakt %s by chcel získať oprávnenie vidieť, kedy ste pripojený" msgstr "Kontakt %s by chcel získať oprávnenie vidieť, kedy ste pripojený"
#: ../js/ui/components/telepathyClient.js:1299 #: ../js/ui/components/telepathyClient.js:1298
msgid "Network error" msgid "Network error"
msgstr "Chyba siete" msgstr "Chyba siete"
#: ../js/ui/components/telepathyClient.js:1301 #: ../js/ui/components/telepathyClient.js:1300
msgid "Authentication failed" msgid "Authentication failed"
msgstr "Overenie totožnosti zlyhalo" msgstr "Overenie totožnosti zlyhalo"
#: ../js/ui/components/telepathyClient.js:1303 #: ../js/ui/components/telepathyClient.js:1302
msgid "Encryption error" msgid "Encryption error"
msgstr "Chyba šifrovania" msgstr "Chyba šifrovania"
#: ../js/ui/components/telepathyClient.js:1305 #: ../js/ui/components/telepathyClient.js:1304
msgid "Certificate not provided" msgid "Certificate not provided"
msgstr "Neposkytnutý certifikát" msgstr "Neposkytnutý certifikát"
#: ../js/ui/components/telepathyClient.js:1307 #: ../js/ui/components/telepathyClient.js:1306
msgid "Certificate untrusted" msgid "Certificate untrusted"
msgstr "Nedôveryhodný certifikát" msgstr "Nedôveryhodný certifikát"
#: ../js/ui/components/telepathyClient.js:1309 #: ../js/ui/components/telepathyClient.js:1308
msgid "Certificate expired" msgid "Certificate expired"
msgstr "Certifikát s ukončenou platnosťou" msgstr "Certifikát s ukončenou platnosťou"
#: ../js/ui/components/telepathyClient.js:1311 #: ../js/ui/components/telepathyClient.js:1310
msgid "Certificate not activated" msgid "Certificate not activated"
msgstr "Neaktivovaný certifikát" msgstr "Neaktivovaný certifikát"
#: ../js/ui/components/telepathyClient.js:1313 #: ../js/ui/components/telepathyClient.js:1312
msgid "Certificate hostname mismatch" msgid "Certificate hostname mismatch"
msgstr "Certifikát s nesúhlasným názvom hostiteľa" msgstr "Certifikát s nesúhlasným názvom hostiteľa"
#: ../js/ui/components/telepathyClient.js:1315 #: ../js/ui/components/telepathyClient.js:1314
msgid "Certificate fingerprint mismatch" msgid "Certificate fingerprint mismatch"
msgstr "Certifikát s nesúhlasným odtlačkom" msgstr "Certifikát s nesúhlasným odtlačkom"
#: ../js/ui/components/telepathyClient.js:1317 #: ../js/ui/components/telepathyClient.js:1316
msgid "Certificate self-signed" msgid "Certificate self-signed"
msgstr "Sebou podpísaný certifikát" msgstr "Sebou podpísaný certifikát"
#: ../js/ui/components/telepathyClient.js:1319 #: ../js/ui/components/telepathyClient.js:1318
msgid "Status is set to offline" msgid "Status is set to offline"
msgstr "Stav je nastavený na odhlásený" msgstr "Stav je nastavený na odhlásený"
#: ../js/ui/components/telepathyClient.js:1321 #: ../js/ui/components/telepathyClient.js:1320
msgid "Encryption is not available" msgid "Encryption is not available"
msgstr "Šifrovanie nie je dostupné" msgstr "Šifrovanie nie je dostupné"
#: ../js/ui/components/telepathyClient.js:1323 #: ../js/ui/components/telepathyClient.js:1322
msgid "Certificate is invalid" msgid "Certificate is invalid"
msgstr "Certifikát je neplatný" msgstr "Certifikát je neplatný"
#: ../js/ui/components/telepathyClient.js:1325 #: ../js/ui/components/telepathyClient.js:1324
msgid "Connection has been refused" msgid "Connection has been refused"
msgstr "Pripojenie bolo odmietnuté" msgstr "Pripojenie bolo odmietnuté"
#: ../js/ui/components/telepathyClient.js:1327 #: ../js/ui/components/telepathyClient.js:1326
msgid "Connection can't be established" msgid "Connection can't be established"
msgstr "Nedá sa nadviazať spojenie" msgstr "Nedá sa nadviazať spojenie"
#: ../js/ui/components/telepathyClient.js:1329 #: ../js/ui/components/telepathyClient.js:1328
msgid "Connection has been lost" msgid "Connection has been lost"
msgstr "Spojenie sa stratilo" msgstr "Spojenie sa stratilo"
#: ../js/ui/components/telepathyClient.js:1331 #: ../js/ui/components/telepathyClient.js:1330
msgid "This account is already connected to the server" msgid "This account is already connected to the server"
msgstr "Tento účet je už pripojený k serveru" msgstr "Tento účet je už pripojený k serveru"
#: ../js/ui/components/telepathyClient.js:1333 #: ../js/ui/components/telepathyClient.js:1332
msgid "" msgid ""
"Connection has been replaced by a new connection using the same resource" "Connection has been replaced by a new connection using the same resource"
msgstr "Pripojenie bolo nahradené novým, ktoré používa rovnaký zdroj" msgstr "Pripojenie bolo nahradené novým, ktoré používa rovnaký zdroj"
#: ../js/ui/components/telepathyClient.js:1335 #: ../js/ui/components/telepathyClient.js:1334
msgid "The account already exists on the server" msgid "The account already exists on the server"
msgstr "Účet na serveri už existuje" msgstr "Účet na serveri už existuje"
#: ../js/ui/components/telepathyClient.js:1337 #: ../js/ui/components/telepathyClient.js:1336
msgid "Server is currently too busy to handle the connection" msgid "Server is currently too busy to handle the connection"
msgstr "Server je momentálne príliš zaneprázdnený na zvládnutie pripojenia" msgstr "Server je momentálne príliš zaneprázdnený na zvládnutie pripojenia"
#: ../js/ui/components/telepathyClient.js:1339 #: ../js/ui/components/telepathyClient.js:1338
msgid "Certificate has been revoked" msgid "Certificate has been revoked"
msgstr "Certifikát bol zrušený" msgstr "Certifikát bol zrušený"
#: ../js/ui/components/telepathyClient.js:1341 #: ../js/ui/components/telepathyClient.js:1340
msgid "" msgid ""
"Certificate uses an insecure cipher algorithm or is cryptographically weak" "Certificate uses an insecure cipher algorithm or is cryptographically weak"
msgstr "" msgstr ""
"Šifrovací algoritmus používaný certifikátom nie je bezpečný alebo je " "Šifrovací algoritmus používaný certifikátom nie je bezpečný alebo je "
"kryptograficky slabý" "kryptograficky slabý"
#: ../js/ui/components/telepathyClient.js:1343 #: ../js/ui/components/telepathyClient.js:1342
msgid "" msgid ""
"The length of the server certificate, or the depth of the server certificate " "The length of the server certificate, or the depth of the server certificate "
"chain, exceed the limits imposed by the cryptography library" "chain, exceed the limits imposed by the cryptography library"
@@ -1022,22 +1017,22 @@ msgstr ""
"Dĺžka certifikátu servera, alebo hĺbka reťazca certifikátu servera presahuje " "Dĺžka certifikátu servera, alebo hĺbka reťazca certifikátu servera presahuje "
"limit stanovený kryptografickou knižnicou." "limit stanovený kryptografickou knižnicou."
#: ../js/ui/components/telepathyClient.js:1345 #: ../js/ui/components/telepathyClient.js:1344
msgid "Internal error" msgid "Internal error"
msgstr "Vnútorná chyba" msgstr "Vnútorná chyba"
#. translators: argument is the account name, like #. translators: argument is the account name, like
#. * name@jabber.org for example. #. * name@jabber.org for example.
#: ../js/ui/components/telepathyClient.js:1355 #: ../js/ui/components/telepathyClient.js:1354
#, c-format #, c-format
msgid "Unable to connect to %s" msgid "Unable to connect to %s"
msgstr "Nepodarilo sa pripojiť účet %s" msgstr "Nepodarilo sa pripojiť účet %s"
#: ../js/ui/components/telepathyClient.js:1360 #: ../js/ui/components/telepathyClient.js:1359
msgid "View account" msgid "View account"
msgstr "Zobraziť účet" msgstr "Zobraziť účet"
#: ../js/ui/components/telepathyClient.js:1399 #: ../js/ui/components/telepathyClient.js:1398
msgid "Unknown reason" msgid "Unknown reason"
msgstr "Neznámy dôvod" msgstr "Neznámy dôvod"
@@ -1052,26 +1047,26 @@ msgstr "Zobrazí aplikácie"
#. Translators: this is the name of the dock/favorites area on #. Translators: this is the name of the dock/favorites area on
#. the left of the overview #. the left of the overview
#: ../js/ui/dash.js:439 #: ../js/ui/dash.js:435
msgid "Dash" msgid "Dash"
msgstr "Dok" msgstr "Dok"
#: ../js/ui/dateMenu.js:85 #: ../js/ui/dateMenu.js:86
msgid "Open Calendar" msgid "Open Calendar"
msgstr "Otvoriť kalendár" msgstr "Otvoriť kalendár"
#: ../js/ui/dateMenu.js:89 #: ../js/ui/dateMenu.js:90
msgid "Open Clocks" msgid "Open Clocks"
msgstr "Otvoriť hodiny" msgstr "Otvoriť hodiny"
#: ../js/ui/dateMenu.js:96 #: ../js/ui/dateMenu.js:97
msgid "Date & Time Settings" msgid "Date & Time Settings"
msgstr "Nastavenia dátumu a času" msgstr "Nastavenia dátumu a času"
#. Translators: This is the date format to use when the calendar popup is #. 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"). #. * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
#. #.
#: ../js/ui/dateMenu.js:201 #: ../js/ui/dateMenu.js:208
msgid "%A %B %e, %Y" msgid "%A %B %e, %Y"
msgstr "%A, %e. %B %Y" msgstr "%A, %e. %B %Y"
@@ -1180,7 +1175,7 @@ msgstr "Inštalovať"
msgid "Download and install '%s' from extensions.gnome.org?" msgid "Download and install '%s' from extensions.gnome.org?"
msgstr "Stiahnuť a nainštalovať „%s“ z extensions.gnome.org?" msgstr "Stiahnuť a nainštalovať „%s“ z extensions.gnome.org?"
#: ../js/ui/keyboard.js:619 ../js/ui/status/keyboard.js:333 #: ../js/ui/keyboard.js:619 ../js/ui/status/keyboard.js:314
#: ../js/ui/status/power.js:211 #: ../js/ui/status/power.js:211
msgid "Keyboard" msgid "Keyboard"
msgstr "Klávesnica" msgstr "Klávesnica"
@@ -1234,33 +1229,33 @@ msgstr "Zobraziť zdroj"
msgid "Web Page" msgid "Web Page"
msgstr "Webová stránka" msgstr "Webová stránka"
#: ../js/ui/messageTray.js:1241 #: ../js/ui/messageTray.js:1182
msgid "Open" msgid "Open"
msgstr "Otvoriť" msgstr "Otvoriť"
#: ../js/ui/messageTray.js:1248 #: ../js/ui/messageTray.js:1189
msgid "Remove" msgid "Remove"
msgstr "Odstrániť" msgstr "Odstrániť"
#: ../js/ui/messageTray.js:1560 #: ../js/ui/messageTray.js:1501
msgid "Clear Messages" msgid "Clear Messages"
msgstr "Vymazať správy" msgstr "Vymazať správy"
#: ../js/ui/messageTray.js:1587 #: ../js/ui/messageTray.js:1528
msgid "Notification Settings" msgid "Notification Settings"
msgstr "Nastavenia oznámení" msgstr "Nastavenia oznámení"
#: ../js/ui/messageTray.js:1770 #: ../js/ui/messageTray.js:1707
msgid "No Messages" msgid "No Messages"
msgstr "Žiadne správy" msgstr "Žiadne správy"
# DK: zvazoval som pouzit "Panel správ" # DK: zvazoval som pouzit "Panel správ"
# neviem co bude vhodnejsie ako preklad "tray" # neviem co bude vhodnejsie ako preklad "tray"
#: ../js/ui/messageTray.js:1842 #: ../js/ui/messageTray.js:1780
msgid "Message Tray" msgid "Message Tray"
msgstr "Lišta správ" msgstr "Lišta správ"
#: ../js/ui/messageTray.js:2854 #: ../js/ui/messageTray.js:2800
msgid "System Information" msgid "System Information"
msgstr "Informácie o systéme" msgstr "Informácie o systéme"
@@ -1269,7 +1264,7 @@ msgctxt "program"
msgid "Unknown" msgid "Unknown"
msgstr "Neznámy" msgstr "Neznámy"
#: ../js/ui/overviewControls.js:474 ../js/ui/screenShield.js:150 #: ../js/ui/overviewControls.js:472 ../js/ui/screenShield.js:150
#, c-format #, c-format
msgid "%d new message" msgid "%d new message"
msgid_plural "%d new messages" msgid_plural "%d new messages"
@@ -1294,17 +1289,17 @@ msgstr "Prehľad"
msgid "Type to search…" msgid "Type to search…"
msgstr "Zadajte text na vyhľadanie…" msgstr "Zadajte text na vyhľadanie…"
#: ../js/ui/panel.js:567 #: ../js/ui/panel.js:642
msgid "Quit" msgid "Quit"
msgstr "Ukončiť" msgstr "Ukončiť"
#. Translators: If there is no suitable word for "Activities" #. Translators: If there is no suitable word for "Activities"
#. in your language, you can use the word for "Overview". #. in your language, you can use the word for "Overview".
#: ../js/ui/panel.js:618 #: ../js/ui/panel.js:693
msgid "Activities" msgid "Activities"
msgstr "Aktivity" msgstr "Aktivity"
#: ../js/ui/panel.js:914 #: ../js/ui/panel.js:989
msgid "Top Bar" msgid "Top Bar"
msgstr "Horná lišta" msgstr "Horná lišta"
@@ -1313,7 +1308,7 @@ msgstr "Horná lišta"
#. "ON" and "OFF") or "toggle-switch-intl" (for toggle #. "ON" and "OFF") or "toggle-switch-intl" (for toggle
#. switches containing "◯" and "|"). Other values will #. switches containing "◯" and "|"). Other values will
#. simply result in invisible toggle switches. #. simply result in invisible toggle switches.
#: ../js/ui/popupMenu.js:549 #: ../js/ui/popupMenu.js:738
msgid "toggle-switch-us" msgid "toggle-switch-us"
msgstr "toggle-switch-intl" msgstr "toggle-switch-intl"
@@ -1342,7 +1337,7 @@ msgstr[0] "%d nových oznámení"
msgstr[1] "%d nové oznámenie" msgstr[1] "%d nové oznámenie"
msgstr[2] "%d nové oznámenia" msgstr[2] "%d nové oznámenia"
#: ../js/ui/screenShield.js:449 ../js/ui/userMenu.js:759 #: ../js/ui/screenShield.js:449 ../js/ui/userMenu.js:807
msgid "Lock" msgid "Lock"
msgstr "Uzamknúť" msgstr "Uzamknúť"
@@ -1357,19 +1352,19 @@ msgstr "Prostredie GNOME vyžaduje uzamknutie obrazovky"
#. #.
#. XXX: another option is to kick the user into the gdm login #. XXX: another option is to kick the user into the gdm login
#. screen, where we're not affected by grabs #. screen, where we're not affected by grabs
#: ../js/ui/screenShield.js:788 ../js/ui/screenShield.js:1215 #: ../js/ui/screenShield.js:773 ../js/ui/screenShield.js:1213
msgid "Unable to lock" msgid "Unable to lock"
msgstr "Nepodarilo sa uzamknúť obrazovku" msgstr "Nepodarilo sa uzamknúť obrazovku"
#: ../js/ui/screenShield.js:789 ../js/ui/screenShield.js:1216 #: ../js/ui/screenShield.js:774 ../js/ui/screenShield.js:1214
msgid "Lock was blocked by an application" msgid "Lock was blocked by an application"
msgstr "Uzamknutie bolo zablokované aplikáciou" msgstr "Uzamknutie bolo zablokované aplikáciou"
#: ../js/ui/searchDisplay.js:445 #: ../js/ui/searchDisplay.js:453
msgid "Searching…" msgid "Searching…"
msgstr "Hľadá sa…" msgstr "Hľadá sa…"
#: ../js/ui/searchDisplay.js:489 #: ../js/ui/searchDisplay.js:497
msgid "No results." msgid "No results."
msgstr "Žiadne výsledky." msgstr "Žiadne výsledky."
@@ -1397,7 +1392,7 @@ msgstr "Heslo"
msgid "Remember Password" msgid "Remember Password"
msgstr "Zapamätať heslo" msgstr "Zapamätať heslo"
#: ../js/ui/shellMountOperation.js:403 ../js/ui/unlockDialog.js:108 #: ../js/ui/shellMountOperation.js:403 ../js/ui/unlockDialog.js:109
msgid "Unlock" msgid "Unlock"
msgstr "Odblokovať" msgstr "Odblokovať"
@@ -1453,9 +1448,9 @@ msgid "Large Text"
msgstr "Veľký text" msgstr "Veľký text"
#: ../js/ui/status/bluetooth.js:28 ../js/ui/status/bluetooth.js:32 #: ../js/ui/status/bluetooth.js:28 ../js/ui/status/bluetooth.js:32
#: ../js/ui/status/bluetooth.js:290 ../js/ui/status/bluetooth.js:327 #: ../js/ui/status/bluetooth.js:289 ../js/ui/status/bluetooth.js:321
#: ../js/ui/status/bluetooth.js:355 ../js/ui/status/bluetooth.js:391 #: ../js/ui/status/bluetooth.js:357 ../js/ui/status/bluetooth.js:388
#: ../js/ui/status/bluetooth.js:422 ../js/ui/status/network.js:713 #: ../js/ui/status/network.js:739
msgid "Bluetooth" msgid "Bluetooth"
msgstr "Bluetooth" msgstr "Bluetooth"
@@ -1476,117 +1471,106 @@ msgid "Bluetooth Settings"
msgstr "Nastavenia Bluetooth" msgstr "Nastavenia Bluetooth"
#. TRANSLATORS: this means that bluetooth was disabled by hardware rfkill #. TRANSLATORS: this means that bluetooth was disabled by hardware rfkill
#: ../js/ui/status/bluetooth.js:105 ../js/ui/status/network.js:140 #: ../js/ui/status/bluetooth.js:104 ../js/ui/status/network.js:142
msgid "hardware disabled" msgid "hardware disabled"
msgstr "hardvér zakázaný" msgstr "hardvér zakázaný"
#: ../js/ui/status/bluetooth.js:198 #: ../js/ui/status/bluetooth.js:197
msgid "Connection" msgid "Connection"
msgstr "Pripojenie" msgstr "Pripojenie"
#: ../js/ui/status/bluetooth.js:209 ../js/ui/status/network.js:399 #: ../js/ui/status/bluetooth.js:208 ../js/ui/status/network.js:404
msgid "disconnecting..." msgid "disconnecting..."
msgstr "odpája sa…" msgstr "odpája sa…"
#: ../js/ui/status/bluetooth.js:222 ../js/ui/status/network.js:405 #: ../js/ui/status/bluetooth.js:221 ../js/ui/status/network.js:410
#: ../js/ui/status/network.js:1298 #: ../js/ui/status/network.js:1343
msgid "connecting..." msgid "connecting..."
msgstr "pripája sa…" msgstr "pripája sa…"
# menu item # menu item
#: ../js/ui/status/bluetooth.js:240 #: ../js/ui/status/bluetooth.js:239
msgid "Send Files…" msgid "Send Files…"
msgstr "Odoslať súbory…" msgstr "Odoslať súbory…"
#: ../js/ui/status/bluetooth.js:247 #: ../js/ui/status/bluetooth.js:246
msgid "Keyboard Settings" msgid "Keyboard Settings"
msgstr "Nastavenia klávesnice" msgstr "Nastavenia klávesnice"
#: ../js/ui/status/bluetooth.js:250 #: ../js/ui/status/bluetooth.js:249
msgid "Mouse Settings" msgid "Mouse Settings"
msgstr "Nastavenia myši" msgstr "Nastavenia myši"
#: ../js/ui/status/bluetooth.js:255 ../js/ui/status/volume.js:313 #: ../js/ui/status/bluetooth.js:254 ../js/ui/status/volume.js:316
msgid "Sound Settings" msgid "Sound Settings"
msgstr "Nastavenia zvuku" msgstr "Nastavenia zvuku"
#: ../js/ui/status/bluetooth.js:328 ../js/ui/status/bluetooth.js:356 #: ../js/ui/status/bluetooth.js:322
#, c-format #, c-format
msgid "Authorization request from %s" msgid "Authorization request from %s"
msgstr "Žiadosť o potvrdenie prístupu od %s" msgstr "Žiadosť o potvrdenie prístupu od %s"
#: ../js/ui/status/bluetooth.js:334 ../js/ui/status/bluetooth.js:399 #: ../js/ui/status/bluetooth.js:328
#: ../js/ui/status/bluetooth.js:430
#, c-format
msgid "Device %s wants to pair with this computer"
msgstr "Zariadenie %s sa chce spárovať s týmto počítačom"
# button
#: ../js/ui/status/bluetooth.js:336
msgid "Allow"
msgstr "Povoliť"
# button
#: ../js/ui/status/bluetooth.js:337
msgid "Deny"
msgstr "Zakázať"
#: ../js/ui/status/bluetooth.js:362
#, c-format #, c-format
msgid "Device %s wants access to the service '%s'" msgid "Device %s wants access to the service '%s'"
msgstr "Zariadenie %s chce pristupovať k službe „%s“" msgstr "Zariadenie %s chce pristupovať k službe „%s“"
#: ../js/ui/status/bluetooth.js:364 #: ../js/ui/status/bluetooth.js:330
msgid "Always grant access" msgid "Always grant access"
msgstr "Vždy povoliť prístup" msgstr "Vždy povoliť prístup"
#: ../js/ui/status/bluetooth.js:365 #: ../js/ui/status/bluetooth.js:331
msgid "Grant this time only" msgid "Grant this time only"
msgstr "Povoliť iba teraz" msgstr "Povoliť iba teraz"
#: ../js/ui/status/bluetooth.js:366 #: ../js/ui/status/bluetooth.js:332
msgid "Reject" msgid "Reject"
msgstr "Odmietnuť" msgstr "Odmietnuť"
#. Translators: argument is the device short name #. Translators: argument is the device short name
#: ../js/ui/status/bluetooth.js:393 #: ../js/ui/status/bluetooth.js:359
#, c-format #, c-format
msgid "Pairing confirmation for %s" msgid "Pairing confirmation for %s"
msgstr "Potvrdenie spárovania pre %s" msgstr "Potvrdenie spárovania pre %s"
#: ../js/ui/status/bluetooth.js:400 #: ../js/ui/status/bluetooth.js:365 ../js/ui/status/bluetooth.js:396
#, c-format
msgid "Device %s wants to pair with this computer"
msgstr "Zariadenie %s sa chce spárovať s týmto počítačom"
#: ../js/ui/status/bluetooth.js:366
#, c-format #, c-format
msgid "" msgid ""
"Please confirm whether the Passkey '%06d' matches the one on the device." "Please confirm whether the Passkey '%06d' matches the one on the device."
msgstr "Prosím, potvrďte, či sa heslo „%06d“ zhoduje s tým na zariadení." msgstr "Prosím, potvrďte, či sa heslo „%06d“ zhoduje s tým na zariadení."
#. Translators: this is the verb, not the noun #. Translators: this is the verb, not the noun
#: ../js/ui/status/bluetooth.js:403 #: ../js/ui/status/bluetooth.js:369
msgid "Matches" msgid "Matches"
msgstr "Zhoduje sa" msgstr "Zhoduje sa"
#: ../js/ui/status/bluetooth.js:404 #: ../js/ui/status/bluetooth.js:370
msgid "Does not match" msgid "Does not match"
msgstr "Nezhoduje sa" msgstr "Nezhoduje sa"
#: ../js/ui/status/bluetooth.js:423 #: ../js/ui/status/bluetooth.js:389
#, c-format #, c-format
msgid "Pairing request for %s" msgid "Pairing request for %s"
msgstr "Požiadavka na spárovanie pre %s" msgstr "Požiadavka na spárovanie pre %s"
#: ../js/ui/status/bluetooth.js:431 #: ../js/ui/status/bluetooth.js:397
msgid "Please enter the PIN mentioned on the device." msgid "Please enter the PIN mentioned on the device."
msgstr "Zadajte PIN, ktoré je uvedené na zariadení." msgstr "Zadajte PIN, ktoré je uvedené na zariadení."
#: ../js/ui/status/bluetooth.js:448 #: ../js/ui/status/bluetooth.js:414
msgid "OK" msgid "OK"
msgstr "Ok" msgstr "Ok"
#: ../js/ui/status/keyboard.js:396 #: ../js/ui/status/keyboard.js:368
msgid "Show Keyboard Layout" msgid "Show Keyboard Layout"
msgstr "Zobraziť rozloženie klávesnice" msgstr "Zobraziť rozloženie klávesnice"
#: ../js/ui/status/keyboard.js:401 #: ../js/ui/status/keyboard.js:373
msgid "Region & Language Settings" msgid "Region & Language Settings"
msgstr "Miestne a jazykové nastavenia" msgstr "Miestne a jazykové nastavenia"
@@ -1599,81 +1583,87 @@ msgstr "Hlasitosť, sieť, batéria"
msgid "<unknown>" msgid "<unknown>"
msgstr "<neznáme>" msgstr "<neznáme>"
#: ../js/ui/status/network.js:125 #: ../js/ui/status/network.js:127
msgid "Wi-Fi" msgid "Wi-Fi"
msgstr "Wi-Fi" msgstr "Wi-Fi"
#. Translators: this indicates that wireless or wwan is disabled by hardware killswitch #. Translators: this indicates that wireless or wwan is disabled by hardware killswitch
#: ../js/ui/status/network.js:162 #: ../js/ui/status/network.js:164
msgid "disabled" msgid "disabled"
msgstr "zakázané" msgstr "zakázané"
#. Translators: this is for network devices that are physically present but are not #. Translators: this is for network devices that are physically present but are not
#. under NetworkManager's control (and thus cannot be used in the menu) #. under NetworkManager's control (and thus cannot be used in the menu)
#: ../js/ui/status/network.js:397 #: ../js/ui/status/network.js:402
msgid "unmanaged" msgid "unmanaged"
msgstr "nespravované" msgstr "nespravované"
#. Translators: this is for network connections that require some kind of key or password #. Translators: this is for network connections that require some kind of key or password
#: ../js/ui/status/network.js:408 ../js/ui/status/network.js:1301 #: ../js/ui/status/network.js:413 ../js/ui/status/network.js:1346
msgid "authentication required" msgid "authentication required"
msgstr "požaduje sa overenie totožnosti" msgstr "požaduje sa overenie totožnosti"
#. Translators: this is for devices that require some kind of firmware or kernel #. Translators: this is for devices that require some kind of firmware or kernel
#. module, which is missing #. module, which is missing
#: ../js/ui/status/network.js:419 #: ../js/ui/status/network.js:423
msgid "firmware missing" msgid "firmware missing"
msgstr "chýba firmvér" msgstr "chýba firmvér"
#. Translators: this is for wired network devices that are physically disconnected
#: ../js/ui/status/network.js:430
msgid "cable unplugged"
msgstr "kábel odpojený"
#. Translators: this is for a network device that cannot be activated (for example it #. Translators: this is for a network device that cannot be activated (for example it
#. is disabled by rfkill, or it has no coverage #. is disabled by rfkill, or it has no coverage
#: ../js/ui/status/network.js:423 #: ../js/ui/status/network.js:435
msgid "unavailable" msgid "unavailable"
msgstr "nedostupné" msgstr "nedostupné"
#: ../js/ui/status/network.js:425 ../js/ui/status/network.js:1303 #: ../js/ui/status/network.js:437 ../js/ui/status/network.js:1348
msgid "connection failed" msgid "connection failed"
msgstr "pripojenie zlyhalo" msgstr "pripojenie zlyhalo"
#: ../js/ui/status/network.js:478 ../js/ui/status/network.js:1190 #: ../js/ui/status/network.js:490 ../js/ui/status/network.js:1236
#: ../js/ui/status/network.js:1424
msgid "More…" msgid "More…"
msgstr "Viac…" msgstr "Viac…"
#. TRANSLATORS: this is the indication that a connection for another logged in user is active, #. TRANSLATORS: this is the indication that a connection for another logged in user is active,
#. and we cannot access its settings (including the name) #. and we cannot access its settings (including the name)
#: ../js/ui/status/network.js:506 ../js/ui/status/network.js:1142 #: ../js/ui/status/network.js:518 ../js/ui/status/network.js:1191
msgid "Connected (private)" msgid "Connected (private)"
msgstr "Pripojené (súkromne)" msgstr "Pripojené (súkromne)"
#: ../js/ui/status/network.js:572 #: ../js/ui/status/network.js:597
msgid "Wired" msgid "Wired"
msgstr "Drôtové pripojenie" msgstr "Drôtové pripojenie"
#: ../js/ui/status/network.js:592 #: ../js/ui/status/network.js:611
msgid "Mobile broadband" msgid "Mobile broadband"
msgstr "Širokopásmové pripojenie" msgstr "Širokopásmové pripojenie"
#: ../js/ui/status/network.js:1474 #: ../js/ui/status/network.js:1522
msgid "Enable networking" msgid "Enable networking"
msgstr "Povoliť sieť" msgstr "Povoliť sieť"
#: ../js/ui/status/network.js:1522 #: ../js/ui/status/network.js:1583
msgid "Network Settings" msgid "Network Settings"
msgstr "Nastavenia siete" msgstr "Nastavenia siete"
#: ../js/ui/status/network.js:1539 #: ../js/ui/status/network.js:1600
msgid "Network Manager" msgid "Network Manager"
msgstr "Správca siete" msgstr "Správca siete"
#: ../js/ui/status/network.js:1623 #: ../js/ui/status/network.js:1690
msgid "Connection failed" msgid "Connection failed"
msgstr "Pripojenie zlyhalo" msgstr "Pripojenie zlyhalo"
#: ../js/ui/status/network.js:1624 #: ../js/ui/status/network.js:1691
msgid "Activation of network connection failed" msgid "Activation of network connection failed"
msgstr "Aktivácia pripojenia k sieti zlyhala" msgstr "Aktivácia pripojenia k sieti zlyhala"
#: ../js/ui/status/network.js:1938 #: ../js/ui/status/network.js:2047
msgid "Networking is disabled" msgid "Networking is disabled"
msgstr "Sieť je zakázaná" msgstr "Sieť je zakázaná"
@@ -1778,72 +1768,72 @@ msgctxt "device"
msgid "Unknown" msgid "Unknown"
msgstr "Neznáme" msgstr "Neznáme"
#: ../js/ui/status/volume.js:121 #: ../js/ui/status/volume.js:124
msgid "Volume changed" msgid "Volume changed"
msgstr "Hlasitosť bola zmenená" msgstr "Hlasitosť bola zmenená"
#. Translators: This is the label for audio volume #. Translators: This is the label for audio volume
#: ../js/ui/status/volume.js:246 ../js/ui/status/volume.js:294 #: ../js/ui/status/volume.js:249 ../js/ui/status/volume.js:297
msgid "Volume" msgid "Volume"
msgstr "Hlasitosť" msgstr "Hlasitosť"
#: ../js/ui/status/volume.js:255 #: ../js/ui/status/volume.js:258
msgid "Microphone" msgid "Microphone"
msgstr "Mikrofón" msgstr "Mikrofón"
#: ../js/ui/unlockDialog.js:119 #: ../js/ui/unlockDialog.js:120
msgid "Log in as another user" msgid "Log in as another user"
msgstr "Prihlásiť ako iný používateľ" msgstr "Prihlásiť ako iný používateľ"
#: ../js/ui/unlockDialog.js:140 #: ../js/ui/unlockDialog.js:141
msgid "Unlock Window" msgid "Unlock Window"
msgstr "Odomykacie okno" msgstr "Odomykacie okno"
#: ../js/ui/userMenu.js:149 #: ../js/ui/userMenu.js:193
msgid "Available" msgid "Available"
msgstr "Prítomný" msgstr "Prítomný"
#: ../js/ui/userMenu.js:152 #: ../js/ui/userMenu.js:196
msgid "Busy" msgid "Busy"
msgstr "Zaneprázdnený" msgstr "Zaneprázdnený"
#: ../js/ui/userMenu.js:155 #: ../js/ui/userMenu.js:199
msgid "Invisible" msgid "Invisible"
msgstr "Neviditeľný" msgstr "Neviditeľný"
#: ../js/ui/userMenu.js:158 #: ../js/ui/userMenu.js:202
msgid "Away" msgid "Away"
msgstr "Neprítomný" msgstr "Neprítomný"
#: ../js/ui/userMenu.js:161 #: ../js/ui/userMenu.js:205
msgid "Idle" msgid "Idle"
msgstr "Nečinný" msgstr "Nečinný"
#: ../js/ui/userMenu.js:164 #: ../js/ui/userMenu.js:208
msgid "Offline" msgid "Offline"
msgstr "Odhlásený" msgstr "Odhlásený"
#: ../js/ui/userMenu.js:736 #: ../js/ui/userMenu.js:781
msgid "Notifications" msgid "Notifications"
msgstr "Upozornenia" msgstr "Upozornenia"
#: ../js/ui/userMenu.js:749 #: ../js/ui/userMenu.js:797
msgid "Switch User" msgid "Switch User"
msgstr "Prepnúť používateľa" msgstr "Prepnúť používateľa"
#: ../js/ui/userMenu.js:754 #: ../js/ui/userMenu.js:802
msgid "Log Out" msgid "Log Out"
msgstr "Odhlásiť sa" msgstr "Odhlásiť sa"
#: ../js/ui/userMenu.js:774 #: ../js/ui/userMenu.js:822
msgid "Install Updates & Restart" msgid "Install Updates & Restart"
msgstr "Nainštalovať aktualizácie a reštartovať" msgstr "Nainštalovať aktualizácie a reštartovať"
#: ../js/ui/userMenu.js:792 #: ../js/ui/userMenu.js:840
msgid "Your chat status will be set to busy" msgid "Your chat status will be set to busy"
msgstr "Váš stav bude nastavený na zaneprázdnený" msgstr "Váš stav bude nastavený na zaneprázdnený"
#: ../js/ui/userMenu.js:793 #: ../js/ui/userMenu.js:841
msgid "" msgid ""
"Notifications are now disabled, including chat messages. Your online status " "Notifications are now disabled, including chat messages. Your online status "
"has been adjusted to let others know that you might not see their messages." "has been adjusted to let others know that you might not see their messages."
@@ -1851,22 +1841,22 @@ msgstr ""
"Oznámenia, vrátane správ rozhovoru, sú teraz zakázané. Váš stav online bol " "Oznámenia, vrátane správ rozhovoru, sú teraz zakázané. Váš stav online bol "
"nastavený tak, aby ostatní vedeli, že nemusíte vidieť ich správy." "nastavený tak, aby ostatní vedeli, že nemusíte vidieť ich správy."
#: ../js/ui/userMenu.js:834 #: ../js/ui/userMenu.js:888
msgid "Other users are logged in." msgid "Other users are logged in."
msgstr "Sú prihlásení iní používatelia." msgstr "Sú prihlásení iní používatelia."
#: ../js/ui/userMenu.js:839 #: ../js/ui/userMenu.js:893
msgid "Shutting down might cause them to lose unsaved work." msgid "Shutting down might cause them to lose unsaved work."
msgstr "Vypnutie môže spôsobiť stratu neuloženej práce." msgstr "Vypnutie môže spôsobiť stratu neuloženej práce."
#. Translators: Remote here refers to a remote session, like a ssh login #. Translators: Remote here refers to a remote session, like a ssh login
#: ../js/ui/userMenu.js:867 #: ../js/ui/userMenu.js:921
#, c-format #, c-format
msgid "%s (remote)" msgid "%s (remote)"
msgstr "%s (vzdialená relácia)" msgstr "%s (vzdialená relácia)"
#. Translators: Console here refers to a tty like a VT console #. Translators: Console here refers to a tty like a VT console
#: ../js/ui/userMenu.js:870 #: ../js/ui/userMenu.js:924
#, c-format #, c-format
msgid "%s (console)" msgid "%s (console)"
msgstr "%s (konzola)" msgstr "%s (konzola)"
@@ -1928,19 +1918,19 @@ msgstr[2] "%u vstupy"
msgid "System Sounds" msgid "System Sounds"
msgstr "Systémové zvuky" msgstr "Systémové zvuky"
#: ../src/main.c:353 #: ../src/main.c:372
msgid "Print version" msgid "Print version"
msgstr "Verzia pre tlač" msgstr "Verzia pre tlač"
#: ../src/main.c:359 #: ../src/main.c:378
msgid "Mode used by GDM for login screen" msgid "Mode used by GDM for login screen"
msgstr "Režim používaný GDM pre prihlasovaciu obrazovku" msgstr "Režim používaný GDM pre prihlasovaciu obrazovku"
#: ../src/main.c:365 #: ../src/main.c:384
msgid "Use a specific mode, e.g. \"gdm\" for login screen" msgid "Use a specific mode, e.g. \"gdm\" for login screen"
msgstr "Použitie zvláštneho režimu, napr. „gdm“ pre prihlasovaciu obrazovku" msgstr "Použitie zvláštneho režimu, napr. „gdm“ pre prihlasovaciu obrazovku"
#: ../src/main.c:371 #: ../src/main.c:390
msgid "List possible modes" msgid "List possible modes"
msgstr "Zoznam možných režimov" msgstr "Zoznam možných režimov"
@@ -1962,9 +1952,6 @@ msgstr "Heslo nemôže byť prázdne"
msgid "Authentication dialog was dismissed by the user" msgid "Authentication dialog was dismissed by the user"
msgstr "Dialógové okno overenia totožnosti bolo zatvorené používateľom" msgstr "Dialógové okno overenia totožnosti bolo zatvorené používateľom"
#~ msgid "cable unplugged"
#~ msgstr "kábel odpojený"
#~ msgid "Whether to collect stats about applications usage" #~ msgid "Whether to collect stats about applications usage"
#~ msgstr "Či sa majú zhromažďovať štatistické údaje o používaní aplikácií" #~ msgstr "Či sa majú zhromažďovať štatistické údaje o používaní aplikácií"

755
po/tg.po

File diff suppressed because it is too large Load Diff

694
po/vi.po

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -214,7 +214,6 @@ shell_app_create_icon_texture (ShellApp *app,
typedef struct { typedef struct {
ShellApp *app; ShellApp *app;
int size; int size;
ClutterTextDirection direction;
} CreateFadedIconData; } CreateFadedIconData;
static CoglHandle static CoglHandle
@@ -232,7 +231,7 @@ shell_app_create_faded_icon_cpu (StTextureCache *cache,
guint8 n_channels; guint8 n_channels;
gboolean have_alpha; gboolean have_alpha;
gint fade_start; gint fade_start;
gint fade_end; gint fade_range;
guint i, j; guint i, j;
guint pixbuf_byte_size; guint pixbuf_byte_size;
guint8 *orig_pixels; guint8 *orig_pixels;
@@ -284,26 +283,14 @@ shell_app_create_faded_icon_cpu (StTextureCache *cache,
pixels = g_malloc0 (rowstride * height); pixels = g_malloc0 (rowstride * height);
memcpy (pixels, orig_pixels, pixbuf_byte_size); memcpy (pixels, orig_pixels, pixbuf_byte_size);
/* fade on the right side for LTR, left side for RTL */
if (data->direction == CLUTTER_TEXT_DIRECTION_LTR)
{
fade_start = width / 2; fade_start = width / 2;
fade_end = width; fade_range = width - fade_start;
} for (i = fade_start; i < width; i++)
else
{
fade_start = 0;
fade_end = width / 2;
}
for (i = fade_start; i < fade_end; i++)
{ {
for (j = 0; j < height; j++) for (j = 0; j < height; j++)
{ {
guchar *pixel = &pixels[j * rowstride + i * n_channels]; guchar *pixel = &pixels[j * rowstride + i * n_channels];
float fade = ((float) i - fade_start) / (fade_end - fade_start); float fade = 1.0 - ((float) i - fade_start) / fade_range;
if (data->direction == CLUTTER_TEXT_DIRECTION_LTR)
fade = 1.0 - fade;
pixel[0] = 0.5 + pixel[0] * fade; pixel[0] = 0.5 + pixel[0] * fade;
pixel[1] = 0.5 + pixel[1] * fade; pixel[1] = 0.5 + pixel[1] * fade;
pixel[2] = 0.5 + pixel[2] * fade; pixel[2] = 0.5 + pixel[2] * fade;
@@ -329,14 +316,13 @@ shell_app_create_faded_icon_cpu (StTextureCache *cache,
* shell_app_get_faded_icon: * shell_app_get_faded_icon:
* @app: A #ShellApp * @app: A #ShellApp
* @size: Size in pixels * @size: Size in pixels
* @direction: Whether to fade on the left or right
* *
* Return an actor with a horizontally faded look. * Return an actor with a horizontally faded look.
* *
* Return value: (transfer none): A floating #ClutterActor, or %NULL if no icon * Return value: (transfer none): A floating #ClutterActor, or %NULL if no icon
*/ */
ClutterActor * ClutterActor *
shell_app_get_faded_icon (ShellApp *app, int size, ClutterTextDirection direction) shell_app_get_faded_icon (ShellApp *app, int size)
{ {
CoglHandle texture; CoglHandle texture;
ClutterActor *result; ClutterActor *result;
@@ -352,13 +338,9 @@ shell_app_get_faded_icon (ShellApp *app, int size, ClutterTextDirection directio
/* Use icon: prefix so that we get evicted from the cache on /* Use icon: prefix so that we get evicted from the cache on
* icon theme changes. */ * icon theme changes. */
cache_key = g_strdup_printf ("icon:%s,size=%d,faded-%s", cache_key = g_strdup_printf ("icon:%s,size=%d,faded", shell_app_get_id (app), size);
shell_app_get_id (app),
size,
direction == CLUTTER_TEXT_DIRECTION_RTL ? "rtl" : "ltr");
data.app = app; data.app = app;
data.size = size; data.size = size;
data.direction = direction;
texture = st_texture_cache_load (st_texture_cache_get_default (), texture = st_texture_cache_load (st_texture_cache_get_default (),
cache_key, cache_key,
ST_TEXTURE_CACHE_POLICY_FOREVER, ST_TEXTURE_CACHE_POLICY_FOREVER,

View File

@@ -43,7 +43,7 @@ GMenuTreeEntry *shell_app_get_tree_entry (ShellApp *app);
GDesktopAppInfo *shell_app_get_app_info (ShellApp *app); GDesktopAppInfo *shell_app_get_app_info (ShellApp *app);
ClutterActor *shell_app_create_icon_texture (ShellApp *app, int size); ClutterActor *shell_app_create_icon_texture (ShellApp *app, int size);
ClutterActor *shell_app_get_faded_icon (ShellApp *app, int size, ClutterTextDirection direction); ClutterActor *shell_app_get_faded_icon (ShellApp *app, int size);
const char *shell_app_get_name (ShellApp *app); const char *shell_app_get_name (ShellApp *app);
const char *shell_app_get_description (ShellApp *app); const char *shell_app_get_description (ShellApp *app);
gboolean shell_app_is_window_backed (ShellApp *app); gboolean shell_app_is_window_backed (ShellApp *app);

View File

@@ -70,6 +70,7 @@ struct _ShellGlobal {
GtkWindow *grab_notifier; GtkWindow *grab_notifier;
gboolean gtk_grab_active; gboolean gtk_grab_active;
ShellStageInputMode input_mode;
XserverRegion input_region; XserverRegion input_region;
GjsContext *js_context; GjsContext *js_context;
@@ -93,8 +94,6 @@ struct _ShellGlobal {
guint32 xdnd_timestamp; guint32 xdnd_timestamp;
gint64 last_gc_end_time; gint64 last_gc_end_time;
gboolean has_modal;
}; };
enum { enum {
@@ -107,6 +106,7 @@ enum {
PROP_SCREEN_WIDTH, PROP_SCREEN_WIDTH,
PROP_SCREEN_HEIGHT, PROP_SCREEN_HEIGHT,
PROP_STAGE, PROP_STAGE,
PROP_STAGE_INPUT_MODE,
PROP_WINDOW_GROUP, PROP_WINDOW_GROUP,
PROP_TOP_WINDOW_GROUP, PROP_TOP_WINDOW_GROUP,
PROP_WINDOW_MANAGER, PROP_WINDOW_MANAGER,
@@ -141,6 +141,9 @@ shell_global_set_property(GObject *object,
switch (prop_id) switch (prop_id)
{ {
case PROP_STAGE_INPUT_MODE:
shell_global_set_stage_input_mode (global, g_value_get_enum (value));
break;
case PROP_SESSION_MODE: case PROP_SESSION_MODE:
g_clear_pointer (&global->session_mode, g_free); g_clear_pointer (&global->session_mode, g_free);
global->session_mode = g_ascii_strdown (g_value_get_string (value), -1); global->session_mode = g_ascii_strdown (g_value_get_string (value), -1);
@@ -192,6 +195,9 @@ shell_global_get_property(GObject *object,
case PROP_STAGE: case PROP_STAGE:
g_value_set_object (value, global->stage); g_value_set_object (value, global->stage);
break; break;
case PROP_STAGE_INPUT_MODE:
g_value_set_enum (value, global->input_mode);
break;
case PROP_WINDOW_GROUP: case PROP_WINDOW_GROUP:
g_value_set_object (value, meta_get_window_group_for_screen (global->meta_screen)); g_value_set_object (value, meta_get_window_group_for_screen (global->meta_screen));
break; break;
@@ -272,6 +278,8 @@ shell_global_init (ShellGlobal *global)
g_signal_connect (global->grab_notifier, "grab-notify", G_CALLBACK (grab_notify), global); g_signal_connect (global->grab_notifier, "grab-notify", G_CALLBACK (grab_notify), global);
global->gtk_grab_active = FALSE; global->gtk_grab_active = FALSE;
global->input_mode = SHELL_STAGE_INPUT_MODE_NORMAL;
global->sound_context = ca_gtk_context_get (); global->sound_context = ca_gtk_context_get ();
ca_context_change_props (global->sound_context, ca_context_change_props (global->sound_context,
CA_PROP_APPLICATION_NAME, "GNOME Shell", CA_PROP_APPLICATION_NAME, "GNOME Shell",
@@ -409,6 +417,14 @@ shell_global_class_init (ShellGlobalClass *klass)
"Stage holding the desktop scene graph", "Stage holding the desktop scene graph",
CLUTTER_TYPE_ACTOR, CLUTTER_TYPE_ACTOR,
G_PARAM_READABLE)); G_PARAM_READABLE));
g_object_class_install_property (gobject_class,
PROP_STAGE_INPUT_MODE,
g_param_spec_enum ("stage-input-mode",
"Stage input mode",
"The stage input mode",
SHELL_TYPE_STAGE_INPUT_MODE,
SHELL_STAGE_INPUT_MODE_NORMAL,
G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, g_object_class_install_property (gobject_class,
PROP_WINDOW_GROUP, PROP_WINDOW_GROUP,
g_param_spec_object ("window-group", g_param_spec_object ("window-group",
@@ -513,18 +529,6 @@ shell_global_get (void)
return the_object; return the_object;
} }
static guint32
get_current_time_maybe_roundtrip (ShellGlobal *global)
{
guint32 time;
time = shell_global_get_current_time (global);
if (time != CurrentTime)
return time;
return meta_display_get_current_time_roundtrip (global->meta_display);
}
static void static void
focus_window_changed (MetaDisplay *display, focus_window_changed (MetaDisplay *display,
GParamSpec *param, GParamSpec *param,
@@ -532,71 +536,58 @@ focus_window_changed (MetaDisplay *display,
{ {
ShellGlobal *global = user_data; ShellGlobal *global = user_data;
if (global->has_modal) if (global->input_mode == SHELL_STAGE_INPUT_MODE_FOCUSED &&
return; meta_display_get_focus_window (display) != NULL)
shell_global_set_stage_input_mode (global, SHELL_STAGE_INPUT_MODE_NORMAL);
/* If the stage window became unfocused, drop the key focus
* on Clutter's side. */
if (!meta_stage_is_focused (global->meta_screen))
clutter_stage_set_key_focus (global->stage, NULL);
} }
static ClutterActor * /**
get_key_focused_actor (ShellGlobal *global) * shell_global_set_stage_input_mode:
* @global: the #ShellGlobal
* @mode: the stage input mode
*
* Sets the input mode of the stage; when @mode is
* %SHELL_STAGE_INPUT_MODE_NORMAL, then the stage accepts clicks in
* the region defined by shell_global_set_stage_input_region() but
* passes through clicks outside that region. When it is
* %SHELL_STAGE_INPUT_MODE_FULLSCREEN, the stage absorbs all input.
*
* When the input mode is %SHELL_STAGE_INPUT_MODE_FOCUSED, the pointer
* is handled as with %SHELL_STAGE_INPUT_MODE_NORMAL, but additionally
* the stage window has the keyboard focus. If the stage loses the
* focus (eg, because the user clicked into a window) the input mode
* will revert to %SHELL_STAGE_INPUT_MODE_NORMAL.
*
* Note that whenever a mutter-internal Gtk widget has a pointer grab,
* the shell goes unresponsive and passes things to the underlying GTK+
* widget to ensure that the widget gets any clicks it is expecting.
*/
void
shell_global_set_stage_input_mode (ShellGlobal *global,
ShellStageInputMode mode)
{ {
ClutterActor *actor; MetaScreen *screen;
actor = clutter_stage_get_key_focus (global->stage); g_return_if_fail (SHELL_IS_GLOBAL (global));
/* If there's no explicit key focus, clutter_stage_get_key_focus() screen = meta_plugin_get_screen (global->plugin);
* returns the stage. This is a terrible API. */
if (actor == CLUTTER_ACTOR (global->stage))
actor = NULL;
return actor;
}
static void
sync_stage_window_focus (ShellGlobal *global)
{
ClutterActor *actor;
if (global->has_modal)
return;
actor = get_key_focused_actor (global);
/* An actor got key focus and the stage needs to be focused. */
if (actor != NULL && !meta_stage_is_focused (global->meta_screen))
meta_focus_stage_window (global->meta_screen,
get_current_time_maybe_roundtrip (global));
/* An actor dropped key focus. Focus the default window. */
else if (actor == NULL && meta_stage_is_focused (global->meta_screen))
meta_screen_focus_default_window (global->meta_screen,
get_current_time_maybe_roundtrip (global));
}
static void
focus_actor_changed (ClutterStage *stage,
GParamSpec *param,
gpointer user_data)
{
ShellGlobal *global = user_data;
sync_stage_window_focus (global);
}
static void
sync_input_region (ShellGlobal *global)
{
MetaScreen *screen = global->meta_screen;
if (global->gtk_grab_active) if (global->gtk_grab_active)
meta_empty_stage_input_region (screen); meta_empty_stage_input_region (screen);
else if (global->has_modal) else if (mode == SHELL_STAGE_INPUT_MODE_FULLSCREEN || !global->input_region)
meta_set_stage_input_region (screen, None); meta_set_stage_input_region (screen, None);
else else
meta_set_stage_input_region (screen, global->input_region); meta_set_stage_input_region (screen, global->input_region);
if (mode == SHELL_STAGE_INPUT_MODE_FOCUSED)
meta_focus_stage_window (global->meta_screen,
shell_global_get_current_time (global));
if (mode != global->input_mode)
{
global->input_mode = mode;
g_object_notify (G_OBJECT (global), "stage-input-mode");
}
} }
/** /**
@@ -691,7 +682,8 @@ shell_global_unset_cursor (ShellGlobal *global)
* describing the input region. * describing the input region.
* *
* Sets the area of the stage that is responsive to mouse clicks when * Sets the area of the stage that is responsive to mouse clicks when
* we don't have a modal or grab. * the stage mode is %SHELL_STAGE_INPUT_MODE_NORMAL (but does not change the
* current stage mode).
*/ */
void void
shell_global_set_stage_input_region (ShellGlobal *global, shell_global_set_stage_input_region (ShellGlobal *global,
@@ -721,7 +713,10 @@ shell_global_set_stage_input_region (ShellGlobal *global,
global->input_region = XFixesCreateRegion (global->xdisplay, rects, nrects); global->input_region = XFixesCreateRegion (global->xdisplay, rects, nrects);
g_free (rects); g_free (rects);
sync_input_region (global); /* set_stage_input_mode() will figure out whether or not we
* should actually change the input region right now.
*/
shell_global_set_stage_input_mode (global, global->input_mode);
} }
/** /**
@@ -961,8 +956,6 @@ _shell_global_set_plugin (ShellGlobal *global,
"End of stage page repaint", "End of stage page repaint",
""); "");
g_signal_connect (global->stage, "notify::key-focus",
G_CALLBACK (focus_actor_changed), global);
g_signal_connect (global->meta_display, "notify::focus-window", g_signal_connect (global->meta_display, "notify::focus-window",
G_CALLBACK (focus_window_changed), global); G_CALLBACK (focus_window_changed), global);
@@ -998,14 +991,7 @@ shell_global_begin_modal (ShellGlobal *global,
guint32 timestamp, guint32 timestamp,
MetaModalOptions options) MetaModalOptions options)
{ {
/* Make it an error to call begin_modal while we already return meta_plugin_begin_modal (global->plugin, global->stage_xwindow, None, options, timestamp);
* have a modal active. */
if (global->has_modal)
return FALSE;
global->has_modal = meta_plugin_begin_modal (global->plugin, global->stage_xwindow, None, options, timestamp);
sync_input_region (global);
return global->has_modal;
} }
/** /**
@@ -1018,25 +1004,7 @@ void
shell_global_end_modal (ShellGlobal *global, shell_global_end_modal (ShellGlobal *global,
guint32 timestamp) guint32 timestamp)
{ {
ClutterActor *actor;
if (!global->has_modal)
return;
meta_plugin_end_modal (global->plugin, timestamp); meta_plugin_end_modal (global->plugin, timestamp);
global->has_modal = FALSE;
/* If the stage window is unfocused, ensure that there's no
* actor focused on Clutter's side. */
if (!meta_stage_is_focused (global->meta_screen))
clutter_stage_set_key_focus (global->stage, NULL);
/* An actor dropped key focus. Focus the default window. */
else if (get_key_focused_actor (global) && meta_stage_is_focused (global->meta_screen))
meta_screen_focus_default_window (global->meta_screen,
get_current_time_maybe_roundtrip (global));
sync_input_region (global);
} }
void void
@@ -1254,7 +1222,7 @@ grab_notify (GtkWidget *widget, gboolean was_grabbed, gpointer user_data)
global->gtk_grab_active = !was_grabbed; global->gtk_grab_active = !was_grabbed;
/* Update for the new setting of gtk_grab_active */ /* Update for the new setting of gtk_grab_active */
sync_input_region (global); shell_global_set_stage_input_mode (global, global->input_mode);
} }
/** /**
@@ -1346,6 +1314,10 @@ shell_global_sync_pointer (ShellGlobal *global)
event.type = CLUTTER_MOTION; event.type = CLUTTER_MOTION;
event.time = shell_global_get_current_time (global); event.time = shell_global_get_current_time (global);
event.flags = 0; event.flags = 0;
/* This is wrong: we should be setting event.stage to NULL if the
* pointer is not inside the bounds of the stage given the current
* stage_input_mode. For our current purposes however, this works.
*/
event.stage = global->stage; event.stage = global->stage;
event.x = x; event.x = x;
event.y = y; event.y = y;

View File

@@ -47,6 +47,14 @@ void shell_global_end_modal (ShellGlobal *global,
void shell_global_freeze_keyboard (ShellGlobal *global, void shell_global_freeze_keyboard (ShellGlobal *global,
guint32 timestamp); guint32 timestamp);
typedef enum {
SHELL_STAGE_INPUT_MODE_NORMAL,
SHELL_STAGE_INPUT_MODE_FOCUSED,
SHELL_STAGE_INPUT_MODE_FULLSCREEN
} ShellStageInputMode;
void shell_global_set_stage_input_mode (ShellGlobal *global,
ShellStageInputMode mode);
void shell_global_set_stage_input_region (ShellGlobal *global, void shell_global_set_stage_input_region (ShellGlobal *global,
GSList *rectangles); GSList *rectangles);

View File

@@ -43,6 +43,8 @@ struct _ShellNetworkAgentClass
/* used by SHELL_TYPE_NETWORK_AGENT */ /* used by SHELL_TYPE_NETWORK_AGENT */
GType shell_network_agent_get_type (void); GType shell_network_agent_get_type (void);
ShellNetworkAgent *shell_network_agent_new (void);
void shell_network_agent_set_password (ShellNetworkAgent *self, void shell_network_agent_set_password (ShellNetworkAgent *self,
gchar *request_id, gchar *request_id,
gchar *setting_key, gchar *setting_key,

View File

@@ -104,7 +104,7 @@ shell_slicer_paint_child (ShellSlicer *self)
return; return;
st_bin_get_alignment (ST_BIN (self), &x_align, &y_align); st_bin_get_alignment (ST_BIN (self), &x_align, &y_align);
st_get_align_factors (x_align, y_align, _st_get_align_factors (x_align, y_align,
&x_align_factor, &y_align_factor); &x_align_factor, &y_align_factor);
clutter_actor_get_allocation_box (CLUTTER_ACTOR (self), &self_box); clutter_actor_get_allocation_box (CLUTTER_ACTOR (self), &self_box);

View File

@@ -108,7 +108,7 @@ st_bin_allocate (ClutterActor *self,
gdouble x_align_f, y_align_f; gdouble x_align_f, y_align_f;
st_theme_node_get_content_box (theme_node, box, &childbox); st_theme_node_get_content_box (theme_node, box, &childbox);
st_get_align_factors (priv->x_align, priv->y_align, _st_get_align_factors (priv->x_align, priv->y_align,
&x_align_f, &y_align_f); &x_align_f, &y_align_f);
clutter_actor_allocate_align_fill (priv->child, &childbox, clutter_actor_allocate_align_fill (priv->child, &childbox,
x_align_f, y_align_f, x_align_f, y_align_f,

View File

@@ -285,6 +285,7 @@ get_content_preferred_width (StBoxLayout *self,
{ {
StBoxLayoutPrivate *priv = self->priv; StBoxLayoutPrivate *priv = self->priv;
gint n_children = 0; gint n_children = 0;
gint n_fixed = 0;
gfloat min_width, natural_width; gfloat min_width, natural_width;
ClutterActor *child; ClutterActor *child;
@@ -303,6 +304,12 @@ get_content_preferred_width (StBoxLayout *self,
n_children++; n_children++;
if (clutter_actor_get_fixed_position_set (child))
{
n_fixed++;
continue;
}
if (priv->is_vertical) if (priv->is_vertical)
{ {
_st_actor_get_preferred_width (child, -1, FALSE, _st_actor_get_preferred_width (child, -1, FALSE,
@@ -322,10 +329,10 @@ get_content_preferred_width (StBoxLayout *self,
} }
} }
if (!priv->is_vertical && n_children > 1) if (!priv->is_vertical && (n_children - n_fixed) > 1)
{ {
min_width += priv->spacing * (n_children - 1); min_width += priv->spacing * (n_children - n_fixed - 1);
natural_width += priv->spacing * (n_children - 1); natural_width += priv->spacing * (n_children - n_fixed - 1);
} }
if (min_width_p) if (min_width_p)
@@ -360,6 +367,7 @@ get_content_preferred_height (StBoxLayout *self,
{ {
StBoxLayoutPrivate *priv = self->priv; StBoxLayoutPrivate *priv = self->priv;
gint n_children = 0; gint n_children = 0;
gint n_fixed = 0;
gfloat min_height, natural_height; gfloat min_height, natural_height;
ClutterActor *child; ClutterActor *child;
@@ -378,6 +386,12 @@ get_content_preferred_height (StBoxLayout *self,
n_children++; n_children++;
if (clutter_actor_get_fixed_position_set (child))
{
n_fixed++;
continue;
}
if (priv->is_vertical) if (priv->is_vertical)
{ {
clutter_container_child_get ((ClutterContainer*) self, child, clutter_container_child_get ((ClutterContainer*) self, child,
@@ -402,10 +416,10 @@ get_content_preferred_height (StBoxLayout *self,
} }
} }
if (priv->is_vertical && n_children > 1) if (priv->is_vertical && (n_children - n_fixed) > 1)
{ {
min_height += priv->spacing * (n_children - 1); min_height += priv->spacing * (n_children - n_fixed - 1);
natural_height += priv->spacing * (n_children - 1); natural_height += priv->spacing * (n_children - n_fixed - 1);
} }
if (min_height_p) if (min_height_p)
@@ -497,9 +511,12 @@ compute_shrinks (StBoxLayout *self,
{ {
gfloat child_min, child_nat; gfloat child_min, child_nat;
gboolean child_fill; gboolean child_fill;
gboolean fixed;
fixed = clutter_actor_get_fixed_position_set (child);
shrinks[i].child_index = i; shrinks[i].child_index = i;
if (CLUTTER_ACTOR_IS_VISIBLE (child)) if (CLUTTER_ACTOR_IS_VISIBLE (child) && !fixed)
{ {
if (priv->is_vertical) if (priv->is_vertical)
{ {
@@ -721,13 +738,20 @@ st_box_layout_allocate (ClutterActor *actor,
{ {
ClutterActorBox child_box; ClutterActorBox child_box;
gfloat child_min, child_nat, child_allocated; gfloat child_min, child_nat, child_allocated;
gboolean xfill, yfill, expand; gboolean xfill, yfill, expand, fixed;
StAlign xalign, yalign; StAlign xalign, yalign;
gdouble xalign_f, yalign_f; gdouble xalign_f, yalign_f;
if (!CLUTTER_ACTOR_IS_VISIBLE (child)) if (!CLUTTER_ACTOR_IS_VISIBLE (child))
goto next_child; goto next_child;
fixed = clutter_actor_get_fixed_position_set (child);
if (fixed)
{
clutter_actor_allocate_preferred_size (child, flags);
goto next_child;
}
clutter_container_child_get ((ClutterContainer*) actor, child, clutter_container_child_get ((ClutterContainer*) actor, child,
"x-fill", &xfill, "x-fill", &xfill,
"y-fill", &yfill, "y-fill", &yfill,
@@ -736,7 +760,7 @@ st_box_layout_allocate (ClutterActor *actor,
"expand", &expand, "expand", &expand,
NULL); NULL);
st_get_align_factors (xalign, yalign, &xalign_f, &yalign_f); _st_get_align_factors (xalign, yalign, &xalign_f, &yalign_f);
if (priv->is_vertical) if (priv->is_vertical)
{ {

View File

@@ -98,6 +98,66 @@ _st_actor_get_preferred_height (ClutterActor *actor,
clutter_actor_get_preferred_height (actor, for_width, min_height_p, natural_height_p); clutter_actor_get_preferred_height (actor, for_width, min_height_p, natural_height_p);
} }
/**
* _st_get_align_factors:
* @x_align: an #StAlign
* @y_align: an #StAlign
* @x_align_out: (out) (allow-none): @x_align as a #gdouble
* @y_align_out: (out) (allow-none): @y_align as a #gdouble
*
* Converts @x_align and @y_align to #gdouble values.
*/
void
_st_get_align_factors (StAlign x_align,
StAlign y_align,
gdouble *x_align_out,
gdouble *y_align_out)
{
if (x_align_out)
{
switch (x_align)
{
case ST_ALIGN_START:
*x_align_out = 0.0;
break;
case ST_ALIGN_MIDDLE:
*x_align_out = 0.5;
break;
case ST_ALIGN_END:
*x_align_out = 1.0;
break;
default:
g_warn_if_reached ();
break;
}
}
if (y_align_out)
{
switch (y_align)
{
case ST_ALIGN_START:
*y_align_out = 0.0;
break;
case ST_ALIGN_MIDDLE:
*y_align_out = 0.5;
break;
case ST_ALIGN_END:
*y_align_out = 1.0;
break;
default:
g_warn_if_reached ();
break;
}
}
}
/** /**
* _st_set_text_from_style: * _st_set_text_from_style:
* @text: Target #ClutterText * @text: Target #ClutterText

View File

@@ -45,6 +45,11 @@ G_END_DECLS
ClutterActor *_st_widget_get_dnd_clone (StWidget *widget); ClutterActor *_st_widget_get_dnd_clone (StWidget *widget);
void _st_get_align_factors (StAlign x_align,
StAlign y_align,
gdouble *x_align_out,
gdouble *y_align_out);
void _st_actor_get_preferred_width (ClutterActor *actor, void _st_actor_get_preferred_width (ClutterActor *actor,
gfloat for_height, gfloat for_height,
gboolean y_fill, gboolean y_fill,

View File

@@ -598,7 +598,7 @@ st_scroll_view_allocate (ClutterActor *actor,
*/ */
/* Vertical scrollbar */ /* Vertical scrollbar */
if (vscrollbar_visible) if (CLUTTER_ACTOR_IS_VISIBLE (priv->vscroll))
{ {
if (clutter_actor_get_text_direction (actor) == CLUTTER_TEXT_DIRECTION_RTL) if (clutter_actor_get_text_direction (actor) == CLUTTER_TEXT_DIRECTION_RTL)
{ {
@@ -617,7 +617,7 @@ st_scroll_view_allocate (ClutterActor *actor,
} }
/* Horizontal scrollbar */ /* Horizontal scrollbar */
if (hscrollbar_visible) if (CLUTTER_ACTOR_IS_VISIBLE (priv->hscroll))
{ {
if (clutter_actor_get_text_direction (actor) == CLUTTER_TEXT_DIRECTION_RTL) if (clutter_actor_get_text_direction (actor) == CLUTTER_TEXT_DIRECTION_RTL)
{ {

View File

@@ -257,7 +257,7 @@ st_table_homogeneous_allocate (ClutterActor *self,
row_span = meta->row_span; row_span = meta->row_span;
col_span = meta->col_span; col_span = meta->col_span;
st_get_align_factors (meta->x_align, meta->y_align, _st_get_align_factors (meta->x_align, meta->y_align,
&x_align_f, &y_align_f); &x_align_f, &y_align_f);
if (ltr) if (ltr)
@@ -609,7 +609,7 @@ st_table_preferred_allocate (ClutterActor *self,
row_span = meta->row_span; row_span = meta->row_span;
col_span = meta->col_span; col_span = meta->col_span;
st_get_align_factors (meta->x_align, meta->y_align, _st_get_align_factors (meta->x_align, meta->y_align,
&x_align_f, &y_align_f); &x_align_f, &y_align_f);
/* initialise the width and height */ /* initialise the width and height */

View File

@@ -1268,7 +1268,8 @@ st_theme_node_prerender_background (StThemeNode *node,
return texture; return texture;
} }
static void st_theme_node_paint_borders (StThemeNodePaintState *state, static void st_theme_node_paint_borders (StThemeNode *node,
StThemeNodePaintState *state,
const ClutterActorBox *box, const ClutterActorBox *box,
guint8 paint_opacity); guint8 paint_opacity);
@@ -1369,11 +1370,9 @@ st_theme_node_load_background_image (StThemeNode *node)
return node->background_texture != COGL_INVALID_HANDLE; return node->background_texture != COGL_INVALID_HANDLE;
} }
static void st_theme_node_prerender_shadow (StThemeNodePaintState *state);
static void static void
st_theme_node_render_resources (StThemeNodePaintState *state, st_theme_node_render_resources (StThemeNode *node,
StThemeNode *node, StThemeNodePaintState *state,
float width, float width,
float height) float height)
{ {
@@ -1391,7 +1390,6 @@ st_theme_node_render_resources (StThemeNodePaintState *state,
*/ */
st_theme_node_paint_state_free (state); st_theme_node_paint_state_free (state);
st_theme_node_paint_state_set_node (state, node);
state->alloc_width = width; state->alloc_width = width;
state->alloc_height = height; state->alloc_height = height;
@@ -1476,80 +1474,38 @@ st_theme_node_render_resources (StThemeNodePaintState *state,
state->box_shadow_material = _st_create_shadow_material (box_shadow_spec, state->box_shadow_material = _st_create_shadow_material (box_shadow_spec,
state->prerendered_texture); state->prerendered_texture);
else if (node->background_color.alpha > 0 || has_border) else if (node->background_color.alpha > 0 || has_border)
st_theme_node_prerender_shadow (state);
}
/* If we don't have cached textures yet, check whether we can cache
them. */
if (!node->cached_textures)
{ {
if (state->prerendered_material == COGL_INVALID_HANDLE && CoglHandle buffer, offscreen;
width >= node->box_shadow_min_width && int texture_width = ceil (width);
height >= node->box_shadow_min_height) int texture_height = ceil (height);
buffer = cogl_texture_new_with_size (texture_width,
texture_height,
COGL_TEXTURE_NO_SLICING,
COGL_PIXEL_FORMAT_ANY);
offscreen = cogl_offscreen_new_to_texture (buffer);
if (offscreen != COGL_INVALID_HANDLE)
{ {
st_theme_node_paint_state_copy (&node->cached_state, state); ClutterActorBox box = { 0, 0, width, height };
node->cached_textures = TRUE; CoglColor clear_color;
}
}
}
static void cogl_push_framebuffer (offscreen);
st_theme_node_update_resources (StThemeNodePaintState *state, cogl_ortho (0, width, height, 0, 0, 1.0);
StThemeNode *node,
float width,
float height)
{
gboolean had_prerendered_texture = FALSE;
gboolean had_box_shadow = FALSE;
StShadow *box_shadow_spec;
g_return_if_fail (width > 0 && height > 0); cogl_color_set_from_4ub (&clear_color, 0, 0, 0, 0);
cogl_clear (&clear_color, COGL_BUFFER_BIT_COLOR);
/* Free handles we can't reuse */ st_theme_node_paint_borders (node, state, &box, 0xFF);
if (state->prerendered_texture != COGL_INVALID_HANDLE) cogl_pop_framebuffer ();
{ cogl_handle_unref (offscreen);
cogl_handle_unref (state->prerendered_texture);
state->prerendered_texture = COGL_INVALID_HANDLE;
had_prerendered_texture = TRUE;
}
if (state->prerendered_material != COGL_INVALID_HANDLE)
{
cogl_handle_unref (state->prerendered_material);
state->prerendered_material = COGL_INVALID_HANDLE;
if (node->border_slices_texture == COGL_INVALID_HANDLE &&
state->box_shadow_material != COGL_INVALID_HANDLE)
{
cogl_handle_unref (state->box_shadow_material);
state->box_shadow_material = COGL_INVALID_HANDLE;
had_box_shadow = TRUE;
}
}
st_theme_node_paint_state_set_node (state, node);
state->alloc_width = width;
state->alloc_height = height;
box_shadow_spec = st_theme_node_get_box_shadow (node);
if (had_prerendered_texture)
{
state->prerendered_texture = st_theme_node_prerender_background (node, width, height);
state->prerendered_material = _st_create_texture_material (state->prerendered_texture);
}
else
{
int corner_id;
for (corner_id = 0; corner_id < 4; corner_id++)
if (state->corner_material[corner_id] == COGL_INVALID_HANDLE)
state->corner_material[corner_id] =
st_theme_node_lookup_corner (node, width, height, corner_id);
}
if (had_box_shadow)
state->box_shadow_material = _st_create_shadow_material (box_shadow_spec, state->box_shadow_material = _st_create_shadow_material (box_shadow_spec,
state->prerendered_texture); buffer);
}
cogl_handle_unref (buffer);
}
}
} }
static void static void
@@ -1571,11 +1527,11 @@ paint_material_with_opacity (CoglHandle material,
} }
static void static void
st_theme_node_paint_borders (StThemeNodePaintState *state, st_theme_node_paint_borders (StThemeNode *node,
StThemeNodePaintState *state,
const ClutterActorBox *box, const ClutterActorBox *box,
guint8 paint_opacity) guint8 paint_opacity)
{ {
StThemeNode *node = state->node;
float width, height; float width, height;
int border_width[4]; int border_width[4];
guint border_radius[4]; guint border_radius[4];
@@ -1836,353 +1792,6 @@ st_theme_node_paint_borders (StThemeNodePaintState *state,
} }
} }
static void
st_theme_node_paint_sliced_shadow (StThemeNodePaintState *state,
const ClutterActorBox *box,
guint8 paint_opacity)
{
StThemeNode *node = state->node;
guint border_radius[4];
CoglColor color;
StShadow *box_shadow_spec;
gfloat xoffset, yoffset;
gfloat width, height;
gfloat shadow_width, shadow_height;
gfloat xend, yend, top, bottom, left, right;
gfloat s_top, s_bottom, s_left, s_right;
gfloat shadow_blur_radius, x_spread_factor, y_spread_factor;
float rectangles[8 * 9];
gint idx;
if (paint_opacity == 0)
return;
st_theme_node_reduce_border_radius (node, box->x2 - box->x1, box->y2 - box->y1, border_radius);
box_shadow_spec = st_theme_node_get_box_shadow (node);
/* Compute input & output areas :
*
* yoffset ----------------------------
* | | | |
* | | | |
* | | | |
* top ----------------------------
* | | | |
* | | | |
* | | | |
* bottom ----------------------------
* | | | |
* | | | |
* | | | |
* yend ----------------------------
* xoffset left right xend
*
* s_top = top in offscreen's coordinates (0.0 - 1.0)
* s_bottom = bottom in offscreen's coordinates (0.0 - 1.0)
* s_left = left in offscreen's coordinates (0.0 - 1.0)
* s_right = right in offscreen's coordinates (0.0 - 1.0)
*/
if (box_shadow_spec->blur == 0)
shadow_blur_radius = 0;
else
shadow_blur_radius = (5 * (box_shadow_spec->blur / 2.0)) / 2;
shadow_width = state->box_shadow_width + 2 * shadow_blur_radius;
shadow_height = state->box_shadow_height + 2 * shadow_blur_radius;
/* Compute input regions parameters */
s_top = shadow_blur_radius + box_shadow_spec->blur +
MAX (node->border_radius[ST_CORNER_TOPLEFT],
node->border_radius[ST_CORNER_TOPRIGHT]);
s_bottom = shadow_blur_radius + box_shadow_spec->blur +
MAX (node->border_radius[ST_CORNER_BOTTOMLEFT],
node->border_radius[ST_CORNER_BOTTOMRIGHT]);
s_left = shadow_blur_radius + box_shadow_spec->blur +
MAX (node->border_radius[ST_CORNER_TOPLEFT],
node->border_radius[ST_CORNER_BOTTOMLEFT]);
s_right = shadow_blur_radius + box_shadow_spec->blur +
MAX (node->border_radius[ST_CORNER_TOPRIGHT],
node->border_radius[ST_CORNER_BOTTOMRIGHT]);
/* Compute output regions parameters */
xoffset = box->x1 + box_shadow_spec->xoffset - shadow_blur_radius - box_shadow_spec->spread;
yoffset = box->y1 + box_shadow_spec->yoffset - shadow_blur_radius - box_shadow_spec->spread;
width = box->x2 - box->x1 + 2 * shadow_blur_radius;
height = box->y2 - box->y1 + 2 * shadow_blur_radius;
x_spread_factor = (width + 2 * box_shadow_spec->spread) / width;
y_spread_factor = (height + 2 * box_shadow_spec->spread) / height;
width += 2 * box_shadow_spec->spread;
height += 2 * box_shadow_spec->spread;
xend = xoffset + width;
yend = yoffset + height;
top = s_top * y_spread_factor;
bottom = s_bottom * y_spread_factor;
left = s_left * x_spread_factor;
right = s_right * x_spread_factor;
bottom = height - bottom;
right = width - right;
/* Final adjustments */
s_top /= shadow_height;
s_bottom /= shadow_height;
s_left /= shadow_width;
s_right /= shadow_width;
s_bottom = 1.0 - s_bottom;
s_right = 1.0 - s_right;
top += yoffset;
bottom += yoffset;
left += xoffset;
right += xoffset;
/* Setup pipeline */
cogl_color_set_from_4ub (&color,
box_shadow_spec->color.red * paint_opacity / 255,
box_shadow_spec->color.green * paint_opacity / 255,
box_shadow_spec->color.blue * paint_opacity / 255,
box_shadow_spec->color.alpha * paint_opacity / 255);
cogl_color_premultiply (&color);
cogl_material_set_layer_combine_constant (state->box_shadow_material, 0, &color);
cogl_set_source (state->box_shadow_material);
idx = 0;
if (top > 0)
{
if (left > 0)
{
/* Top left corner */
rectangles[idx++] = xoffset;
rectangles[idx++] = yoffset;
rectangles[idx++] = left;
rectangles[idx++] = top;
rectangles[idx++] = 0;
rectangles[idx++] = 0;
rectangles[idx++] = s_left;
rectangles[idx++] = s_top;
}
/* Top middle */
rectangles[idx++] = left;
rectangles[idx++] = yoffset;
rectangles[idx++] = right;
rectangles[idx++] = top;
rectangles[idx++] = s_left;
rectangles[idx++] = 0;
rectangles[idx++] = s_right;
rectangles[idx++] = s_top;
if (right > 0)
{
/* Top right corner */
rectangles[idx++] = right;
rectangles[idx++] = yoffset;
rectangles[idx++] = xend;
rectangles[idx++] = top;
rectangles[idx++] = s_right;
rectangles[idx++] = 0;
rectangles[idx++] = 1;
rectangles[idx++] = s_top;
}
}
if (left > 0)
{
/* Left middle */
rectangles[idx++] = xoffset;
rectangles[idx++] = top;
rectangles[idx++] = left;
rectangles[idx++] = bottom;
rectangles[idx++] = 0;
rectangles[idx++] = s_top;
rectangles[idx++] = s_left;
rectangles[idx++] = s_bottom;
}
/* Center middle */
rectangles[idx++] = left;
rectangles[idx++] = top;
rectangles[idx++] = right;
rectangles[idx++] = bottom;
rectangles[idx++] = s_left;
rectangles[idx++] = s_top;
rectangles[idx++] = s_right;
rectangles[idx++] = s_bottom;
if (right > 0)
{
/* Right middle */
rectangles[idx++] = right;
rectangles[idx++] = top;
rectangles[idx++] = xend;
rectangles[idx++] = bottom;
rectangles[idx++] = s_right;
rectangles[idx++] = s_top;
rectangles[idx++] = 1;
rectangles[idx++] = s_bottom;
}
if (bottom > 0)
{
if (left > 0)
{
/* Bottom left corner */
rectangles[idx++] = xoffset;
rectangles[idx++] = bottom;
rectangles[idx++] = left;
rectangles[idx++] = yend;
rectangles[idx++] = 0;
rectangles[idx++] = s_bottom;
rectangles[idx++] = s_left;
rectangles[idx++] = 1;
}
/* Bottom middle */
rectangles[idx++] = left;
rectangles[idx++] = bottom;
rectangles[idx++] = right;
rectangles[idx++] = yend;
rectangles[idx++] = s_left;
rectangles[idx++] = s_bottom;
rectangles[idx++] = s_right;
rectangles[idx++] = 1;
if (right > 0)
{
/* Bottom right corner */
rectangles[idx++] = right;
rectangles[idx++] = bottom;
rectangles[idx++] = xend;
rectangles[idx++] = yend;
rectangles[idx++] = s_right;
rectangles[idx++] = s_bottom;
rectangles[idx++] = 1;
rectangles[idx++] = 1;
}
}
cogl_rectangles_with_texture_coords (rectangles, idx / 8);
#if 0
/* Visual feedback on shadow's 9-slice and orignal offscreen buffer,
for debug purposes */
cogl_rectangle (xend, yoffset, xend + shadow_width, yoffset + shadow_height);
cogl_set_source_color4ub (0xff, 0x0, 0x0, 0xff);
cogl_rectangle (xoffset, top, xend, top + 1);
cogl_rectangle (xoffset, bottom, xend, bottom + 1);
cogl_rectangle (left, yoffset, left + 1, yend);
cogl_rectangle (right, yoffset, right + 1, yend);
cogl_rectangle (xend, yoffset, xend + shadow_width, yoffset + 1);
cogl_rectangle (xend, yoffset + shadow_height, xend + shadow_width, yoffset + shadow_height + 1);
cogl_rectangle (xend, yoffset, xend + 1, yoffset + shadow_height);
cogl_rectangle (xend + shadow_width, yoffset, xend + shadow_width + 1, yoffset + shadow_height);
s_top *= shadow_height;
s_bottom *= shadow_height;
s_left *= shadow_width;
s_right *= shadow_width;
cogl_rectangle (xend, yoffset + s_top, xend + shadow_width, yoffset + s_top + 1);
cogl_rectangle (xend, yoffset + s_bottom, xend + shadow_width, yoffset + s_bottom + 1);
cogl_rectangle (xend + s_left, yoffset, xend + s_left + 1, yoffset + shadow_height);
cogl_rectangle (xend + s_right, yoffset, xend + s_right + 1, yoffset + shadow_height);
#endif
}
static void
st_theme_node_prerender_shadow (StThemeNodePaintState *state)
{
StThemeNode *node = state->node;
guint border_radius[4];
int max_borders[4];
int center_radius, corner_id;
CoglHandle buffer, offscreen;
/* Get infos from the node */
if (state->alloc_width < node->box_shadow_min_width ||
state->alloc_height < node->box_shadow_min_height)
st_theme_node_reduce_border_radius (node, state->alloc_width, state->alloc_height, border_radius);
else
for (corner_id = 0; corner_id < 4; corner_id++)
border_radius[corner_id] = node->border_radius[corner_id];
/* Compute maximum borders sizes */
max_borders[ST_SIDE_TOP] = MAX (node->border_radius[ST_CORNER_TOPLEFT],
node->border_radius[ST_CORNER_TOPRIGHT]);
max_borders[ST_SIDE_BOTTOM] = MAX (node->border_radius[ST_CORNER_BOTTOMLEFT],
node->border_radius[ST_CORNER_BOTTOMRIGHT]);
max_borders[ST_SIDE_LEFT] = MAX (node->border_radius[ST_CORNER_TOPLEFT],
node->border_radius[ST_CORNER_BOTTOMLEFT]);
max_borders[ST_SIDE_RIGHT] = MAX (node->border_radius[ST_CORNER_TOPRIGHT],
node->border_radius[ST_CORNER_BOTTOMRIGHT]);
center_radius = (node->box_shadow->blur > 0) ? (2 * node->box_shadow->blur + 1) : 1;
node->box_shadow_min_width = max_borders[ST_SIDE_LEFT] + max_borders[ST_SIDE_RIGHT] + center_radius;
node->box_shadow_min_height = max_borders[ST_SIDE_TOP] + max_borders[ST_SIDE_BOTTOM] + center_radius;
if (state->alloc_width < node->box_shadow_min_width ||
state->alloc_height < node->box_shadow_min_height)
{
state->box_shadow_width = state->alloc_width;
state->box_shadow_height = state->alloc_height;
}
else
{
state->box_shadow_width = node->box_shadow_min_width;
state->box_shadow_height = node->box_shadow_min_height;
}
/* Render offscreen */
buffer = cogl_texture_new_with_size (state->box_shadow_width,
state->box_shadow_height,
COGL_TEXTURE_NO_SLICING,
COGL_PIXEL_FORMAT_ANY);
offscreen = cogl_offscreen_new_to_texture (buffer);
if (offscreen != COGL_INVALID_HANDLE)
{
ClutterActorBox box = { 0, 0, state->box_shadow_width, state->box_shadow_height};
CoglColor clear_color;
cogl_push_framebuffer (offscreen);
cogl_ortho (0, state->box_shadow_width, state->box_shadow_height, 0, 0, 1.0);
cogl_color_set_from_4ub (&clear_color, 0, 0, 0, 0);
cogl_clear (&clear_color, COGL_BUFFER_BIT_COLOR);
st_theme_node_paint_borders (state, &box, 0xFF);
cogl_pop_framebuffer ();
cogl_handle_unref (offscreen);
state->box_shadow_material = _st_create_shadow_material (st_theme_node_get_box_shadow (node),
buffer);
}
cogl_handle_unref (buffer);
}
static void static void
st_theme_node_paint_sliced_border_image (StThemeNode *node, st_theme_node_paint_sliced_border_image (StThemeNode *node,
float width, float width,
@@ -2336,43 +1945,6 @@ st_theme_node_paint_outline (StThemeNode *node,
cogl_rectangles (rects, 4); cogl_rectangles (rects, 4);
} }
static gboolean
st_theme_node_needs_new_box_shadow_for_size (StThemeNodePaintState *state,
StThemeNode *node,
float width,
float height)
{
if (!node->rendered_once)
return TRUE;
/* The allocation hasn't changed, no need to recompute a new
box-shadow. */
if (state->alloc_width == width &&
state->alloc_height == height)
return FALSE;
/* If there is no shadow, no need to recompute a new box-shadow. */
if (node->box_shadow_min_width == 0 ||
node->box_shadow_min_height == 0)
return FALSE;
/* If the new size is inferior to the box-shadow minimum size (we
already know the size has changed), we need to recompute the
box-shadow. */
if (width < node->box_shadow_min_width ||
height < node->box_shadow_min_height)
return TRUE;
/* Now checking whether the size of the node has crossed the minimum
box-shadow size boundary, from below to above the minimum size .
If that's the case, we need to recompute the box-shadow */
if (state->alloc_width < node->box_shadow_min_width ||
state->alloc_height < node->box_shadow_min_height)
return TRUE;
return FALSE;
}
void void
st_theme_node_paint (StThemeNode *node, st_theme_node_paint (StThemeNode *node,
StThemeNodePaintState *state, StThemeNodePaintState *state,
@@ -2392,28 +1964,11 @@ st_theme_node_paint (StThemeNode *node,
if (width <= 0 || height <= 0) if (width <= 0 || height <= 0)
return; return;
/* Check whether we need to recreate the textures of the paint if (state->alloc_width != width || state->alloc_height != height)
* state, either because :
* 1) the theme node associated to the paint state has changed
* 2) the allocation size change requires recreating textures
*/
if (state->node != node ||
st_theme_node_needs_new_box_shadow_for_size (state, node, width, height))
{ {
/* If we had the ability to cache textures on the node, then we state->node = node;
can just copy them over to the paint state and avoid all st_theme_node_render_resources (node, state, width, height);
rendering. We end up sharing textures a cross different
widgets. */
if (node->rendered_once && node->cached_textures &&
width >= node->box_shadow_min_width && height >= node->box_shadow_min_height)
st_theme_node_paint_state_copy (state, &node->cached_state);
else
st_theme_node_render_resources (state, node, width, height);
node->rendered_once = TRUE;
} }
else if (state->alloc_width != width || state->alloc_height != height)
st_theme_node_update_resources (state, node, width, height);
/* Rough notes about the relationship of borders and backgrounds in CSS3; /* Rough notes about the relationship of borders and backgrounds in CSS3;
* see http://www.w3.org/TR/css3-background/ for more accurate details. * see http://www.w3.org/TR/css3-background/ for more accurate details.
@@ -2442,18 +1997,10 @@ st_theme_node_paint (StThemeNode *node,
*/ */
if (state->box_shadow_material) if (state->box_shadow_material)
{
if (state->alloc_width < node->box_shadow_min_width ||
state->alloc_height < node->box_shadow_min_height)
_st_paint_shadow_with_opacity (node->box_shadow, _st_paint_shadow_with_opacity (node->box_shadow,
state->box_shadow_material, state->box_shadow_material,
&allocation, &allocation,
paint_opacity); paint_opacity);
else
st_theme_node_paint_sliced_shadow (state,
&allocation,
paint_opacity);
}
if (state->prerendered_material != COGL_INVALID_HANDLE || if (state->prerendered_material != COGL_INVALID_HANDLE ||
st_theme_node_load_border_image (node)) st_theme_node_load_border_image (node))
@@ -2477,7 +2024,7 @@ st_theme_node_paint (StThemeNode *node,
} }
else else
{ {
st_theme_node_paint_borders (state, box, paint_opacity); st_theme_node_paint_borders (node, state, box, paint_opacity);
} }
st_theme_node_paint_outline (node, box, paint_opacity); st_theme_node_paint_outline (node, box, paint_opacity);
@@ -2529,9 +2076,8 @@ st_theme_node_paint (StThemeNode *node,
} }
} }
static void void
st_theme_node_paint_state_node_free_internal (StThemeNodePaintState *state, st_theme_node_paint_state_free (StThemeNodePaintState *state)
gboolean unref_node)
{ {
int corner_id; int corner_id;
@@ -2546,46 +2092,14 @@ st_theme_node_paint_state_node_free_internal (StThemeNodePaintState *state,
if (state->corner_material[corner_id] != COGL_INVALID_HANDLE) if (state->corner_material[corner_id] != COGL_INVALID_HANDLE)
cogl_handle_unref (state->corner_material[corner_id]); cogl_handle_unref (state->corner_material[corner_id]);
if (unref_node)
st_theme_node_paint_state_set_node (state, NULL);
st_theme_node_paint_state_init (state); st_theme_node_paint_state_init (state);
} }
static void
st_theme_node_paint_state_node_freed (StThemeNodePaintState *state)
{
st_theme_node_paint_state_node_free_internal (state, FALSE);
}
void
st_theme_node_paint_state_set_node (StThemeNodePaintState *state, StThemeNode *node)
{
if (state->node)
g_object_weak_unref (G_OBJECT (state->node),
(GWeakNotify) st_theme_node_paint_state_node_freed,
state);
state->node = node;
if (state->node)
g_object_weak_ref (G_OBJECT (state->node),
(GWeakNotify) st_theme_node_paint_state_node_freed,
state);
}
void
st_theme_node_paint_state_free (StThemeNodePaintState *state)
{
st_theme_node_paint_state_node_free_internal (state, TRUE);
}
void void
st_theme_node_paint_state_init (StThemeNodePaintState *state) st_theme_node_paint_state_init (StThemeNodePaintState *state)
{ {
int corner_id; int corner_id;
state->alloc_width = 0;
state->alloc_height = 0;
state->node = NULL; state->node = NULL;
state->box_shadow_material = COGL_INVALID_HANDLE; state->box_shadow_material = COGL_INVALID_HANDLE;
state->prerendered_texture = COGL_INVALID_HANDLE; state->prerendered_texture = COGL_INVALID_HANDLE;
@@ -2606,12 +2120,10 @@ st_theme_node_paint_state_copy (StThemeNodePaintState *state,
st_theme_node_paint_state_free (state); st_theme_node_paint_state_free (state);
st_theme_node_paint_state_set_node (state, other->node); state->node = other->node;
state->alloc_width = other->alloc_width; state->alloc_width = other->alloc_width;
state->alloc_height = other->alloc_height; state->alloc_height = other->alloc_height;
state->box_shadow_width = other->box_shadow_width;
state->box_shadow_height = other->box_shadow_height;
if (other->box_shadow_material) if (other->box_shadow_material)
state->box_shadow_material = cogl_handle_ref (other->box_shadow_material); state->box_shadow_material = cogl_handle_ref (other->box_shadow_material);

View File

@@ -99,19 +99,12 @@ struct _StThemeNode {
guint background_image_shadow_computed : 1; guint background_image_shadow_computed : 1;
guint text_shadow_computed : 1; guint text_shadow_computed : 1;
guint link_type : 2; guint link_type : 2;
guint rendered_once : 1;
guint cached_textures : 1;
int box_shadow_min_width;
int box_shadow_min_height;
CoglHandle border_slices_texture; CoglHandle border_slices_texture;
CoglHandle border_slices_material; CoglHandle border_slices_material;
CoglHandle background_texture; CoglHandle background_texture;
CoglHandle background_material; CoglHandle background_material;
CoglHandle background_shadow_material; CoglHandle background_shadow_material;
StThemeNodePaintState cached_state;
}; };
struct _StThemeNodeClass { struct _StThemeNodeClass {

View File

@@ -148,13 +148,9 @@ st_theme_node_transition_update (StThemeNodeTransition *transition,
if (st_theme_node_equal (new_node, old_node)) if (st_theme_node_equal (new_node, old_node))
{ {
{ {
StThemeNodePaintState tmp; StThemeNodePaintState tmp = priv->old_paint_state;
priv->old_paint_state = priv->new_paint_state;
st_theme_node_paint_state_init (&tmp); priv->new_paint_state = tmp;
st_theme_node_paint_state_copy (&tmp, &priv->old_paint_state);
st_theme_node_paint_state_copy (&priv->old_paint_state, &priv->new_paint_state);
st_theme_node_paint_state_copy (&priv->new_paint_state, &tmp);
st_theme_node_paint_state_free (&tmp);
} }
if (clutter_timeline_get_elapsed_time (priv->timeline) > 0) if (clutter_timeline_get_elapsed_time (priv->timeline) > 0)

View File

@@ -54,8 +54,6 @@ st_theme_node_init (StThemeNode *node)
node->background_shadow_material = COGL_INVALID_HANDLE; node->background_shadow_material = COGL_INVALID_HANDLE;
node->border_slices_texture = COGL_INVALID_HANDLE; node->border_slices_texture = COGL_INVALID_HANDLE;
node->border_slices_material = COGL_INVALID_HANDLE; node->border_slices_material = COGL_INVALID_HANDLE;
st_theme_node_paint_state_init (&node->cached_state);
} }
static void static void
@@ -103,8 +101,6 @@ st_theme_node_dispose (GObject *gobject)
g_signal_handlers_disconnect_by_func (node->theme, g_signal_handlers_disconnect_by_func (node->theme,
on_custom_stylesheets_changed, node); on_custom_stylesheets_changed, node);
st_theme_node_paint_state_free (&node->cached_state);
g_clear_object (&node->theme); g_clear_object (&node->theme);
G_OBJECT_CLASS (st_theme_node_parent_class)->dispose (gobject); G_OBJECT_CLASS (st_theme_node_parent_class)->dispose (gobject);

View File

@@ -102,9 +102,6 @@ struct _StThemeNodePaintState {
float alloc_width; float alloc_width;
float alloc_height; float alloc_height;
float box_shadow_width;
float box_shadow_height;
CoglHandle box_shadow_material; CoglHandle box_shadow_material;
CoglHandle prerendered_texture; CoglHandle prerendered_texture;
CoglHandle prerendered_material; CoglHandle prerendered_material;
@@ -281,8 +278,6 @@ void st_theme_node_paint_state_free (StThemeNodePaintState *state);
void st_theme_node_paint_state_copy (StThemeNodePaintState *state, void st_theme_node_paint_state_copy (StThemeNodePaintState *state,
StThemeNodePaintState *other); StThemeNodePaintState *other);
void st_theme_node_paint_state_invalidate (StThemeNodePaintState *state); void st_theme_node_paint_state_invalidate (StThemeNodePaintState *state);
void st_theme_node_paint_state_set_node (StThemeNodePaintState *state,
StThemeNode *node);
G_END_DECLS G_END_DECLS

View File

@@ -67,8 +67,6 @@ struct _StWidgetPrivate
gboolean hover : 1; gboolean hover : 1;
gboolean can_focus : 1; gboolean can_focus : 1;
gulong texture_file_changed_id;
AtkObject *accessible; AtkObject *accessible;
AtkRole accessible_role; AtkRole accessible_role;
AtkStateSet *local_state_set; AtkStateSet *local_state_set;
@@ -364,11 +362,9 @@ st_widget_dispose (GObject *gobject)
priv->label_actor = NULL; priv->label_actor = NULL;
} }
if (priv->texture_file_changed_id != 0) g_signal_handlers_disconnect_by_func (st_texture_cache_get_default (),
{ st_widget_texture_cache_changed,
g_signal_handler_disconnect (st_texture_cache_get_default (), priv->texture_file_changed_id); actor);
priv->texture_file_changed_id = 0;
}
g_clear_object (&priv->prev_first_child); g_clear_object (&priv->prev_first_child);
g_clear_object (&priv->prev_last_child); g_clear_object (&priv->prev_last_child);
@@ -389,7 +385,7 @@ st_widget_finalize (GObject *gobject)
g_free (priv->inline_style); g_free (priv->inline_style);
for (i = 0; i < G_N_ELEMENTS (priv->paint_states); i++) for (i = 0; i < G_N_ELEMENTS (priv->paint_states); i++)
st_theme_node_paint_state_free (&priv->paint_states[i]); st_theme_node_paint_state_init (&priv->paint_states[i]);
G_OBJECT_CLASS (st_widget_parent_class)->finalize (gobject); G_OBJECT_CLASS (st_widget_parent_class)->finalize (gobject);
} }
@@ -1556,7 +1552,7 @@ st_widget_init (StWidget *actor)
g_signal_connect (actor, "notify::first-child", G_CALLBACK (st_widget_first_child_notify), NULL); g_signal_connect (actor, "notify::first-child", G_CALLBACK (st_widget_first_child_notify), NULL);
g_signal_connect (actor, "notify::last-child", G_CALLBACK (st_widget_last_child_notify), NULL); g_signal_connect (actor, "notify::last-child", G_CALLBACK (st_widget_last_child_notify), NULL);
priv->texture_file_changed_id = g_signal_connect (st_texture_cache_get_default (), "texture-file-changed", g_signal_connect (st_texture_cache_get_default (), "texture-file-changed",
G_CALLBACK (st_widget_texture_cache_changed), actor); G_CALLBACK (st_widget_texture_cache_changed), actor);
for (i = 0; i < G_N_ELEMENTS (priv->paint_states); i++) for (i = 0; i < G_N_ELEMENTS (priv->paint_states); i++)
@@ -2885,63 +2881,3 @@ st_widget_get_focus_chain (StWidget *widget)
{ {
return ST_WIDGET_GET_CLASS (widget)->get_focus_chain (widget); return ST_WIDGET_GET_CLASS (widget)->get_focus_chain (widget);
} }
/**
* st_get_align_factors:
* @x_align: an #StAlign
* @y_align: an #StAlign
* @x_align_out: (out) (allow-none): @x_align as a #gdouble
* @y_align_out: (out) (allow-none): @y_align as a #gdouble
*
* Converts @x_align and @y_align to #gdouble values.
*/
void
st_get_align_factors (StAlign x_align,
StAlign y_align,
gdouble *x_align_out,
gdouble *y_align_out)
{
if (x_align_out)
{
switch (x_align)
{
case ST_ALIGN_START:
*x_align_out = 0.0;
break;
case ST_ALIGN_MIDDLE:
*x_align_out = 0.5;
break;
case ST_ALIGN_END:
*x_align_out = 1.0;
break;
default:
g_warn_if_reached ();
break;
}
}
if (y_align_out)
{
switch (y_align)
{
case ST_ALIGN_START:
*y_align_out = 0.0;
break;
case ST_ALIGN_MIDDLE:
*y_align_out = 0.5;
break;
case ST_ALIGN_END:
*y_align_out = 1.0;
break;
default:
g_warn_if_reached ();
break;
}
}
}

View File

@@ -151,6 +151,7 @@ StThemeNode * st_widget_peek_theme_node (StWidget *widg
GList * st_widget_get_focus_chain (StWidget *widget); GList * st_widget_get_focus_chain (StWidget *widget);
void st_widget_paint_background (StWidget *widget); void st_widget_paint_background (StWidget *widget);
/* debug methods */ /* debug methods */
char *st_describe_actor (ClutterActor *actor); char *st_describe_actor (ClutterActor *actor);
void st_set_slow_down_factor (gfloat factor); void st_set_slow_down_factor (gfloat factor);
@@ -168,12 +169,6 @@ void st_widget_set_accessible_name (StWidget *widget,
const gchar *name); const gchar *name);
const gchar * st_widget_get_accessible_name (StWidget *widget); const gchar * st_widget_get_accessible_name (StWidget *widget);
/* utility methods */
void st_get_align_factors (StAlign x_align,
StAlign y_align,
gdouble *x_align_out,
gdouble *y_align_out);
G_END_DECLS G_END_DECLS
#endif /* __ST_WIDGET_H__ */ #endif /* __ST_WIDGET_H__ */

View File

@@ -8,7 +8,6 @@ TEST_JS = \
interactive/border-radius.js \ interactive/border-radius.js \
interactive/border-width.js \ interactive/border-width.js \
interactive/box-layout.js \ interactive/box-layout.js \
interactive/box-shadow-animated.js \
interactive/box-shadows.js \ interactive/box-shadows.js \
interactive/calendar.js \ interactive/calendar.js \
interactive/css-fonts.js \ interactive/css-fonts.js \

View File

@@ -45,6 +45,12 @@ function test() {
style: 'border: 1px solid #aaaaaa; ' style: 'border: 1px solid #aaaaaa; '
+ 'background: #cceeff' })); + 'background: #cceeff' }));
b2.add(new St.Label({ x: 50,
y: 50,
text: "Fixed",
style: 'border: 1px solid #aaaaaa;'
+ 'background: #ffffcc' }));
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
function createCollapsableBox(width) { function createCollapsableBox(width) {

View File

@@ -1,84 +0,0 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const St = imports.gi.St;
const Mainloop = imports.mainloop;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const UI = imports.testcommon.ui;
const DELAY = 2000;
function resize_animated(label) {
if (label.width == 100) {
label.save_easing_state();
label.set_easing_mode(Clutter.AnimationMode.EASE_OUT_QUAD);
label.set_easing_duration(DELAY - 50);
label.set_size(500, 500);
label.restore_easing_state();
} else {
label.save_easing_state();
label.set_easing_mode(Clutter.AnimationMode.EASE_OUT_QUAD);
label.set_easing_duration(DELAY - 50);
label.set_size(100, 100);
label.restore_easing_state();
}
}
function get_css_style(shadow_style)
{
return 'border: 20px solid black;' +
'border-radius: 20px;' +
'background-color: white; ' +
'padding: 5px;' + shadow_style;
}
function test() {
let stage = new Clutter.Stage({ width: 1000, height: 600 });
UI.init(stage);
let iter = 0;
let shadowStyles = [ 'box-shadow: 3px 50px 0px 4px rgba(0,0,0,0.5);',
'box-shadow: 3px 4px 10px 4px rgba(0,0,0,0.5);',
'box-shadow: 0px 50px 0px 0px rgba(0,0,0,0.5);',
'box-shadow: 100px 100px 20px 4px rgba(0,0,0,0.5);'];
let label1 = new St.Label({ style: get_css_style(shadowStyles[iter]),
text: shadowStyles[iter],
x: 20,
y: 20,
width: 100,
height: 100
});
stage.add_actor(label1);
let label2 = new St.Label({ style: get_css_style(shadowStyles[iter]),
text: shadowStyles[iter],
x: 500,
y: 20,
width: 100,
height: 100
});
stage.add_actor(label2);
resize_animated(label1);
resize_animated(label2);
Mainloop.timeout_add(DELAY, Lang.bind(this, function() {
log(label1 + label1.get_size());
resize_animated(label1);
resize_animated(label2);
return true;
}));
Mainloop.timeout_add(2 * DELAY, Lang.bind(this, function() {
iter += 1;
iter %= shadowStyles.length;
label1.set_style(get_css_style(shadowStyles[iter]));
label1.set_text(shadowStyles[iter]);
label2.set_style(get_css_style(shadowStyles[iter]));
label2.set_text(shadowStyles[iter]);
return true;
}));
UI.main(stage);
}
test();