Compare commits

..

2 Commits

Author SHA1 Message Date
6d4ca1fcc8 WorkspaceThumbnails: show attached modal dialogs togheter with their parents
The window clones in the central part of the overview are showing modal
dialogs now, and this creates an inconsistency if the thumbnail doesn't too.
Code is intentionally similar in the two places.

https://bugzilla.gnome.org/show_bug.cgi?id=650843
2013-03-04 17:50:18 +01:00
93b1af401f Workspace: show attached modal dialog
Windows in the overview should be like they appear in the workspace,
including modal dialogs that are attached above them.
In addition, hiding the dialogs in the overview causes a flash as
dialog appears at the end of the transition.

Based on a patch by Maxim Ermilov <zaspire@rambler.ru>

https://bugzilla.gnome.org/show_bug.cgi?id=650843
2013-03-04 17:50:18 +01:00
93 changed files with 14664 additions and 21248 deletions

102
NEWS

@ -1,105 +1,3 @@
3.8.0.1
=======
* Background bug fixes [Ray; #696712]
3.8.0
=====
* Remove blur and desaturation from lock screen [Jasper; #696322]
* Remove scroll view fade near edges [Adel; #696404]
* dateMenu: Open calendar component when using Evolution [Florian; #696432]
* Fix unlocking on fast user switch [Cosimo; #696287]
* Tweak screen shield animation [Rui; #696380]
* Fix major memory leak when changing backgrounds [Ray; #696157]
* Miscellaneous bug fixes [Jasper, Adel, Florian; #696199, #696212, #696422,
#696447, #696235]
Contributors:
Giovanni Campagna, Cosimo Cecchi, Adel Gadllah, Rui Matos, Florian Müllner,
Jasper St. Pierre, Ray Strode
Translations:
Alexandre Franke [fr], Victor Ibragimov [tg], Arash Mousavi [fa],
Gabor Kelemen [hu], Sandeep Sheshrao Shedmake [mr], ManojKumar Giri [or],
Shantha kumar [ta], Rajesh Ranjan [hi], Stas Solovey [ru],
Shankar Prasad [kn], Dušan Kazik [sk], Ihar Hrachyshka [be],
Wouter Bolsterlee [nl], Kris Thomsen [da], Jiro Matsuzawa [ja],
Daniel Korostil [uk], Ani Peter [ml], Krishnababu Krothapalli [te],
Mantas Kriaučiūnas [lt], Praveen Illa [te]
3.7.92
======
* Drop fallback lock implementation [Florian; #693403]
* Don't let the user trigger message-tray when in fullscreen [Jasper; #694997]
* Scroll search results when using keynav [Jasper; #689681]
* Allow raising the shield by starting to type the password [Jasper; #686740]
* Improve tracking of fullscreen windows [Owen; #649748]
* Improve animation of new windows in overview [Giovanni; #695582]
* workspace switcher: Animate new workspaces created by DND [Giovanni; #685285]
* Give user time to read messages on login screen [Ray; #694688]
* Misc bug fixes and cleanups [Jasper, Ray, Florian, Cosimo, Giovanni, Adel,
Stef, Takao, Rui, Neil; #695154, #694993, #695272, #691578, #694321, #695338,
#695409, #695458, #695526, #695601, #695471, #695324, #695650, #695656,
#695659, #695485, #695395, #694951, #695824, #695841, #695801, #694321,
#693708, #695800, #695607, #695882, #691578, #685851, #694371, #690857,
#694092, #695747, #696007, #693438, #696064, #696102
Contributors:
Giovanni Campagna, Cosimo Cecchi, Allan Day, Takao Fujiwara, Adel Gadllah,
Tim Lunn, Rui Matos, Florian Müllner, Neil Roberts, Jasper St. Pierre,
Ray Strode, Stef Walter, Colin Walters, Owen W. Taylor
Translations:
Nilamdyuti Goswami [as], Chao-Hsiung Liao [zh_HK, zh_TW],
Yuri Myasoedov [ru], Gheyret Kenji [ug], Baurzhan Muftakhidinov [kk],
Ville-Pekka Vainio [fi], Matej Urbančič [sl],
Мирослав Николић [sr, sr@latin], Rūdolfs Mazurs [lv], Christian Kirbach [de],
Andika Triwidada [id], Gil Forcada [ca], Mattias Põldaru [et],
Duarte Loreto [pt], Adam Matoušek [cs], Changwoo Ryu [ko],
Ihar Hrachyshka [be], Carles Ferrando [ca@valencia], Sweta Kothari [gu]
3.7.91
======
* overview: Fade out controls during DND that are not targets [Cosimo; #686984]
* overview: Keep open when a Control key is held [Florian; #686984]
* Improve login screen => session transition [Ray; #694321]
* Center application grid horizontally [Florian; #694261]
* Fix hiding panel when fullscreen windows span multiple monitors [Adel; 646861]
* Tweak thresholds of pressure barrier [Jasper; #694467]
* Tweak window picker layout [Jasper; #694902]
* Expose key grab DBus API to gnome-settings-daemon [Florian; #643111]
* Don't always show message tray in overview, add message indicator
[Cosimo; #687787]
* Tweak startup animation [Ray; #694326]
* Add OSD popups and expose them to gnome-settings-daemon [Florian; #613543]
* Move loupe icon to the start of the search entry [Jasper; #695069]
* Don't show the input switcher with less than 2 items [Rui; 695000]
* Fix auto-completion of system modals immediately upon display [Stef; #692937]
* Ignore workspaces in alt-tab [Florian; #661156]
* Disable copying text from password entries [Florian; #695104]
* Use standard styling for ibus candidate popups [Allan; #694796]
* Fix calendar changing height on month changes [Giovanni; #641383]
* Port the hot corner to use pressure barriers [Jasper; #663661]
* Misc bug fixes and cleanups: [Hashem, Giovanni, Alban, Jasper, Cosimo,
Florian, Adel, Daniel, Matthias, Ray, Rui, Guillaume, Stef; #685849, #690643,
#694292, #693814, #694234, #694365, #694287, #694336, #694256, #694261,
#663601, #694441, #694284, #694463, #694475, #687248, #694394, #694320,
#694701, #694784, #694858, #694906, #694327, #694876, #694905, #694969,
#694970, #694988, #695006, #695001, #694998, #695023, #695002, #695073,
#695126, #687748, #694837, #693907, #679851, #694988]
Contributors:
Giovanni Campagna, Cosimo Cecchi, Matthias Clasen, Alban Crequy, Allan Day,
Guillaume Desmottes, Adel Gadllah, Rui Matos, Daniel Mustieles,
Hashem Nasarat, Jasper St. Pierre, Ray Strode, Stef Walter
Translations:
Yuri Myasoedov [ru], Adam Matoušek [cs], Piotr Drąg [pl], Matej Urbančič [sl],
Sweta Kothari [gu], Kjartan Maraas [nb], Nguyễn Thái Ngọc Duy [vi],
Chao-Hsiung Liao [zh_HK, zh_TW], Dimitris Spingos [el],
Inaki Larranaga Murgoitio [eu], Luca Ferretti [it], A S Alam [pa],
Gheyret Kenji [ug], Stas Solovey [ru], Enrico Nicoletto [pt_BR],
Fran Diéguez [gl], Daniel Mustieles [es], Aurimas Černius [lt]
3.7.90 3.7.90
====== ======
* Let GNOME Shell work on EGL and GLES2 [Neil; #693225, #693438, #693339] * Let GNOME Shell work on EGL and GLES2 [Neil; #693225, #693438, #693339]

@ -1,5 +1,5 @@
AC_PREREQ(2.63) AC_PREREQ(2.63)
AC_INIT([gnome-shell],[3.8.0.1],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell]) AC_INIT([gnome-shell],[3.7.90],[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.8.0 MUTTER_MIN_VERSION=3.7.90
GTK_MIN_VERSION=3.7.9 GTK_MIN_VERSION=3.7.9
GIO_MIN_VERSION=2.35.0 GIO_MIN_VERSION=2.35.0
LIBECAL_MIN_VERSION=3.5.3 LIBECAL_MIN_VERSION=3.5.3
@ -96,7 +96,7 @@ PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes
libnm-glib libnm-util >= $NETWORKMANAGER_MIN_VERSION libnm-glib libnm-util >= $NETWORKMANAGER_MIN_VERSION
libnm-gtk >= $NETWORKMANAGER_MIN_VERSION libnm-gtk >= $NETWORKMANAGER_MIN_VERSION
libsecret-unstable gcr-3 >= $GCR_MIN_VERSION) gnome-keyring-1 gcr-3 >= $GCR_MIN_VERSION)
PKG_CHECK_MODULES(GNOME_SHELL_JS, gio-2.0 gjs-internals-1.0 >= $GJS_MIN_VERSION) PKG_CHECK_MODULES(GNOME_SHELL_JS, gio-2.0 gjs-internals-1.0 >= $GJS_MIN_VERSION)
PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 >= 0.6.8 x11) PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 >= 0.6.8 x11)

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

@ -125,8 +125,7 @@ StScrollBar StButton#vhandle:active {
/* PopupMenu */ /* PopupMenu */
.popup-menu-boxpointer, .popup-menu-boxpointer {
.candidate-popup-boxpointer {
-arrow-border-radius: 8px; -arrow-border-radius: 8px;
-arrow-background-color: rgba(0,0,0,0.9); -arrow-background-color: rgba(0,0,0,0.9);
-arrow-border-width: 2px; -arrow-border-width: 2px;
@ -316,20 +315,10 @@ StScrollBar StButton#vhandle:active {
.notification-button:focus, .notification-button:focus,
.notification-icon-button:focus, .notification-icon-button:focus,
.hotplug-notification-item:focus, .hotplug-notification-item:focus,
.modal-dialog-button:focus, .modal-dialog-button:focus {
.app-view-control:focus {
border-width: 2px; border-width: 2px;
} }
.app-view-control:focus {
padding: 3px;
}
.app-view-control:first-child:ltr:focus,
.app-view-control:last-child:rtl:focus {
border-right-width: 1px;
}
.candidate-page-button:active, .candidate-page-button:active,
.candidate-page-button:pressed, .candidate-page-button:pressed,
.notification-button:active, .notification-button:active,
@ -897,8 +886,8 @@ StScrollBar StButton#vhandle:active {
.search-display > StBoxLayout, .search-display > StBoxLayout,
.all-apps > StBoxLayout, .all-apps > StBoxLayout,
.frequent-apps > StBoxLayout { .frequent-apps > StBoxLayout {
/* horizontal padding to make sure scrollbars or dash don't overlap content */ /* horizontal padding to make sure the scrollbar doesn't overlap content */
padding: 0px 88px; padding: 0px 18px;
} }
.app-folder-icon { .app-folder-icon {
@ -974,10 +963,6 @@ StScrollBar StButton#vhandle:active {
color:white; color:white;
} }
.app-display .app-well-app > .overview-icon {
border-radius: 10px;
}
.list-search-result:hover .list-search-result-description { .list-search-result:hover .list-search-result-description {
text-shadow: rgba(0,0,0,0.8) 0px 1px 2px; text-shadow: rgba(0,0,0,0.8) 0px 1px 2px;
} }
@ -1272,15 +1257,15 @@ StScrollBar StButton#vhandle:active {
font-weight: bold; font-weight: bold;
} }
.calendar-other-month-day {
color: #333333;
}
.calendar-day-with-events { .calendar-day-with-events {
font-weight: bold; font-weight: bold;
color: white; color: white;
} }
.calendar-other-month-day {
color: #333333;
}
.events-header-vbox { .events-header-vbox {
spacing: 6pt; spacing: 6pt;
padding-right: .5em; padding-right: .5em;
@ -2144,12 +2129,23 @@ StScrollBar StButton#vhandle:active {
} }
/* IBus Candidate Popup */ /* IBus Candidate Popup */
.candidate-popup-boxpointer {
-arrow-border-radius: 8px;
-arrow-background-color: #707070;
-arrow-border-width: 0px;
-arrow-base: 24px;
-arrow-rise: 11px;
}
.candidate-popup-content { .candidate-popup-content {
padding: 0.5em; padding: 0.5em;
spacing: 0.3em; spacing: 0.3em;
} }
.candidate-popup-text {
font-size: 9pt;
}
.candidate-index { .candidate-index {
padding: 0 0.5em 0 0; padding: 0 0.5em 0 0;
color: #cccccc; color: #cccccc;
@ -2215,9 +2211,6 @@ StScrollBar StButton#vhandle:active {
border: none; border: none;
background-color: transparent; background-color: transparent;
padding-bottom: 80px;
padding-top: 80px;
border-radius: 16px; border-radius: 16px;
min-height: 150px; min-height: 150px;
max-height: 700px; max-height: 700px;

Binary file not shown.

Before

(image error) Size: 13 KiB

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

@ -594,7 +594,6 @@ const LoginDialog = new Lang.Class({
this._promptEntry = new St.Entry({ style_class: 'login-dialog-prompt-entry', this._promptEntry = new St.Entry({ style_class: 'login-dialog-prompt-entry',
can_focus: true }); can_focus: true });
this._promptEntryTextChangedId = 0; this._promptEntryTextChangedId = 0;
this._promptEntryActivateId = 0;
this._promptBox.add(this._promptEntry, this._promptBox.add(this._promptEntry,
{ expand: true, { expand: true,
x_fill: true, x_fill: true,
@ -691,8 +690,6 @@ const LoginDialog = new Lang.Class({
}, },
_reset: function() { _reset: function() {
this._userVerifier.clear();
this._updateSensitivity(true); this._updateSensitivity(true);
this._promptMessage.hide(); this._promptMessage.hide();
this._user = null; this._user = null;
@ -814,11 +811,6 @@ const LoginDialog = new Lang.Class({
Lang.bind(this, function() { Lang.bind(this, function() {
this._updateSignInButtonSensitivity(this._promptEntry.text.length > 0); this._updateSignInButtonSensitivity(this._promptEntry.text.length > 0);
})); }));
this._promptEntryActivateId =
this._promptEntry.clutter_text.connect('activate', function() {
hold.release();
});
}, },
_updateSensitivity: function(sensitive) { _updateSensitivity: function(sensitive) {
@ -843,11 +835,6 @@ const LoginDialog = new Lang.Class({
this._promptEntryTextChangedId = 0; this._promptEntryTextChangedId = 0;
} }
if (this._promptEntryActivateId > 0) {
this._promptEntry.clutter_text.disconnect(this._promptEntryActivateId);
this._promptEntryActivateId = 0;
}
this._setWorking(false); this._setWorking(false);
this._promptBox.hide(); this._promptBox.hide();
this._promptLoginHint.hide(); this._promptLoginHint.hide();
@ -930,7 +917,7 @@ const LoginDialog = new Lang.Class({
return batch.run(); return batch.run();
}, },
_startSession: function(serviceName) { _onSessionOpened: function(client, serviceName) {
Tweener.addTween(this.dialogLayout, Tweener.addTween(this.dialogLayout,
{ opacity: 0, { opacity: 0,
time: _FADE_ANIMATION_TIME, time: _FADE_ANIMATION_TIME,
@ -953,18 +940,6 @@ const LoginDialog = new Lang.Class({
onCompleteScope: this }); onCompleteScope: this });
}, },
_onSessionOpened: function(client, serviceName) {
if (!this._userVerifier.hasPendingMessages) {
this._startSession(serviceName);
} else {
let signalId = this._userVerifier.connect('no-more-messages',
Lang.bind(this, function() {
this._userVerifier.disconnect(signalId);
this._startSession(serviceName);
}));
}
},
_waitForItemForUser: function(userName) { _waitForItemForUser: function(userName) {
let item = this._userList.getItemFromUserName(userName); let item = this._userList.getItemFromUserName(userName);
@ -1028,7 +1003,7 @@ const LoginDialog = new Lang.Class({
function() { function() {
// If we're just starting out, start on the right // If we're just starting out, start on the right
// item. // item.
if (!this._userManager.is_loaded) { if (!this.is_loaded) {
this._userList.jumpToItem(this._timedLoginItem); this._userList.jumpToItem(this._timedLoginItem);
} }
}, },
@ -1166,6 +1141,15 @@ const LoginDialog = new Lang.Class({
Lang.bind(this, function(userManager, user) { Lang.bind(this, function(userManager, user) {
this._userList.removeUser(user); this._userList.removeUser(user);
})); }));
// emitted in idle so caller doesn't have to explicitly check if
// it's loaded immediately after construction
// (since there's no way the caller could be listening for
// 'loaded' yet)
Mainloop.idle_add(Lang.bind(this, function() {
this.emit('loaded');
this.is_loaded = true;
}));
}, },
_onOpened: function() { _onOpened: function() {
@ -1180,9 +1164,5 @@ const LoginDialog = new Lang.Class({
this.parent(); this.parent();
Main.ctrlAltTabManager.removeGroup(this.dialogLayout); Main.ctrlAltTabManager.removeGroup(this.dialogLayout);
}, }
addCharacter: function(unichar) {
this._promptEntry.clutter_text.insert_unichar(unichar);
},
}); });

@ -2,7 +2,6 @@
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio; const Gio = imports.gi.Gio;
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;
@ -28,9 +27,6 @@ const ALLOWED_FAILURES_KEY = 'allowed-failures';
const LOGO_KEY = 'logo'; const LOGO_KEY = 'logo';
const DISABLE_USER_LIST_KEY = 'disable-user-list'; const DISABLE_USER_LIST_KEY = 'disable-user-list';
// Give user 16ms to read each character of a PAM message
const USER_READ_TIME = 16
function fadeInActor(actor) { function fadeInActor(actor) {
if (actor.opacity == 255 && actor.visible) if (actor.opacity == 255 && actor.visible)
return null; return null;
@ -118,9 +114,6 @@ const ShellUserVerifier = new Lang.Class({
this._fprintManager = new Fprint.FprintManager(); this._fprintManager = new Fprint.FprintManager();
this._realmManager = new Realmd.Manager(); this._realmManager = new Realmd.Manager();
this._messageQueue = [];
this._messageQueueTimeoutId = 0;
this.hasPendingMessages = false;
this._failCounter = 0; this._failCounter = 0;
}, },
@ -160,73 +153,13 @@ const ShellUserVerifier = new Lang.Class({
this._userVerifier.run_dispose(); this._userVerifier.run_dispose();
this._userVerifier = null; this._userVerifier = null;
} }
this._clearMessageQueue();
}, },
answerQuery: function(serviceName, answer) { answerQuery: function(serviceName, answer) {
if (!this._userVerifier.hasPendingMessages) { // Clear any previous message
this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
} else {
let signalId = this._userVerifier.connect('no-more-messages',
Lang.bind(this, function() {
this._userVerifier.disconnect(signalId);
this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
}));
}
},
_getIntervalForMessage: function(message) {
// We probably could be smarter here
return message.length * USER_READ_TIME;
},
finishMessageQueue: function() {
if (!this.hasPendingMessages)
return;
this._messageQueue = [];
this.hasPendingMessages = false;
this.emit('no-more-messages');
},
_queueMessageTimeout: function() {
if (this._messageQueue.length == 0) {
this.finishMessageQueue();
return;
}
if (this._messageQueueTimeoutId != 0)
return;
let message = this._messageQueue.shift();
this.emit('show-message', message.text, message.iconName);
this._messageQueueTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
message.interval,
Lang.bind(this, function() {
this._messageQueueTimeoutId = 0;
this._queueMessageTimeout();
}));
},
_queueMessage: function(message, iconName) {
let interval = this._getIntervalForMessage(message);
this.hasPendingMessages = true;
this._messageQueue.push({ text: message, interval: interval, iconName: iconName });
this._queueMessageTimeout();
},
_clearMessageQueue: function() {
this.finishMessageQueue();
if (this._messageQueueTimeoutId != 0) {
GLib.source_remove(this._messageQueueTimeoutId);
this._messageQueueTimeoutId = 0;
}
this.emit('show-message', null, null); this.emit('show-message', null, null);
this._userVerifier.call_answer_query(serviceName, answer, this._cancellable, null);
}, },
_checkForFingerprintReader: function() { _checkForFingerprintReader: function() {
@ -246,7 +179,7 @@ const ShellUserVerifier = new Lang.Class({
logError(error, where); logError(error, where);
this._hold.release(); this._hold.release();
this._queueMessage(_("Authentication error"), 'login-dialog-message-warning'); this.emit('show-message', _("Authentication error"), 'login-dialog-message-warning');
this._verificationFailed(false); this._verificationFailed(false);
}, },
@ -365,7 +298,7 @@ const ShellUserVerifier = new Lang.Class({
// 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) { } else if (serviceName == PASSWORD_SERVICE_NAME) {
this._queueMessage(info, 'login-dialog-message-info'); this.emit('show-message', info, 'login-dialog-message-info');
} }
}, },
@ -374,7 +307,7 @@ const ShellUserVerifier = new Lang.Class({
// users who haven't enrolled their fingerprint. // users who haven't enrolled their fingerprint.
if (serviceName != PASSWORD_SERVICE_NAME) if (serviceName != PASSWORD_SERVICE_NAME)
return; return;
this._queueMessage(problem, 'login-dialog-message-warning'); this.emit('show-message', problem, 'login-dialog-message-warning');
}, },
_showRealmLoginHint: function() { _showRealmLoginHint: function() {
@ -413,6 +346,8 @@ const ShellUserVerifier = new Lang.Class({
}, },
_onReset: function() { _onReset: function() {
this.clear();
// Clear previous attempts to authenticate // Clear previous attempts to authenticate
this._failCounter = 0; this._failCounter = 0;
@ -423,15 +358,6 @@ const ShellUserVerifier = new Lang.Class({
this.emit('verification-complete'); this.emit('verification-complete');
}, },
_cancelAndReset: function() {
this.cancel();
this._onReset();
},
_retry: function() {
this.begin(this._userName, new Batch.Hold());
},
_verificationFailed: function(retry) { _verificationFailed: function(retry) {
// For Not Listed / enterprise logins, immediately reset // For Not Listed / enterprise logins, immediately reset
// the dialog // the dialog
@ -443,25 +369,15 @@ 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._userVerifier.hasPendingMessages) { this.clear();
this._retry(); this.begin(this._userName, new Batch.Hold());
} else {
let signalId = this._userVerifier.connect('no-more-messages',
Lang.bind(this, function() {
this._userVerifier.disconnect(signalId);
this._retry();
}));
}
} else { } else {
if (!this._userVerifier.hasPendingMessages) { // Allow some time to see the message, then reset everything
this._cancelAndReset(); Mainloop.timeout_add(3000, Lang.bind(this, function() {
} else { this.cancel();
let signalId = this._userVerifier.connect('no-more-messages',
Lang.bind(this, function() { this._onReset();
this._userVerifier.disconnect(signalId); }));
this._cancelAndReset();
}));
}
} }
this.emit('verification-failed'); this.emit('verification-failed');

@ -33,10 +33,6 @@ const SystemdLoginManagerIface = <interface name='org.freedesktop.login1.Manager
<arg type='s' direction='in'/> <arg type='s' direction='in'/>
<arg type='h' direction='out'/> <arg type='h' direction='out'/>
</method> </method>
<method name='GetSession'>
<arg type='s' direction='in'/>
<arg type='o' direction='out'/>
</method>
<method name='ListSessions'> <method name='ListSessions'>
<arg name='sessions' type='a(susso)' direction='out'/> <arg name='sessions' type='a(susso)' direction='out'/>
</method> </method>
@ -79,35 +75,6 @@ function haveSystemd() {
return GLib.access("/sys/fs/cgroup/systemd", 0) >= 0; return GLib.access("/sys/fs/cgroup/systemd", 0) >= 0;
} }
function versionCompare(required, reference) {
required = required.split('.');
reference = reference.split('.');
for (let i = 0; i < required.length; i++) {
if (required[i] != reference[i])
return required[i] < reference[i];
}
return true;
}
function canLock() {
try {
let params = GLib.Variant.new('(ss)', ['org.gnome.DisplayManager.Manager', 'Version']);
let result = Gio.DBus.system.call_sync('org.gnome.DisplayManager',
'/org/gnome/DisplayManager/Manager',
'org.freedesktop.DBus.Properties',
'Get', params, null,
Gio.DBusCallFlags.NONE,
-1, null);
let version = result.deep_unpack()[0].deep_unpack();
return versionCompare('3.5.91', version);
} catch(e) {
return false;
}
}
let _loginManager = null; let _loginManager = null;
/** /**
@ -140,23 +107,15 @@ const LoginManagerSystemd = new Lang.Class({
// Having this function is a bit of a hack since the Systemd and ConsoleKit // Having this function is a bit of a hack since the Systemd and ConsoleKit
// session objects have different interfaces - but in both cases there are // session objects have different interfaces - but in both cases there are
// Lock/Unlock signals, and that's all we count upon at the moment. // Lock/Unlock signals, and that's all we count upon at the moment.
getCurrentSessionProxy: function(callback) { getCurrentSessionProxy: function() {
if (this._currentSession) { if (!this._currentSession) {
callback (this._currentSession); this._currentSession = new SystemdLoginSession(Gio.DBus.system,
return; 'org.freedesktop.login1',
'/org/freedesktop/login1/session/' +
GLib.getenv('XDG_SESSION_ID'));
} }
this._proxy.GetSessionRemote(GLib.getenv('XDG_SESSION_ID'), Lang.bind(this, return this._currentSession;
function(result, error) {
if (error) {
logError(error, 'Could not get a proxy for the current session');
} else {
this._currentSession = new SystemdLoginSession(Gio.DBus.system,
'org.freedesktop.login1',
result[0]);
callback(this._currentSession);
}
}));
}, },
canPowerOff: function(asyncCallback) { canPowerOff: function(asyncCallback) {
@ -245,23 +204,15 @@ const LoginManagerConsoleKit = new Lang.Class({
// Having this function is a bit of a hack since the Systemd and ConsoleKit // Having this function is a bit of a hack since the Systemd and ConsoleKit
// session objects have different interfaces - but in both cases there are // session objects have different interfaces - but in both cases there are
// Lock/Unlock signals, and that's all we count upon at the moment. // Lock/Unlock signals, and that's all we count upon at the moment.
getCurrentSessionProxy: function(callback) { getCurrentSessionProxy: function() {
if (this._currentSession) { if (!this._currentSession) {
callback (this._currentSession); let [currentSessionId] = this._proxy.GetCurrentSessionSync();
return; this._currentSession = new ConsoleKitSession(Gio.DBus.system,
'org.freedesktop.ConsoleKit',
currentSessionId);
} }
this._proxy.GetCurrentSessionRemote(Lang.bind(this, return this._currentSession;
function(result, error) {
if (error) {
logError(error, 'Could not get a proxy for the current session');
} else {
this._currentSession = new ConsoleKitSession(Gio.DBus.system,
'org.freedesktop.ConsoleKit',
result[0]);
callback(this._currentSession);
}
}));
}, },
canPowerOff: function(asyncCallback) { canPowerOff: function(asyncCallback) {

@ -5,9 +5,6 @@ const GLib = imports.gi.GLib;
const St = imports.gi.St; const St = imports.gi.St;
const Main = imports.ui.main; const Main = imports.ui.main;
const Tweener = imports.ui.tweener;
const SCROLL_TIME = 0.1;
// http://daringfireball.net/2010/07/improved_regex_for_matching_urls // http://daringfireball.net/2010/07/improved_regex_for_matching_urls
const _balancedParens = '\\((?:[^\\s()<>]+|(?:\\(?:[^\\s()<>]+\\)))*\\)'; const _balancedParens = '\\((?:[^\\s()<>]+|(?:\\(?:[^\\s()<>]+\\)))*\\)';
@ -237,39 +234,3 @@ function makeCloseButton() {
return closeButton; return closeButton;
} }
function ensureActorVisibleInScrollView(scrollView, actor) {
let adjustment = scrollView.vscroll.adjustment;
let [value, lower, upper, stepIncrement, pageIncrement, pageSize] = adjustment.get_values();
let offset = 0;
let vfade = scrollView.get_effect("fade");
if (vfade)
offset = vfade.vfade_offset;
let box = actor.get_allocation_box();
let y1 = box.y1, y2 = box.y2;
let parent = actor.get_parent();
while (parent != scrollView) {
if (!parent)
throw new Error("actor not in scroll view");
let box = parent.get_allocation_box();
y1 += box.y1;
y2 += box.y1;
parent = parent.get_parent();
}
if (y1 < value + offset)
value = Math.max(0, y1 - offset);
else if (y2 > value + pageSize - offset)
value = Math.min(upper, y2 + offset - pageSize);
else
return;
Tweener.addTween(adjustment,
{ value: value,
time: SCROLL_TIME,
transition: 'easeOutQuad' });
}

@ -29,6 +29,7 @@ const Util = imports.misc.util;
const MAX_APPLICATION_WORK_MILLIS = 75; const MAX_APPLICATION_WORK_MILLIS = 75;
const MENU_POPUP_TIMEOUT = 600; const MENU_POPUP_TIMEOUT = 600;
const SCROLL_TIME = 0.1;
const MAX_COLUMNS = 6; const MAX_COLUMNS = 6;
const INACTIVE_GRID_OPACITY = 77; const INACTIVE_GRID_OPACITY = 77;
@ -156,30 +157,6 @@ const FolderView = new Lang.Class({
} }
}); });
const AllViewLayout = new Lang.Class({
Name: 'AllViewLayout',
Extends: Clutter.BinLayout,
vfunc_get_preferred_height: function(container, forWidth) {
let minBottom = 0;
let naturalBottom = 0;
for (let child = container.get_first_child();
child;
child = child.get_next_sibling()) {
let childY = child.y;
let [childMin, childNatural] = child.get_preferred_height(forWidth);
if (childMin + childY > minBottom)
minBottom = childMin + childY;
if (childNatural + childY > naturalBottom)
naturalBottom = childNatural + childY;
}
return [minBottom, naturalBottom];
}
});
const AllView = new Lang.Class({ const AllView = new Lang.Class({
Name: 'AllView', Name: 'AllView',
Extends: AlphabeticalView, Extends: AlphabeticalView,
@ -188,7 +165,7 @@ const AllView = new Lang.Class({
this.parent(); this.parent();
let box = new St.BoxLayout({ vertical: true }); let box = new St.BoxLayout({ vertical: true });
this._stack = new St.Widget({ layout_manager: new AllViewLayout() }); this._stack = new St.Widget({ layout_manager: new Clutter.BinLayout() });
this._stack.add_actor(this._grid.actor); this._stack.add_actor(this._grid.actor);
this._eventBlocker = new St.Widget({ x_expand: true, y_expand: true }); this._eventBlocker = new St.Widget({ x_expand: true, y_expand: true });
this._stack.add_actor(this._eventBlocker); this._stack.add_actor(this._eventBlocker);
@ -282,7 +259,29 @@ const AllView = new Lang.Class({
}, },
_ensureIconVisible: function(icon) { _ensureIconVisible: function(icon) {
Util.ensureActorVisibleInScrollView(this.actor, icon); let adjustment = this.actor.vscroll.adjustment;
let [value, lower, upper, stepIncrement, pageIncrement, pageSize] = adjustment.get_values();
let offset = 0;
let vfade = this.actor.get_effect("fade");
if (vfade)
offset = vfade.vfade_offset;
// If this gets called as part of a right-click, the actor
// will be needs_allocation, and so "icon.y" would return 0
let box = icon.get_allocation_box();
if (box.y1 < value + offset)
value = Math.max(0, box.y1 - offset);
else if (box.y2 > value + pageSize - offset)
value = Math.min(upper, box.y2 + offset - pageSize);
else
return;
Tweener.addTween(adjustment,
{ value: value,
time: SCROLL_TIME,
transition: 'easeOutQuad' });
}, },
_updateIconOpacities: function(folderOpen) { _updateIconOpacities: function(folderOpen) {

@ -38,7 +38,6 @@ const BackgroundCache = new Lang.Class({
_init: function() { _init: function() {
this._patterns = []; this._patterns = [];
this._images = []; this._images = [];
this._pendingFileLoads = [];
this._fileMonitors = {}; this._fileMonitors = {};
}, },
@ -85,9 +84,9 @@ const BackgroundCache = new Lang.Class({
} else { } else {
content.load_gradient(params.shadingType, params.color, params.secondColor); content.load_gradient(params.shadingType, params.color, params.secondColor);
} }
}
this._patterns.push(content); this._patterns.push(content);
}
return content; return content;
}, },
@ -129,73 +128,6 @@ const BackgroundCache = new Lang.Class({
this._removeContent(this._images, content); this._removeContent(this._images, content);
}, },
_loadImageContent: function(params) {
params = Params.parse(params, { monitorIndex: 0,
style: null,
filename: null,
effects: Meta.BackgroundEffects.NONE,
cancellable: null,
onFinished: null });
for (let i = 0; i < this._pendingFileLoads.length; i++) {
if (this._pendingFileLoads[i].filename == params.filename &&
this._pendingFileLoads[i].style == params.style) {
this._pendingFileLoads[i].callers.push({ shouldCopy: true,
monitorIndex: params.monitorIndex,
effects: params.effects,
onFinished: params.onFinished });
return;
}
}
this._pendingFileLoads.push({ filename: params.filename,
style: params.style,
callers: [{ shouldCopy: false,
monitorIndex: params.monitorIndex,
effects: params.effects,
onFinished: params.onFinished }] });
let content = new Meta.Background({ meta_screen: global.screen,
monitor: params.monitorIndex,
effects: params.effects });
content.load_file_async(params.filename,
params.style,
params.cancellable,
Lang.bind(this,
function(object, result) {
try {
content.load_file_finish(result);
this._monitorFile(params.filename);
this._images.push(content);
} catch(e) {
content = null;
}
for (let i = 0; i < this._pendingFileLoads.length; i++) {
let pendingLoad = this._pendingFileLoads[i];
if (pendingLoad.filename != params.filename ||
pendingLoad.style != params.style)
continue;
for (let j = 0; j < pendingLoad.callers.length; j++) {
if (pendingLoad.callers[j].onFinished) {
if (content && pendingLoad.callers[j].shouldCopy) {
content = object.copy(pendingLoad.callers[j].monitorIndex,
pendingLoad.callers[j].effects);
}
pendingLoad.callers[j].onFinished(content);
}
}
this._pendingFileLoads.splice(i, 1);
}
}));
},
getImageContent: function(params) { getImageContent: function(params) {
params = Params.parse(params, { monitorIndex: 0, params = Params.parse(params, { monitorIndex: 0,
style: null, style: null,
@ -233,19 +165,31 @@ const BackgroundCache = new Lang.Class({
if (params.cancellable && params.cancellable.is_cancelled()) if (params.cancellable && params.cancellable.is_cancelled())
content = null; content = null;
else
this._images.push(content);
if (params.onFinished) if (params.onFinished)
params.onFinished(content); params.onFinished(content);
} else { } else {
this._loadImageContent({ filename: params.filename, content = new Meta.Background({ meta_screen: global.screen,
style: params.style, monitor: params.monitorIndex,
effects: params.effects, effects: params.effects });
monitorIndex: params.monitorIndex,
cancellable: params.cancellable,
onFinished: params.onFinished });
content.load_file_async(params.filename,
params.style,
params.cancellable,
Lang.bind(this,
function(object, result) {
try {
content.load_file_finish(result);
this._monitorFile(params.filename);
this._images.push(content);
} catch(e) {
content = null;
}
if (params.onFinished)
params.onFinished(content);
}));
} }
}, },
@ -323,9 +267,9 @@ const Background = new Lang.Class({
_destroy: function() { _destroy: function() {
this._cancellable.cancel(); this._cancellable.cancel();
if (this._updateAnimationTimeoutId) { if (this._animationUpdateTimeoutId) {
GLib.source_remove (this._updateAnimationTimeoutId); GLib.source_remove (this._animationUpdateTimeoutId);
this._updateAnimationTimeoutId = 0; this._animationUpdateTimeoutId = 0
} }
let i; let i;
@ -412,10 +356,7 @@ const Background = new Lang.Class({
let actor = new Meta.BackgroundActor(); let actor = new Meta.BackgroundActor();
actor.content = content; actor.content = content;
this.actor.add_child(actor);
// The background pattern is the first actor in
// the group, and all images should be above that.
this.actor.insert_child_at_index(actor, index + 1);
this._images[index] = actor; this._images[index] = actor;
this._watchCacheFile(filename); this._watchCacheFile(filename);
@ -426,27 +367,27 @@ const Background = new Lang.Class({
content.brightness = this._brightness; content.brightness = this._brightness;
content.vignette_sharpness = this._vignetteSharpness; content.vignette_sharpness = this._vignetteSharpness;
this._cache.removeImageContent(content);
this._images[index].content = content; this._images[index].content = content;
this._watchCacheFile(filename); this._watchCacheFile(filename);
}, },
_updateAnimationProgress: function() { _updateAnimationProgress: function() {
if (this._images[1]) if (this._images[1]) {
this._images[1].raise_top();
this._images[1].opacity = this._animation.transitionProgress * 255; this._images[1].opacity = this._animation.transitionProgress * 255;
}
this._queueUpdateAnimation(); this._queueAnimationUpdate();
}, },
_updateAnimation: function() { _updateAnimation: function() {
this._updateAnimationTimeoutId = 0; this._animationUpdateTimeoutId = 0;
this._animation.update(this._layoutManager.monitors[this._monitorIndex]); let files = this._animation.getKeyFrameFiles(this._layoutManager.monitors[this._monitorIndex]);
let files = this._animation.keyFrameFiles;
if (files.length == 0) { if (!files) {
this._setLoaded(); this._setLoaded();
this._queueUpdateAnimation(); this._queueAnimationUpdate();
return; return;
} }
@ -465,7 +406,7 @@ const Background = new Lang.Class({
style: this._style, style: this._style,
filename: files[i], filename: files[i],
cancellable: this._cancellable, cancellable: this._cancellable,
onFinished: Lang.bind(this, function(content, i) { onFinished: Lang.bind(this, function(content) {
numPendingImages--; numPendingImages--;
if (!content) { if (!content) {
@ -485,34 +426,27 @@ const Background = new Lang.Class({
this._setLoaded(); this._setLoaded();
this._updateAnimationProgress(); this._updateAnimationProgress();
} }
}, i) })
}); });
} }
}, },
_queueUpdateAnimation: function() { _queueAnimationUpdate: function() {
if (this._updateAnimationTimeoutId != 0) if (this._animationUpdateTimeoutId != 0)
return; return;
if (!this._cancellable || this._cancellable.is_cancelled()) if (!this._cancellable || this._cancellable.is_cancelled())
return; return;
if (!this._animation.transitionDuration) if (!this._animation.duration)
return; return;
let nSteps = 255 / ANIMATION_OPACITY_STEP_INCREMENT;
let timePerStep = (this._animation.transitionDuration * 1000) / nSteps;
let interval = Math.max(ANIMATION_MIN_WAKEUP_INTERVAL * 1000, let interval = Math.max(ANIMATION_MIN_WAKEUP_INTERVAL * 1000,
timePerStep); ANIMATION_OPACITY_STEP_INCREMENT / this._animation.duration);
this._animationUpdateTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
if (interval > GLib.MAXUINT32)
return;
this._updateAnimationTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
interval, interval,
Lang.bind(this, function() { Lang.bind(this, function() {
this._updateAnimationTimeoutId = 0; this._animationUpdateTimeoutId = 0;
this._updateAnimation(); this._updateAnimation();
return false; return false;
})); }));
@ -651,9 +585,9 @@ const Animation = new Lang.Class({
params = Params.parse(params, { filename: null }); params = Params.parse(params, { filename: null });
this.filename = params.filename; this.filename = params.filename;
this.keyFrameFiles = []; this._keyFrames = [];
this.duration = 0.0;
this.transitionProgress = 0.0; this.transitionProgress = 0.0;
this.transitionDuration = 0.0;
this.loaded = false; this.loaded = false;
}, },
@ -665,31 +599,33 @@ const Animation = new Lang.Class({
this._show.load_async(null, this._show.load_async(null,
Lang.bind(this, Lang.bind(this,
function(object, result) { function(object, result) {
this.duration = this._show.get_total_duration();
this.loaded = true; this.loaded = true;
if (callback) if (callback)
callback(); callback();
})); }));
}, },
update: function(monitor) { getKeyFrameFiles: function(monitor) {
this.keyFrameFiles = [];
if (!this._show) if (!this._show)
return; return null;
if (this._show.get_num_slides() < 1) if (this._show.get_num_slides() < 1)
return; return null;
let [progress, duration, isFixed, file1, file2] = this._show.get_current_slide(monitor.width, monitor.height); let [progress, duration, isFixed, file1, file2] = this._show.get_current_slide(monitor.width, monitor.height);
this.transitionDuration = duration;
this.transitionProgress = progress; this.transitionProgress = progress;
let files = [];
if (file1) if (file1)
this.keyFrameFiles.push(file1); files.push(file1);
if (file2) if (file2)
this.keyFrameFiles.push(file2); files.push(file2);
return files;
}, },
}); });
Signals.addSignalMethods(Animation.prototype); Signals.addSignalMethods(Animation.prototype);
@ -712,9 +648,17 @@ const BackgroundManager = new Lang.Class({
this.background = this._createBackground(); this.background = this._createBackground();
this._newBackground = null; this._newBackground = null;
this._loadedSignalId = 0;
this._changedSignalId = 0;
}, },
destroy: function() { destroy: function() {
if (this._loadedSignalId)
this._newBackground.disconnect(this._loadedSignalId);
if (this._changedSignalId)
this.background.disconnect(this._changedSignalId);
if (this._newBackground) { if (this._newBackground) {
this._newBackground.actor.destroy(); this._newBackground.actor.destroy();
this._newBackground = null; this._newBackground = null;
@ -733,24 +677,22 @@ const BackgroundManager = new Lang.Class({
newBackground.saturation = background.saturation; newBackground.saturation = background.saturation;
newBackground.visible = background.visible; newBackground.visible = background.visible;
newBackground.loadedSignalId = newBackground.connect('loaded', let signalId = newBackground.connect('loaded',
Lang.bind(this, function() { Lang.bind(this, function() {
newBackground.disconnect(newBackground.loadedSignalId); newBackground.disconnect(signalId);
newBackground.loadedSignalId = 0;
Tweener.addTween(background.actor, Tweener.addTween(background.actor,
{ opacity: 0, { opacity: 0,
time: FADE_ANIMATION_TIME, time: FADE_ANIMATION_TIME,
transition: 'easeOutQuad', transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() { onComplete: Lang.bind(this, function() {
if (this.background == background) { this.background = newBackground;
this.background = newBackground; this._newBackground = null;
this._newBackground = null; background.actor.destroy();
background.actor.destroy();
}
this.emit('changed'); this.emit('changed');
}) })
}); });
})); }));
this._loadedSignalId = signalId;
this._newBackground = newBackground; this._newBackground = newBackground;
}, },
@ -769,19 +711,12 @@ const BackgroundManager = new Lang.Class({
background.actor.lower_bottom(); background.actor.lower_bottom();
} }
background.changeSignalId = background.connect('changed', Lang.bind(this, function() { let signalId = background.connect('changed', Lang.bind(this, function() {
background.disconnect(background.changeSignalId); background.disconnect(signalId);
background.changeSignalId = 0;
this._updateBackground(background, this._monitorIndex); this._updateBackground(background, this._monitorIndex);
})); }));
background.actor.connect('destroy', Lang.bind(this, function() { this._changedSignalId = signalId;
if (background.changeSignalId)
background.disconnect(background.changeSignalId);
if (background.loadedSignalId)
background.disconnect(background.loadedSignalId);
}));
return background; return background;
}, },

@ -168,12 +168,6 @@ const EmptyEventSource = new Lang.Class({
Name: 'EmptyEventSource', Name: 'EmptyEventSource',
_init: function() { _init: function() {
this.isLoading = false;
this.isDummy = true;
this.hasCalendars = false;
},
destroy: function() {
}, },
requestRange: function(begin, end) { requestRange: function(begin, end) {
@ -197,7 +191,6 @@ const CalendarServerIface = <interface name="org.gnome.Shell.CalendarServer">
<arg type="b" direction="in" /> <arg type="b" direction="in" />
<arg type="a(sssbxxa{sv})" direction="out" /> <arg type="a(sssbxxa{sv})" direction="out" />
</method> </method>
<property name="HasCalendars" type="b" access="read" />
<signal name="Changed" /> <signal name="Changed" />
</interface>; </interface>;
@ -208,7 +201,8 @@ function CalendarServer() {
g_interface_name: CalendarServerInfo.name, g_interface_name: CalendarServerInfo.name,
g_interface_info: CalendarServerInfo, g_interface_info: CalendarServerInfo,
g_name: 'org.gnome.Shell.CalendarServer', g_name: 'org.gnome.Shell.CalendarServer',
g_object_path: '/org/gnome/Shell/CalendarServer' }); g_object_path: '/org/gnome/Shell/CalendarServer',
g_flags: Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES });
} }
function _datesEqual(a, b) { function _datesEqual(a, b) {
@ -235,8 +229,6 @@ const DBusEventSource = new Lang.Class({
_init: function() { _init: function() {
this._resetCache(); this._resetCache();
this.isLoading = false;
this.isDummy = false;
this._initialized = false; this._initialized = false;
this._dbusProxy = new CalendarServer(); this._dbusProxy = new CalendarServer();
@ -257,27 +249,11 @@ const DBusEventSource = new Lang.Class({
this._onNameVanished(); this._onNameVanished();
})); }));
this._dbusProxy.connect('g-properties-changed', Lang.bind(this, function() {
this.emit('notify::has-calendars');
}));
this._initialized = true; this._initialized = true;
this.emit('notify::has-calendars');
this._onNameAppeared(); this._onNameAppeared();
})); }));
}, },
destroy: function() {
this._dbusProxy.run_dispose();
},
get hasCalendars() {
if (this._initialized)
return this._dbusProxy.HasCalendars;
else
return false;
},
_resetCache: function() { _resetCache: function() {
this._events = []; this._events = [];
this._lastRequestBegin = null; this._lastRequestBegin = null;
@ -317,7 +293,6 @@ const DBusEventSource = new Lang.Class({
} }
this._events = newEvents; this._events = newEvents;
this.isLoading = false;
this.emit('changed'); this.emit('changed');
}, },
@ -340,7 +315,6 @@ const DBusEventSource = new Lang.Class({
requestRange: function(begin, end, forceReload) { requestRange: function(begin, end, forceReload) {
if (forceReload || !(_datesEqual(begin, this._lastRequestBegin) && _datesEqual(end, this._lastRequestEnd))) { if (forceReload || !(_datesEqual(begin, this._lastRequestBegin) && _datesEqual(end, this._lastRequestEnd))) {
this.isLoading = true;
this._lastRequestBegin = begin; this._lastRequestBegin = begin;
this._lastRequestEnd = end; this._lastRequestEnd = end;
this._curRequestBegin = begin; this._curRequestBegin = begin;
@ -415,11 +389,19 @@ const Calendar = new Lang.Class({
// @eventSource: is an object implementing the EventSource API, e.g. the // @eventSource: is an object implementing the EventSource API, e.g. the
// requestRange(), getEvents(), hasEvents() methods and the ::changed signal. // requestRange(), getEvents(), hasEvents() methods and the ::changed signal.
setEventSource: function(eventSource) { setEventSource: function(eventSource) {
if (this._eventSource) {
this._eventSource.disconnect(this._eventSourceChangedId);
this._eventSource = null;
}
this._eventSource = eventSource; this._eventSource = eventSource;
this._eventSource.connect('changed', Lang.bind(this, function() {
this._update(false); if (this._eventSource) {
})); this._eventSourceChangedId = this._eventSource.connect('changed', Lang.bind(this, function() {
this._update(true); this._update(false);
}));
this._update(true);
}
}, },
// Sets the calendar to show a specific date // Sets the calendar to show a specific date
@ -556,44 +538,20 @@ const Calendar = new Lang.Class({
children[i].destroy(); children[i].destroy();
// Start at the beginning of the week before the start of the month // Start at the beginning of the week before the start of the month
//
// We want to show always 6 weeks (to keep the calendar menu at the same
// height if there are no events), so we pad it according to the following
// policy:
//
// 1 - If a month has 6 weeks, we place no padding (example: Dec 2012)
// 2 - If a month has 5 weeks and it starts on week start, we pad one week
// before it (example: Apr 2012)
// 3 - If a month has 5 weeks and it starts on any other day, we pad one week
// after it (example: Nov 2012)
// 4 - If a month has 4 weeks, we pad one week before and one after it
// (example: Feb 2010)
//
// Actually computing the number of weeks is complex, but we know that the
// problematic categories (2 and 4) always start on week start, and that
// all months at the end have 6 weeks.
let beginDate = new Date(this._selectedDate); let beginDate = new Date(this._selectedDate);
beginDate.setDate(1); beginDate.setDate(1);
beginDate.setSeconds(0); beginDate.setSeconds(0);
beginDate.setHours(12); beginDate.setHours(12);
let year = beginDate.getYear();
let daysToWeekStart = (7 + beginDate.getDay() - this._weekStart) % 7; let daysToWeekStart = (7 + beginDate.getDay() - this._weekStart) % 7;
let startsOnWeekStart = daysToWeekStart == 0; beginDate.setTime(beginDate.getTime() - daysToWeekStart * MSECS_IN_DAY);
let weekPadding = startsOnWeekStart ? 7 : 0;
beginDate.setTime(beginDate.getTime() - (weekPadding + daysToWeekStart) * MSECS_IN_DAY);
let iter = new Date(beginDate); let iter = new Date(beginDate);
let row = 2; let row = 2;
// nRows here means 6 weeks + one header + one navbar while (true) {
let nRows = 8;
while (row < 8) {
let button = new St.Button({ label: iter.getDate().toString() }); let button = new St.Button({ label: iter.getDate().toString() });
let rtl = button.get_text_direction() == Clutter.TextDirection.RTL; let rtl = button.get_text_direction() == Clutter.TextDirection.RTL;
if (this._eventSource.isDummy) if (!this._eventSource)
button.reactive = false; button.reactive = false;
let iterStr = iter.toUTCString(); let iterStr = iter.toUTCString();
@ -602,9 +560,8 @@ const Calendar = new Lang.Class({
this.setDate(newlySelectedDate, false); this.setDate(newlySelectedDate, false);
})); }));
let hasEvents = this._eventSource.hasEvents(iter); let hasEvents = this._eventSource && this._eventSource.hasEvents(iter);
let styleClass = 'calendar-day-base calendar-day'; let styleClass = 'calendar-day-base calendar-day';
if (_isWorkDay(iter)) if (_isWorkDay(iter))
styleClass += ' calendar-work-day' styleClass += ' calendar-work-day'
else else
@ -644,13 +601,17 @@ const Calendar = new Lang.Class({
} }
iter.setTime(iter.getTime() + MSECS_IN_DAY); iter.setTime(iter.getTime() + MSECS_IN_DAY);
if (iter.getDay() == this._weekStart) {
if (iter.getDay() == this._weekStart) // We stop on the first "first day of the week" after the month we are displaying
if (iter.getMonth() > this._selectedDate.getMonth() || iter.getYear() > this._selectedDate.getYear())
break;
row++; row++;
}
} }
// Signal to the event source that we are interested in events // Signal to the event source that we are interested in events
// only from this date range // only from this date range
this._eventSource.requestRange(beginDate, iter, forceReload); if (this._eventSource)
this._eventSource.requestRange(beginDate, iter, forceReload);
} }
}); });
@ -668,8 +629,16 @@ const EventsList = new Lang.Class({
}, },
setEventSource: function(eventSource) { setEventSource: function(eventSource) {
if (this._eventSource) {
this._eventSource.disconnect(this._eventSourceChangedId);
this._eventSource = null;
}
this._eventSource = eventSource; this._eventSource = eventSource;
this._eventSource.connect('changed', Lang.bind(this, this._update));
if (this._eventSource) {
this._eventSourceChangedId = this._eventSource.connect('changed', Lang.bind(this, this._update));
}
}, },
_addEvent: function(dayNameBox, timeBox, eventTitleBox, includeDayName, day, time, desc) { _addEvent: function(dayNameBox, timeBox, eventTitleBox, includeDayName, day, time, desc) {
@ -686,6 +655,9 @@ const EventsList = new Lang.Class({
}, },
_addPeriod: function(header, begin, end, includeDayName, showNothingScheduled) { _addPeriod: function(header, begin, end, includeDayName, showNothingScheduled) {
if (!this._eventSource)
return;
let events = this._eventSource.getEvents(begin, end); let events = this._eventSource.getEvents(begin, end);
let clockFormat = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);; let clockFormat = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);;
@ -782,9 +754,6 @@ const EventsList = new Lang.Class({
}, },
_update: function() { _update: function() {
if (this._eventSource.isLoading)
return;
let today = new Date(); let today = new Date();
if (_sameDay (this._date, today)) { if (_sameDay (this._date, today)) {
this._showToday(); this._showToday();

@ -292,7 +292,6 @@ const AutorunResidentSource = new Lang.Class({
_init: function(manager) { _init: function(manager) {
this.parent(_("Removable Devices"), 'media-removable'); this.parent(_("Removable Devices"), 'media-removable');
this.resident = true;
this._mounts = []; this._mounts = [];

@ -222,7 +222,7 @@ const KeyringPrompter = new Lang.Class({
enable: function() { enable: function() {
this._prompter.register(Gio.DBus.session); this._prompter.register(Gio.DBus.session);
this._dbusId = Gio.DBus.session.own_name('org.gnome.keyring.SystemPrompter', this._dbusId = Gio.DBus.session.own_name('org.gnome.keyring.SystemPrompter',
Gio.BusNameOwnerFlags.ALLOW_REPLACEMENT, null, null); Gio.BusNameOwnerFlags.REPLACE, null, null);
}, },
disable: function() { disable: function() {

@ -103,11 +103,6 @@ const CtrlAltTabManager = new Lang.Class({
else else
icon = textureCache.bind_pixbuf_property(windows[i], 'icon'); icon = textureCache.bind_pixbuf_property(windows[i], 'icon');
items.push({ name: windows[i].title, items.push({ name: windows[i].title,
proxy: windows[i].get_compositor_private(),
focusCallback: Lang.bind(windows[i],
function(timestamp) {
Main.activateWindow(this, timestamp);
}),
iconActor: icon, iconActor: icon,
sortGroup: SortGroup.MIDDLE }); sortGroup: SortGroup.MIDDLE });
} }

@ -161,10 +161,8 @@ const DateMenuButton = new Lang.Class({
this._openClocksItem.actor.visible = app !== null; this._openClocksItem.actor.visible = app !== null;
}, },
_updateEventsVisibility: function() { _setEventsVisibility: function(visible) {
let visible = this._eventSource.hasCalendars;
this._openCalendarItem.actor.visible = visible; this._openCalendarItem.actor.visible = visible;
this._openClocksItem.actor.visible = visible;
this._separator.visible = visible; this._separator.visible = visible;
if (visible) { if (visible) {
let alignment = 0.25; let alignment = 0.25;
@ -179,16 +177,8 @@ const DateMenuButton = new Lang.Class({
}, },
_setEventSource: function(eventSource) { _setEventSource: function(eventSource) {
if (this._eventSource)
this._eventSource.destroy();
this._calendar.setEventSource(eventSource); this._calendar.setEventSource(eventSource);
this._eventList.setEventSource(eventSource); this._eventList.setEventSource(eventSource);
this._eventSource = eventSource;
this._eventSource.connect('notify::has-calendars', Lang.bind(this, function() {
this._updateEventsVisibility();
}));
}, },
_sessionUpdated: function() { _sessionUpdated: function() {
@ -197,10 +187,10 @@ const DateMenuButton = new Lang.Class({
if (showEvents) { if (showEvents) {
eventSource = new Calendar.DBusEventSource(); eventSource = new Calendar.DBusEventSource();
} else { } else {
eventSource = new Calendar.EmptyEventSource(); eventSource = null;
} }
this._setEventSource(eventSource); this._setEventSource(eventSource);
this._updateEventsVisibility(); this._setEventsVisibility(showEvents);
// This needs to be handled manually, as the code to // This needs to be handled manually, as the code to
// autohide separators doesn't work across the vbox // autohide separators doesn't work across the vbox
@ -221,8 +211,6 @@ const DateMenuButton = new Lang.Class({
this.menu.close(); this.menu.close();
let app = Gio.AppInfo.get_default_for_type('text/calendar', false); let app = Gio.AppInfo.get_default_for_type('text/calendar', false);
if (app.get_id() == 'evolution')
app = Gio.DesktopAppInfo.new('evolution-calendar');
app.launch([], global.create_app_launch_context()); app.launch([], global.create_app_launch_context());
}, },

@ -85,13 +85,11 @@ const _Draggable = new Lang.Class({
this.actor.connect('destroy', Lang.bind(this, function() { this.actor.connect('destroy', Lang.bind(this, function() {
this._actorDestroyed = true; this._actorDestroyed = true;
// If the drag actor is destroyed and we were going to fix // If the drag actor is destroyed and we were going to fix
// up its hover state, fix up the parent hover state instead // up its hover state, fix up the parent hover state instead
if (this.actor == this._firstLeaveActor) if (this.actor == this._firstLeaveActor)
this._firstLeaveActor = this._dragOrigParent; this._firstLeaveActor = this._dragOrigParent;
if (this._dragInProgress)
if (this._dragInProgress && this._dragCancellable)
this._cancelDrag(global.get_current_time()); this._cancelDrag(global.get_current_time());
this.disconnectAll(); this.disconnectAll();
})); }));
@ -104,7 +102,6 @@ const _Draggable = new Lang.Class({
this._buttonDown = false; // The mouse button has been pressed and has not yet been released. this._buttonDown = false; // The mouse button has been pressed and has not yet been released.
this._dragInProgress = false; // The drag has been started, and has not been dropped or cancelled yet. this._dragInProgress = false; // The drag has been started, and has not been dropped or cancelled yet.
this._animationInProgress = false; // The drag is over and the item is in the process of animating to its original position (snapping back or reverting). this._animationInProgress = false; // The drag is over and the item is in the process of animating to its original position (snapping back or reverting).
this._dragCancellable = true;
// During the drag, we eat enter/leave events so that actors don't prelight. // During the drag, we eat enter/leave events so that actors don't prelight.
// But we remember the actors that we first left/last entered so we can // But we remember the actors that we first left/last entered so we can
@ -442,11 +439,6 @@ const _Draggable = new Lang.Class({
} }
} }
// At this point it is too late to cancel a drag by destroying
// the actor, the fate of which is decided by acceptDrop and its
// side-effects
this._dragCancellable = false;
while (target) { while (target) {
if (target._delegate && target._delegate.acceptDrop) { if (target._delegate && target._delegate.acceptDrop) {
let [r, targX, targY] = target.transform_stage_point(dropX, dropY); let [r, targX, targY] = target.transform_stage_point(dropX, dropY);
@ -455,6 +447,8 @@ const _Draggable = new Lang.Class({
targX, targX,
targY, targY,
event.get_time())) { event.get_time())) {
if (this._actorDestroyed)
return true;
// If it accepted the drop without taking the actor, // If it accepted the drop without taking the actor,
// handle it ourselves. // handle it ourselves.
if (this._dragActor.get_parent() == Main.uiGroup) { if (this._dragActor.get_parent() == Main.uiGroup) {

@ -248,7 +248,7 @@ const GrabHelper = new Lang.Class({
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED); global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
} }
global.screen.focus_default_window(global.display.get_current_time_roundtrip()); global.screen.focus_default_window(global.get_current_time());
}, },
// ignoreRelease: // ignoreRelease:
@ -308,8 +308,6 @@ const GrabHelper = new Lang.Class({
if (!this.grabbed && this._capturedEventId > 0) { if (!this.grabbed && this._capturedEventId > 0) {
global.stage.disconnect(this._capturedEventId); global.stage.disconnect(this._capturedEventId);
this._capturedEventId = 0; this._capturedEventId = 0;
this._ignoreRelease = false;
} }
if (hadFocus) { if (hadFocus) {
@ -355,7 +353,6 @@ const GrabHelper = new Lang.Class({
this._ignoreRelease = true; this._ignoreRelease = true;
let i = this._actorInGrabStack(event.get_source()) + 1; let i = this._actorInGrabStack(event.get_source()) + 1;
this.ungrab({ actor: this._grabStack[i].actor, isUser: true }); this.ungrab({ actor: this._grabStack[i].actor, isUser: true });
return true;
} }
return this._modalCount > 0; return this._modalCount > 0;

@ -18,6 +18,7 @@ const Main = imports.ui.main;
const Params = imports.misc.params; const Params = imports.misc.params;
const Tweener = imports.ui.tweener; const Tweener = imports.ui.tweener;
const HOT_CORNER_ACTIVATION_TIMEOUT = 0.5;
const STARTUP_ANIMATION_TIME = 0.5; const STARTUP_ANIMATION_TIME = 0.5;
const KEYBOARD_ANIMATION_TIME = 0.15; const KEYBOARD_ANIMATION_TIME = 0.15;
const BACKGROUND_FADE_ANIMATION_TIME = 1.0; const BACKGROUND_FADE_ANIMATION_TIME = 1.0;
@ -28,9 +29,6 @@ const DEFAULT_BACKGROUND_COLOR = Clutter.Color.from_pixel(0x2e3436ff);
const MESSAGE_TRAY_PRESSURE_THRESHOLD = 250; // pixels const MESSAGE_TRAY_PRESSURE_THRESHOLD = 250; // pixels
const MESSAGE_TRAY_PRESSURE_TIMEOUT = 1000; // ms const MESSAGE_TRAY_PRESSURE_TIMEOUT = 1000; // ms
const HOT_CORNER_PRESSURE_THRESHOLD = 100; // pixels
const HOT_CORNER_PRESSURE_TIMEOUT = 1000; // ms
function isPopupMetaWindow(actor) { function isPopupMetaWindow(actor) {
switch(actor.meta_window.get_window_type()) { switch(actor.meta_window.get_window_type()) {
case Meta.WindowType.DROPDOWN_MENU: case Meta.WindowType.DROPDOWN_MENU:
@ -132,9 +130,9 @@ const LayoutManager = new Lang.Class({
this.monitors = []; this.monitors = [];
this.primaryMonitor = null; this.primaryMonitor = null;
this.primaryIndex = -1; this.primaryIndex = -1;
this.hotCorners = [];
this._keyboardIndex = -1; this._keyboardIndex = -1;
this._hotCorners = [];
this._leftPanelBarrier = null;
this._rightPanelBarrier = null; this._rightPanelBarrier = null;
this._trayBarrier = null; this._trayBarrier = null;
@ -142,7 +140,6 @@ const LayoutManager = new Lang.Class({
this._updateRegionIdle = 0; this._updateRegionIdle = 0;
this._trackedActors = []; this._trackedActors = [];
this._topActors = [];
this._isPopupWindowVisible = false; this._isPopupWindowVisible = false;
this._startingUp = true; this._startingUp = true;
@ -194,7 +191,6 @@ const LayoutManager = new Lang.Class({
this.trayBox = new St.Widget({ name: 'trayBox', this.trayBox = new St.Widget({ name: 'trayBox',
layout_manager: new Clutter.BinLayout() }); layout_manager: new Clutter.BinLayout() });
this.addChrome(this.trayBox); this.addChrome(this.trayBox);
this._setupTrayPressure();
this.keyboardBox = new St.BoxLayout({ name: 'keyboardBox', this.keyboardBox = new St.BoxLayout({ name: 'keyboardBox',
reactive: true, reactive: true,
@ -222,8 +218,6 @@ const LayoutManager = new Lang.Class({
Lang.bind(this, this._windowsRestacked)); Lang.bind(this, this._windowsRestacked));
global.screen.connect('monitors-changed', global.screen.connect('monitors-changed',
Lang.bind(this, this._monitorsChanged)); Lang.bind(this, this._monitorsChanged));
global.screen.connect('in-fullscreen-changed',
Lang.bind(this, this._updateFullscreen));
this._monitorsChanged(); this._monitorsChanged();
}, },
@ -234,8 +228,6 @@ const LayoutManager = new Lang.Class({
Main.overview.connect('showing', Lang.bind(this, this._overviewShowing)); Main.overview.connect('showing', Lang.bind(this, this._overviewShowing));
Main.overview.connect('hidden', Lang.bind(this, this._overviewHidden)); Main.overview.connect('hidden', Lang.bind(this, this._overviewHidden));
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated)); Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
this._prepareStartupAnimation();
}, },
_overviewShowing: function() { _overviewShowing: function() {
@ -283,58 +275,56 @@ const LayoutManager = new Lang.Class({
_updateHotCorners: function() { _updateHotCorners: function() {
// destroy old hot corners // destroy old hot corners
for (let i = 0; i < this.hotCorners.length; i++) for (let i = 0; i < this._hotCorners.length; i++)
this.hotCorners[i].destroy(); this._hotCorners[i].destroy();
this.hotCorners = []; this._hotCorners = [];
let size = this.panelBox.height;
// build new hot corners // build new hot corners
for (let i = 0; i < this.monitors.length; i++) { for (let i = 0; i < this.monitors.length; i++) {
if (i == this.primaryIndex)
continue;
let monitor = this.monitors[i]; let monitor = this.monitors[i];
let cornerX = this._rtl ? monitor.x + monitor.width : monitor.x; let cornerX = this._rtl ? monitor.x + monitor.width : monitor.x;
let cornerY = monitor.y; let cornerY = monitor.y;
if (i != this.primaryIndex) { let haveTopLeftCorner = true;
let haveTopLeftCorner = true;
// Check if we have a top left (right for RTL) corner. // Check if we have a top left (right for RTL) corner.
// I.e. if there is no monitor directly above or to the left(right) // I.e. if there is no monitor directly above or to the left(right)
let besideX = this._rtl ? monitor.x + 1 : cornerX - 1; let besideX = this._rtl ? monitor.x + 1 : cornerX - 1;
let besideY = cornerY; let besideY = cornerY;
let aboveX = cornerX; let aboveX = cornerX;
let aboveY = cornerY - 1; let aboveY = cornerY - 1;
for (let j = 0; j < this.monitors.length; j++) { for (let j = 0; j < this.monitors.length; j++) {
if (i == j) if (i == j)
continue;
let otherMonitor = this.monitors[j];
if (besideX >= otherMonitor.x &&
besideX < otherMonitor.x + otherMonitor.width &&
besideY >= otherMonitor.y &&
besideY < otherMonitor.y + otherMonitor.height) {
haveTopLeftCorner = false;
break;
}
if (aboveX >= otherMonitor.x &&
aboveX < otherMonitor.x + otherMonitor.width &&
aboveY >= otherMonitor.y &&
aboveY < otherMonitor.y + otherMonitor.height) {
haveTopLeftCorner = false;
break;
}
}
if (!haveTopLeftCorner)
continue; continue;
let otherMonitor = this.monitors[j];
if (besideX >= otherMonitor.x &&
besideX < otherMonitor.x + otherMonitor.width &&
besideY >= otherMonitor.y &&
besideY < otherMonitor.y + otherMonitor.height) {
haveTopLeftCorner = false;
break;
}
if (aboveX >= otherMonitor.x &&
aboveX < otherMonitor.x + otherMonitor.width &&
aboveY >= otherMonitor.y &&
aboveY < otherMonitor.y + otherMonitor.height) {
haveTopLeftCorner = false;
break;
}
} }
let corner = new HotCorner(this, monitor, cornerX, cornerY); if (!haveTopLeftCorner)
corner.setBarrierSize(size); continue;
this.hotCorners.push(corner);
}
this.emit('hot-corners-changed'); let corner = new HotCorner(this);
this._hotCorners.push(corner);
corner.actor.set_position(cornerX, cornerY);
this.addChrome(corner.actor);
}
}, },
_createBackground: function(monitorIndex) { _createBackground: function(monitorIndex) {
@ -404,15 +394,16 @@ const LayoutManager = new Lang.Class({
}, },
_panelBoxChanged: function() { _panelBoxChanged: function() {
this._updatePanelBarrier(); this.emit('panel-box-changed');
this._updatePanelBarriers();
let size = this.panelBox.height;
this.hotCorners.forEach(function(corner) {
corner.setBarrierSize(size);
});
}, },
_updatePanelBarrier: function() { _updatePanelBarriers: function() {
if (this._leftPanelBarrier) {
this._leftPanelBarrier.destroy();
this._leftPanelBarrier = null;
}
if (this._rightPanelBarrier) { if (this._rightPanelBarrier) {
this._rightPanelBarrier.destroy(); this._rightPanelBarrier.destroy();
this._rightPanelBarrier = null; this._rightPanelBarrier = null;
@ -421,6 +412,10 @@ const LayoutManager = new Lang.Class({
if (this.panelBox.height) { if (this.panelBox.height) {
let primary = this.primaryMonitor; let primary = this.primaryMonitor;
this._leftPanelBarrier = new Meta.Barrier({ display: global.display,
x1: primary.x, y1: primary.y,
x2: primary.x, y2: primary.y + this.panelBox.height,
directions: Meta.BarrierDirection.POSITIVE_X });
this._rightPanelBarrier = new Meta.Barrier({ display: global.display, this._rightPanelBarrier = new Meta.Barrier({ display: global.display,
x1: primary.x + primary.width, y1: primary.y, x1: primary.x + primary.width, y1: primary.y,
x2: primary.x + primary.width, y2: primary.y + this.panelBox.height, x2: primary.x + primary.width, y2: primary.y + this.panelBox.height,
@ -428,44 +423,28 @@ const LayoutManager = new Lang.Class({
} }
}, },
_setupTrayPressure: function() {
this._trayPressure = new PressureBarrier(MESSAGE_TRAY_PRESSURE_THRESHOLD,
MESSAGE_TRAY_PRESSURE_TIMEOUT,
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW);
this._trayPressure.setEventFilter(this._trayBarrierEventFilter);
this._trayPressure.connect('trigger', function(barrier) {
if (Main.layoutManager.bottomMonitor.inFullscreen)
return;
Main.messageTray.openTray();
});
},
_updateTrayBarrier: function() { _updateTrayBarrier: function() {
let monitor = this.bottomMonitor; let monitor = this.bottomMonitor;
if (this._trayBarrier) { if (this._trayBarrier) {
this._trayPressure.removeBarrier(this._trayBarrier);
this._trayBarrier.destroy(); this._trayBarrier.destroy();
this._trayBarrier = null; this._trayBarrier = null;
} }
if (this._trayPressure) {
this._trayPressure.destroy();
this._trayPressure = null;
}
this._trayBarrier = new Meta.Barrier({ display: global.display, this._trayBarrier = new Meta.Barrier({ display: global.display,
x1: monitor.x, x2: monitor.x + monitor.width, x1: monitor.x, x2: monitor.x + monitor.width,
y1: monitor.y + monitor.height, y2: monitor.y + monitor.height, y1: monitor.y + monitor.height, y2: monitor.y + monitor.height,
directions: Meta.BarrierDirection.NEGATIVE_Y }); directions: Meta.BarrierDirection.NEGATIVE_Y });
this._trayPressure.addBarrier(this._trayBarrier);
},
_trayBarrierEventFilter: function(event) { this._trayPressure = new PressureBarrier(this._trayBarrier, MESSAGE_TRAY_PRESSURE_THRESHOLD, MESSAGE_TRAY_PRESSURE_TIMEOUT);
// Throw out all events where the pointer was grabbed by another this._trayPressure.connect('trigger', function(barrier) {
// client, as the client that grabbed the pointer expects to have Main.messageTray.openTray();
// complete control over it });
if (event.grabbed && Main.modalCount == 0)
return true;
return false;
}, },
_monitorsChanged: function() { _monitorsChanged: function() {
@ -555,7 +534,7 @@ const LayoutManager = new Lang.Class({
// MetaBackgroundActor inside global.window_group covers the entirety of the // MetaBackgroundActor inside global.window_group covers the entirety of the
// 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() {
// Set ourselves to FULLSCREEN input mode while the animation is running // Set ourselves to FULLSCREEN input mode while the animation is running
// so events don't get delivered to X11 windows (which are distorted by the animation) // so events don't get delivered to X11 windows (which are distorted by the animation)
global.stage_input_mode = Shell.StageInputMode.FULLSCREEN; global.stage_input_mode = Shell.StageInputMode.FULLSCREEN;
@ -593,25 +572,25 @@ const LayoutManager = new Lang.Class({
Lang.bind(this, function() { Lang.bind(this, function() {
this._systemBackground.disconnect(signalId); this._systemBackground.disconnect(signalId);
this._systemBackground.actor.show(); this._systemBackground.actor.show();
global.stage.show();
this.emit('startup-prepared');
// We're mostly prepared for the startup animation // We're mostly prepared for the startup animation
// now, but since a lot is going on asynchronously // now, but since a lot is going on asynchronously
// during startup, let's defer the startup animation // during startup, let's defer emission of the
// until the event loop is uncontended and idle. // startup-prepared signal until the event loop is
// This helps to prevent us from running the animation // uncontended and idle. This helps to prevent us
// when the system is bogged down // from running the animation when the system is
// bogged down
GLib.idle_add(GLib.PRIORITY_LOW, GLib.idle_add(GLib.PRIORITY_LOW,
Lang.bind(this, function() { Lang.bind(this, function() {
this._startupAnimation(); this.emit('startup-prepared');
return false; return false;
})); }));
})); }));
}, },
_startupAnimation: function() { startupAnimation: function() {
global.stage.show();
if (Main.sessionMode.isGreeter) if (Main.sessionMode.isGreeter)
this._startupAnimationGreeter(); this._startupAnimationGreeter();
else else
@ -657,9 +636,9 @@ const LayoutManager = new Lang.Class({
if (!Main.sessionMode.isGreeter) if (!Main.sessionMode.isGreeter)
this._createSecondaryBackgrounds(); this._createSecondaryBackgrounds();
this._queueUpdateRegions(); this.emit('panel-box-changed');
this.emit('startup-complete'); this._queueUpdateRegions();
}, },
showKeyboard: function () { showKeyboard: function () {
@ -835,11 +814,6 @@ const LayoutManager = new Lang.Class({
}, },
_updateVisibility: function() { _updateVisibility: function() {
let windowsVisible = Main.sessionMode.hasWindows && !this._inOverview;
global.window_group.visible = windowsVisible;
global.top_window_group.visible = windowsVisible;
for (let i = 0; i < this._trackedActors.length; i++) { for (let i = 0; i < this._trackedActors.length; i++) {
let actorData = this._trackedActors[i], visible; let actorData = this._trackedActors[i], visible;
if (!actorData.trackFullscreen) if (!actorData.trackFullscreen)
@ -847,7 +821,7 @@ const LayoutManager = new Lang.Class({
if (!actorData.isToplevel) if (!actorData.isToplevel)
continue; continue;
if (!windowsVisible) if (this._inOverview || !Main.sessionMode.hasWindows)
visible = true; visible = true;
else if (this.findMonitorForActor(actorData.actor).inFullscreen) else if (this.findMonitorForActor(actorData.actor).inFullscreen)
visible = false; visible = false;
@ -897,25 +871,95 @@ const LayoutManager = new Lang.Class({
}, },
_updateFullscreen: function() { _updateFullscreen: function() {
let windows = this._getWindowActorsForWorkspace(global.screen.get_active_workspace());
// Reset all monitors to not fullscreen
for (let i = 0; i < this.monitors.length; i++) for (let i = 0; i < this.monitors.length; i++)
this.monitors[i].inFullscreen = global.screen.get_monitor_in_fullscreen (i); this.monitors[i].inFullscreen = false;
this._updateVisibility(); // Ordinary chrome should be visible unless there is a window
this._queueUpdateRegions(); // with layer FULLSCREEN, or a window with layer
// OVERRIDE_REDIRECT that covers the whole screen.
// ('override_redirect' is not actually a layer above all
// other windows, but this seems to be how mutter treats it
// currently...) If we wanted to be extra clever, we could
// figure out when an OVERRIDE_REDIRECT window was trying to
// partially overlap us, and then adjust the input region and
// our clip region accordingly...
this.emit('fullscreen-changed'); // @windows is sorted bottom to top.
for (let i = windows.length - 1; i > -1; i--) {
let window = windows[i];
let metaWindow = window.meta_window;
let layer = metaWindow.get_layer();
// Skip minimized windows
if (!metaWindow.showing_on_its_workspace())
continue;
if (layer == Meta.StackLayer.FULLSCREEN ||
(layer == Meta.StackLayer.OVERRIDE_REDIRECT && metaWindow.is_monitor_sized())) {
if (metaWindow.is_screen_sized()) {
for (let i = 0; i < this.monitors.length; i++)
this.monitors[i].inFullscreen = true;
} else {
let monitors = metaWindow.get_all_monitors();
for (let i = 0; i < monitors.length; i++) {
let index = monitors[i];
this.monitors[index].inFullscreen = true;
}
}
}
}
}, },
_windowsRestacked: function() { _windowsRestacked: function() {
let changed = false; let wasInFullscreen = [];
for (let i = 0; i < this.monitors.length; i++)
wasInFullscreen[i] = this.monitors[i].inFullscreen;
if (this._isPopupWindowVisible != global.top_window_group.get_children().some(isPopupMetaWindow)) let primaryWasInFullscreen = this.primaryMonitor.inFullscreen;
this._updateFullscreen();
let changed = false;
for (let i = 0; i < wasInFullscreen.length; i++) {
if (wasInFullscreen[i] != this.monitors[i].inFullscreen) {
changed = true;
break;
}
}
if (!changed && (this._isPopupWindowVisible != global.top_window_group.get_children().some(isPopupMetaWindow)))
changed = true; changed = true;
if (changed) { if (changed) {
this._updateVisibility(); this._updateVisibility();
this._queueUpdateRegions(); this._queueUpdateRegions();
} }
if (primaryWasInFullscreen != this.primaryMonitor.inFullscreen) {
let windows = this._getWindowActorsForWorkspace(global.screen.get_active_workspace());
for (let i = 0; i < windows.length; i++) {
let window = windows[i];
let metaWindow = window.meta_window;
// Skip minimized windows
if (!metaWindow.showing_on_its_workspace())
continue;
// Skip windows that aren't on the primary monitor
if (!metaWindow.is_on_primary_monitor())
continue;
// Minimize monitor sized windows that are not focused
if (metaWindow.is_monitor_sized() &&
!metaWindow.appears_focused)
metaWindow.minimize();
}
this.emit('primary-fullscreen-changed', this.primaryMonitor.inFullscreen);
}
}, },
_updateRegions: function() { _updateRegions: function() {
@ -1045,25 +1089,54 @@ Signals.addSignalMethods(LayoutManager.prototype);
const HotCorner = new Lang.Class({ const HotCorner = new Lang.Class({
Name: 'HotCorner', Name: 'HotCorner',
_init : function(layoutManager, monitor, x, y) { _init : function(layoutManager) {
// We use this flag to mark the case where the user has entered the // We use this flag to mark the case where the user has entered the
// hot corner and has not left both the hot corner and a surrounding // hot corner and has not left both the hot corner and a surrounding
// guard area (the "environs"). This avoids triggering the hot corner // guard area (the "environs"). This avoids triggering the hot corner
// multiple times due to an accidental jitter. // multiple times due to an accidental jitter.
this._entered = false; this._entered = false;
this._monitor = monitor; this.actor = new Clutter.Actor({ name: 'hot-corner-environs',
width: 3,
height: 3,
reactive: true });
this._x = x; this._corner = new Clutter.Rectangle({ name: 'hot-corner',
this._y = y; width: 1,
height: 1,
opacity: 0,
reactive: true });
this._corner._delegate = this;
this._setupFallbackCornerIfNeeded(layoutManager); this.actor.add_child(this._corner);
this._pressureBarrier = new PressureBarrier(HOT_CORNER_PRESSURE_THRESHOLD, if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) {
HOT_CORNER_PRESSURE_TIMEOUT, this._corner.set_position(this.actor.width - this._corner.width, 0);
Shell.KeyBindingMode.NORMAL | this.actor.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
Shell.KeyBindingMode.OVERVIEW); } else {
this._pressureBarrier.connect('trigger', Lang.bind(this, this._toggleOverview)); this._corner.set_position(0, 0);
}
this._activationTime = 0;
this.actor.connect('leave-event',
Lang.bind(this, this._onEnvironsLeft));
// Clicking on the hot corner environs should result in the
// same behavior as clicking on the hot corner.
this.actor.connect('button-release-event',
Lang.bind(this, this._onCornerClicked));
// In addition to being triggered by the mouse enter event,
// the hot corner can be triggered by clicking on it. This is
// useful if the user wants to undo the effect of triggering
// the hot corner once in the hot corner.
this._corner.connect('enter-event',
Lang.bind(this, this._onCornerEntered));
this._corner.connect('button-release-event',
Lang.bind(this, this._onCornerClicked));
this._corner.connect('leave-event',
Lang.bind(this, this._onCornerLeft));
// Cache the three ripples instead of dynamically creating and destroying them. // Cache the three ripples instead of dynamically creating and destroying them.
this._ripple1 = new St.BoxLayout({ style_class: 'ripple-box', opacity: 0, visible: false }); this._ripple1 = new St.BoxLayout({ style_class: 'ripple-box', opacity: 0, visible: false });
@ -1075,74 +1148,8 @@ const HotCorner = new Lang.Class({
layoutManager.uiGroup.add_actor(this._ripple3); layoutManager.uiGroup.add_actor(this._ripple3);
}, },
setBarrierSize: function(size) {
if (this._verticalBarrier) {
this._pressureBarrier.removeBarrier(this._verticalBarrier);
this._verticalBarrier.destroy();
this._verticalBarrier = null;
}
if (this._horizontalBarrier) {
this._pressureBarrier.removeBarrier(this._horizontalBarrier);
this._horizontalBarrier.destroy();
this._horizontalBarrier = null;
}
if (size > 0) {
this._verticalBarrier = new Meta.Barrier({ display: global.display,
x1: this._x, x2: this._x, y1: this._y, y2: this._y + size,
directions: Meta.BarrierDirection.POSITIVE_X });
this._horizontalBarrier = new Meta.Barrier({ display: global.display,
x1: this._x, x2: this._x + size, y1: this._y, y2: this._y,
directions: Meta.BarrierDirection.POSITIVE_Y });
this._pressureBarrier.addBarrier(this._verticalBarrier);
this._pressureBarrier.addBarrier(this._horizontalBarrier);
}
},
_setupFallbackCornerIfNeeded: function(layoutManager) {
if (!global.display.supports_extended_barriers()) {
this.actor = new Clutter.Actor({ name: 'hot-corner-environs',
x: this._x, y: this._y,
width: 3,
height: 3,
reactive: true });
this._corner = new Clutter.Rectangle({ name: 'hot-corner',
width: 1,
height: 1,
opacity: 0,
reactive: true });
this._corner._delegate = this;
this.actor.add_child(this._corner);
layoutManager.addChrome(this.actor);
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) {
this._corner.set_position(this.actor.width - this._corner.width, 0);
this.actor.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
} else {
this._corner.set_position(0, 0);
}
this.actor.connect('leave-event',
Lang.bind(this, this._onEnvironsLeft));
this._corner.connect('enter-event',
Lang.bind(this, this._onCornerEntered));
this._corner.connect('leave-event',
Lang.bind(this, this._onCornerLeft));
}
},
destroy: function() { destroy: function() {
this.setBarrierSize(0); this.actor.destroy();
this._pressureBarrier.destroy();
this._pressureBarrier = null;
if (this.actor)
this.actor.destroy();
}, },
_animRipple : function(ripple, delay, time, startScale, startOpacity, finalScale) { _animRipple : function(ripple, delay, time, startScale, startOpacity, finalScale) {
@ -1162,8 +1169,9 @@ const HotCorner = new Lang.Class({
ripple.opacity = 255 * Math.sqrt(startOpacity); ripple.opacity = 255 * Math.sqrt(startOpacity);
ripple.scale_x = ripple.scale_y = startScale; ripple.scale_x = ripple.scale_y = startScale;
ripple.x = this._x; let [x, y] = this._corner.get_transformed_position();
ripple.y = this._y; ripple.x = x;
ripple.y = y;
Tweener.addTween(ripple, { _opacity: 0, Tweener.addTween(ripple, { _opacity: 0,
scale_x: finalScale, scale_x: finalScale,
@ -1175,7 +1183,7 @@ const HotCorner = new Lang.Class({
onComplete: function() { ripple.visible = false; } }); onComplete: function() { ripple.visible = false; } });
}, },
_rippleAnimation: function() { rippleAnimation: function() {
// Show three concentric ripples expanding outwards; the exact // Show three concentric ripples expanding outwards; the exact
// parameters were found by trial and error, so don't look // parameters were found by trial and error, so don't look
// for them to make perfect sense mathematically // for them to make perfect sense mathematically
@ -1186,21 +1194,14 @@ const HotCorner = new Lang.Class({
this._animRipple(this._ripple3, 0.35, 1.0, 0.0, 0.3, 1); this._animRipple(this._ripple3, 0.35, 1.0, 0.0, 0.3, 1);
}, },
_toggleOverview: function() {
if (this._monitor.inFullscreen)
return;
if (Main.overview.shouldToggleByCornerOrButton()) {
this._rippleAnimation();
Main.overview.toggle();
}
},
handleDragOver: function(source, actor, x, y, time) { handleDragOver: function(source, actor, x, y, time) {
if (source != Main.xdndHandler) if (source != Main.xdndHandler)
return DND.DragMotionResult.CONTINUE; return DND.DragMotionResult.CONTINUE;
this._toggleOverview(); if (!Main.overview.visible && !Main.overview.animationInProgress) {
this.rippleAnimation();
Main.overview.showTemporarily();
}
return DND.DragMotionResult.CONTINUE; return DND.DragMotionResult.CONTINUE;
}, },
@ -1208,11 +1209,22 @@ const HotCorner = new Lang.Class({
_onCornerEntered : function() { _onCornerEntered : function() {
if (!this._entered) { if (!this._entered) {
this._entered = true; this._entered = true;
this._toggleOverview(); if (!Main.overview.animationInProgress) {
this._activationTime = Date.now() / 1000;
this.rippleAnimation();
Main.overview.toggle();
}
} }
return false; return false;
}, },
_onCornerClicked : function() {
if (this.shouldToggleOverviewOnClick())
Main.overview.toggle();
return true;
},
_onCornerLeft : function(actor, event) { _onCornerLeft : function(actor, event) {
if (event.get_related() != this.actor) if (event.get_related() != this.actor)
this._entered = false; this._entered = false;
@ -1224,47 +1236,42 @@ const HotCorner = new Lang.Class({
if (event.get_related() != this._corner) if (event.get_related() != this._corner)
this._entered = false; this._entered = false;
return false; return false;
},
// Checks if the Activities button is currently sensitive to
// clicks. The first call to this function within the
// HOT_CORNER_ACTIVATION_TIMEOUT time of the hot corner being
// triggered will return false. This avoids opening and closing
// the overview if the user both triggered the hot corner and
// clicked the Activities button.
shouldToggleOverviewOnClick: function() {
if (Main.overview.animationInProgress)
return false;
if (this._activationTime == 0 || Date.now() / 1000 - this._activationTime > HOT_CORNER_ACTIVATION_TIMEOUT)
return true;
return false;
} }
}); });
const PressureBarrier = new Lang.Class({ const PressureBarrier = new Lang.Class({
Name: 'PressureBarrier', Name: 'PressureBarrier',
_init: function(threshold, timeout, keybindingMode) { _init: function(barrier, threshold, timeout) {
this._barrier = barrier;
this._threshold = threshold; this._threshold = threshold;
this._timeout = timeout; this._timeout = timeout;
this._keybindingMode = keybindingMode; this._orientation = (barrier.y1 == barrier.y2) ? Clutter.Orientation.HORIZONTAL : Clutter.Orientation.VERTICAL;
this._barriers = [];
this._eventFilter = null;
this._isTriggered = false;
this._reset(); this._reset();
},
addBarrier: function(barrier) { this._barrierHitId = this._barrier.connect('hit', Lang.bind(this, this._onBarrierHit));
barrier._pressureHitId = barrier.connect('hit', Lang.bind(this, this._onBarrierHit)); this._barrierLeftId = this._barrier.connect('left', Lang.bind(this, this._onBarrierLeft));
barrier._pressureLeftId = barrier.connect('left', Lang.bind(this, this._onBarrierLeft));
this._barriers.push(barrier);
},
_disconnectBarrier: function(barrier) {
barrier.disconnect(barrier._pressureHitId);
barrier.disconnect(barrier._pressureLeftId);
},
removeBarrier: function(barrier) {
this._disconnectBarrier(barrier);
this._barriers.splice(this._barriers.indexOf(barrier), 1);
}, },
destroy: function() { destroy: function() {
this._barriers.forEach(Lang.bind(this, this._disconnectBarrier)); this._barrier.disconnect(this._barrierHitId);
this._barriers = []; this._barrier.disconnect(this._barrierLeftId);
}, this._barrier = null;
setEventFilter: function(filter) {
this._eventFilter = filter;
}, },
_reset: function() { _reset: function() {
@ -1273,34 +1280,33 @@ const PressureBarrier = new Lang.Class({
this._lastTime = 0; this._lastTime = 0;
}, },
_isHorizontal: function(barrier) { _getDistanceAcrossBarrier: function(event) {
return barrier.y1 == barrier.y2; if (this._orientation == Clutter.Orientation.HORIZONTAL)
},
_getDistanceAcrossBarrier: function(barrier, event) {
if (this._isHorizontal(barrier))
return Math.abs(event.dy); return Math.abs(event.dy);
else else
return Math.abs(event.dx); return Math.abs(event.dx);
}, },
_getDistanceAlongBarrier: function(barrier, event) { _getDistanceAlongBarrier: function(event) {
if (this._isHorizontal(barrier)) if (this._orientation == Clutter.Orientation.HORIZONTAL)
return Math.abs(event.dx); return Math.abs(event.dx);
else else
return Math.abs(event.dy); return Math.abs(event.dy);
}, },
_isBarrierEventTooOld: function(event) {
// Ignore all events older than this time
let threshold = this._lastTime - this._timeout;
return event.time < threshold;
},
_trimBarrierEvents: function() { _trimBarrierEvents: function() {
// Events are guaranteed to be sorted in time order from // Events are guaranteed to be sorted in time order from
// oldest to newest, so just look for the first old event, // oldest to newest, so just look for the first old event,
// and then chop events after that off. // and then chop events after that off.
let i = 0; let i = 0;
let threshold = this._lastTime - this._timeout;
while (i < this._barrierEvents.length) { while (i < this._barrierEvents.length) {
let [time, distance] = this._barrierEvents[i]; if (!this._isBarrierEventTooOld(this._barrierEvents[i]))
if (time >= threshold)
break; break;
i++; i++;
} }
@ -1308,8 +1314,7 @@ const PressureBarrier = new Lang.Class({
let firstNewEvent = i; let firstNewEvent = i;
for (i = 0; i < firstNewEvent; i++) { for (i = 0; i < firstNewEvent; i++) {
let [time, distance] = this._barrierEvents[i]; this._currentPressure -= this._getDistanceAcrossBarrier(this._barrierEvents[i]);
this._currentPressure -= distance;
} }
this._barrierEvents = this._barrierEvents.slice(firstNewEvent); this._barrierEvents = this._barrierEvents.slice(firstNewEvent);
@ -1317,30 +1322,29 @@ const PressureBarrier = new Lang.Class({
_onBarrierLeft: function(barrier, event) { _onBarrierLeft: function(barrier, event) {
this._reset(); this._reset();
this._isTriggered = false;
}, },
_trigger: function() { _trigger: function() {
this._isTriggered = true;
this.emit('trigger'); this.emit('trigger');
this._reset(); this._reset();
}, },
_onBarrierHit: function(barrier, event) { _onBarrierHit: function(barrier, event) {
// If we've triggered the barrier, wait until the pointer has the // Throw out all events where the pointer was grabbed by another
// left the barrier hitbox until we trigger it again. // client, as the client that grabbed the pointer expects to have
if (this._isTriggered) // complete control over it
if (event.grabbed && Main.modalCount == 0)
return; return;
if (this._eventFilter && this._eventFilter(event)) let isOverview = ((Main.keybindingMode & (Shell.KeyBindingMode.OVERVIEW)) != 0);
// Throw out events where the grab is taken by something that's
// not the overview (modal dialogs, etc.)
if (event.grabbed && !isOverview)
return; return;
// Throw out all events not in the proper keybinding mode let slide = this._getDistanceAlongBarrier(event);
if (!(this._keybindingMode & Main.keybindingMode)) let distance = this._getDistanceAcrossBarrier(event);
return;
let slide = this._getDistanceAlongBarrier(barrier, event);
let distance = this._getDistanceAcrossBarrier(barrier, event);
if (distance >= this._threshold) { if (distance >= this._threshold) {
this._trigger(); this._trigger();
@ -1356,10 +1360,8 @@ const PressureBarrier = new Lang.Class({
this._lastTime = event.time; this._lastTime = event.time;
this._trimBarrierEvents(); this._trimBarrierEvents();
distance = Math.min(15, distance); this._barrierEvents.push(event);
this._currentPressure += Math.min(15, distance);
this._barrierEvents.push([event.time, distance]);
this._currentPressure += distance;
if (this._currentPressure >= this._threshold) if (this._currentPressure >= this._threshold)
this._trigger(); this._trigger();

@ -24,7 +24,6 @@ const Panel = imports.ui.panel;
const Params = imports.misc.params; const Params = imports.misc.params;
const RunDialog = imports.ui.runDialog; const RunDialog = imports.ui.runDialog;
const Layout = imports.ui.layout; const Layout = imports.ui.layout;
const LoginManager = imports.misc.loginManager;
const LookingGlass = imports.ui.lookingGlass; const LookingGlass = imports.ui.lookingGlass;
const NotificationDaemon = imports.ui.notificationDaemon; const NotificationDaemon = imports.ui.notificationDaemon;
const WindowAttentionHandler = imports.ui.windowAttentionHandler; const WindowAttentionHandler = imports.ui.windowAttentionHandler;
@ -33,6 +32,7 @@ const Scripting = imports.ui.scripting;
const SessionMode = imports.ui.sessionMode; const SessionMode = imports.ui.sessionMode;
const ShellDBus = imports.ui.shellDBus; const ShellDBus = imports.ui.shellDBus;
const ShellMountOperation = imports.ui.shellMountOperation; const ShellMountOperation = imports.ui.shellMountOperation;
const UnlockDialog = imports.ui.unlockDialog;
const WindowManager = imports.ui.windowManager; const WindowManager = imports.ui.windowManager;
const Magnifier = imports.ui.magnifier; const Magnifier = imports.ui.magnifier;
const XdndHandler = imports.ui.xdndHandler; const XdndHandler = imports.ui.xdndHandler;
@ -58,7 +58,7 @@ let shellDBusService = null;
let shellMountOpDBusService = null; let shellMountOpDBusService = null;
let screenSaverDBus = null; let screenSaverDBus = null;
let modalCount = 0; let modalCount = 0;
let keybindingMode = Shell.KeyBindingMode.NONE; let keybindingMode = Shell.KeyBindingMode.NORMAL;
let modalActorFocusStack = []; let modalActorFocusStack = [];
let uiGroup = null; let uiGroup = null;
let magnifier = null; let magnifier = null;
@ -71,8 +71,6 @@ let _cssStylesheet = null;
let _overridesSettings = null; let _overridesSettings = null;
function _sessionUpdated() { function _sessionUpdated() {
_loadDefaultStylesheet();
wm.setCustomKeybindingHandler('panel-main-menu', wm.setCustomKeybindingHandler('panel-main-menu',
Shell.KeyBindingMode.NORMAL | Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW, Shell.KeyBindingMode.OVERVIEW,
@ -84,9 +82,8 @@ function _sessionUpdated() {
Shell.KeyBindingMode.NORMAL | Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW, Shell.KeyBindingMode.OVERVIEW,
sessionMode.hasRunDialog ? openRunDialog : null); sessionMode.hasRunDialog ? openRunDialog : null);
if (sessionMode.isGreeter)
if (!sessionMode.hasRunDialog && lookingGlass) screenShield.showDialog();
lookingGlass.close();
} }
function start() { function start() {
@ -94,27 +91,29 @@ function start() {
global.logError = window.log; global.logError = window.log;
global.log = window.log; global.log = window.log;
// Hide the stage until we're ready for it
global.stage.hide();
// Chain up async errors reported from C // Chain up async errors reported from C
global.connect('notify-error', function (global, msg, detail) { notifyError(msg, detail); }); global.connect('notify-error', function (global, msg, detail) { notifyError(msg, detail); });
Gio.DesktopAppInfo.set_desktop_env('GNOME'); Gio.DesktopAppInfo.set_desktop_env('GNOME');
sessionMode = new SessionMode.SessionMode(); sessionMode = new SessionMode.SessionMode();
sessionMode.connect('sessions-loaded', _sessionsLoaded);
sessionMode.init(); // start session after we know what mode we're running in
let signalId = sessionMode.connect('updated', function() {
sessionMode.disconnect(signalId);
startSession();
});
} }
function _sessionsLoaded() { function startSession() {
sessionMode.connect('updated', _sessionUpdated); sessionMode.connect('updated', _loadDefaultStylesheet);
_initializeUI();
shellDBusService = new ShellDBus.GnomeShell(); shellDBusService = new ShellDBus.GnomeShell();
shellMountOpDBusService = new ShellMountOperation.GnomeShellMountOpHandler(); shellMountOpDBusService = new ShellMountOperation.GnomeShellMountOpHandler();
_sessionUpdated();
}
function _initializeUI() {
// Ensure ShellWindowTracker and ShellAppUsage are initialized; this will // Ensure ShellWindowTracker and ShellAppUsage are initialized; this will
// also initialize ShellAppSystem first. ShellAppSystem // also initialize ShellAppSystem first. ShellAppSystem
// needs to load all the .desktop files, and ShellWindowTracker // needs to load all the .desktop files, and ShellWindowTracker
@ -144,9 +143,13 @@ function _initializeUI() {
overview = new Overview.Overview(); overview = new Overview.Overview();
wm = new WindowManager.WindowManager(); wm = new WindowManager.WindowManager();
magnifier = new Magnifier.Magnifier(); magnifier = new Magnifier.Magnifier();
if (LoginManager.canLock()) if (UnlockDialog.isSupported())
screenShield = new ScreenShield.ScreenShield(); screenShield = new ScreenShield.ScreenShield();
else
screenShield = new ScreenShield.ScreenShieldFallback();
// The message tray relies on being constructed
// after the panel.
panel = new Panel.Panel(); panel = new Panel.Panel();
messageTray = new MessageTray.MessageTray(); messageTray = new MessageTray.MessageTray();
keyboard = new Keyboard.Keyboard(); keyboard = new Keyboard.Keyboard();
@ -155,11 +158,14 @@ function _initializeUI() {
componentManager = new Components.ComponentManager(); componentManager = new Components.ComponentManager();
layoutManager.init(); layoutManager.init();
layoutManager.prepareStartupAnimation();
overview.init(); overview.init();
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT, global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT,
false, -1, 1); false, -1, 1);
global.display.connect('overlay-key', Lang.bind(overview, overview.toggle)); global.display.connect('overlay-key', Lang.bind(overview, overview.toggle));
sessionMode.connect('updated', _sessionUpdated);
_sessionUpdated();
// Provide the bus object for gnome-session to // Provide the bus object for gnome-session to
// initiate logouts. // initiate logouts.
@ -193,16 +199,8 @@ function _initializeUI() {
ExtensionDownloader.init(); ExtensionDownloader.init();
ExtensionSystem.init(); ExtensionSystem.init();
if (sessionMode.isGreeter && screenShield) { layoutManager.connect('startup-prepared', function() {
layoutManager.connect('startup-prepared', function() { layoutManager.startupAnimation();
screenShield.showDialog();
});
}
layoutManager.connect('startup-complete', function() {
if (keybindingMode == Shell.KeyBindingMode.NONE) {
keybindingMode = Shell.KeyBindingMode.NORMAL;
}
}); });
} }

@ -1591,13 +1591,17 @@ const MessageTray = new Lang.Class({
global.focus_manager.add_group(this.actor); global.focus_manager.add_group(this.actor);
this._summary = new St.BoxLayout({ style_class: 'message-tray-summary', this._summary = new St.BoxLayout({ style_class: 'message-tray-summary',
reactive: true, reactive: true,
track_hover: true,
x_align: Clutter.ActorAlign.END, x_align: Clutter.ActorAlign.END,
x_expand: true, x_expand: true,
y_align: Clutter.ActorAlign.CENTER, y_align: Clutter.ActorAlign.CENTER,
y_expand: true }); y_expand: true });
this._summary.connect('notify::hover', Lang.bind(this, this._onSummaryHoverChanged));
this.actor.add_actor(this._summary); this.actor.add_actor(this._summary);
this._summary.opacity = 0;
this._summaryMotionId = 0; this._summaryMotionId = 0;
this._trayMotionId = 0;
this._summaryBoxPointer = new BoxPointer.BoxPointer(St.Side.BOTTOM, this._summaryBoxPointer = new BoxPointer.BoxPointer(St.Side.BOTTOM,
{ reactive: true, { reactive: true,
@ -1632,22 +1636,15 @@ const MessageTray = new Lang.Class({
Main.layoutManager.connect('keyboard-visible-changed', Lang.bind(this, this._onKeyboardVisibleChanged)); Main.layoutManager.connect('keyboard-visible-changed', Lang.bind(this, this._onKeyboardVisibleChanged));
this._trayState = State.HIDDEN; this._trayState = State.HIDDEN;
this._locked = false;
this._traySummoned = false; this._traySummoned = false;
this._useLongerTrayLeftTimeout = false; this._useLongerTrayLeftTimeout = false;
this._trayLeftTimeoutId = 0; this._trayLeftTimeoutId = 0;
// pointerInTray is sort of a misnomer -- it tracks whether
// a message tray notification should expand. The value is
// partially driven by the hover state of the tray, but has
// a lot of complex state related to timeouts and the current
// state of the pointer when a notification pops up.
this._pointerInTray = false; this._pointerInTray = false;
this._pointerInKeyboard = false;
// This tracks this.actor.hover and is used to fizzle
// out non-changing hover notifications in onTrayHoverChanged.
this._trayHovered = false;
this._keyboardVisible = false; this._keyboardVisible = false;
this._summaryState = State.HIDDEN;
this._pointerInSummary = false;
this._notificationClosed = false; this._notificationClosed = false;
this._notificationState = State.HIDDEN; this._notificationState = State.HIDDEN;
this._notificationTimeoutId = 0; this._notificationTimeoutId = 0;
@ -1669,18 +1666,20 @@ const MessageTray = new Lang.Class({
}); });
Main.layoutManager.trayBox.add_actor(this.actor); Main.layoutManager.trayBox.add_actor(this.actor);
this.actor.y = 0;
Main.layoutManager.trackChrome(this.actor); Main.layoutManager.trackChrome(this.actor);
Main.layoutManager.trackChrome(this._notificationWidget); Main.layoutManager.trackChrome(this._notificationWidget);
Main.layoutManager.trackChrome(this._closeButton); Main.layoutManager.trackChrome(this._closeButton);
Main.layoutManager.connect('fullscreen-changed', Lang.bind(this, this._updateState)); Main.layoutManager.connect('primary-fullscreen-changed', Lang.bind(this, this._onFullscreenChanged));
Main.layoutManager.connect('hot-corners-changed', Lang.bind(this, this._hotCornersChanged));
// If the overview shows or hides while we're in // If the overview shows or hides while we're in
// the message tray, revert back to normal mode. // the message tray, revert back to normal mode.
Main.overview.connect('showing', Lang.bind(this, this._escapeTray)); Main.overview.connect('showing', Lang.bind(this, this._escapeTray));
Main.overview.connect('hiding', Lang.bind(this, this._escapeTray)); Main.overview.connect('hiding', Lang.bind(this, this._escapeTray));
// Track if we've added the activities button
this._activitiesButtonAdded = false;
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated)); Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
Main.wm.addKeybinding('toggle-message-tray', Main.wm.addKeybinding('toggle-message-tray',
@ -1704,7 +1703,6 @@ const MessageTray = new Lang.Class({
this._trayDwellTimeoutId = 0; this._trayDwellTimeoutId = 0;
this._setupTrayDwellIfNeeded(); this._setupTrayDwellIfNeeded();
this._sessionUpdated(); this._sessionUpdated();
this._hotCornersChanged();
this._noMessages = new St.Label({ text: _("No Messages"), this._noMessages = new St.Label({ text: _("No Messages"),
style_class: 'no-messages-label', style_class: 'no-messages-label',
@ -1745,11 +1743,13 @@ const MessageTray = new Lang.Class({
_openContextMenu: function () { _openContextMenu: function () {
let [x, y, mask] = global.get_pointer(); let [x, y, mask] = global.get_pointer();
this._lock();
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,
grabFocus: true, grabFocus: true,
onUngrab: Lang.bind(this, function () { onUngrab: Lang.bind(this, function () {
this._contextMenu.close(BoxPointer.PopupAnimation.FULL); this._contextMenu.close(BoxPointer.PopupAnimation.FULL);
this._unlock();
}) })
}); });
this._contextMenu.open(BoxPointer.PopupAnimation.FULL); this._contextMenu.open(BoxPointer.PopupAnimation.FULL);
@ -1775,6 +1775,11 @@ const MessageTray = new Lang.Class({
}, },
_sessionUpdated: function() { _sessionUpdated: function() {
if (!this._activitiesButtonAdded && Main.panel.statusArea.activities) {
this._activitiesButtonAdded = true;
this._grabHelper.addActor(Main.panel.statusArea.activities.hotCorner.actor);
}
if ((Main.sessionMode.isLocked || Main.sessionMode.isGreeter) && this._inCtrlAltTab) { if ((Main.sessionMode.isLocked || Main.sessionMode.isGreeter) && this._inCtrlAltTab) {
Main.ctrlAltTabManager.removeGroup(this._summary); Main.ctrlAltTabManager.removeGroup(this._summary);
this._inCtrlAltTab = false; this._inCtrlAltTab = false;
@ -1823,9 +1828,6 @@ const MessageTray = new Lang.Class({
_trayDwellTimeout: function() { _trayDwellTimeout: function() {
this._trayDwellTimeoutId = 0; this._trayDwellTimeoutId = 0;
if (Main.layoutManager.bottomMonitor.inFullscreen)
return false;
// We don't want to open the tray when a modal dialog // We don't want to open the tray when a modal dialog
// is up, so we check the modal count for that. When we are in the // is up, so we check the modal count for that. When we are in the
// overview we have to take the overview's modal push into account // overview we have to take the overview's modal push into account
@ -1997,6 +1999,18 @@ const MessageTray = new Lang.Class({
this._notificationQueue.splice(index, 1); this._notificationQueue.splice(index, 1);
}, },
_lock: function() {
this._locked = true;
},
_unlock: function() {
if (!this._locked)
return;
this._locked = false;
this._pointerInTray = this.actor.hover;
this._updateState();
},
openTray: function() { openTray: function() {
if (Main.overview.animationInProgress) if (Main.overview.animationInProgress)
return; return;
@ -2022,6 +2036,7 @@ const MessageTray = new Lang.Class({
hide: function() { hide: function() {
this._traySummoned = false; this._traySummoned = false;
this.actor.set_hover(false); this.actor.set_hover(false);
this._summary.set_hover(false);
this._updateState(); this._updateState();
}, },
@ -2081,19 +2096,13 @@ const MessageTray = new Lang.Class({
this._updateState(); this._updateState();
}, },
_hotCornersChanged: function() { _onSummaryHoverChanged: function() {
let primary = Main.layoutManager.primaryIndex; this._pointerInSummary = this._summary.hover;
let corner = Main.layoutManager.hotCorners[primary]; this._updateState();
if (corner && corner.actor)
this._grabHelper.addActor(corner.actor);
}, },
_onTrayHoverChanged: function() { _onTrayHoverChanged: function() {
if (this.actor.hover == this._trayHovered) if (this.actor.hover) {
return;
this._trayHovered = this.actor.hover;
if (this._trayHovered) {
// No dwell inside notifications at the bottom of the screen // No dwell inside notifications at the bottom of the screen
this._cancelTrayDwell(); this._cancelTrayDwell();
@ -2101,6 +2110,13 @@ const MessageTray = new Lang.Class({
if (this._trayState == State.HIDDEN && this._notificationState == State.HIDDEN) if (this._trayState == State.HIDDEN && this._notificationState == State.HIDDEN)
return; return;
// Don't do anything if this._useLongerTrayLeftTimeout is true, meaning the notification originally
// popped up under the pointer, but this._trayLeftTimeoutId is 0, meaning the pointer didn't leave
// the tray yet. We need to check for this case because sometimes _onTrayHoverChanged() gets called
// multiple times while this.actor.hover is true.
if (this._useLongerTrayLeftTimeout && !this._trayLeftTimeoutId)
return;
this._useLongerTrayLeftTimeout = false; this._useLongerTrayLeftTimeout = false;
if (this._trayLeftTimeoutId) { if (this._trayLeftTimeoutId) {
Mainloop.source_remove(this._trayLeftTimeoutId); Mainloop.source_remove(this._trayLeftTimeoutId);
@ -2149,6 +2165,11 @@ const MessageTray = new Lang.Class({
this._updateState(); this._updateState();
}, },
_onFullscreenChanged: function(obj, state) {
this._inFullscreen = state;
this._updateState();
},
_onStatusChanged: function(status) { _onStatusChanged: function(status) {
if (status == GnomeSession.PresenceStatus.BUSY) { if (status == GnomeSession.PresenceStatus.BUSY) {
// remove notification and allow the summary to be closed now // remove notification and allow the summary to be closed now
@ -2180,6 +2201,7 @@ const MessageTray = new Lang.Class({
this._trayLeftTimeoutId = 0; this._trayLeftTimeoutId = 0;
this._useLongerTrayLeftTimeout = false; this._useLongerTrayLeftTimeout = false;
this._pointerInTray = false; this._pointerInTray = false;
this._pointerInSummary = false;
this._updateNotificationTimeout(0); this._updateNotificationTimeout(0);
this._updateState(); this._updateState();
} }
@ -2187,7 +2209,9 @@ const MessageTray = new Lang.Class({
}, },
_escapeTray: function() { _escapeTray: function() {
this._unlock();
this._pointerInTray = false; this._pointerInTray = false;
this._pointerInSummary = false;
this._traySummoned = false; this._traySummoned = false;
this._setClickedSummaryItem(null); this._setClickedSummaryItem(null);
this._updateNotificationTimeout(0); this._updateNotificationTimeout(0);
@ -2196,7 +2220,7 @@ const MessageTray = new Lang.Class({
// All of the logic for what happens when occurs here; the various // All of the logic for what happens when occurs here; the various
// event handlers merely update variables such as // event handlers merely update variables such as
// 'this._pointerInTray', 'this._traySummoned', etc, and // 'this._pointerInTray', 'this._summaryState', etc, and
// _updateState() figures out what (if anything) needs to be done // _updateState() figures out what (if anything) needs to be done
// at the present time. // at the present time.
_updateState: function() { _updateState: function() {
@ -2204,22 +2228,25 @@ const MessageTray = new Lang.Class({
let notificationQueue = this._notificationQueue; let notificationQueue = this._notificationQueue;
let notificationUrgent = notificationQueue.length > 0 && notificationQueue[0].urgency == Urgency.CRITICAL; let notificationUrgent = notificationQueue.length > 0 && notificationQueue[0].urgency == Urgency.CRITICAL;
let notificationForFeedback = notificationQueue.length > 0 && notificationQueue[0].forFeedback; let notificationForFeedback = notificationQueue.length > 0 && notificationQueue[0].forFeedback;
let notificationsLimited = this._busy || Main.layoutManager.bottomMonitor.inFullscreen; let notificationsLimited = this._busy || this._inFullscreen;
let notificationsPending = notificationQueue.length > 0 && (!notificationsLimited || notificationUrgent || notificationForFeedback) && Main.sessionMode.hasNotifications; let notificationsPending = notificationQueue.length > 0 && (!notificationsLimited || notificationUrgent || notificationForFeedback) && Main.sessionMode.hasNotifications;
let nextNotification = notificationQueue.length > 0 ? notificationQueue[0] : null; let nextNotification = notificationQueue.length > 0 ? notificationQueue[0] : null;
let notificationPinned = this._pointerInTray && !this._notificationRemoved; let notificationPinned = this._pointerInTray && !this._pointerInSummary && !this._notificationRemoved;
let notificationExpanded = this._notification && this._notification.expanded; let notificationExpanded = this._notification && this._notification.expanded;
let notificationExpired = this._notificationTimeoutId == 0 && let notificationExpired = this._notificationTimeoutId == 0 &&
!(this._notification && this._notification.urgency == Urgency.CRITICAL) && !(this._notification && this._notification.urgency == Urgency.CRITICAL) &&
!(this._notification && this._notification.focused) && !(this._notification && this._notification.focused) &&
!this._pointerInTray; !this._pointerInTray &&
!this._locked &&
!(this._pointerInKeyboard && notificationExpanded);
let notificationLockedOut = !Main.sessionMode.hasNotifications && this._notification; let notificationLockedOut = !Main.sessionMode.hasNotifications && this._notification;
let notificationMustClose = this._notificationRemoved || notificationLockedOut || (notificationExpired && this._userActiveWhileNotificationShown) || this._notificationClosed; let notificationMustClose = this._notificationRemoved || notificationLockedOut || (notificationExpired && this._userActiveWhileNotificationShown) || this._notificationClosed;
let canShowNotification = notificationsPending && this._trayState == State.HIDDEN; let canShowNotification = notificationsPending && this._summaryState == State.HIDDEN;
if (this._notificationState == State.HIDDEN) { if (this._notificationState == State.HIDDEN) {
if (canShowNotification) if (canShowNotification) {
this._showNotification(); this._showNotification();
}
} else if (this._notificationState == State.SHOWN) { } else if (this._notificationState == State.SHOWN) {
if (notificationMustClose) if (notificationMustClose)
this._hideNotification(); this._hideNotification();
@ -2229,17 +2256,27 @@ const MessageTray = new Lang.Class({
this._ensureNotificationFocused(); this._ensureNotificationFocused();
} }
// Summary
let summarySummoned = this._pointerInSummary || this._traySummoned;
let summaryPinned = this._pointerInTray || summarySummoned || this._locked;
let summaryHovered = this._pointerInTray || this._pointerInSummary;
let notificationsVisible = this._notificationState != State.HIDDEN; let notificationsVisible = this._notificationState != State.HIDDEN;
let notificationsDone = !notificationsVisible && !notificationsPending; let notificationsDone = !notificationsVisible && !notificationsPending;
let mustHideTray = ((notificationsPending && notificationUrgent) let mustHideSummary = ((notificationsPending && notificationUrgent)
|| notificationsVisible || !Main.sessionMode.hasNotifications); || notificationsVisible || !Main.sessionMode.hasNotifications);
if (this._summaryState == State.HIDDEN && !mustHideSummary && summarySummoned)
this._showSummary();
else if (this._summaryState == State.SHOWN && (!summaryPinned || mustHideSummary))
this._hideSummary();
// Summary notification // Summary notification
let haveClickedSummaryItem = this._clickedSummaryItem != null; let haveClickedSummaryItem = this._clickedSummaryItem != null;
let summarySourceIsMainNotificationSource = (haveClickedSummaryItem && this._notification && let summarySourceIsMainNotificationSource = (haveClickedSummaryItem && this._notification &&
this._clickedSummaryItem.source == this._notification.source); this._clickedSummaryItem.source == this._notification.source);
let canShowSummaryBoxPointer = this._trayState == State.SHOWN; let canShowSummaryBoxPointer = this._summaryState == State.SHOWN;
// We only have sources with empty notification stacks for legacy tray icons. Currently, we never attempt // We only have sources with empty notification stacks for legacy tray icons. Currently, we never attempt
// to show notifications for legacy tray icons, but this would be necessary if we did. // to show notifications for legacy tray icons, but this would be necessary if we did.
let requestedNotificationStackIsEmpty = (this._clickedSummaryItemMouseButton == 1 && this._clickedSummaryItem.source.notifications.length == 0); let requestedNotificationStackIsEmpty = (this._clickedSummaryItemMouseButton == 1 && this._clickedSummaryItem.source.notifications.length == 0);
@ -2254,7 +2291,7 @@ const MessageTray = new Lang.Class({
if (haveClickedSummaryItem && !summarySourceIsMainNotificationSource && canShowSummaryBoxPointer && !requestedNotificationStackIsEmpty) if (haveClickedSummaryItem && !summarySourceIsMainNotificationSource && canShowSummaryBoxPointer && !requestedNotificationStackIsEmpty)
this._showSummaryBoxPointer(); this._showSummaryBoxPointer();
} else if (this._summaryBoxPointerState == State.SHOWN) { } else if (this._summaryBoxPointerState == State.SHOWN) {
if (!haveClickedSummaryItem || !canShowSummaryBoxPointer || wrongSummaryBoxPointer || mustHideTray) { if (!haveClickedSummaryItem || !canShowSummaryBoxPointer || wrongSummaryBoxPointer || mustHideSummary) {
this._hideSummaryBoxPointer(); this._hideSummaryBoxPointer();
if (wrongSummaryBoxPointer) if (wrongSummaryBoxPointer)
this._showSummaryBoxPointer(); this._showSummaryBoxPointer();
@ -2264,7 +2301,9 @@ const MessageTray = new Lang.Class({
// Tray itself // Tray itself
let trayIsVisible = (this._trayState == State.SHOWING || let trayIsVisible = (this._trayState == State.SHOWING ||
this._trayState == State.SHOWN); this._trayState == State.SHOWN);
let trayShouldBeVisible = this._traySummoned && !this._keyboardVisible && !mustHideTray; let trayShouldBeVisible = (this._summaryState == State.SHOWING ||
this._summaryState == State.SHOWN) &&
!this._keyboardVisible;
if (!trayIsVisible && trayShouldBeVisible) if (!trayIsVisible && trayShouldBeVisible)
trayShouldBeVisible = this._showTray(); trayShouldBeVisible = this._showTray();
else if (trayIsVisible && !trayShouldBeVisible) else if (trayIsVisible && !trayShouldBeVisible)
@ -2402,7 +2441,7 @@ const MessageTray = new Lang.Class({
_showNotification: function() { _showNotification: function() {
this._notification = this._notificationQueue.shift(); this._notification = this._notificationQueue.shift();
this._userActiveWhileNotificationShown = this.idleMonitor.get_idletime() <= IDLE_TIME; this._userActiveWhileNotificationShown = this.idleMonitor.get_idletime() > IDLE_TIME;
if (!this._userActiveWhileNotificationShown) { if (!this._userActiveWhileNotificationShown) {
// If the user isn't active, set up a watch to let us know // If the user isn't active, set up a watch to let us know
// when the user becomes active. // when the user becomes active.
@ -2537,14 +2576,6 @@ const MessageTray = new Lang.Class({
this._notificationUnfocusedId = 0; this._notificationUnfocusedId = 0;
} }
this._useLongerTrayLeftTimeout = false;
if (this._trayLeftTimeoutId) {
Mainloop.source_remove(this._trayLeftTimeoutId);
this._trayLeftTimeoutId = 0;
this._trayLeftMouseX = -1;
this._trayLeftMouseY = -1;
}
if (this._notificationRemoved) { if (this._notificationRemoved) {
Tweener.removeTweens(this._notificationWidget); Tweener.removeTweens(this._notificationWidget);
this._notificationWidget.y = this.actor.height; this._notificationWidget.y = this.actor.height;
@ -2575,11 +2606,7 @@ const MessageTray = new Lang.Class({
this._notificationRemoved = false; this._notificationRemoved = false;
this._closeButton.hide(); this._closeButton.hide();
this._pointerInTray = false; this._pointerInTray = false;
this.actor.hover = false; // Clutter doesn't emit notify::hover when actors move
// Clutter will send a leave-event the next time the mouse
// moves, but we need to set this here now to update the
// state machine.
this.actor.hover = false;
this._notificationBin.child = null; this._notificationBin.child = null;
this._notificationWidget.hide(); this._notificationWidget.hide();
}, },
@ -2592,9 +2619,10 @@ const MessageTray = new Lang.Class({
}, },
_expandNotification: function(autoExpanding) { _expandNotification: function(autoExpanding) {
// Don't focus notifications that are auto-expanding. // Don't grab focus in notifications that are auto-expanded.
if (!autoExpanding) if (!autoExpanding)
this._ensureNotificationFocused(); this._grabHelper.grab({ actor: this._notification.actor,
grabFocus: true });
if (!this._notificationExpandedId) if (!this._notificationExpandedId)
this._notificationExpandedId = this._notificationExpandedId =
@ -2624,11 +2652,30 @@ const MessageTray = new Lang.Class({
} }
}, },
// We use this function to grab focus when the user moves the pointer
// to a notification with CRITICAL urgency that was already auto-expanded.
_ensureNotificationFocused: function() { _ensureNotificationFocused: function() {
this._grabHelper.grab({ actor: this._notification.actor, this._grabHelper.grab({ actor: this._notification.actor,
grabFocus: true }); grabFocus: true });
}, },
_showSummary: function() {
this._summary.opacity = 0;
this._tween(this._summary, '_summaryState', State.SHOWN,
{ opacity: 255,
time: ANIMATION_TIME,
transition: 'easeOutQuad',
});
},
_hideSummary: function() {
this._tween(this._summary, '_summaryState', State.HIDDEN,
{ opacity: 0,
time: ANIMATION_TIME,
transition: 'easeOutQuad',
});
},
_onSourceDoneDisplayingContent: function(source, closeTray) { _onSourceDoneDisplayingContent: function(source, closeTray) {
if (closeTray) { if (closeTray) {
this._escapeTray(); this._escapeTray();
@ -2672,6 +2719,7 @@ const MessageTray = new Lang.Class({
this._grabHelper.grab({ actor: this._summaryBoxPointer.bin.child, this._grabHelper.grab({ actor: this._summaryBoxPointer.bin.child,
grabFocus: true, grabFocus: true,
onUngrab: Lang.bind(this, this._onSummaryBoxPointerUngrabbed) }); onUngrab: Lang.bind(this, this._onSummaryBoxPointerUngrabbed) });
this._lock();
this._summaryBoxPointer.actor.opacity = 0; this._summaryBoxPointer.actor.opacity = 0;
this._summaryBoxPointer.actor.show(); this._summaryBoxPointer.actor.show();
@ -2706,8 +2754,10 @@ const MessageTray = new Lang.Class({
this._clickedSummaryItem.actor.remove_style_pseudo_class('selected'); this._clickedSummaryItem.actor.remove_style_pseudo_class('selected');
this._clickedSummaryItem.actor.disconnect(this._clickedSummaryItemAllocationChangedId); this._clickedSummaryItem.actor.disconnect(this._clickedSummaryItemAllocationChangedId);
this._summary.disconnect(this._summaryMotionId); this._summary.disconnect(this._summaryMotionId);
Main.layoutManager.trayBox.disconnect(this._trayMotionId);
this._clickedSummaryItemAllocationChangedId = 0; this._clickedSummaryItemAllocationChangedId = 0;
this._summaryMotionId = 0; this._summaryMotionId = 0;
this._trayMotionId = 0;
} }
this._clickedSummaryItem = item; this._clickedSummaryItem = item;
@ -2722,6 +2772,8 @@ const MessageTray = new Lang.Class({
// _clickedSummaryItem.actor can change absolute position without changing allocation // _clickedSummaryItem.actor can change absolute position without changing allocation
this._summaryMotionId = this._summary.connect('allocation-changed', this._summaryMotionId = this._summary.connect('allocation-changed',
Lang.bind(this, this._adjustSummaryBoxPointerPosition)); Lang.bind(this, this._adjustSummaryBoxPointerPosition));
this._trayMotionId = Main.layoutManager.trayBox.connect('notify::anchor-y',
Lang.bind(this, this._adjustSummaryBoxPointerPosition));
} }
return true; return true;
@ -2758,6 +2810,8 @@ const MessageTray = new Lang.Class({
this._sourceDoneDisplayingId = 0; this._sourceDoneDisplayingId = 0;
} }
this._unlock();
if (this._summaryBoxPointerItem.source.notifications.length == 0) { if (this._summaryBoxPointerItem.source.notifications.length == 0) {
this._summaryBoxPointer.actor.hide(); this._summaryBoxPointer.actor.hide();
this._hideSummaryBoxPointerCompleted(); this._hideSummaryBoxPointerCompleted();

@ -244,7 +244,7 @@ const NotificationDaemon = new Lang.Class({
Main.overview.connect('hidden', Main.overview.connect('hidden',
Lang.bind(this, this._onFocusAppChanged)); Lang.bind(this, this._onFocusAppChanged));
this._trayManager.manage_screen(global.screen, Main.messageTray.actor); this._trayManager.manage_stage(global.stage, Main.messageTray.actor);
}, },
_imageForNotificationData: function(hints) { _imageForNotificationData: function(hints) {

@ -164,12 +164,12 @@ const OsdWindow = new Lang.Class({
}, },
_monitorsChanged: function() { _monitorsChanged: function() {
/* assume 110x110 on a 640x480 display and scale from there */ /* assume 130x130 on a 640x480 display and scale from there */
let monitor = Main.layoutManager.primaryMonitor; let monitor = Main.layoutManager.primaryMonitor;
let scalew = monitor.width / 640.0; let scalew = monitor.width / 640.0;
let scaleh = monitor.height / 480.0; let scaleh = monitor.height / 480.0;
let scale = Math.min(scalew, scaleh); let scale = Math.min(scalew, scaleh);
let size = 110 * Math.max(1, scale); let size = 130 * Math.max(1, scale);
this._box.set_size(size, size); this._box.set_size(size, size);
this._box.translation_y = monitor.height / 4; this._box.translation_y = monitor.height / 4;

@ -33,8 +33,6 @@ const SHADE_ANIMATION_TIME = .20;
const DND_WINDOW_SWITCH_TIMEOUT = 1250; const DND_WINDOW_SWITCH_TIMEOUT = 1250;
const OVERVIEW_ACTIVATION_TIMEOUT = 0.5;
const ShellInfo = new Lang.Class({ const ShellInfo = new Lang.Class({
Name: 'ShellInfo', Name: 'ShellInfo',
@ -95,7 +93,9 @@ const Overview = new Lang.Class({
_init: function() { _init: function() {
this._overviewCreated = false; this._overviewCreated = false;
this._initCalled = false; this._initCalled = false;
this._controlPressed = false;
global.stage.connect('captured-event', Lang.bind(this, this._capturedEvent));
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated)); Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
this._sessionUpdated(); this._sessionUpdated();
}, },
@ -146,10 +146,9 @@ const Overview = new Lang.Class({
this._backgroundGroup.hide(); this._backgroundGroup.hide();
this._bgManagers = []; this._bgManagers = [];
this._activationTime = 0;
this.visible = false; // animating to overview, in overview, animating out this.visible = false; // animating to overview, in overview, animating out
this._shown = false; // show() and not hide() this._shown = false; // show() and not hide()
this._shownTemporarily = false; // showTemporarily() and not hideTemporarily()
this._modal = false; // have a modal grab this._modal = false; // have a modal grab
this.animationInProgress = false; this.animationInProgress = false;
this.visibleTarget = false; this.visibleTarget = false;
@ -239,6 +238,20 @@ const Overview = new Lang.Class({
} }
}, },
_capturedEvent: function(actor, event) {
let type = event.type();
if (type != Clutter.EventType.KEY_PRESS &&
type != Clutter.EventType.KEY_RELEASE)
return false;
let symbol = event.get_key_symbol();
if (symbol == Clutter.KEY_Control_L ||
symbol == Clutter.KEY_Control_R)
this._controlPressed = type == Clutter.EventType.KEY_PRESS;
return false;
},
_sessionUpdated: function() { _sessionUpdated: function() {
this.isDummy = !Main.sessionMode.hasOverview; this.isDummy = !Main.sessionMode.hasOverview;
this._createOverview(); this._createOverview();
@ -333,22 +346,18 @@ const Overview = new Lang.Class({
}, },
_onDragBegin: function() { _onDragBegin: function() {
this._inXdndDrag = true;
DND.addDragMonitor(this._dragMonitor); DND.addDragMonitor(this._dragMonitor);
// Remember the workspace we started from // Remember the workspace we started from
this._lastActiveWorkspaceIndex = global.screen.get_active_workspace_index(); this._lastActiveWorkspaceIndex = global.screen.get_active_workspace_index();
}, },
_onDragEnd: function(time) { _onDragEnd: function(time) {
this._inXdndDrag = false;
// In case the drag was canceled while in the overview // In case the drag was canceled while in the overview
// we have to go back to where we started and hide // we have to go back to where we started and hide
// the overview // the overview
if (this._shown) { if (this._shownTemporarily) {
global.screen.get_workspace_by_index(this._lastActiveWorkspaceIndex).activate(time); global.screen.get_workspace_by_index(this._lastActiveWorkspaceIndex).activate(time);
this.hide(); this.hideTemporarily();
} }
this._resetWindowSwitchTimeout(); this._resetWindowSwitchTimeout();
this._lastHoveredWindow = null; this._lastHoveredWindow = null;
@ -396,7 +405,7 @@ const Overview = new Lang.Class({
this._needsFakePointerEvent = true; this._needsFakePointerEvent = true;
Main.activateWindow(dragEvent.targetActor._delegate.metaWindow, Main.activateWindow(dragEvent.targetActor._delegate.metaWindow,
this._windowSwitchTimestamp); this._windowSwitchTimestamp);
this.hide(); this.hideTemporarily();
this._lastHoveredWindow = null; this._lastHoveredWindow = null;
})); }));
} }
@ -492,10 +501,9 @@ const Overview = new Lang.Class({
if (this._shown) if (this._shown)
return; return;
this._shown = true; this._shown = true;
this._syncInputMode();
if (!this._syncInputMode()) if (!this._modal)
return; return;
this._animateVisible(); this._animateVisible();
}, },
@ -528,7 +536,6 @@ const Overview = new Lang.Class({
this.visible = true; this.visible = true;
this.animationInProgress = true; this.animationInProgress = true;
this.visibleTarget = true; this.visibleTarget = true;
this._activationTime = Date.now() / 1000;
// All the the actors in the window group are completely obscured, // All the the actors in the window group are completely obscured,
// hiding the group holding them while the Overview is displayed greatly // hiding the group holding them while the Overview is displayed greatly
@ -540,6 +547,8 @@ const Overview = new Lang.Class({
// //
// Disable unredirection while in the overview // Disable unredirection while in the overview
Meta.disable_unredirect_for_screen(global.screen); Meta.disable_unredirect_for_screen(global.screen);
global.window_group.hide();
global.top_window_group.hide();
this._stack.show(); this._stack.show();
this._backgroundGroup.show(); this._backgroundGroup.show();
this._viewSelector.show(); this._viewSelector.show();
@ -559,6 +568,24 @@ const Overview = new Lang.Class({
this.emit('showing'); this.emit('showing');
}, },
// showTemporarily:
//
// Animates the overview visible without grabbing mouse and keyboard input;
// if show() has already been called, this has no immediate effect, but
// will result in the overview not being hidden until hideTemporarily() is
// called.
showTemporarily: function() {
if (this.isDummy)
return;
if (this._shownTemporarily)
return;
this._syncInputMode();
this._animateVisible();
this._shownTemporarily = true;
},
// hide: // hide:
// //
// Reverses the effect of show() // Reverses the effect of show()
@ -569,22 +596,33 @@ const Overview = new Lang.Class({
if (!this._shown) if (!this._shown)
return; return;
let event = Clutter.get_current_event(); if (this._controlPressed)
if (event) { return;
let type = event.type();
let button = (type == Clutter.EventType.BUTTON_PRESS ||
type == Clutter.EventType.BUTTON_RELEASE);
let ctrl = (event.get_state() & Clutter.ModifierType.CONTROL_MASK) != 0;
if (button && ctrl)
return;
}
this._animateNotVisible(); if (!this._shownTemporarily)
this._animateNotVisible();
this._shown = false; this._shown = false;
this._syncInputMode(); this._syncInputMode();
}, },
// hideTemporarily:
//
// Reverses the effect of showTemporarily()
hideTemporarily: function() {
if (this.isDummy)
return;
if (!this._shownTemporarily)
return;
if (!this._shown)
this._animateNotVisible();
this._shownTemporarily = false;
this._syncInputMode();
},
toggle: function() { toggle: function() {
if (this.isDummy) if (this.isDummy)
return; return;
@ -595,20 +633,6 @@ const Overview = new Lang.Class({
this.show(); this.show();
}, },
// Checks if the Activities button is currently sensitive to
// clicks. The first call to this function within the
// OVERVIEW_ACTIVATION_TIMEOUT time of the hot corner being
// triggered will return false. This avoids opening and closing
// the overview if the user both triggered the hot corner and
// clicked the Activities button.
shouldToggleByCornerOrButton: function() {
if (this.animationInProgress)
return false;
if (this._activationTime == 0 || Date.now() / 1000 - this._activationTime > OVERVIEW_ACTIVATION_TIMEOUT)
return true;
return false;
},
//// Private methods //// //// Private methods ////
_syncInputMode: function() { _syncInputMode: function() {
@ -616,23 +640,22 @@ const Overview = new Lang.Class({
// overview we don't have a problem with the release of a press/release // overview we don't have a problem with the release of a press/release
// going to an application. // going to an application.
if (this.animationInProgress) if (this.animationInProgress)
return true; return;
if (this._shown) { if (this._shown) {
let shouldBeModal = !this._inXdndDrag; if (!this._modal) {
if (shouldBeModal) { if (Main.pushModal(this._overview,
if (!this._modal) { { keybindingMode: Shell.KeyBindingMode.OVERVIEW }))
if (Main.pushModal(this._overview, this._modal = true;
{ keybindingMode: Shell.KeyBindingMode.OVERVIEW })) { else
this._modal = true; this.hide();
} else {
this.hide();
return false;
}
}
} else {
global.stage_input_mode = Shell.StageInputMode.FULLSCREEN;
} }
} else if (this._shownTemporarily) {
if (this._modal) {
Main.popModal(this._overview);
this._modal = false;
}
global.stage_input_mode = Shell.StageInputMode.FULLSCREEN;
} else { } else {
if (this._modal) { if (this._modal) {
Main.popModal(this._overview); Main.popModal(this._overview);
@ -641,7 +664,6 @@ const Overview = new Lang.Class({
else if (global.stage_input_mode == Shell.StageInputMode.FULLSCREEN) else if (global.stage_input_mode == Shell.StageInputMode.FULLSCREEN)
global.stage_input_mode = Shell.StageInputMode.NORMAL; global.stage_input_mode = Shell.StageInputMode.NORMAL;
} }
return true;
}, },
_animateNotVisible: function() { _animateNotVisible: function() {
@ -675,7 +697,7 @@ const Overview = new Lang.Class({
this.emit('shown'); this.emit('shown');
// Handle any calls to hide* while we were showing // Handle any calls to hide* while we were showing
if (!this._shown) if (!this._shown && !this._shownTemporarily)
this._animateNotVisible(); this._animateNotVisible();
this._syncInputMode(); this._syncInputMode();
@ -686,6 +708,9 @@ const Overview = new Lang.Class({
// Re-enable unredirection // Re-enable unredirection
Meta.enable_unredirect_for_screen(global.screen); Meta.enable_unredirect_for_screen(global.screen);
global.window_group.show();
global.top_window_group.show();
this._viewSelector.hide(); this._viewSelector.hide();
this._desktopFade.hide(); this._desktopFade.hide();
this._backgroundGroup.hide(); this._backgroundGroup.hide();
@ -698,7 +723,7 @@ const Overview = new Lang.Class({
this.emit('hidden'); this.emit('hidden');
// Handle any calls to show* while we were hiding // Handle any calls to show* while we were hiding
if (this._shown) if (this._shown || this._shownTemporarily)
this._animateVisible(); this._animateVisible();
this._syncInputMode(); this._syncInputMode();

@ -452,11 +452,9 @@ const MessagesIndicator = new Lang.Class({
_updateCount: function() { _updateCount: function() {
let count = 0; let count = 0;
let hasChats = false;
this._sources.forEach(Lang.bind(this, this._sources.forEach(Lang.bind(this,
function(source) { function(source) {
count += source.indicatorCount; count += source.indicatorCount;
hasChats |= source.isChat;
})); }));
this._count = count; this._count = count;
@ -464,7 +462,6 @@ const MessagesIndicator = new Lang.Class({
"%d new messages", "%d new messages",
count).format(count); count).format(count);
this._icon.visible = hasChats;
this._updateVisibility(); this._updateVisibility();
}, },

@ -18,6 +18,7 @@ const Atk = imports.gi.Atk;
const Config = imports.misc.config; const Config = imports.misc.config;
const CtrlAltTab = imports.ui.ctrlAltTab; const CtrlAltTab = imports.ui.ctrlAltTab;
const DND = imports.ui.dnd; const DND = imports.ui.dnd;
const Layout = imports.ui.layout;
const Overview = imports.ui.overview; const Overview = imports.ui.overview;
const PopupMenu = imports.ui.popupMenu; const PopupMenu = imports.ui.popupMenu;
const PanelMenu = imports.ui.panelMenu; const PanelMenu = imports.ui.panelMenu;
@ -629,15 +630,23 @@ const ActivitiesButton = new Lang.Class({
this.parent(0.0, null, true); this.parent(0.0, null, true);
this.actor.accessible_role = Atk.Role.TOGGLE_BUTTON; this.actor.accessible_role = Atk.Role.TOGGLE_BUTTON;
let container = new Shell.GenericContainer();
container.connect('get-preferred-width', Lang.bind(this, this._containerGetPreferredWidth));
container.connect('get-preferred-height', Lang.bind(this, this._containerGetPreferredHeight));
container.connect('allocate', Lang.bind(this, this._containerAllocate));
this.actor.add_actor(container);
this.actor.name = 'panelActivities'; this.actor.name = 'panelActivities';
/* 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". */
this._label = new St.Label({ text: _("Activities") }); this._label = new St.Label({ text: _("Activities") });
this.actor.add_actor(this._label); container.add_actor(this._label);
this.actor.label_actor = this._label; this.actor.label_actor = this._label;
this.hotCorner = new Layout.HotCorner(Main.layoutManager);
container.add_actor(this.hotCorner.actor);
this.actor.connect('captured-event', Lang.bind(this, this._onCapturedEvent)); this.actor.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
this.actor.connect_after('button-release-event', Lang.bind(this, this._onButtonRelease)); this.actor.connect_after('button-release-event', Lang.bind(this, this._onButtonRelease));
this.actor.connect_after('key-release-event', Lang.bind(this, this._onKeyRelease)); this.actor.connect_after('key-release-event', Lang.bind(this, this._onKeyRelease));
@ -652,6 +661,44 @@ const ActivitiesButton = new Lang.Class({
})); }));
this._xdndTimeOut = 0; this._xdndTimeOut = 0;
// Since the hot corner uses stage coordinates, Clutter won't
// queue relayouts for us when the panel moves. Queue a relayout
// when that happens.
Main.layoutManager.connect('panel-box-changed', Lang.bind(this, function() {
container.queue_relayout();
}));
},
_containerGetPreferredWidth: function(actor, forHeight, alloc) {
[alloc.min_size, alloc.natural_size] = this._label.get_preferred_width(forHeight);
},
_containerGetPreferredHeight: function(actor, forWidth, alloc) {
[alloc.min_size, alloc.natural_size] = this._label.get_preferred_height(forWidth);
},
_containerAllocate: function(actor, box, flags) {
this._label.allocate(box, flags);
// The hot corner needs to be outside any padding/alignment
// that has been imposed on us
let primary = Main.layoutManager.primaryMonitor;
let hotBox = new Clutter.ActorBox();
let ok, x, y;
if (actor.get_text_direction() == Clutter.TextDirection.LTR) {
[ok, x, y] = actor.transform_stage_point(primary.x, primary.y)
} else {
[ok, x, y] = actor.transform_stage_point(primary.x + primary.width, primary.y);
// hotCorner.actor has northeast gravity, so we don't need
// to adjust x for its width
}
hotBox.x1 = Math.round(x);
hotBox.x2 = hotBox.x1 + this.hotCorner.actor.width;
hotBox.y1 = Math.round(y);
hotBox.y2 = hotBox.y1 + this.hotCorner.actor.height;
this.hotCorner.actor.allocate(hotBox, flags);
}, },
handleDragOver: function(source, actor, x, y, time) { handleDragOver: function(source, actor, x, y, time) {
@ -661,14 +708,14 @@ const ActivitiesButton = new Lang.Class({
if (this._xdndTimeOut != 0) if (this._xdndTimeOut != 0)
Mainloop.source_remove(this._xdndTimeOut); Mainloop.source_remove(this._xdndTimeOut);
this._xdndTimeOut = Mainloop.timeout_add(BUTTON_DND_ACTIVATION_TIMEOUT, this._xdndTimeOut = Mainloop.timeout_add(BUTTON_DND_ACTIVATION_TIMEOUT,
Lang.bind(this, this._xdndToggleOverview, actor)); Lang.bind(this, this._xdndShowOverview, actor));
return DND.DragMotionResult.CONTINUE; return DND.DragMotionResult.CONTINUE;
}, },
_onCapturedEvent: function(actor, event) { _onCapturedEvent: function(actor, event) {
if (event.type() == Clutter.EventType.BUTTON_PRESS) { if (event.type() == Clutter.EventType.BUTTON_PRESS) {
if (!Main.overview.shouldToggleByCornerOrButton()) if (!this.hotCorner.shouldToggleOverviewOnClick())
return true; return true;
} }
return false; return false;
@ -685,12 +732,15 @@ const ActivitiesButton = new Lang.Class({
} }
}, },
_xdndToggleOverview: function(actor) { _xdndShowOverview: function(actor) {
let [x, y, mask] = global.get_pointer(); let [x, y, mask] = global.get_pointer();
let pickedActor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y); let pickedActor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y);
if (pickedActor == this.actor && Main.overview.shouldToggleByCornerOrButton()) if (pickedActor == this.actor) {
Main.overview.toggle(); if (!Main.overview.visible && !Main.overview.animationInProgress) {
Main.overview.showTemporarily();
}
}
Mainloop.source_remove(this._xdndTimeOut); Mainloop.source_remove(this._xdndTimeOut);
this._xdndTimeOut = 0; this._xdndTimeOut = 0;

@ -30,6 +30,138 @@ const EXEC_ARG_KEY = 'exec-arg';
const DIALOG_GROW_TIME = 0.1; const DIALOG_GROW_TIME = 0.1;
const CommandCompleter = new Lang.Class({
Name: 'CommandCompleter',
_init : function() {
this._changedCount = 0;
this._paths = GLib.getenv('PATH').split(':');
this._paths.push(GLib.get_home_dir());
this._valid = false;
this._updateInProgress = false;
this._childs = new Array(this._paths.length);
this._monitors = new Array(this._paths.length);
for (let i = 0; i < this._paths.length; i++) {
this._childs[i] = [];
let file = Gio.file_new_for_path(this._paths[i]);
let info;
try {
info = file.query_info(Gio.FILE_ATTRIBUTE_STANDARD_TYPE, Gio.FileQueryInfoFlags.NONE, null);
} catch (e) {
// FIXME catchall
this._paths[i] = null;
continue;
}
if (info.get_attribute_uint32(Gio.FILE_ATTRIBUTE_STANDARD_TYPE) != Gio.FileType.DIRECTORY)
continue;
this._paths[i] = file.get_path();
this._monitors[i] = file.monitor_directory(Gio.FileMonitorFlags.NONE, null);
if (this._monitors[i] != null) {
this._monitors[i].connect('changed', Lang.bind(this, this._onChanged));
}
}
this._paths = this._paths.filter(function(a) {
return a != null;
});
this._update(0);
},
update : function() {
if (this._valid)
return;
this._update(0);
},
_update : function(i) {
if (i == 0 && this._updateInProgress)
return;
this._updateInProgress = true;
this._changedCount = 0;
this._i = i;
if (i >= this._paths.length) {
this._valid = true;
this._updateInProgress = false;
return;
}
let file = Gio.file_new_for_path(this._paths[i]);
this._childs[this._i] = [];
FileUtils.listDirAsync(file, Lang.bind(this, function (files) {
for (let i = 0; i < files.length; i++) {
this._childs[this._i].push(files[i].get_name());
}
this._update(this._i + 1);
}));
},
_onChanged : function(m, f, of, type) {
if (!this._valid)
return;
let path = f.get_parent().get_path();
let k = undefined;
for (let i = 0; i < this._paths.length; i++) {
if (this._paths[i] == path)
k = i;
}
if (k === undefined) {
return;
}
if (type == Gio.FileMonitorEvent.CREATED) {
this._childs[k].push(f.get_basename());
}
if (type == Gio.FileMonitorEvent.DELETED) {
this._changedCount++;
if (this._changedCount > MAX_FILE_DELETED_BEFORE_INVALID) {
this._valid = false;
}
let name = f.get_basename();
this._childs[k] = this._childs[k].filter(function(e) {
return e != name;
});
}
if (type == Gio.FileMonitorEvent.UNMOUNTED) {
this._childs[k] = [];
}
},
getCompletion: function(text) {
let common = '';
let notInit = true;
if (!this._valid) {
this._update(0);
return common;
}
function _getCommon(s1, s2) {
let k = 0;
for (; k < s1.length && k < s2.length; k++) {
if (s1[k] != s2[k])
break;
}
if (k == 0)
return '';
return s1.substr(0, k);
}
function _hasPrefix(s1, prefix) {
return s1.indexOf(prefix) == 0;
}
for (let i = 0; i < this._childs.length; i++) {
for (let k = 0; k < this._childs[i].length; k++) {
if (!_hasPrefix(this._childs[i][k], text))
continue;
if (notInit) {
common = this._childs[i][k];
notInit = false;
}
common = _getCommon(common, this._childs[i][k]);
}
}
if (common.length)
return common.substr(text.length);
return common;
}
});
const RunDialog = new Lang.Class({ const RunDialog = new Lang.Class({
Name: 'RunDialog', Name: 'RunDialog',
Extends: ModalDialog.ModalDialog, Extends: ModalDialog.ModalDialog,
@ -110,6 +242,8 @@ const RunDialog = new Lang.Class({
key: Clutter.Escape }]); key: Clutter.Escape }]);
this._pathCompleter = new Gio.FilenameCompleter(); this._pathCompleter = new Gio.FilenameCompleter();
this._commandCompleter = new CommandCompleter();
this._group.connect('notify::visible', Lang.bind(this._commandCompleter, this._commandCompleter.update));
this._history = new History.HistoryManager({ gsettingsKey: HISTORY_KEY, this._history = new History.HistoryManager({ gsettingsKey: HISTORY_KEY,
entry: this._entryText }); entry: this._entryText });
@ -125,6 +259,18 @@ const RunDialog = new Lang.Class({
return true; return true;
} }
if (symbol == Clutter.slash) {
// Need preload data before get completion. GFilenameCompleter load content of parent directory.
// Parent directory for /usr/include/ is /usr/. So need to add fake name('a').
let text = o.get_text().concat('/a');
let prefix;
if (text.lastIndexOf(' ') == -1)
prefix = text;
else
prefix = text.substr(text.lastIndexOf(' ') + 1);
this._getCompletion(prefix);
return false;
}
if (symbol == Clutter.Tab) { if (symbol == Clutter.Tab) {
let text = o.get_text(); let text = o.get_text();
let prefix; let prefix;
@ -136,6 +282,8 @@ const RunDialog = new Lang.Class({
if (postfix != null && postfix.length > 0) { if (postfix != null && postfix.length > 0) {
o.insert_text(postfix, -1); o.insert_text(postfix, -1);
o.set_cursor_position(text.length + postfix.length); o.set_cursor_position(text.length + postfix.length);
if (postfix[postfix.length - 1] == '/')
this._getCompletion(text + postfix + 'a');
} }
return true; return true;
} }
@ -143,53 +291,11 @@ const RunDialog = new Lang.Class({
})); }));
}, },
_getCommandCompletion: function(text) {
function _getCommon(s1, s2) {
if (s1 == null)
return s2;
let k = 0;
for (; k < s1.length && k < s2.length; k++) {
if (s1[k] != s2[k])
break;
}
if (k == 0)
return '';
return s1.substr(0, k);
}
let paths = GLib.getenv('PATH').split(':');
paths.push(GLib.get_home_dir());
let someResults = paths.map(function(path) {
let results = [];
try {
let file = Gio.File.new_for_path(path);
let fileEnum = file.enumerate_children('standard::name', Gio.FileQueryInfoFlags.NONE, null);
let info;
while ((info = fileEnum.next_file(null))) {
let name = info.get_name();
if (name.slice(0, text.length) == text)
results.push(name);
}
} catch (e if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND) &&
!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_DIRECTORY))) {
log(e);
} finally {
return results;
}
});
let results = someResults.reduce(function(a, b) {
return a.concat(b);
}, []);
let common = results.reduce(_getCommon, null);
return common.substr(text.length);
},
_getCompletion : function(text) { _getCompletion : function(text) {
if (text.indexOf('/') != -1) { if (text.indexOf('/') != -1) {
return this._pathCompleter.get_completion_suffix(text); return this._pathCompleter.get_completion_suffix(text);
} else { } else {
return this._getCommandCompletion(text); return this._commandCompleter.getCompletion(text);
} }
}, },

@ -38,7 +38,11 @@ const ARROW_DRAG_THRESHOLD = 0.1;
const N_ARROWS = 3; const N_ARROWS = 3;
const ARROW_ANIMATION_TIME = 0.6; const ARROW_ANIMATION_TIME = 0.6;
const ARROW_ANIMATION_PEAK_OPACITY = 0.4; const ARROW_ANIMATION_PEAK_OPACITY = 0.4;
const ARROW_IDLE_TIME = 30000; // ms
// The distance in px that the lock screen will move to when pressing
// a key that has no effect in the lock screen (bumping it)
const BUMP_SIZE = 25;
const BUMP_TIME = 0.3;
const SUMMARY_ICON_SIZE = 48; const SUMMARY_ICON_SIZE = 48;
@ -50,7 +54,7 @@ const SUMMARY_ICON_SIZE = 48;
// - CURTAIN_SLIDE_TIME is used when raising the shield before unlocking // - CURTAIN_SLIDE_TIME is used when raising the shield before unlocking
// - INITIAL_FADE_IN_TIME is used for the initial fade in at startup // - INITIAL_FADE_IN_TIME is used for the initial fade in at startup
const STANDARD_FADE_TIME = 10; const STANDARD_FADE_TIME = 10;
const MANUAL_FADE_TIME = 0.3; const MANUAL_FADE_TIME = 0.8;
const BACKGROUND_FADE_TIME = 1.0; const BACKGROUND_FADE_TIME = 1.0;
const CURTAIN_SLIDE_TIME = 0.3; const CURTAIN_SLIDE_TIME = 0.3;
const INITIAL_FADE_IN_TIME = 0.25; const INITIAL_FADE_IN_TIME = 0.25;
@ -431,8 +435,8 @@ const ScreenShield = new Lang.Class({
name: 'lockScreenGroup', name: 'lockScreenGroup',
visible: false, visible: false,
}); });
this._lockScreenGroup.connect('key-press-event', this._lockScreenGroup.connect('key-release-event',
Lang.bind(this, this._onLockScreenKeyPress)); Lang.bind(this, this._onLockScreenKeyRelease));
this._lockScreenGroup.connect('scroll-event', this._lockScreenGroup.connect('scroll-event',
Lang.bind(this, this._onLockScreenScroll)); Lang.bind(this, this._onLockScreenScroll));
Main.ctrlAltTabManager.addGroup(this._lockScreenGroup, _("Lock"), 'changes-prevent-symbolic'); Main.ctrlAltTabManager.addGroup(this._lockScreenGroup, _("Lock"), 'changes-prevent-symbolic');
@ -452,9 +456,6 @@ const ScreenShield = new Lang.Class({
this._updateBackgrounds(); this._updateBackgrounds();
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._updateBackgrounds)); Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._updateBackgrounds));
this._arrowAnimationId = 0;
this._arrowWatchId = 0;
this._arrowActiveWatchId = 0;
this._arrowContainer = new St.BoxLayout({ style_class: 'screen-shield-arrows', this._arrowContainer = new St.BoxLayout({ style_class: 'screen-shield-arrows',
vertical: true, vertical: true,
x_align: Clutter.ActorAlign.CENTER, x_align: Clutter.ActorAlign.CENTER,
@ -506,18 +507,14 @@ const ScreenShield = new Lang.Class({
this._screenSaverDBus = new ShellDBus.ScreenSaverDBus(this); this._screenSaverDBus = new ShellDBus.ScreenSaverDBus(this);
this._inhibitor = null; this._inhibitor = null;
this._aboutToSuspend = false;
this._loginManager = LoginManager.getLoginManager(); this._loginManager = LoginManager.getLoginManager();
this._loginManager.connect('prepare-for-sleep', this._loginManager.connect('prepare-for-sleep',
Lang.bind(this, this._prepareForSleep)); Lang.bind(this, this._prepareForSleep));
this._inhibitSuspend(); this._inhibitSuspend();
this._loginManager.getCurrentSessionProxy(Lang.bind(this, this._loginSession = this._loginManager.getCurrentSessionProxy();
function(sessionProxy) { this._loginSession.connectSignal('Lock', Lang.bind(this, function() { this.lock(false); }));
this._loginSession = sessionProxy; this._loginSession.connectSignal('Unlock', Lang.bind(this, function() { this.deactivate(false); }));
this._loginSession.connectSignal('Lock', Lang.bind(this, function() { this.lock(false); }));
this._loginSession.connectSignal('Unlock', Lang.bind(this, function() { this.deactivate(false); }));
}));
this._settings = new Gio.Settings({ schema: SCREENSAVER_SCHEMA }); this._settings = new Gio.Settings({ schema: SCREENSAVER_SCHEMA });
@ -550,7 +547,9 @@ const ScreenShield = new Lang.Class({
let bgManager = new Background.BackgroundManager({ container: widget, let bgManager = new Background.BackgroundManager({ container: widget,
monitorIndex: monitorIndex, monitorIndex: monitorIndex,
effects: Meta.BackgroundEffects.BLUR | Meta.BackgroundEffects.DESATURATE,
controlPosition: false }); controlPosition: false });
bgManager.background.saturation = 0.6;
this._bgManagers.push(bgManager); this._bgManagers.push(bgManager);
@ -591,28 +590,28 @@ const ScreenShield = new Lang.Class({
return this._isModal; return this._isModal;
}, },
_onLockScreenKeyPress: function(actor, event) { _onLockScreenKeyRelease: function(actor, event) {
let symbol = event.get_key_symbol(); let symbol = event.get_key_symbol();
let unichar = event.get_key_unicode();
// Do nothing if the lock screen is not fully shown. // Do nothing if the lock screen is not fully shown.
// This avoids reusing the previous (and stale) unlock // This avoids reusing the previous (and stale) unlock
// dialog if esc is pressed while the curtain is going // dialog if esc is pressed while the curtain is going
// down after cancel. // down after cancel.
// Similarly, don't bump if the lock screen is not showing or is
// animating, as the bump overrides the animation and would
// remove any onComplete handler.
if (this._lockScreenState != MessageTray.State.SHOWN) if (this._lockScreenState != MessageTray.State.SHOWN)
return false; return false;
let isEnter = (symbol == Clutter.KEY_Return || symbol == Clutter.KEY_KP_Enter); if (symbol == Clutter.KEY_Escape ||
if (!isEnter && !(GLib.unichar_isprint(unichar) || symbol == Clutter.KEY_Escape)) symbol == Clutter.KEY_Return ||
return false; symbol == Clutter.KEY_KP_Enter) {
this._liftShield(false, 0);
return true;
}
this._ensureUnlockDialog(true, true); this._bumpLockScreen();
if (GLib.unichar_isgraph(unichar))
this._dialog.addCharacter(unichar);
this._liftShield(true, 0);
return true; return true;
}, },
@ -650,8 +649,6 @@ const ScreenShield = new Lang.Class({
}, },
_prepareForSleep: function(loginManager, aboutToSuspend) { _prepareForSleep: function(loginManager, aboutToSuspend) {
this._aboutToSuspend = aboutToSuspend;
if (aboutToSuspend) { if (aboutToSuspend) {
if (!this._settings.get_boolean(LOCK_ENABLED_KEY)) { if (!this._settings.get_boolean(LOCK_ENABLED_KEY)) {
this._uninhibitSuspend(); this._uninhibitSuspend();
@ -852,6 +849,21 @@ const ScreenShield = new Lang.Class({
this._hideLockScreen(false, 0); this._hideLockScreen(false, 0);
}, },
_bumpLockScreen: function() {
Tweener.removeTweens(this._lockScreenGroup);
Tweener.addTween(this._lockScreenGroup,
{ y: -BUMP_SIZE,
time: BUMP_TIME / 2,
transition: 'easeOutQuad',
onComplete: function() {
Tweener.addTween(this,
{ y: 0,
time: BUMP_TIME / 2,
transition: 'easeInQuad' });
}
});
},
_hideLockScreenComplete: function() { _hideLockScreenComplete: function() {
if (Main.sessionMode.currentMode == 'lock-screen') if (Main.sessionMode.currentMode == 'lock-screen')
Main.sessionMode.popMode('lock-screen'); Main.sessionMode.popMode('lock-screen');
@ -905,12 +917,14 @@ const ScreenShield = new Lang.Class({
let time = global.get_current_time(); let time = global.get_current_time();
if (!this._dialog.open(time, onPrimary)) { this._dialog.connect('loaded', Lang.bind(this, function() {
// This is kind of an impossible error: we're already modal if (!this._dialog.open(time, onPrimary)) {
// by the time we reach this... // This is kind of an impossible error: we're already modal
log('Could not open login dialog: failed to acquire grab'); // by the time we reach this...
this.deactivate(true); log('Could not open login dialog: failed to acquire grab');
} this.deactivate(true);
}
}));
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.connect('unlocked', Lang.bind(this, this._onUnlockSucceded));
@ -976,60 +990,16 @@ const ScreenShield = new Lang.Class({
Main.sessionMode.pushMode('lock-screen'); Main.sessionMode.pushMode('lock-screen');
}, },
_startArrowAnimation: function() {
this._arrowActiveWatchId = 0;
if (!this._arrowAnimationId) {
this._arrowAnimationId = Mainloop.timeout_add(6000, Lang.bind(this, this._animateArrows));
this._animateArrows();
}
if (!this._arrowWatchId)
this._arrowWatchId = this.idleMonitor.add_idle_watch(ARROW_IDLE_TIME,
Lang.bind(this, this._pauseArrowAnimation));
},
_pauseArrowAnimation: function() {
if (this._arrowAnimationId) {
Mainloop.source_remove(this._arrowAnimationId);
this._arrowAnimationId = 0;
}
if (!this._arrowActiveWatchId)
this._arrowActiveWatchId = this.idleMonitor.add_user_active_watch(Lang.bind(this, this._startArrowAnimation));
},
_stopArrowAnimation: function() {
if (this._arrowAnimationId) {
Mainloop.source_remove(this._arrowAnimationId);
this._arrowAnimationId = 0;
}
if (this._arrowActiveWatchId) {
this.idleMonitor.remove_watch(this._arrowActiveWatchId);
this._arrowActiveWatchId = 0;
}
if (this._arrowWatchId) {
this.idleMonitor.remove_watch(this._arrowWatchId);
this._arrowWatchId = 0;
}
},
_checkArrowAnimation: function() {
let idleTime = this.idleMonitor.get_idletime();
if (idleTime < ARROW_IDLE_TIME)
this._startArrowAnimation();
else
this._pauseArrowAnimation();
},
_lockScreenShown: function() { _lockScreenShown: function() {
if (this._dialog && !this._isGreeter) { if (this._dialog && !this._isGreeter) {
this._dialog.destroy(); this._dialog.destroy();
this._dialog = null; this._dialog = null;
} }
this._checkArrowAnimation(); if (this._arrowAnimationId)
Mainloop.source_remove(this._arrowAnimationId);
this._arrowAnimationId = Mainloop.timeout_add(6000, Lang.bind(this, this._animateArrows));
this._animateArrows();
let motionId = global.stage.connect('captured-event', function(stage, event) { let motionId = global.stage.connect('captured-event', function(stage, event) {
if (event.type() == Clutter.EventType.MOTION) { if (event.type() == Clutter.EventType.MOTION) {
@ -1051,8 +1021,7 @@ const ScreenShield = new Lang.Class({
if (prevIsActive != this._isActive) if (prevIsActive != this._isActive)
this.emit('active-changed'); this.emit('active-changed');
if (this._aboutToSuspend) this._uninhibitSuspend();
this._uninhibitSuspend();
this.emit('lock-screen-shown'); this.emit('lock-screen-shown');
}, },
@ -1092,10 +1061,13 @@ const ScreenShield = new Lang.Class({
this._notificationsBox = null; this._notificationsBox = null;
} }
this._stopArrowAnimation();
this._lockScreenContentsBox.destroy(); this._lockScreenContentsBox.destroy();
if (this._arrowAnimationId) {
Mainloop.source_remove(this._arrowAnimationId);
this._arrowAnimationId = 0;
}
this._hasLockScreen = false; this._hasLockScreen = false;
}, },
@ -1206,3 +1178,62 @@ const ScreenShield = new Lang.Class({
}, },
}); });
Signals.addSignalMethods(ScreenShield.prototype); Signals.addSignalMethods(ScreenShield.prototype);
/* Fallback code to handle session locking using gnome-screensaver,
in case the required GDM dependency is not there
*/
const ScreenShieldFallback = new Lang.Class({
Name: 'ScreenShieldFallback',
_init: function() {
Util.spawn(['gnome-screensaver']);
this._proxy = new Gio.DBusProxy({ g_connection: Gio.DBus.session,
g_name: 'org.gnome.ScreenSaver',
g_object_path: '/org/gnome/ScreenSaver',
g_interface_name: 'org.gnome.ScreenSaver',
g_flags: (Gio.DBusProxyFlags.DO_NOT_AUTO_START |
Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES),
});
this._proxy.init(null);
this._proxy.connect('g-signal', Lang.bind(this, this._onSignal));
this._proxy.connect('notify::g-name-owner', Lang.bind(this, this._onNameOwnerChanged));
},
_onNameOwnerChanged: function(object, pspec) {
if (this._proxy.g_name_owner)
[this._locked] = this._proxy.call_sync('GetActive', null,
Gio.DBusCallFlags.NONE, -1, null).deep_unpack();
else
this._locked = false;
this.emit('active-changed', this._locked);
},
_onSignal: function(proxy, senderName, signalName, params) {
if (signalName == 'ActiveChanged') {
[this._locked] = params.deep_unpack();
this.emit('active-changed', this._locked);
}
},
get locked() {
return this._locked;
},
lock: function() {
this._proxy.call('Lock', null, Gio.DBusCallFlags.NONE, -1, null,
Lang.bind(this, function(proxy, result) {
proxy.call_finish(result);
this.emit('lock-screen-shown');
}));
},
unlock: function() {
this._proxy.call('SetActive', GLib.Variant.new('(b)', false),
Gio.DBusCallFlags.NONE, -1, null, null);
},
});
Signals.addSignalMethods(ScreenShieldFallback.prototype);

@ -4,7 +4,6 @@ const Clutter = imports.gi.Clutter;
const Lang = imports.lang; const Lang = imports.lang;
const Gtk = imports.gi.Gtk; const Gtk = imports.gi.Gtk;
const Meta = imports.gi.Meta; const Meta = imports.gi.Meta;
const Signals = imports.signals;
const St = imports.gi.St; const St = imports.gi.St;
const Atk = imports.gi.Atk; const Atk = imports.gi.Atk;
@ -14,7 +13,6 @@ const Main = imports.ui.main;
const Overview = imports.ui.overview; const Overview = imports.ui.overview;
const Separator = imports.ui.separator; const Separator = imports.ui.separator;
const Search = imports.ui.search; const Search = imports.ui.search;
const Util = imports.misc.util;
const MAX_LIST_SEARCH_RESULTS_ROWS = 3; const MAX_LIST_SEARCH_RESULTS_ROWS = 3;
const MAX_GRID_SEARCH_RESULTS_ROWS = 1; const MAX_GRID_SEARCH_RESULTS_ROWS = 1;
@ -104,7 +102,6 @@ const ListSearchResult = new Lang.Class({
y_fill: false, y_fill: false,
x_align: St.Align.START, x_align: St.Align.START,
y_align: St.Align.START }); y_align: St.Align.START });
this.actor.label_actor = title;
if (this.metaInfo['description']) { if (this.metaInfo['description']) {
let description = new St.Label({ style_class: 'list-search-result-description' }); let description = new St.Label({ style_class: 'list-search-result-description' });
@ -142,7 +139,6 @@ const GridSearchResult = new Lang.Class({
} }
this.actor.set_child(content); this.actor.set_child(content);
this.actor.label_actor = content.label_actor;
let draggable = DND.makeDraggable(this.actor); let draggable = DND.makeDraggable(this.actor);
draggable.connect('drag-begin', draggable.connect('drag-begin',
@ -231,14 +227,9 @@ const ListSearchResults = new Lang.Class({
this._pendingClear = true; this._pendingClear = true;
}, },
_keyFocusIn: function(icon) {
this.emit('key-focus-in', icon);
},
renderResults: function(metas) { renderResults: function(metas) {
for (let i = 0; i < metas.length; i++) { for (let i = 0; i < metas.length; i++) {
let display = new ListSearchResult(this.provider, metas[i], this._terms); let display = new ListSearchResult(this.provider, metas[i], this._terms);
display.actor.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
this._content.add_actor(display.actor); this._content.add_actor(display.actor);
} }
}, },
@ -255,7 +246,6 @@ const ListSearchResults = new Lang.Class({
return null; return null;
} }
}); });
Signals.addSignalMethods(ListSearchResults.prototype);
const GridSearchResults = new Lang.Class({ const GridSearchResults = new Lang.Class({
Name: 'GridSearchResults', Name: 'GridSearchResults',
@ -298,14 +288,9 @@ const GridSearchResults = new Lang.Class({
this._pendingClear = true; this._pendingClear = true;
}, },
_keyFocusIn: function(icon) {
this.emit('key-focus-in', icon);
},
renderResults: function(metas) { renderResults: function(metas) {
for (let i = 0; i < metas.length; i++) { for (let i = 0; i < metas.length; i++) {
let display = new GridSearchResult(this.provider, metas[i], this._terms); let display = new GridSearchResult(this.provider, metas[i], this._terms);
display.actor.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
this._grid.addItem(display.actor); this._grid.addItem(display.actor);
} }
}, },
@ -322,7 +307,6 @@ const GridSearchResults = new Lang.Class({
return null; return null;
} }
}); });
Signals.addSignalMethods(GridSearchResults.prototype);
const SearchResults = new Lang.Class({ const SearchResults = new Lang.Class({
Name: 'SearchResults', Name: 'SearchResults',
@ -382,10 +366,6 @@ const SearchResults = new Lang.Class({
return false; return false;
}, },
_keyFocusIn: function(provider, icon) {
Util.ensureActorVisibleInScrollView(this._scrollView, icon);
},
createProviderMeta: function(provider) { createProviderMeta: function(provider) {
let providerBox = new St.BoxLayout({ style_class: 'search-section', let providerBox = new St.BoxLayout({ style_class: 'search-section',
vertical: true }); vertical: true });
@ -399,8 +379,6 @@ const SearchResults = new Lang.Class({
resultDisplay = new GridSearchResults(provider); resultDisplay = new GridSearchResults(provider);
} }
resultDisplay.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
let resultDisplayBin = new St.Bin({ child: resultDisplay.actor, let resultDisplayBin = new St.Bin({ child: resultDisplay.actor,
x_fill: true, x_fill: true,
y_fill: true }); y_fill: true });
@ -579,7 +557,6 @@ const ProviderIcon = new Lang.Class({
this.parent({ style_class: 'search-provider-icon', this.parent({ style_class: 'search-provider-icon',
reactive: true, reactive: true,
can_focus: true, can_focus: true,
accessible_name: provider.appInfo.get_name(),
track_hover: true }); track_hover: true });
this._content = new St.Widget({ layout_manager: new Clutter.BinLayout() }); this._content = new St.Widget({ layout_manager: new Clutter.BinLayout() });

@ -79,13 +79,12 @@ const _modes = {
}, },
'initial-setup': { 'initial-setup': {
hasWindows: true,
isPrimary: true, isPrimary: true,
components: ['keyring'], components: ['keyring'],
panel: { panel: {
left: [], left: [],
center: ['dateMenu'], center: ['dateMenu'],
right: ['a11yGreeter', 'keyboard', 'volume', 'battery'] right: ['a11yGreeter', 'keyboard', 'volume']
} }
}, },
@ -159,7 +158,12 @@ function listModes() {
const SessionMode = new Lang.Class({ const SessionMode = new Lang.Class({
Name: 'SessionMode', Name: 'SessionMode',
init: function() { _init: function() {
global.connect('notify::session-mode', Lang.bind(this, this._sync));
this._modes = _modes;
this._modeStack = [DEFAULT_MODE];
this._sync();
_getModes(Lang.bind(this, function(modes) { _getModes(Lang.bind(this, function(modes) {
this._modes = modes; this._modes = modes;
let primary = modes[global.session_mode] && let primary = modes[global.session_mode] &&
@ -167,8 +171,6 @@ const SessionMode = new Lang.Class({
let mode = primary ? global.session_mode : 'user'; let mode = primary ? global.session_mode : 'user';
this._modeStack = [mode]; this._modeStack = [mode];
this._sync(); this._sync();
this.emit('sessions-loaded');
})); }));
}, },

@ -337,14 +337,14 @@ const InputSourceIndicator = new Lang.Class({
Main.wm.addKeybinding('switch-input-source', Main.wm.addKeybinding('switch-input-source',
new Gio.Settings({ schema: "org.gnome.desktop.wm.keybindings" }), new Gio.Settings({ schema: "org.gnome.desktop.wm.keybindings" }),
Meta.KeyBindingFlags.REVERSES, Meta.KeyBindingFlags.REVERSES,
Shell.KeyBindingMode.ALL & ~Shell.KeyBindingMode.MESSAGE_TRAY, Shell.KeyBindingMode.ALL,
Lang.bind(this, this._switchInputSource)); Lang.bind(this, this._switchInputSource));
this._keybindingActionBackward = this._keybindingActionBackward =
Main.wm.addKeybinding('switch-input-source-backward', Main.wm.addKeybinding('switch-input-source-backward',
new Gio.Settings({ schema: "org.gnome.desktop.wm.keybindings" }), new Gio.Settings({ schema: "org.gnome.desktop.wm.keybindings" }),
Meta.KeyBindingFlags.REVERSES | Meta.KeyBindingFlags.REVERSES |
Meta.KeyBindingFlags.REVERSED, Meta.KeyBindingFlags.REVERSED,
Shell.KeyBindingMode.ALL & ~Shell.KeyBindingMode.MESSAGE_TRAY, Shell.KeyBindingMode.ALL,
Lang.bind(this, this._switchInputSource)); Lang.bind(this, this._switchInputSource));
this._settings = new Gio.Settings({ schema: DESKTOP_INPUT_SOURCES_SCHEMA }); this._settings = new Gio.Settings({ schema: DESKTOP_INPUT_SOURCES_SCHEMA });
this._settings.connect('changed::' + KEY_CURRENT_INPUT_SOURCE, Lang.bind(this, this._currentInputSourceChanged)); this._settings.connect('changed::' + KEY_CURRENT_INPUT_SOURCE, Lang.bind(this, this._currentInputSourceChanged));
@ -416,11 +416,6 @@ const InputSourceIndicator = new Lang.Class({
let oldSource; let oldSource;
[oldSource, this._currentSource] = [this._currentSource, newSource]; [oldSource, this._currentSource] = [this._currentSource, newSource];
if (oldSource) {
oldSource.menuItem.setShowDot(false);
oldSource.indicatorLabel.hide();
}
if (!newSource || (nVisibleSources < 2 && !newSource.properties)) { if (!newSource || (nVisibleSources < 2 && !newSource.properties)) {
// This source index might be invalid if we weren't able // This source index might be invalid if we weren't able
// to build a menu item for it, so we hide ourselves since // to build a menu item for it, so we hide ourselves since
@ -435,6 +430,11 @@ const InputSourceIndicator = new Lang.Class({
this.actor.show(); this.actor.show();
if (oldSource) {
oldSource.menuItem.setShowDot(false);
oldSource.indicatorLabel.hide();
}
newSource.menuItem.setShowDot(true); newSource.menuItem.setShowDot(true);
newSource.indicatorLabel.show(); newSource.indicatorLabel.show();
@ -487,7 +487,7 @@ const InputSourceIndicator = new Lang.Class({
let is = new InputSource(type, id, displayName, shortName, i); let is = new InputSource(type, id, displayName, shortName, i);
is.connect('activate', Lang.bind(this, function() { is.connect('activate', Lang.bind(this, function() {
if (this._currentSource && this._currentSource.index == is.index) if (this._currentSource.index == is.index)
return; return;
this._settings.set_value(KEY_CURRENT_INPUT_SOURCE, this._settings.set_value(KEY_CURRENT_INPUT_SOURCE,
GLib.Variant.new_uint32(is.index)); GLib.Variant.new_uint32(is.index));
@ -703,7 +703,7 @@ const InputSourceIndicator = new Lang.Class({
item.prop = prop; item.prop = prop;
item.connect('activate', Lang.bind(this, function() { item.connect('activate', Lang.bind(this, function() {
this._ibusManager.activateProperty(item.prop.get_key(), this._ibusManager.activateProperty(item.prop.get_key(),
item.prop.get_state()); IBus.PropState.CHECKED);
})); }));
break; break;

@ -27,6 +27,35 @@ const LoginDialog = imports.gdm.loginDialog;
// The timeout before going back automatically to the lock screen (in seconds) // The timeout before going back automatically to the lock screen (in seconds)
const IDLE_TIMEOUT = 2 * 60; const IDLE_TIMEOUT = 2 * 60;
function versionCompare(required, reference) {
required = required.split('.');
reference = reference.split('.');
for (let i = 0; i < required.length; i++) {
if (required[i] != reference[i])
return required[i] < reference[i];
}
return true;
}
function isSupported() {
try {
let params = GLib.Variant.new('(ss)', ['org.gnome.DisplayManager.Manager', 'Version']);
let result = Gio.DBus.system.call_sync('org.gnome.DisplayManager',
'/org/gnome/DisplayManager/Manager',
'org.freedesktop.DBus.Properties',
'Get', params, null,
Gio.DBusCallFlags.NONE,
-1, null);
let version = result.deep_unpack()[0].deep_unpack();
return versionCompare('3.5.91', version);
} catch(e) {
return false;
}
}
const UnlockDialog = new Lang.Class({ const UnlockDialog = new Lang.Class({
Name: 'UnlockDialog', Name: 'UnlockDialog',
Extends: ModalDialog.ModalDialog, Extends: ModalDialog.ModalDialog,
@ -47,7 +76,6 @@ const UnlockDialog = new Lang.Class({
this._greeterClient = new Gdm.Client(); this._greeterClient = new Gdm.Client();
this._userVerifier = new GdmUtil.ShellUserVerifier(this._greeterClient, { reauthenticationOnly: true }); this._userVerifier = new GdmUtil.ShellUserVerifier(this._greeterClient, { reauthenticationOnly: true });
this._userVerified = false;
this._userVerifier.connect('ask-question', Lang.bind(this, this._onAskQuestion)); this._userVerifier.connect('ask-question', Lang.bind(this, this._onAskQuestion));
this._userVerifier.connect('show-message', Lang.bind(this, this._showMessage)); this._userVerifier.connect('show-message', Lang.bind(this, this._showMessage));
@ -70,7 +98,6 @@ const UnlockDialog = new Lang.Class({
this._promptEntry = new St.Entry({ style_class: 'login-dialog-prompt-entry', this._promptEntry = new St.Entry({ style_class: 'login-dialog-prompt-entry',
can_focus: true }); can_focus: true });
this._promptEntry.clutter_text.connect('activate', Lang.bind(this, this._doUnlock));
this._promptEntry.clutter_text.set_password_char('\u25cf'); this._promptEntry.clutter_text.set_password_char('\u25cf');
ShellEntry.addContextMenu(this._promptEntry, { isPassword: true }); ShellEntry.addContextMenu(this._promptEntry, { isPassword: true });
this.setInitialKeyFocus(this._promptEntry); this.setInitialKeyFocus(this._promptEntry);
@ -142,6 +169,11 @@ const UnlockDialog = new Lang.Class({
let batch = new Batch.Hold(); let batch = new Batch.Hold();
this._userVerifier.begin(this._userName, batch); this._userVerifier.begin(this._userName, batch);
GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
this.emit('loaded');
return false;
}));
Main.ctrlAltTabManager.addGroup(this.dialogLayout, _("Unlock Window"), 'dialog-password-symbolic'); Main.ctrlAltTabManager.addGroup(this.dialogLayout, _("Unlock Window"), 'dialog-password-symbolic');
this._idleMonitor = new GnomeDesktop.IdleMonitor(); this._idleMonitor = new GnomeDesktop.IdleMonitor();
@ -250,35 +282,18 @@ const UnlockDialog = new Lang.Class({
this._userVerifier.answerQuery(query, this._promptEntry.text); this._userVerifier.answerQuery(query, this._promptEntry.text);
}, },
_finishUnlock: function() { _onVerificationComplete: function() {
this._userVerifier.clear(); this._userVerifier.clear();
this.emit('unlocked'); 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() { _onReset: function() {
if (!this._userVerified) { this.emit('failed');
this._userVerifier.clear();
this.emit('failed');
}
}, },
_onVerificationFailed: function() { _onVerificationFailed: function() {
this._currentQuery = null; this._currentQuery = null;
this._firstQuestion = true; this._firstQuestion = true;
this._userVerified = false;
this._promptEntry.text = ''; this._promptEntry.text = '';
this._promptEntry.clutter_text.set_password_char('\u25cf'); this._promptEntry.clutter_text.set_password_char('\u25cf');
@ -318,8 +333,4 @@ const UnlockDialog = new Lang.Class({
this.destroy(); this.destroy();
}, },
addCharacter: function(unichar) {
this._promptEntry.clutter_text.insert_unichar(unichar);
},
}); });

@ -600,8 +600,7 @@ const UserMenuButton = new Lang.Class({
Lang.bind(this, this._updateHaveShutdown)); Lang.bind(this, this._updateHaveShutdown));
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated)); Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
if (Main.screenShield) Main.screenShield.connect('locked-changed', Lang.bind(this, this._updatePresenceIcon));
Main.screenShield.connect('locked-changed', Lang.bind(this, this._updatePresenceIcon));
this._sessionUpdated(); this._sessionUpdated();
}, },
@ -657,7 +656,7 @@ const UserMenuButton = new Lang.Class({
_updateLockScreen: function() { _updateLockScreen: function() {
let allowLockScreen = !this._lockdownSettings.get_boolean(DISABLE_LOCK_SCREEN_KEY); let allowLockScreen = !this._lockdownSettings.get_boolean(DISABLE_LOCK_SCREEN_KEY);
this._lockScreenItem.actor.visible = allowLockScreen && LoginManager.canLock(); this._lockScreenItem.actor.visible = allowLockScreen;
}, },
_updateInstallUpdates: function() { _updateInstallUpdates: function() {
@ -865,8 +864,7 @@ const UserMenuButton = new Lang.Class({
_onLoginScreenActivate: function() { _onLoginScreenActivate: function() {
this.menu.close(BoxPointer.PopupAnimation.NONE); this.menu.close(BoxPointer.PopupAnimation.NONE);
Main.overview.hide(); Main.overview.hide();
if (Main.screenShield) Main.screenShield.lock(false);
Main.screenShield.lock(false);
Gdm.goto_login_session_sync(null); Gdm.goto_login_session_sync(null);
}, },
@ -917,10 +915,8 @@ const UserMenuButton = new Lang.Class({
session.user.get_real_name() : session.username; session.user.get_real_name() : session.username;
if (session.info.remote) if (session.info.remote)
/* Translators: Remote here refers to a remote session, like a ssh login */
userLabelText = _("%s (remote)").format(userName); userLabelText = _("%s (remote)").format(userName);
else if (session.info.type == "tty") else if (session.info.type == "tty")
/* Translators: Console here refers to a tty like a VT console */
userLabelText = _("%s (console)").format(userName); userLabelText = _("%s (console)").format(userName);
else else
userLabelText = userName; userLabelText = userName;

@ -11,14 +11,12 @@ const Layout = imports.ui.layout;
const Main = imports.ui.main; const Main = imports.ui.main;
const Panel = imports.ui.panel; const Panel = imports.ui.panel;
// we could make these gsettings
const FISH_NAME = 'wanda'; const FISH_NAME = 'wanda';
const FISH_FILENAME = 'wanda.png';
const FISH_SPEED = 300; const FISH_SPEED = 300;
const FISH_COMMAND = 'fortune'; const FISH_COMMAND = 'fortune';
// The size of an individual frame in the animation
const FISH_HEIGHT = 22;
const FISH_WIDTH = 36;
const GNOME_PANEL_PIXMAPDIR = '../gnome-panel/fish';
const FISH_GROUP = 'Fish Animation'; const FISH_GROUP = 'Fish Animation';
const MAGIC_FISH_KEY = 'free the fish'; const MAGIC_FISH_KEY = 'free the fish';
@ -28,16 +26,33 @@ const WandaIcon = new Lang.Class({
Extends: IconGrid.BaseIcon, Extends: IconGrid.BaseIcon,
_init : function(fish, label, params) { _init : function(fish, label, params) {
this.parent(label, params);
this._fish = fish; this._fish = fish;
this._imageFile = GLib.build_filenamev([global.datadir, fish + '.png']); let file = GLib.build_filenamev([global.datadir, GNOME_PANEL_PIXMAPDIR, fish + '.fish']);
this._imgHeight = FISH_HEIGHT; if (GLib.file_test(file, GLib.FileTest.EXISTS)) {
this._imgWidth = FISH_WIDTH; this._keyfile = new GLib.KeyFile();
this._keyfile.load_from_file(file, GLib.KeyFileFlags.NONE);
this._imageFile = GLib.build_filenamev([global.datadir, GNOME_PANEL_PIXMAPDIR,
this._keyfile.get_string(FISH_GROUP, 'image')]);
let tmpPixbuf = GdkPixbuf.Pixbuf.new_from_file(this._imageFile);
this._imgHeight = tmpPixbuf.height;
this._imgWidth = tmpPixbuf.width / this._keyfile.get_integer(FISH_GROUP, 'frames');
} else {
this._imageFile = null;
}
this.parent(label, params);
}, },
createIcon: function(iconSize) { createIcon: function(iconSize) {
if (!this._imageFile) {
return new St.Icon({ icon_name: 'face-smile',
icon_size: iconSize });
}
this._animations = new Panel.Animation(this._imageFile, this._imgWidth, this._imgHeight, FISH_SPEED); this._animations = new Panel.Animation(this._imageFile, this._imgWidth, this._imgHeight, FISH_SPEED);
this._animations.play(); this._animations.play();
return this._animations.actor; return this._animations.actor;

@ -174,15 +174,6 @@ const WindowManager = new Lang.Class({
Meta.KeyBindingFlags.NONE, Meta.KeyBindingFlags.NONE,
Shell.KeyBindingMode.NORMAL, Shell.KeyBindingMode.NORMAL,
Lang.bind(this, this._openAppMenu)); Lang.bind(this, this._openAppMenu));
Main.overview.connect('showing', Lang.bind(this, function() {
for (let i = 0; i < this._dimmedWindows.length; i++)
this._undimWindow(this._dimmedWindows[i]);
}));
Main.overview.connect('hiding', Lang.bind(this, function() {
for (let i = 0; i < this._dimmedWindows.length; i++)
this._dimWindow(this._dimmedWindows[i]);
}));
}, },
setCustomKeybindingHandler: function(name, modes, handler) { setCustomKeybindingHandler: function(name, modes, handler) {
@ -342,15 +333,13 @@ const WindowManager = new Lang.Class({
if (shouldDim && !window._dimmed) { if (shouldDim && !window._dimmed) {
window._dimmed = true; window._dimmed = true;
this._dimmedWindows.push(window); this._dimmedWindows.push(window);
if (!Main.overview.visible) this._dimWindow(window);
this._dimWindow(window);
} else if (!shouldDim && window._dimmed) { } else if (!shouldDim && window._dimmed) {
window._dimmed = false; window._dimmed = false;
this._dimmedWindows = this._dimmedWindows.filter(function(win) { this._dimmedWindows = this._dimmedWindows.filter(function(win) {
return win != window; return win != window;
}); });
if (!Main.overview.visible) this._undimWindow(window);
this._undimWindow(window);
} }
}, },

@ -15,6 +15,7 @@ const Main = imports.ui.main;
const Overview = imports.ui.overview; const Overview = imports.ui.overview;
const Panel = imports.ui.panel; const Panel = imports.ui.panel;
const Tweener = imports.ui.tweener; const Tweener = imports.ui.tweener;
const WindowManager = imports.ui.windowManager;
const FOCUS_ANIMATION_TIME = 0.15; const FOCUS_ANIMATION_TIME = 0.15;
@ -41,6 +42,68 @@ function _interpolate(start, end, step) {
return start + (end - start) * step; return start + (end - start) * step;
} }
const WindowCloneLayout = new Lang.Class({
Name: 'WindowCloneLayout',
Extends: Clutter.LayoutManager,
_init: function(boundingBox) {
this.parent();
this._boundingBox = boundingBox;
},
get boundingBox() {
return this._boundingBox;
},
set boundingBox(b) {
this._boundingBox = b;
this.layout_changed();
},
_makeBoxForWindow: function(window) {
// We need to adjust the position of the actor because of the
// consequences of invisible borders -- in reality, the texture
// has an extra set of "padding" around it that we need to trim
// down.
// The outer rect (from which we compute the bounding box)
// paradoxically is the smaller rectangle, containing the positions
// of the visible frame. The input rect contains everything,
// including the invisible border padding.
let inputRect = window.get_input_rect();
let box = new Clutter.ActorBox();
box.set_origin(inputRect.x - this._boundingBox.x,
inputRect.y - this._boundingBox.y);
box.set_size(inputRect.width, inputRect.height);
return box;
},
vfunc_get_preferred_height: function(container, forWidth) {
return [this._boundingBox.height, this._boundingBox.height];
},
vfunc_get_preferred_width: function(container, forHeight) {
return [this._boundingBox.width, this._boundingBox.width];
},
vfunc_allocate: function(container, box, flags) {
let clone = container.get_children().forEach(function (child) {
let realWindow;
if (child == container._delegate._windowClone)
realWindow = container._delegate.realWindow;
else
realWindow = child.source;
child.allocate(this._makeBoxForWindow(realWindow.meta_window),
flags);
}, this);
},
});
const WindowClone = new Lang.Class({ const WindowClone = new Lang.Class({
Name: 'WindowClone', Name: 'WindowClone',
@ -50,10 +113,7 @@ const WindowClone = new Lang.Class({
this.metaWindow._delegate = this; this.metaWindow._delegate = this;
this._workspace = workspace; this._workspace = workspace;
let [borderX, borderY] = this._getInvisibleBorderPadding(); this._windowClone = new Clutter.Clone({ source: realWindow.get_texture() });
this._windowClone = new Clutter.Clone({ source: realWindow.get_texture(),
x: -borderX,
y: -borderY });
// We expect this.actor to be used for all interaction rather than // We expect this.actor to be used for all interaction rather than
// this._windowClone; as the former is reactive and the latter // this._windowClone; as the former is reactive and the latter
// is not, this just works for most cases. However, for DND all // is not, this just works for most cases. However, for DND all
@ -61,20 +121,15 @@ const WindowClone = new Lang.Class({
// To avoid this, we hide it from pick. // To avoid this, we hide it from pick.
Shell.util_set_hidden_from_pick(this._windowClone, true); Shell.util_set_hidden_from_pick(this._windowClone, true);
this.origX = realWindow.x + borderX;
this.origY = realWindow.y + borderY;
let outerRect = realWindow.meta_window.get_outer_rect();
// The MetaShapedTexture that we clone has a size that includes // The MetaShapedTexture that we clone has a size that includes
// the invisible border; this is inconvenient; rather than trying // the invisible border; this is inconvenient; rather than trying
// to compensate all over the place we insert a ClutterActor into // to compensate all over the place we insert a custom container into
// the hierarchy that is sized to only the visible portion. // the hierarchy that is sized to only the visible portion.
// As usual, we cannot use a ShellGenericContainer or StWidget here,
// because Workspace plays dirty tricks with reparenting to do DNDs
// and scroll-to-zoom.
this.actor = new Clutter.Actor({ reactive: true, this.actor = new Clutter.Actor({ reactive: true,
x: this.origX, layout_manager: new WindowCloneLayout() });
y: this.origY,
width: outerRect.width,
height: outerRect.height });
this.actor.add_child(this._windowClone); this.actor.add_child(this._windowClone);
@ -84,10 +139,19 @@ const WindowClone = new Lang.Class({
this._dragSlot = [0, 0, 0, 0]; this._dragSlot = [0, 0, 0, 0];
this._stackAbove = null; this._stackAbove = null;
this._sizeChangedId = this.realWindow.connect('size-changed', this._windowClone._updateId = this.realWindow.connect('size-changed',
Lang.bind(this, this._onRealWindowSizeChanged)); Lang.bind(this, this._onRealWindowSizeChanged));
this._realWindowDestroyId = this.realWindow.connect('destroy', this._windowClone._destroyId = this.realWindow.connect('destroy', Lang.bind(this, function() {
Lang.bind(this, this._disconnectRealWindowSignals)); // First destroy the clone and then destroy everything
// This will ensure that we never see it in the _disconnectSignals loop
this._windowClone.destroy();
this.destroy();
}));
this._updateAttachedDialogs();
this._computeBoundingBox();
this.actor.x = this._boundingBox.x;
this.actor.y = this._boundingBox.y;
let clickAction = new Clutter.ClickAction(); let clickAction = new Clutter.ClickAction();
clickAction.connect('clicked', Lang.bind(this, this._onClicked)); clickAction.connect('clicked', Lang.bind(this, this._onClicked));
@ -121,20 +185,95 @@ const WindowClone = new Lang.Class({
return this._slot; return this._slot;
}, },
// Find the actor just below us, respecting reparenting done deleteAll: function() {
// by DND code // Delete all windows, starting from the bottom-most (most-modal) one
getActualStackAbove: function() {
if (this._stackAbove == null)
return null;
if (this.inDrag || this._zooming) { let windows = this.actor.get_children();
if (this._stackAbove._delegate) for (let i = windows.length - 1; i >= 1; i--) {
return this._stackAbove._delegate.getActualStackAbove(); let realWindow = windows[i].source;
else let metaWindow = realWindow.meta_window;
return null;
} else { metaWindow.delete(global.get_current_time());
return this._stackAbove;
} }
this.metaWindow.delete(global.get_current_time());
},
addAttachedDialog: function(win) {
this._doAddAttachedDialog(win, win.get_compositor_private());
this._computeBoundingBox();
this._updateDimmer();
this.emit('size-changed');
},
_doAddAttachedDialog: function(metaWin, realWin) {
let clone = new Clutter.Clone({ source: realWin });
clone._updateId = realWin.connect('size-changed', Lang.bind(this, function() {
this._computeBoundingBox();
this.emit('size-changed');
}));
clone._destroyId = realWin.connect('destroy', Lang.bind(this, function() {
clone.destroy();
this._computeBoundingBox();
this._updateDimmer();
this.emit('size-changed');
}));
this.actor.add_child(clone);
},
_updateAttachedDialogs: function() {
let iter = Lang.bind(this, function(win) {
let actor = win.get_compositor_private();
if (!actor)
return false;
if (!win.is_attached_dialog())
return false;
this._doAddAttachedDialog(win, actor);
win.foreach_transient(iter);
return true;
});
this.metaWindow.foreach_transient(iter);
this._dimmer = new WindowManager.WindowDimmer(this._windowClone);
this._updateDimmer();
},
_updateDimmer: function() {
if (this.actor.get_n_children() > 1) {
this._dimmer.setEnabled(true);
this._dimmer.dimFactor = 1.0;
} else {
this._dimmer.setEnabled(false);
}
},
get boundingBox() {
return this._boundingBox;
},
getOriginalPosition: function() {
return [this._boundingBox.x, this._boundingBox.y];
},
_computeBoundingBox: function() {
let rect = this.metaWindow.get_outer_rect();
this.actor.get_children().forEach(function (child) {
let realWindow;
if (child == this._windowClone)
realWindow = this.realWindow;
else
realWindow = child.source;
let metaWindow = realWindow.meta_window;
rect = rect.union(metaWindow.get_outer_rect());
}, this);
this._boundingBox = rect;
this.actor.layout_manager.boundingBox = rect;
}, },
setStackAbove: function (actor) { setStackAbove: function (actor) {
@ -142,56 +281,36 @@ const WindowClone = new Lang.Class({
if (this.inDrag) if (this.inDrag)
// We'll fix up the stack after the drag // We'll fix up the stack after the drag
return; return;
if (this._stackAbove == null)
let actualAbove = this.getActualStackAbove();
if (actualAbove == null)
this.actor.lower_bottom(); this.actor.lower_bottom();
else else
this.actor.raise(actualAbove); this.actor.raise(this._stackAbove);
}, },
destroy: function () { destroy: function () {
this.actor.destroy(); this.actor.destroy();
}, },
_disconnectRealWindowSignals: function() { _disconnectSignals: function() {
if (this._sizeChangedId > 0) this.actor.get_children().forEach(Lang.bind(this, function (child) {
this.realWindow.disconnect(this._sizeChangedId); let realWindow;
this._sizeChangedId = 0; if (child == this._windowClone)
realWindow = this.realWindow;
else
realWindow = child.source;
if (this._realWindowDestroyId > 0) realWindow.disconnect(child._updateId);
this.realWindow.disconnect(this._realWindowDestroyId); realWindow.disconnect(child._destroyId);
this._realWindowDestroyId = 0; }));
},
_getInvisibleBorderPadding: function() {
// We need to adjust the position of the actor because of the
// consequences of invisible borders -- in reality, the texture
// has an extra set of "padding" around it that we need to trim
// down.
// The outer rect paradoxically is the smaller rectangle,
// containing the positions of the visible frame. The input
// rect contains everything, including the invisible border
// padding.
let outerRect = this.metaWindow.get_outer_rect();
let inputRect = this.metaWindow.get_input_rect();
let [borderX, borderY] = [outerRect.x - inputRect.x,
outerRect.y - inputRect.y];
return [borderX, borderY];
}, },
_onRealWindowSizeChanged: function() { _onRealWindowSizeChanged: function() {
let [borderX, borderY] = this._getInvisibleBorderPadding(); this._computeBoundingBox();
let outerRect = this.metaWindow.get_outer_rect();
this.actor.set_size(outerRect.width, outerRect.height);
this._windowClone.set_position(-borderX, -borderY);
this.emit('size-changed'); this.emit('size-changed');
}, },
_onDestroy: function() { _onDestroy: function() {
this._disconnectRealWindowSignals(); this._disconnectSignals();
this.metaWindow._delegate = null; this.metaWindow._delegate = null;
this.actor._delegate = null; this.actor._delegate = null;
@ -450,7 +569,7 @@ const WindowOverlay = new Lang.Class({
Lang.bind(this, Lang.bind(this,
this._onWindowAdded)); this._onWindowAdded));
metaWindow.delete(global.get_current_time()); this._windowClone.deleteAll();
}, },
_onWindowAdded: function(workspace, win) { _onWindowAdded: function(workspace, win) {
@ -926,7 +1045,7 @@ const Workspace = new Lang.Class({
this._windowOverlays = []; this._windowOverlays = [];
for (let i = 0; i < windows.length; i++) { for (let i = 0; i < windows.length; i++) {
if (this._isOverviewWindow(windows[i])) { if (this._isOverviewWindow(windows[i])) {
this._addWindowClone(windows[i], true); this._addWindowClone(windows[i]);
} }
} }
@ -1051,23 +1170,11 @@ const Workspace = new Lang.Class({
if (clone.inDrag) if (clone.inDrag)
continue; continue;
let cloneWidth = clone.actor.width * scale; clone.slot = [x, y, clone.actor.width * scale, clone.actor.height * scale];
let cloneHeight = clone.actor.height * scale;
clone.slot = [x, y, cloneWidth, cloneHeight];
if (overlay && (initialPositioning || !clone.positioned)) if (overlay && initialPositioning)
overlay.hide(); overlay.hide();
if (!clone.positioned) {
// This window appeared after the overview was already up
// Grow the clone from the center of the slot
clone.actor.x = x + cloneWidth / 2;
clone.actor.y = y + cloneHeight / 2;
clone.actor.scale_x = 0;
clone.actor.scale_y = 0;
clone.positioned = true;
}
if (animate && isOnCurrentWorkspace) { if (animate && isOnCurrentWorkspace) {
if (!metaWindow.showing_on_its_workspace()) { if (!metaWindow.showing_on_its_workspace()) {
/* Hidden windows should fade in and grow /* Hidden windows should fade in and grow
@ -1250,10 +1357,30 @@ const Workspace = new Lang.Class({
if (this._lookupIndex (metaWin) != -1) if (this._lookupIndex (metaWin) != -1)
return; return;
if (!this._isMyWindow(win) || !this._isOverviewWindow(win)) if (!this._isMyWindow(win))
return; return;
let [clone, overlay] = this._addWindowClone(win, false); if (!this._isOverviewWindow(win)) {
if (metaWin.is_attached_dialog()) {
let parent = metaWin.get_transient_for();
while (parent.is_attached_dialog())
parent = metaWin.get_transient_for();
let idx = this._lookupIndex (parent);
if (idx < 0) {
// parent was not created yet, it will take care
// of the dialog when created
return;
}
let clone = this._windows[idx];
clone.addAttachedDialog(metaWin);
}
return;
}
let [clone, overlay] = this._addWindowClone(win);
if (win._overviewHint) { if (win._overviewHint) {
let x = win._overviewHint.x - this.actor.x; let x = win._overviewHint.x - this.actor.x;
@ -1262,10 +1389,16 @@ const Workspace = new Lang.Class({
delete win._overviewHint; delete win._overviewHint;
clone.slot = [x, y, clone.actor.width * scale, clone.actor.height * scale]; clone.slot = [x, y, clone.actor.width * scale, clone.actor.height * scale];
clone.positioned = true;
clone.actor.set_position (x, y); clone.actor.set_position (x, y);
clone.actor.set_scale (scale, scale); clone.actor.set_scale (scale, scale);
clone.overlay.relayout(false); clone.overlay.relayout(false);
} else {
// Position new windows at the top corner of the workspace rather
// than where they were placed for real to avoid the window
// being clipped to the workspaceView. Its not really more
// natural for the window to suddenly appear in the overview
// on some seemingly random location anyway.
clone.actor.set_position (this._x, this._y);
} }
this._currentLayout = null; this._currentLayout = null;
@ -1345,9 +1478,11 @@ const Workspace = new Lang.Class({
overlay.hide(); overlay.hide();
if (clone.metaWindow.showing_on_its_workspace()) { if (clone.metaWindow.showing_on_its_workspace()) {
let [origX, origY] = clone.getOriginalPosition();
Tweener.addTween(clone.actor, Tweener.addTween(clone.actor,
{ x: clone.origX, { x: origX,
y: clone.origY, y: origY,
scale_x: 1.0, scale_x: 1.0,
scale_y: 1.0, scale_y: 1.0,
time: Overview.ANIMATION_TIME, time: Overview.ANIMATION_TIME,
@ -1412,11 +1547,10 @@ const Workspace = new Lang.Class({
}, },
// Create a clone of a (non-desktop) window and add it to the window list // Create a clone of a (non-desktop) window and add it to the window list
_addWindowClone : function(win, positioned) { _addWindowClone : function(win) {
let clone = new WindowClone(win, this); let clone = new WindowClone(win, this);
let overlay = new WindowOverlay(clone, this._windowOverlaysGroup); let overlay = new WindowOverlay(clone, this._windowOverlaysGroup);
clone.overlay = overlay; clone.overlay = overlay;
clone.positioned = positioned;
clone.connect('selected', clone.connect('selected',
Lang.bind(this, this._onCloneSelected)); Lang.bind(this, this._onCloneSelected));

@ -13,6 +13,7 @@ const Background = imports.ui.background;
const DND = imports.ui.dnd; const DND = imports.ui.dnd;
const Main = imports.ui.main; const Main = imports.ui.main;
const Tweener = imports.ui.tweener; const Tweener = imports.ui.tweener;
const WindowManager = imports.ui.windowManager;
const Workspace = imports.ui.workspace; const Workspace = imports.ui.workspace;
const WorkspacesView = imports.ui.workspacesView; const WorkspacesView = imports.ui.workspacesView;
@ -31,20 +32,49 @@ const WORKSPACE_KEEP_ALIVE_TIME = 100;
const OVERRIDE_SCHEMA = 'org.gnome.shell.overrides'; const OVERRIDE_SCHEMA = 'org.gnome.shell.overrides';
/* A layout manager that requests size only for primary_actor, but then allocates
all using a fixed layout */
const PrimaryActorLayout = new Lang.Class({
Name: 'PrimaryActorLayout',
Extends: Clutter.FixedLayout,
_init: function(primaryActor) {
this.parent();
this.primaryActor = primaryActor;
},
vfunc_get_preferred_width: function(forHeight) {
return this.primaryActor.get_preferred_width(forHeight);
},
vfunc_get_preferred_height: function(forWidth) {
return this.primaryActor.get_preferred_height(forWidth);
},
});
const WindowClone = new Lang.Class({ const WindowClone = new Lang.Class({
Name: 'WindowClone', Name: 'WindowClone',
_init : function(realWindow) { _init : function(realWindow) {
this.actor = new Clutter.Clone({ source: realWindow.get_texture(), this.clone = new Clutter.Clone({ source: realWindow });
/* Can't use a Shell.GenericContainer because of DND and reparenting... */
this.actor = new Clutter.Actor({ layout_manager: new PrimaryActorLayout(this.clone),
reactive: true }); reactive: true });
this.actor._delegate = this; this.actor._delegate = this;
this.actor.add_child(this.clone);
this.realWindow = realWindow; this.realWindow = realWindow;
this.metaWindow = realWindow.meta_window; this.metaWindow = realWindow.meta_window;
this._positionChangedId = this.realWindow.connect('position-changed', this.clone._updateId = this.realWindow.connect('position-changed',
Lang.bind(this, this._onPositionChanged)); Lang.bind(this, this._onPositionChanged));
this._realWindowDestroyedId = this.realWindow.connect('destroy', this.clone._destroyId = this.realWindow.connect('destroy', Lang.bind(this, function() {
Lang.bind(this, this._disconnectRealWindowSignals)); // First destroy the clone and then destroy everything
// This will ensure that we never see it in the _disconnectSignals loop
this.clone.destroy();
this.destroy();
}));
this._onPositionChanged(); this._onPositionChanged();
this.actor.connect('button-release-event', this.actor.connect('button-release-event',
@ -60,62 +90,89 @@ const WindowClone = new Lang.Class({
this._draggable.connect('drag-cancelled', Lang.bind(this, this._onDragCancelled)); this._draggable.connect('drag-cancelled', Lang.bind(this, this._onDragCancelled));
this._draggable.connect('drag-end', Lang.bind(this, this._onDragEnd)); this._draggable.connect('drag-end', Lang.bind(this, this._onDragEnd));
this.inDrag = false; this.inDrag = false;
},
// Find the actor just below us, respecting reparenting done let iter = Lang.bind(this, function(win) {
// by DND code let actor = win.get_compositor_private();
getActualStackAbove: function() {
if (this._stackAbove == null)
return null;
if (this.inDrag) { if (!actor)
if (this._stackAbove._delegate) return false;
return this._stackAbove._delegate.getActualStackAbove(); if (!win.is_attached_dialog())
else return false;
return null;
} else { this._doAddAttachedDialog(win, actor);
return this._stackAbove; win.foreach_transient(iter);
}
return true;
});
this.metaWindow.foreach_transient(iter);
this._dimmer = new WindowManager.WindowDimmer(this.clone);
this._updateDimmer();
}, },
setStackAbove: function (actor) { setStackAbove: function (actor) {
this._stackAbove = actor; this._stackAbove = actor;
if (this._stackAbove == null)
// Don't apply the new stacking now, it will be applied
// when dragging ends and window are stacked again
if (actor.inDrag)
return;
let actualAbove = this.getActualStackAbove();
if (actualAbove == null)
this.actor.lower_bottom(); this.actor.lower_bottom();
else else
this.actor.raise(actualAbove); this.actor.raise(this._stackAbove);
}, },
destroy: function () { destroy: function () {
this.actor.destroy(); this.actor.destroy();
}, },
addAttachedDialog: function(win) {
this._doAddAttachedDialog(win, win.get_compositor_private());
this._updateDimmer();
},
_doAddAttachedDialog: function(metaDialog, realDialog) {
let clone = new Clutter.Clone({ source: realDialog });
this._updateDialogPosition(realDialog, clone);
clone._updateId = realDialog.connect('position-changed',
Lang.bind(this, this._updateDialogPosition, clone));
clone._destroyId = realDialog.connect('destroy', Lang.bind(this, function() {
clone.destroy();
this._updateDimmer();
}));
this.actor.add_child(clone);
},
_updateDimmer: function() {
if (this.actor.get_n_children() > 1) {
this._dimmer.setEnabled(true);
this._dimmer.dimFactor = 1.0;
} else {
this._dimmer.setEnabled(false);
}
},
_updateDialogPosition: function(realDialog, cloneDialog) {
let metaDialog = realDialog.meta_window;
let dialogRect = metaDialog.get_outer_rect();
let rect = this.metaWindow.get_outer_rect();
cloneDialog.set_position(dialogRect.x - rect.x, dialogRect.y - rect.y);
},
_onPositionChanged: function() { _onPositionChanged: function() {
let rect = this.metaWindow.get_outer_rect(); let rect = this.metaWindow.get_outer_rect();
this.actor.set_position(this.realWindow.x, this.realWindow.y); this.actor.set_position(this.realWindow.x, this.realWindow.y);
}, },
_disconnectRealWindowSignals: function() { _disconnectSignals: function() {
if (this._positionChangedId != 0) { this.actor.get_children().forEach(function(child) {
this.realWindow.disconnect(this._positionChangedId); let realWindow = child.source;
this._positionChangedId = 0;
}
if (this._realWindowDestroyedId != 0) { realWindow.disconnect(child._updateId);
this.realWindow.disconnect(this._realWindowDestroyedId); realWindow.disconnect(child._destroyId);
this._realWindowDestroyedId = 0; });
}
}, },
_onDestroy: function() { _onDestroy: function() {
this._disconnectRealWindowSignals(); this._disconnectSignals();
this.actor._delegate = null; this.actor._delegate = null;
@ -343,10 +400,26 @@ const WorkspaceThumbnail = new Lang.Class({
if (this._lookupIndex (metaWin) != -1) if (this._lookupIndex (metaWin) != -1)
return; return;
if (!this._isMyWindow(win) || !this._isOverviewWindow(win)) if (!this._isMyWindow(win))
return; return;
let clone = this._addWindowClone(win); if (this._isOverviewWindow(win)) {
this._addWindowClone(win);
} else if (metaWin.is_attached_dialog()) {
let parent = metaWin.get_transient_for();
while (parent.is_attached_dialog())
parent = metaWin.get_transient_for();
let idx = this._lookupIndex (parent);
if (idx < 0) {
// parent was not created yet, it will take care
// of the dialog when created
return;
}
let clone = this._windows[idx];
clone.addAttachedDialog(metaWin);
}
}, },
_windowAdded : function(metaWorkspace, metaWin) { _windowAdded : function(metaWorkspace, metaWin) {
@ -384,8 +457,9 @@ const WorkspaceThumbnail = new Lang.Class({
}, },
destroy : function() { destroy : function() {
if (this.actor) this.actor.destroy();
this.actor.destroy(); this._bgManager.destroy();
this._bgManager = null;
}, },
workspaceRemoved : function() { workspaceRemoved : function() {
@ -406,11 +480,6 @@ const WorkspaceThumbnail = new Lang.Class({
_onDestroy: function(actor) { _onDestroy: function(actor) {
this.workspaceRemoved(); this.workspaceRemoved();
if (this._bgManager) {
this._bgManager.destroy();
this._bgManager = null;
}
this._windows = []; this._windows = [];
this.actor = null; this.actor = null;
}, },
@ -562,7 +631,6 @@ const ThumbnailsBox = new Lang.Class({
this._dropPlaceholderPos = -1; this._dropPlaceholderPos = -1;
this._dropPlaceholder = new St.Bin({ style_class: 'placeholder' }); this._dropPlaceholder = new St.Bin({ style_class: 'placeholder' });
this.actor.add_actor(this._dropPlaceholder); this.actor.add_actor(this._dropPlaceholder);
this._spliceIndex = -1;
this._targetScale = 0; this._targetScale = 0;
this._scale = 0; this._scale = 0;
@ -745,8 +813,6 @@ const ThumbnailsBox = new Lang.Class({
return win.get_workspace() >= newWorkspaceIndex; return win.get_workspace() >= newWorkspaceIndex;
}); });
this._spliceIndex = newWorkspaceIndex;
// ... move them down one. // ... move them down one.
windows.forEach(function(win) { windows.forEach(function(win) {
win.meta_window.change_workspace_by_index(win.get_workspace() + 1, win.meta_window.change_workspace_by_index(win.get_workspace() + 1,
@ -768,14 +834,6 @@ const ThumbnailsBox = new Lang.Class({
WORKSPACE_KEEP_ALIVE_TIME); WORKSPACE_KEEP_ALIVE_TIME);
} }
// Start the animation on the workspace (which is actually
// an old one which just became empty)
let thumbnail = this._thumbnails[newWorkspaceIndex];
this._setThumbnailState(thumbnail, ThumbnailState.NEW);
thumbnail.slidePosition = 1;
this._queueUpdateStates();
return true; return true;
} else { } else {
return false; return false;
@ -863,8 +921,7 @@ const ThumbnailsBox = new Lang.Class({
this._thumbnails.push(thumbnail); this._thumbnails.push(thumbnail);
this.actor.add_actor(thumbnail.actor); this.actor.add_actor(thumbnail.actor);
if (start > 0 && this._spliceIndex == -1) { if (start > 0) { // not the initial fill
// not the initial fill, and not splicing via DND
thumbnail.state = ThumbnailState.NEW; thumbnail.state = ThumbnailState.NEW;
thumbnail.slidePosition = 1; // start slid out thumbnail.slidePosition = 1; // start slid out
this._haveNewThumbnails = true; this._haveNewThumbnails = true;
@ -879,9 +936,6 @@ const ThumbnailsBox = new Lang.Class({
// The thumbnails indicator actually needs to be on top of the thumbnails // The thumbnails indicator actually needs to be on top of the thumbnails
this._indicator.raise_top(); this._indicator.raise_top();
// Clear the splice index, we got the message
this._spliceIndex = -1;
}, },
removeThumbnails: function(start, count) { removeThumbnails: function(start, count) {

@ -386,15 +386,6 @@ const WorkspacesView = new Lang.Class({
let current = Math.round(adj.value); let current = Math.round(adj.value);
if (active != current) { if (active != current) {
if (!this._workspaces[current]) {
// The current workspace was destroyed. This could happen
// when you are on the last empty workspace, and consolidate
// windows using the thumbnail bar.
// In that case, the intended behavior is to stay on the empty
// workspace, which is the last one, so pick it.
current = this._workspaces.length - 1;
}
let metaWorkspace = this._workspaces[current].metaWorkspace; let metaWorkspace = this._workspaces[current].metaWorkspace;
metaWorkspace.activate(global.get_current_time()); metaWorkspace.activate(global.get_current_time());
} }

@ -60,7 +60,6 @@ sr@latin
sv sv
ta ta
te te
tg
th th
tr tr
ug ug

829
po/as.po

File diff suppressed because it is too large Load Diff

797
po/be.po

File diff suppressed because it is too large Load Diff

647
po/ca.po

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

559
po/cs.po

File diff suppressed because it is too large Load Diff

1091
po/da.po

File diff suppressed because it is too large Load Diff

905
po/de.po

File diff suppressed because it is too large Load Diff

275
po/et.po

@ -13,8 +13,8 @@ msgstr ""
"Project-Id-Version: gnome-shell MASTER\n" "Project-Id-Version: gnome-shell MASTER\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-03-11 22:02+0000\n" "POT-Creation-Date: 2012-12-09 13:03+0000\n"
"PO-Revision-Date: 2013-03-12 16:28+0300\n" "PO-Revision-Date: 2012-12-19 16:47+0300\n"
"Last-Translator: Mattias Põldaru <mahfiaz@gmail.com>\n" "Last-Translator: Mattias Põldaru <mahfiaz@gmail.com>\n"
"Language-Team: Estonian <>\n" "Language-Team: Estonian <>\n"
"Language: et\n" "Language: et\n"
@ -104,16 +104,6 @@ msgid ""
"favorites area." "favorites area."
msgstr "Nendele tunnustele vastavaid rakendusi kuvatakse lemmikutes." msgstr "Nendele tunnustele vastavaid rakendusi kuvatakse lemmikutes."
msgid "List of categories that should be displayed as folders"
msgstr "Kategooriad, mida tuleb kuvada kaustadena"
msgid ""
"Each category name in this list will be represented as folder in the "
"application view, rather than being displayed inline in the main view."
msgstr ""
"Kõiki selles loetelus olevaid kategooriaid kuvatakse rakenduste vaates "
"kaustadena, mitte peavaates teistega koos."
msgid "History for command (Alt-F2) dialog" msgid "History for command (Alt-F2) dialog"
msgstr "Käsudialoogi (Alt-F2) ajalugu" msgstr "Käsudialoogi (Alt-F2) ajalugu"
@ -145,6 +135,12 @@ msgstr ""
"See võti keelab automaatse 'Logi välja' menüükirje peitmise, kui arvutis on " "See võti keelab automaatse 'Logi välja' menüükirje peitmise, kui arvutis on "
"üks kasutaja ning avatud üks seanss." "üks kasutaja ning avatud üks seanss."
msgid "Show full name in the user menu"
msgstr "Kasutajamenüüs näidatakse kasutaja tervet nime"
msgid "Whether the users full name is shown in the user menu or not."
msgstr "Kas kasutajamenüüs näidatakse kasutaja kogu nime või mitte."
msgid "" msgid ""
"Whether to remember password for mounting encrypted or remote filesystems" "Whether to remember password for mounting encrypted or remote filesystems"
msgstr "" msgstr ""
@ -303,8 +299,12 @@ msgstr "Laiendus"
msgid "Select an extension to configure using the combobox above." msgid "Select an extension to configure using the combobox above."
msgstr "Vali seadistatav laiendus kasutades ülemist valikukasti." msgstr "Vali seadistatav laiendus kasutades ülemist valikukasti."
msgid "Session" msgid "Session..."
msgstr "Seanss" msgstr "Seanss..."
msgctxt "title"
msgid "Sign In"
msgstr "Sisselogimine"
#. 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
@ -368,12 +368,16 @@ msgstr "Käsku pole võimalik analüüsida:"
msgid "Execution of '%s' failed:" msgid "Execution of '%s' failed:"
msgstr "'%s' käivitamine nurjus:" msgstr "'%s' käivitamine nurjus:"
msgid "Frequent" #. Translators: Filter to display all applications
msgstr "Sage"
msgid "All" msgid "All"
msgstr "Kõik" msgstr "Kõik"
msgid "APPLICATIONS"
msgstr "RAKENDUSED"
msgid "SETTINGS"
msgstr "SEADED"
msgid "New Window" msgid "New Window"
msgstr "Uus aken" msgstr "Uus aken"
@ -391,12 +395,6 @@ msgstr "%s lisati lemmikutesse."
msgid "%s has been removed from your favorites." msgid "%s has been removed from your favorites."
msgstr "%s eemaldati lemmikutest." msgstr "%s eemaldati lemmikutest."
msgid "Settings"
msgstr "Sätted"
msgid "Change Background…"
msgstr "Muuda tausta…"
#. Translators: Shown in calendar event list for all day events #. Translators: Shown in calendar event list for all day events
#. * Keep it short, best if you can use less then 10 characters #. * Keep it short, best if you can use less then 10 characters
#. #.
@ -404,18 +402,15 @@ msgctxt "event list time"
msgid "All Day" msgid "All Day"
msgstr "Kogu päev" msgstr "Kogu päev"
#. Translators: Shown in calendar event list, if 24h format, #. Translators: Shown in calendar event list, if 24h format
#. \u2236 is a ratio character, similar to :
msgctxt "event list time" msgctxt "event list time"
msgid "%H\\u2236%M" msgid "%H:%M"
msgstr "%H\\u2236%M" msgstr "%H:%M"
#. Transators: Shown in calendar event list, if 12h format, #. Transators: Shown in calendar event list, if 12h format
#. \u2236 is a ratio character, similar to : and \u2009 is
#. a thin space
msgctxt "event list time" msgctxt "event list time"
msgid "%l\\u2236%M\\u2009%p" msgid "%l:%M %p"
msgstr "%l\\u2236%M\\u2009%p" msgstr "%l:%M %p"
#. Translators: Calendar grid abbreviation for Sunday. #. Translators: Calendar grid abbreviation for Sunday.
#. * #. *
@ -522,12 +517,6 @@ msgstr "Käesolev nädal"
msgid "Next week" msgid "Next week"
msgstr "Järgmine nädal" msgstr "Järgmine nädal"
msgid "External drive connected"
msgstr "Väline ketas ühendati"
msgid "External drive disconnected"
msgstr "Väline ketas eemaldati"
msgid "Removable Devices" msgid "Removable Devices"
msgstr "Eemaldatavad seadmed" msgstr "Eemaldatavad seadmed"
@ -812,20 +801,12 @@ msgstr "Aknad"
msgid "Show Applications" msgid "Show Applications"
msgstr "Rakenduste kuvamine" msgstr "Rakenduste kuvamine"
#. Translators: this is the name of the dock/favorites area on msgid "Date and Time Settings"
#. the left of the overview msgstr "Kuupäeva ja kellaaja sätted"
msgid "Dash"
msgstr "Dokk"
msgid "Open Calendar" msgid "Open Calendar"
msgstr "Ava kalender" msgstr "Ava kalender"
msgid "Open Clocks"
msgstr "Ava kell"
msgid "Date & Time Settings"
msgstr "Kuupäeva ja kella sätted"
#. 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").
#. #.
@ -916,6 +897,9 @@ msgstr "Paigalda"
msgid "Download and install '%s' from extensions.gnome.org?" msgid "Download and install '%s' from extensions.gnome.org?"
msgstr "Kas laadida alla ja paigaldada '%s' aadressilt extensions.gnome.org?" msgstr "Kas laadida alla ja paigaldada '%s' aadressilt extensions.gnome.org?"
msgid "tray"
msgstr "salv"
msgid "Keyboard" msgid "Keyboard"
msgstr "Klaviatuur" msgstr "Klaviatuur"
@ -936,8 +920,6 @@ msgstr "Näita vigu"
msgid "Enabled" msgid "Enabled"
msgstr "Lubatud" msgstr "Lubatud"
#. translators:
#. * The device has been disabled
msgid "Disabled" msgid "Disabled"
msgstr "Keelatud" msgstr "Keelatud"
@ -962,12 +944,6 @@ msgstr "Ava"
msgid "Remove" msgid "Remove"
msgstr "Eemalda" msgstr "Eemalda"
msgid "Clear Messages"
msgstr "Kustuta teated"
msgid "Notification Settings"
msgstr "Märguannete sätted"
msgid "No Messages" msgid "No Messages"
msgstr "Teateid pole" msgstr "Teateid pole"
@ -981,12 +957,6 @@ msgctxt "program"
msgid "Unknown" msgid "Unknown"
msgstr "Tundmatu" msgstr "Tundmatu"
#, c-format
msgid "%d new message"
msgid_plural "%d new messages"
msgstr[0] "%d uus sõnum"
msgstr[1] "%d uut sõnumit"
msgid "Undo" msgid "Undo"
msgstr "Võta tagasi" msgstr "Võta tagasi"
@ -997,8 +967,13 @@ msgstr "Ülevaade"
#. in the search entry when no search is #. in the search entry when no search is
#. active; it should not exceed ~30 #. active; it should not exceed ~30
#. characters. #. characters.
msgid "Type to search" msgid "Type to search..."
msgstr "Otsing" msgstr "Otsing..."
#. Translators: this is the name of the dock/favorites area on
#. the left of the overview
msgid "Dash"
msgstr "Dokk"
msgid "Quit" msgid "Quit"
msgstr "Sulge" msgstr "Sulge"
@ -1030,6 +1005,12 @@ msgstr "Sulge"
msgid "%A, %B %d" msgid "%A, %B %d"
msgstr "%A, %d. %B" msgstr "%A, %d. %B"
#, c-format
msgid "%d new message"
msgid_plural "%d new messages"
msgstr[0] "%d uus sõnum"
msgstr[1] "%d uut sõnumit"
#, c-format #, c-format
msgid "%d new notification" msgid "%d new notification"
msgid_plural "%d new notifications" msgid_plural "%d new notifications"
@ -1039,24 +1020,8 @@ msgstr[1] "%d uut märguannet"
msgid "Lock" msgid "Lock"
msgstr "Lukusta" msgstr "Lukusta"
msgid "GNOME needs to lock the screen" msgid "Searching..."
msgstr "GNOME peab ekraani lukustama" msgstr "Otsimine..."
#. We could not become modal, so we can't activate the
#. screenshield. The user is probably very upset at this
#. point, but any application using global grabs is broken
#. Just tell him to stop using this app
#.
#. XXX: another option is to kick the user into the gdm login
#. screen, where we're not affected by grabs
msgid "Unable to lock"
msgstr "Pole võimalik lukustada"
msgid "Lock was blocked by an application"
msgstr "Rakendus blokeeris lukustamise"
msgid "Searching…"
msgstr "Otsing…"
msgid "No results." msgid "No results."
msgstr "Tulemused puuduvad." msgstr "Tulemused puuduvad."
@ -1124,11 +1089,11 @@ msgstr "Bluetooth"
msgid "Visibility" msgid "Visibility"
msgstr "Nähtavus" msgstr "Nähtavus"
msgid "Send Files to Device" msgid "Send Files to Device..."
msgstr "Failide saatmine seadmesse" msgstr "Failide saatmine seadmesse..."
msgid "Set Up a New Device" msgid "Set up a New Device..."
msgstr "Uue seadme häälestamine" msgstr "Uue seadme häälestamine..."
msgid "Bluetooth Settings" msgid "Bluetooth Settings"
msgstr "Bluetoothi sätted" msgstr "Bluetoothi sätted"
@ -1146,8 +1111,8 @@ msgstr "ühenduse katkestamine..."
msgid "connecting..." msgid "connecting..."
msgstr "ühendumine..." msgstr "ühendumine..."
msgid "Send Files" msgid "Send Files..."
msgstr "Failide saatmine" msgstr "Failide saatmine..."
msgid "Keyboard Settings" msgid "Keyboard Settings"
msgstr "Klaviatuurisätted" msgstr "Klaviatuurisätted"
@ -1208,7 +1173,7 @@ msgstr "Olgu"
msgid "Show Keyboard Layout" msgid "Show Keyboard Layout"
msgstr "Klaviatuuripaigutuse kuvamine" msgstr "Klaviatuuripaigutuse kuvamine"
msgid "Region & Language Settings" msgid "Region and Language Settings"
msgstr "Asukoha ja keele sätted" msgstr "Asukoha ja keele sätted"
msgid "Volume, network, battery" msgid "Volume, network, battery"
@ -1247,23 +1212,17 @@ msgstr "pole saadaval"
msgid "connection failed" msgid "connection failed"
msgstr "ühendumine nurjus" msgstr "ühendumine nurjus"
msgid "More" msgid "More..."
msgstr "Veel" msgstr "Veel..."
#. 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)
msgid "Connected (private)" msgid "Connected (private)"
msgstr "Ühendatud (privaatne)" msgstr "Ühendatud (privaatne)"
msgid "Wired"
msgstr "Juhtmega"
msgid "Auto Ethernet" msgid "Auto Ethernet"
msgstr "Automaatne ethernet" msgstr "Automaatne ethernet"
msgid "Mobile broadband"
msgstr "Mobiiliühendus"
msgid "Auto broadband" msgid "Auto broadband"
msgstr "Automaatne lairibaühendus" msgstr "Automaatne lairibaühendus"
@ -1310,8 +1269,8 @@ msgstr "Toitesätted..."
#. 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
msgid "Estimating" msgid "Estimating..."
msgstr "Andmete kogumine" msgstr "Andmete kogumine..."
#, c-format #, c-format
msgid "%d hour remaining" msgid "%d hour remaining"
@ -1345,10 +1304,10 @@ msgctxt "percent of battery remaining"
msgid "%d%%" msgid "%d%%"
msgstr "%d%%" msgstr "%d%%"
msgid "AC Adapter" msgid "AC adapter"
msgstr "Laadija" msgstr "Võrgutoite adapter"
msgid "Laptop Battery" msgid "Laptop battery"
msgstr "Sülearvuti aku" msgstr "Sülearvuti aku"
msgid "UPS" msgid "UPS"
@ -1363,10 +1322,10 @@ msgstr "Hiir"
msgid "PDA" msgid "PDA"
msgstr "Elektronmärkmik" msgstr "Elektronmärkmik"
msgid "Cell Phone" msgid "Cell phone"
msgstr "Mobiiltelefon" msgstr "Mobiiltelefon"
msgid "Media Player" msgid "Media player"
msgstr "Meediaesitaja" msgstr "Meediaesitaja"
msgid "Tablet" msgid "Tablet"
@ -1379,9 +1338,6 @@ msgctxt "device"
msgid "Unknown" msgid "Unknown"
msgstr "Tundmatu" msgstr "Tundmatu"
msgid "Volume changed"
msgstr "Helivaljus muutus"
#. Translators: This is the label for audio volume #. Translators: This is the label for audio volume
msgid "Volume" msgid "Volume"
msgstr "Helivaljus" msgstr "Helivaljus"
@ -1416,6 +1372,9 @@ msgstr "Ühendamata"
msgid "Notifications" msgid "Notifications"
msgstr "Märguanded" msgstr "Märguanded"
msgid "Settings"
msgstr "Sätted"
msgid "Switch User" msgid "Switch User"
msgstr "Vaheta kasutajat" msgstr "Vaheta kasutajat"
@ -1435,22 +1394,6 @@ msgstr ""
"Märguanded on nüüd keelatud, sealhulgas vestlusteated. Sinu netiolekut " "Märguanded on nüüd keelatud, sealhulgas vestlusteated. Sinu netiolekut "
"muudeti, et teised teaksid, et sa ei pruugi nende teateid näha." "muudeti, et teised teaksid, et sa ei pruugi nende teateid näha."
msgid "Other users are logged in."
msgstr "Teised kasutajad on sisse logitud."
msgid "Shutting down might cause them to lose unsaved work."
msgstr "Väljalülitamine võib põhjustada salvestamata töö kaotsimineku."
#. Translators: Remote here refers to a remote session, like a ssh login
#, c-format
msgid "%s (remote)"
msgstr "%s (kaugühendus)"
#. Translators: Console here refers to a tty like a VT console
#, c-format
msgid "%s (console)"
msgstr "%s (konsool)"
msgid "Applications" msgid "Applications"
msgstr "Rakendused" msgstr "Rakendused"
@ -1469,6 +1412,9 @@ msgstr ""
msgid "%s the Oracle says" msgid "%s the Oracle says"
msgstr "Oraakel %s ütleb" msgstr "Oraakel %s ütleb"
msgid "Your favorite Easter Egg"
msgstr "Sinu lemmiküllatusmuna"
#, c-format #, c-format
msgid "'%s' is ready" msgid "'%s' is ready"
msgstr "'%s' on valmis" msgstr "'%s' on valmis"
@ -1476,25 +1422,6 @@ msgstr "'%s' on valmis"
msgid "Evolution Calendar" msgid "Evolution Calendar"
msgstr "Evolutioni kalender" msgstr "Evolutioni kalender"
#. translators:
#. * The number of sound outputs on a particular device
#, c-format
msgid "%u Output"
msgid_plural "%u Outputs"
msgstr[0] "%u väljund"
msgstr[1] "%u väljundit"
#. translators:
#. * The number of sound inputs on a particular device
#, c-format
msgid "%u Input"
msgid_plural "%u Inputs"
msgstr[0] "%u sisend"
msgstr[1] "%u sisendit"
msgid "System Sounds"
msgstr "Süsteemi helid"
msgid "Print version" msgid "Print version"
msgstr "Printimise versioon" msgstr "Printimise versioon"
@ -1517,48 +1444,15 @@ msgstr "Paroolid ei kattu."
msgid "Password cannot be blank" msgid "Password cannot be blank"
msgstr "Parool ei saa olla tühi" msgstr "Parool ei saa olla tühi"
msgid "United Kingdom"
msgstr "Suurbritannia"
msgid "Default"
msgstr "Vaikimisi"
msgid "Authentication dialog was dismissed by the user" msgid "Authentication dialog was dismissed by the user"
msgstr "Kasutaja katkestas autentimisdialoogi" msgstr "Kasutaja katkestas autentimisdialoogi"
#~ msgid "Show full name in the user menu"
#~ msgstr "Kasutajamenüüs näidatakse kasutaja tervet nime"
#~ msgid "Whether the users full name is shown in the user menu or not."
#~ msgstr "Kas kasutajamenüüs näidatakse kasutaja kogu nime või mitte."
#~ msgctxt "title"
#~ msgid "Sign In"
#~ msgstr "Sisselogimine"
#~ msgid "APPLICATIONS"
#~ msgstr "RAKENDUSED"
#~ msgid "SETTINGS"
#~ msgstr "SEADED"
#~ msgctxt "event list time"
#~ msgid "%H:%M"
#~ msgstr "%H:%M"
#~ msgctxt "event list time"
#~ msgid "%l:%M %p"
#~ msgstr "%l:%M %p"
#~ msgid "tray"
#~ msgstr "salv"
#~ msgid "More..."
#~ msgstr "Veel..."
#~ msgid "Your favorite Easter Egg"
#~ msgstr "Sinu lemmiküllatusmuna"
#~ msgid "United Kingdom"
#~ msgstr "Suurbritannia"
#~ msgid "Default"
#~ msgstr "Vaikimisi"
#~ msgid "Subscription request" #~ msgid "Subscription request"
#~ msgstr "Tellimuse päring" #~ msgstr "Tellimuse päring"
@ -1577,9 +1471,28 @@ msgstr "Kasutaja katkestas autentimisdialoogi"
#~ msgid "Reconnect" #~ msgid "Reconnect"
#~ msgstr "Ühendu uuesti" #~ msgstr "Ühendu uuesti"
#~ msgid "Wired"
#~ msgstr "Juhtmega"
#~ msgid "Wireless" #~ msgid "Wireless"
#~ msgstr "Juhtmeta" #~ msgstr "Juhtmeta"
#~ msgid "Mobile broadband"
#~ msgstr "Mobiiliühendus"
#~ msgid "%u Output"
#~ msgid_plural "%u Outputs"
#~ msgstr[0] "%u väljund"
#~ msgstr[1] "%u väljundit"
#~ msgid "%u Input"
#~ msgid_plural "%u Inputs"
#~ msgstr[0] "%u sisend"
#~ msgstr[1] "%u sisendit"
#~ msgid "System Sounds"
#~ msgstr "Süsteemi helid"
#~ msgid "disabled OpenSearch providers" #~ msgid "disabled OpenSearch providers"
#~ msgstr "keelatud OpenSearch pakkujad" #~ msgstr "keelatud OpenSearch pakkujad"

1102
po/fa.po

File diff suppressed because it is too large Load Diff

1104
po/fi.po

File diff suppressed because it is too large Load Diff

1097
po/fr.po

File diff suppressed because it is too large Load Diff

750
po/gu.po

File diff suppressed because it is too large Load Diff

1127
po/hi.po

File diff suppressed because it is too large Load Diff

900
po/hu.po

File diff suppressed because it is too large Load Diff

1019
po/id.po

File diff suppressed because it is too large Load Diff

763
po/ja.po

File diff suppressed because it is too large Load Diff

863
po/kk.po

File diff suppressed because it is too large Load Diff

1201
po/kn.po

File diff suppressed because it is too large Load Diff

1044
po/ko.po

File diff suppressed because it is too large Load Diff

664
po/lt.po

File diff suppressed because it is too large Load Diff

1104
po/lv.po

File diff suppressed because it is too large Load Diff

778
po/ml.po

File diff suppressed because it is too large Load Diff

1135
po/mr.po

File diff suppressed because it is too large Load Diff

625
po/nl.po

File diff suppressed because it is too large Load Diff

1204
po/or.po

File diff suppressed because it is too large Load Diff

863
po/pt.po

File diff suppressed because it is too large Load Diff

193
po/ru.po

@ -15,8 +15,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-03-14 18:11+0000\n" "POT-Creation-Date: 2013-03-03 12:52+0000\n"
"PO-Revision-Date: 2013-03-22 21:02+0400\n" "PO-Revision-Date: 2013-03-03 23:21+0400\n"
"Last-Translator: Yuri Myasoedov <omerta13@yandex.ru>\n" "Last-Translator: Yuri Myasoedov <omerta13@yandex.ru>\n"
"Language-Team: русский <gnome-cyr@gnome.org>\n" "Language-Team: русский <gnome-cyr@gnome.org>\n"
"Language: ru\n" "Language: ru\n"
@ -142,11 +142,11 @@ msgstr ""
#: ../data/org.gnome.shell.gschema.xml.in.in.h:11 #: ../data/org.gnome.shell.gschema.xml.in.in.h:11
msgid "History for command (Alt-F2) dialog" msgid "History for command (Alt-F2) dialog"
msgstr "История команд диалогового окна (Alt-F2)" msgstr "История команд диалога (Alt-F2)"
#: ../data/org.gnome.shell.gschema.xml.in.in.h:12 #: ../data/org.gnome.shell.gschema.xml.in.in.h:12
msgid "History for the looking glass dialog" msgid "History for the looking glass dialog"
msgstr "История просмотра прозрачного диалогового окна" msgstr "История просмотра прозрачного диалога"
#: ../data/org.gnome.shell.gschema.xml.in.in.h:13 #: ../data/org.gnome.shell.gschema.xml.in.in.h:13
msgid "" msgid ""
@ -223,11 +223,11 @@ msgstr "Комбинация клавиш для перехода в режим
#: ../data/org.gnome.shell.gschema.xml.in.in.h:25 #: ../data/org.gnome.shell.gschema.xml.in.in.h:25
msgid "Keybinding to toggle the visibility of the message tray" msgid "Keybinding to toggle the visibility of the message tray"
msgstr "Комбинация клавиш для отображения или скрытия панели сообщений" msgstr "Комбинация клавиш для показа и скрытия панели сообщений"
#: ../data/org.gnome.shell.gschema.xml.in.in.h:26 #: ../data/org.gnome.shell.gschema.xml.in.in.h:26
msgid "Keybinding to toggle the visibility of the message tray." msgid "Keybinding to toggle the visibility of the message tray."
msgstr "Комбинация клавиш для отображения или скрытия панели сообщений." msgstr "Комбинация клавиш для показа и скрытия панели сообщений."
#: ../data/org.gnome.shell.gschema.xml.in.in.h:27 #: ../data/org.gnome.shell.gschema.xml.in.in.h:27
msgid "Keybinding to focus the active notification" msgid "Keybinding to focus the active notification"
@ -239,13 +239,12 @@ msgstr "Комбинация клавиш для перевода фокуса
#: ../data/org.gnome.shell.gschema.xml.in.in.h:29 #: ../data/org.gnome.shell.gschema.xml.in.in.h:29
msgid "Keybinding to toggle the screen recorder" msgid "Keybinding to toggle the screen recorder"
msgstr "Комбинация клавиш для включения или выключения средства записи экрана" msgstr "Комбинация клавиш для включения и выключения средства записи экрана"
#: ../data/org.gnome.shell.gschema.xml.in.in.h:30 #: ../data/org.gnome.shell.gschema.xml.in.in.h:30
msgid "Keybinding to start/stop the builtin screen recorder." msgid "Keybinding to start/stop the builtin screen recorder."
msgstr "" msgstr ""
"Комбинация клавиш для запуска или остановки встроенного средства записи " "Комбинация клавиш для запуска и останова встроенного средства записи экрана."
"экрана."
#: ../data/org.gnome.shell.gschema.xml.in.in.h:31 #: ../data/org.gnome.shell.gschema.xml.in.in.h:31
msgid "Which keyboard to use" msgid "Which keyboard to use"
@ -264,8 +263,8 @@ msgid ""
"The framerate of the resulting screencast recordered by GNOME Shell's " "The framerate of the resulting screencast recordered by GNOME Shell's "
"screencast recorder in frames-per-second." "screencast recorder in frames-per-second."
msgstr "" msgstr ""
"Частота смены кадров (кадров/сек) скринкаста, записанного с помощью GNOME " "Частота смены кадров в скринкасте, записанном с помощью GNOME Shell (кадров/"
"Shell." "сек)."
#: ../data/org.gnome.shell.gschema.xml.in.in.h:35 #: ../data/org.gnome.shell.gschema.xml.in.in.h:35
msgid "The gstreamer pipeline used to encode the screencast" msgid "The gstreamer pipeline used to encode the screencast"
@ -300,7 +299,7 @@ msgstr ""
#: ../data/org.gnome.shell.gschema.xml.in.in.h:38 #: ../data/org.gnome.shell.gschema.xml.in.in.h:38
msgid "File extension used for storing the screencast" msgid "File extension used for storing the screencast"
msgstr "Расширение файла, используемое при сохранении скринкастов" msgstr "Расширение файла, использующееся для хранения скринкастов"
#: ../data/org.gnome.shell.gschema.xml.in.in.h:39 #: ../data/org.gnome.shell.gschema.xml.in.in.h:39
msgid "" msgid ""
@ -308,9 +307,9 @@ msgid ""
"current date, and use this extension. It should be changed when recording to " "current date, and use this extension. It should be changed when recording to "
"a different container format." "a different container format."
msgstr "" msgstr ""
"Имя файла записанного скринкаста будет уникальным, основанным на текущей " "Имя файла записанного скринкаста будет уникальным именем, основанным на "
"дате и используемом расширении. Оно должно быть изменено, если запись " "текущей дате, и использует это расширение. Оно должно быть изменено, если "
"выполняется в другой формат контейнера." "запись выполняется в другой контейнерный формат."
#: ../data/org.gnome.shell.gschema.xml.in.in.h:40 #: ../data/org.gnome.shell.gschema.xml.in.in.h:40
msgid "The application icon mode." msgid "The application icon mode."
@ -365,7 +364,7 @@ msgstr "Рабочие места только на основном монит
#: ../js/extensionPrefs/main.js:125 #: ../js/extensionPrefs/main.js:125
#, c-format #, c-format
msgid "There was an error loading the preferences dialog for %s:" msgid "There was an error loading the preferences dialog for %s:"
msgstr "При загрузке диалогового окна параметров для %s произошла ошибка:" msgstr "При загрузке диалога параметров для %s произошла ошибка:"
#: ../js/extensionPrefs/main.js:165 #: ../js/extensionPrefs/main.js:165
msgid "Extension" msgid "Extension"
@ -382,46 +381,46 @@ msgstr "Сеанс…"
#. 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:630 #: ../js/gdm/loginDialog.js:629
msgid "Not listed?" msgid "Not listed?"
msgstr "Нет в списке?" msgstr "Нет в списке?"
#: ../js/gdm/loginDialog.js:784 ../js/ui/components/networkAgent.js:137 #: ../js/gdm/loginDialog.js:783 ../js/ui/components/networkAgent.js:137
#: ../js/ui/components/polkitAgent.js:162 ../js/ui/endSessionDialog.js:375 #: ../js/ui/components/polkitAgent.js:162 ../js/ui/endSessionDialog.js:375
#: ../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:415 ../js/ui/unlockDialog.js:98 #: ../js/ui/status/bluetooth.js:415 ../js/ui/unlockDialog.js:126
#: ../js/ui/userMenu.js:938 #: ../js/ui/userMenu.js:934
msgid "Cancel" msgid "Cancel"
msgstr "Отмена" msgstr "Отмена"
#: ../js/gdm/loginDialog.js:800 #: ../js/gdm/loginDialog.js:799
msgctxt "button" msgctxt "button"
msgid "Sign In" msgid "Sign In"
msgstr "Войти" msgstr "Войти"
#: ../js/gdm/loginDialog.js:800 #: ../js/gdm/loginDialog.js:799
msgid "Next" msgid "Next"
msgstr "Далее" msgstr "Следующий"
#. 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:915 ../js/ui/components/networkAgent.js:260 #: ../js/gdm/loginDialog.js:904 ../js/ui/components/networkAgent.js:260
#: ../js/ui/components/networkAgent.js:278 #: ../js/ui/components/networkAgent.js:278
msgid "Username: " msgid "Username: "
msgstr "Имя пользователя: " msgstr "Имя пользователя: "
#: ../js/gdm/loginDialog.js:1159 #: ../js/gdm/loginDialog.js:1157
msgid "Login Window" msgid "Login Window"
msgstr "Окно входа в систему" msgstr "Окно входа в систему"
#. Translators: accessible name of the power menu in the login screen #. Translators: accessible name of the power menu in the login screen
#: ../js/gdm/powerMenu.js:36 #: ../js/gdm/powerMenu.js:36
msgid "Power" msgid "Power"
msgstr "Электропитание" msgstr "Питание"
#: ../js/gdm/powerMenu.js:93 ../js/ui/userMenu.js:696 ../js/ui/userMenu.js:700 #: ../js/gdm/powerMenu.js:93 ../js/ui/userMenu.js:695 ../js/ui/userMenu.js:699
#: ../js/ui/userMenu.js:816 #: ../js/ui/userMenu.js:815
msgid "Suspend" msgid "Suspend"
msgstr "Ждущий режим" msgstr "Ждущий режим"
@ -429,14 +428,14 @@ msgstr "Ждущий режим"
msgid "Restart" msgid "Restart"
msgstr "Перезапустить" msgstr "Перезапустить"
#: ../js/gdm/powerMenu.js:103 ../js/ui/userMenu.js:698 #: ../js/gdm/powerMenu.js:103 ../js/ui/userMenu.js:697
#: ../js/ui/userMenu.js:700 ../js/ui/userMenu.js:815 ../js/ui/userMenu.js:942 #: ../js/ui/userMenu.js:699 ../js/ui/userMenu.js:814 ../js/ui/userMenu.js:938
msgid "Power Off" msgid "Power Off"
msgstr "Выключить" msgstr "Выключить"
#: ../js/gdm/util.js:182 #: ../js/gdm/util.js:182
msgid "Authentication error" msgid "Authentication error"
msgstr "Ошибка проверки подлинности" msgstr "Ошибка подтверждения подлинности"
#. 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
@ -449,38 +448,38 @@ msgstr "(или проведите пальцем по считывающему
msgid "(e.g., user or %s)" msgid "(e.g., user or %s)"
msgstr "(например, пользователь или %s)" msgstr "(например, пользователь или %s)"
#: ../js/misc/util.js:97 #: ../js/misc/util.js:94
msgid "Command not found" msgid "Command not found"
msgstr "Команда не найдена" msgstr "Команда не найдена"
#. Replace "Error invoking GLib.shell_parse_argv: " with #. Replace "Error invoking GLib.shell_parse_argv: " with
#. something nicer #. something nicer
#: ../js/misc/util.js:130 #: ../js/misc/util.js:127
msgid "Could not parse command:" msgid "Could not parse command:"
msgstr "Не удалось разобрать команду:" msgstr "Не удалось разобрать команду:"
#: ../js/misc/util.js:138 #: ../js/misc/util.js:135
#, c-format #, c-format
msgid "Execution of '%s' failed:" msgid "Execution of '%s' failed:"
msgstr "Не удалось выполнить «%s»:" msgstr "Не удалось выполнить «%s»:"
#: ../js/ui/appDisplay.js:325 #: ../js/ui/appDisplay.js:348
msgid "Frequent" msgid "Frequent"
msgstr "Популярные" msgstr "Часто используемые"
#: ../js/ui/appDisplay.js:332 #: ../js/ui/appDisplay.js:355
msgid "All" msgid "All"
msgstr "Все" msgstr "Все"
#: ../js/ui/appDisplay.js:890 #: ../js/ui/appDisplay.js:913
msgid "New Window" msgid "New Window"
msgstr "Новое окно" msgstr "Новое окно"
#: ../js/ui/appDisplay.js:893 ../js/ui/dash.js:284 #: ../js/ui/appDisplay.js:916 ../js/ui/dash.js:284
msgid "Remove from Favorites" msgid "Remove from Favorites"
msgstr "Удалить из избранного" msgstr "Удалить из избранного"
#: ../js/ui/appDisplay.js:894 #: ../js/ui/appDisplay.js:917
msgid "Add to Favorites" msgid "Add to Favorites"
msgstr "Добавить в избранное" msgstr "Добавить в избранное"
@ -494,7 +493,7 @@ msgstr "Приложение %s добавлено в избранное."
msgid "%s has been removed from your favorites." msgid "%s has been removed from your favorites."
msgstr "Приложение %s удалено из избранного." msgstr "Приложение %s удалено из избранного."
#: ../js/ui/backgroundMenu.js:19 ../js/ui/userMenu.js:789 #: ../js/ui/backgroundMenu.js:19 ../js/ui/userMenu.js:788
msgid "Settings" msgid "Settings"
msgstr "Параметры" msgstr "Параметры"
@ -619,35 +618,35 @@ msgid "S"
msgstr "Сб" msgstr "Сб"
#. Translators: Text to show if there are no events #. Translators: Text to show if there are no events
#: ../js/ui/calendar.js:720 #: ../js/ui/calendar.js:692
msgid "Nothing Scheduled" msgid "Nothing Scheduled"
msgstr "Нет событий" msgstr "Нет событий"
#. 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:736 #: ../js/ui/calendar.js:708
msgctxt "calendar heading" msgctxt "calendar heading"
msgid "%A, %B %d" msgid "%A, %B %d"
msgstr "%A, %B %d" msgstr "%A, %B %d"
#. 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:739 #: ../js/ui/calendar.js:711
msgctxt "calendar heading" msgctxt "calendar heading"
msgid "%A, %B %d, %Y" msgid "%A, %B %d, %Y"
msgstr "%A, %B %d, %Y" msgstr "%A, %B %d, %Y"
#: ../js/ui/calendar.js:749 #: ../js/ui/calendar.js:721
msgid "Today" msgid "Today"
msgstr "Сегодня" msgstr "Сегодня"
#: ../js/ui/calendar.js:753 #: ../js/ui/calendar.js:725
msgid "Tomorrow" msgid "Tomorrow"
msgstr "Завтра" msgstr "Завтра"
#: ../js/ui/calendar.js:764 #: ../js/ui/calendar.js:736
msgid "This week" msgid "This week"
msgstr "Эта неделя" msgstr "Эта неделя"
#: ../js/ui/calendar.js:772 #: ../js/ui/calendar.js:744
msgid "Next week" msgid "Next week"
msgstr "Следующая неделя" msgstr "Следующая неделя"
@ -744,7 +743,7 @@ msgstr "Для широкополосного мобильного устрой
#: ../js/ui/components/networkAgent.js:330 #: ../js/ui/components/networkAgent.js:330
msgid "PIN: " msgid "PIN: "
msgstr "PIN-код: " msgstr "PIN: "
#: ../js/ui/components/networkAgent.js:336 #: ../js/ui/components/networkAgent.js:336
msgid "Mobile broadband network password" msgid "Mobile broadband network password"
@ -773,7 +772,7 @@ msgstr "Подтвердить"
#. * for instance. #. * for instance.
#: ../js/ui/components/polkitAgent.js:256 ../js/ui/shellMountOperation.js:383 #: ../js/ui/components/polkitAgent.js:256 ../js/ui/shellMountOperation.js:383
msgid "Sorry, that didn't work. Please try again." msgid "Sorry, that didn't work. Please try again."
msgstr "Не удалось подтвердить подлинность. Попробуйте снова." msgstr "Извините, это не сработало. Попробуйте снова."
#. Translators: this is a filename used for screencast recording #. Translators: this is a filename used for screencast recording
#: ../js/ui/components/recorder.js:48 #: ../js/ui/components/recorder.js:48
@ -1021,7 +1020,7 @@ msgstr "Показать учётную запись"
msgid "Unknown reason" msgid "Unknown reason"
msgstr "Неизвестная причина" msgstr "Неизвестная причина"
#: ../js/ui/ctrlAltTab.js:29 ../js/ui/viewSelector.js:96 #: ../js/ui/ctrlAltTab.js:29 ../js/ui/viewSelector.js:97
msgid "Windows" msgid "Windows"
msgstr "Окна" msgstr "Окна"
@ -1050,7 +1049,7 @@ msgstr "Настроить дату и время"
#. 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:215 #: ../js/ui/dateMenu.js:205
msgid "%A %B %e, %Y" msgid "%A %B %e, %Y"
msgstr "%A, %e %B, %Y" msgstr "%A, %e %B, %Y"
@ -1058,7 +1057,7 @@ msgstr "%A, %e %B, %Y"
#, c-format #, c-format
msgctxt "title" msgctxt "title"
msgid "Log Out %s" msgid "Log Out %s"
msgstr "Завершение сеанса пользователя %s" msgstr "Завершить сеанс пользователя %s"
#: ../js/ui/endSessionDialog.js:64 #: ../js/ui/endSessionDialog.js:64
msgctxt "title" msgctxt "title"
@ -1229,15 +1228,15 @@ msgstr "Очистить список сообщений"
msgid "Notification Settings" msgid "Notification Settings"
msgstr "Параметры уведомлений" msgstr "Параметры уведомлений"
#: ../js/ui/messageTray.js:1711 #: ../js/ui/messageTray.js:1707
msgid "No Messages" msgid "No Messages"
msgstr "Нет сообщений" msgstr "Нет сообщений"
#: ../js/ui/messageTray.js:1784 #: ../js/ui/messageTray.js:1787
msgid "Message Tray" msgid "Message Tray"
msgstr "Панель сообщений" msgstr "Панель сообщений"
#: ../js/ui/messageTray.js:2816 #: ../js/ui/messageTray.js:2864
msgid "System Information" msgid "System Information"
msgstr "Системная информация" msgstr "Системная информация"
@ -1246,7 +1245,7 @@ msgctxt "program"
msgid "Unknown" msgid "Unknown"
msgstr "Неизвестно" msgstr "Неизвестно"
#: ../js/ui/overviewControls.js:463 ../js/ui/screenShield.js:148 #: ../js/ui/overviewControls.js:460 ../js/ui/screenShield.js:153
#, c-format #, c-format
msgid "%d new message" msgid "%d new message"
msgid_plural "%d new messages" msgid_plural "%d new messages"
@ -1254,7 +1253,7 @@ msgstr[0] "%d новое сообщение"
msgstr[1] "%d новых сообщения" msgstr[1] "%d новых сообщения"
msgstr[2] "%d новых сообщений" msgstr[2] "%d новых сообщений"
#: ../js/ui/overview.js:84 #: ../js/ui/overview.js:82
msgid "Undo" msgid "Undo"
msgstr "Отменить" msgstr "Отменить"
@ -1266,21 +1265,21 @@ msgstr "Обзор"
#. in the search entry when no search is #. in the search entry when no search is
#. active; it should not exceed ~30 #. active; it should not exceed ~30
#. characters. #. characters.
#: ../js/ui/overview.js:271 #: ../js/ui/overview.js:284
msgid "Type to search…" msgid "Type to search…"
msgstr "Найти…" msgstr "Найти…"
#: ../js/ui/panel.js:612 #: ../js/ui/panel.js:613
msgid "Quit" msgid "Quit"
msgstr "Закрыть" msgstr "Закрыть"
#. 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:636 #: ../js/ui/panel.js:642
msgid "Activities" msgid "Activities"
msgstr "Обзор" msgstr "Обзор"
#: ../js/ui/panel.js:933 #: ../js/ui/panel.js:983
msgid "Top Bar" msgid "Top Bar"
msgstr "Верхняя панель" msgstr "Верхняя панель"
@ -1293,33 +1292,33 @@ msgstr "Верхняя панель"
msgid "toggle-switch-us" msgid "toggle-switch-us"
msgstr "toggle-switch-intl" msgstr "toggle-switch-intl"
#: ../js/ui/runDialog.js:73 #: ../js/ui/runDialog.js:205
msgid "Enter a Command" msgid "Enter a Command"
msgstr "Введите команду" msgstr "Введите команду"
#: ../js/ui/runDialog.js:109 #: ../js/ui/runDialog.js:241
msgid "Close" msgid "Close"
msgstr "Закрыть" msgstr "Закрыть"
#. Translators: This is a time format for a date in #. Translators: This is a time format for a date in
#. long format #. long format
#: ../js/ui/screenShield.js:85 #: ../js/ui/screenShield.js:90
msgid "%A, %B %d" msgid "%A, %B %d"
msgstr "%d %B, %A" msgstr "%d %B, %A"
#: ../js/ui/screenShield.js:150 #: ../js/ui/screenShield.js:155
#, c-format #, c-format
msgid "%d new notification" msgid "%d new notification"
msgid_plural "%d new notifications" msgid_plural "%d new notifications"
msgstr[0] "%d новое уведомление" msgstr[0] "%d новые уведомление"
msgstr[1] "%d новых уведомления" msgstr[1] "%d новых уведомленя"
msgstr[2] "%d новых уведомлений" msgstr[2] "%d новых уведомлений"
#: ../js/ui/screenShield.js:437 ../js/ui/userMenu.js:807 #: ../js/ui/screenShield.js:442 ../js/ui/userMenu.js:806
msgid "Lock" msgid "Lock"
msgstr "Заблокировать" msgstr "Заблокировать"
#: ../js/ui/screenShield.js:635 #: ../js/ui/screenShield.js:639
msgid "GNOME needs to lock the screen" msgid "GNOME needs to lock the screen"
msgstr "GNOME необходимо заблокировать экран" msgstr "GNOME необходимо заблокировать экран"
@ -1330,19 +1329,19 @@ msgstr "GNOME необходимо заблокировать экран"
#. #.
#. 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:756 ../js/ui/screenShield.js:1151 #: ../js/ui/screenShield.js:758 ../js/ui/screenShield.js:1169
msgid "Unable to lock" msgid "Unable to lock"
msgstr "Не удалось заблокировать" msgstr "Не удалось заблокировать"
#: ../js/ui/screenShield.js:757 ../js/ui/screenShield.js:1152 #: ../js/ui/screenShield.js:759 ../js/ui/screenShield.js:1170
msgid "Lock was blocked by an application" msgid "Lock was blocked by an application"
msgstr "Блокировке помешало приложение" msgstr "Блокировано помешало приложение"
#: ../js/ui/searchDisplay.js:451 #: ../js/ui/searchDisplay.js:431
msgid "Searching…" msgid "Searching…"
msgstr "Поиск…" msgstr "Поиск…"
#: ../js/ui/searchDisplay.js:495 #: ../js/ui/searchDisplay.js:475
msgid "No results." msgid "No results."
msgstr "Нет результатов." msgstr "Нет результатов."
@ -1354,11 +1353,11 @@ msgstr "Копировать"
msgid "Paste" msgid "Paste"
msgstr "Вставить" msgstr "Вставить"
#: ../js/ui/shellEntry.js:106 #: ../js/ui/shellEntry.js:105
msgid "Show Text" msgid "Show Text"
msgstr "Показать текст" msgstr "Показать текст"
#: ../js/ui/shellEntry.js:108 #: ../js/ui/shellEntry.js:107
msgid "Hide Text" msgid "Hide Text"
msgstr "Скрыть текст" msgstr "Скрыть текст"
@ -1370,7 +1369,7 @@ msgstr "Пароль"
msgid "Remember Password" msgid "Remember Password"
msgstr "Запомнить пароль" msgstr "Запомнить пароль"
#: ../js/ui/shellMountOperation.js:403 ../js/ui/unlockDialog.js:112 #: ../js/ui/shellMountOperation.js:403 ../js/ui/unlockDialog.js:140
msgid "Unlock" msgid "Unlock"
msgstr "Разблокировать" msgstr "Разблокировать"
@ -1671,7 +1670,7 @@ msgstr "Батарея"
#: ../js/ui/status/power.js:81 #: ../js/ui/status/power.js:81
msgid "Power Settings" msgid "Power Settings"
msgstr "Параметры электропитания" msgstr "Параметры питания"
#. 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
@ -1779,11 +1778,11 @@ msgstr "Громкость"
msgid "Microphone" msgid "Microphone"
msgstr "Микрофон" msgstr "Микрофон"
#: ../js/ui/unlockDialog.js:123 #: ../js/ui/unlockDialog.js:151
msgid "Log in as another user" msgid "Log in as another user"
msgstr "Войти от имени другого пользователя" msgstr "Войти от имени другого пользователя"
#: ../js/ui/unlockDialog.js:144 #: ../js/ui/unlockDialog.js:177
msgid "Unlock Window" msgid "Unlock Window"
msgstr "Разблокировать окно" msgstr "Разблокировать окно"
@ -1811,27 +1810,27 @@ msgstr "Бездействует"
msgid "Offline" msgid "Offline"
msgstr "Не в сети" msgstr "Не в сети"
#: ../js/ui/userMenu.js:781 #: ../js/ui/userMenu.js:780
msgid "Notifications" msgid "Notifications"
msgstr "Уведомления" msgstr "Уведомления"
#: ../js/ui/userMenu.js:797 #: ../js/ui/userMenu.js:796
msgid "Switch User" msgid "Switch User"
msgstr "Сменить пользователя" msgstr "Сменить пользователя"
#: ../js/ui/userMenu.js:802 #: ../js/ui/userMenu.js:801
msgid "Log Out" msgid "Log Out"
msgstr "Выйти из системы" msgstr "Выйти из системы"
#: ../js/ui/userMenu.js:822 #: ../js/ui/userMenu.js:821
msgid "Install Updates & Restart" msgid "Install Updates & Restart"
msgstr "Установить обновления и выполнить перезагрузку" msgstr "Установить обновления и выполнить перезагрузку"
#: ../js/ui/userMenu.js:840 #: ../js/ui/userMenu.js:839
msgid "Your chat status will be set to busy" msgid "Your chat status will be set to busy"
msgstr "Будет установлен статус «не беспокоить»" msgstr "Будет установлен статус «не беспокоить»"
#: ../js/ui/userMenu.js:841 #: ../js/ui/userMenu.js:840
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."
@ -1839,35 +1838,33 @@ msgstr ""
"Уведомления отключены, включая сообщения в чате. Ваш статус доступности " "Уведомления отключены, включая сообщения в чате. Ваш статус доступности "
"изменён, чтобы другие знали, что вы возможно не видите их сообщения." "изменён, чтобы другие знали, что вы возможно не видите их сообщения."
#: ../js/ui/userMenu.js:888 #: ../js/ui/userMenu.js:886
msgid "Other users are logged in." msgid "Other users are logged in."
msgstr "В системе имеются открытые сеансы других пользователей." msgstr "В системе имеются сеансы других пользователей."
#: ../js/ui/userMenu.js:893 #: ../js/ui/userMenu.js:891
msgid "Shutting down might cause them to lose unsaved work." msgid "Shutting down might cause them to lose unsaved work."
msgstr "Завершение работы может привести к потере несохранённых изменений." msgstr "Завершение работы может привести к потере несохранённых изменений."
#. Translators: Remote here refers to a remote session, like a ssh login #: ../js/ui/userMenu.js:918
#: ../js/ui/userMenu.js:921
#, c-format #, c-format
msgid "%s (remote)" msgid "%s (remote)"
msgstr "%s (удалённый)" msgstr "%s (удалённый)"
#. Translators: Console here refers to a tty like a VT console #: ../js/ui/userMenu.js:920
#: ../js/ui/userMenu.js:924
#, c-format #, c-format
msgid "%s (console)" msgid "%s (console)"
msgstr "%s (консоль)" msgstr "%s (консоль)"
#: ../js/ui/viewSelector.js:100 #: ../js/ui/viewSelector.js:101
msgid "Applications" msgid "Applications"
msgstr "Приложения" msgstr "Приложения"
#: ../js/ui/viewSelector.js:104 #: ../js/ui/viewSelector.js:105
msgid "Search" msgid "Search"
msgstr "Поиск" msgstr "Поиск"
#: ../js/ui/wanda.js:77 #: ../js/ui/wanda.js:92
#, c-format #, c-format
msgid "" msgid ""
"Sorry, no wisdom for you today:\n" "Sorry, no wisdom for you today:\n"
@ -1876,7 +1873,7 @@ msgstr ""
"Извините, на сегодня нет никаких советов:\n" "Извините, на сегодня нет никаких советов:\n"
"%s" "%s"
#: ../js/ui/wanda.js:81 #: ../js/ui/wanda.js:96
#, c-format #, c-format
msgid "%s the Oracle says" msgid "%s the Oracle says"
msgstr "Оракул говорит: «%s»" msgstr "Оракул говорит: «%s»"

660
po/sk.po

File diff suppressed because it is too large Load Diff

574
po/sl.po

File diff suppressed because it is too large Load Diff

590
po/sr.po

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1137
po/ta.po

File diff suppressed because it is too large Load Diff

1173
po/te.po

File diff suppressed because it is too large Load Diff

1888
po/tg.po

File diff suppressed because it is too large Load Diff

@ -11,8 +11,8 @@ msgid ""
msgstr "" 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-shell&keywords=I18N+L10N&component=general\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell&keywords=I18N+L10N&component=general\n"
"POT-Creation-Date: 2013-03-04 08:38+0000\n" "POT-Creation-Date: 2013-03-02 23:02+0000\n"
"PO-Revision-Date: 2013-03-07 21:01+0900\n" "PO-Revision-Date: 2013-03-03 15:57+0900\n"
"Last-Translator: Gheyret Kenji <gheyret@gmail.com>\n" "Last-Translator: Gheyret Kenji <gheyret@gmail.com>\n"
"Language-Team: Uyghur Computer Science Association <UKIJ@yahoogroups.com>\n" "Language-Team: Uyghur Computer Science Association <UKIJ@yahoogroups.com>\n"
"Language: \n" "Language: \n"
@ -112,13 +112,13 @@ msgstr "مۇناسىپ پروگرامما بەلگىسى يىغقۇچ رايون
#: ../data/org.gnome.shell.gschema.xml.in.in.h:9 #: ../data/org.gnome.shell.gschema.xml.in.in.h:9
msgid "List of categories that should be displayed as folders" msgid "List of categories that should be displayed as folders"
msgstr "قىسقۇچ سۈپىتىدە كۆرسىتىلىدىغان كاتېگورىيەلەرنىڭ تىزىمى" msgstr ""
#: ../data/org.gnome.shell.gschema.xml.in.in.h:10 #: ../data/org.gnome.shell.gschema.xml.in.in.h:10
msgid "" msgid ""
"Each category name in this list will be represented as folder in the " "Each category name in this list will be represented as folder in the "
"application view, rather than being displayed inline in the main view." "application view, rather than being displayed inline in the main view."
msgstr "پروگرامما كۆرۈنۈشىدە ھەر بىر كاتېگورىيە ئاتى قىسقۇچ سۈپىتىدە ئىپادىلىنىدۇ، ئاساسىي كۆرۈنۈشتە كۆرسىتىلمەيدۇ." msgstr ""
#: ../data/org.gnome.shell.gschema.xml.in.in.h:11 #: ../data/org.gnome.shell.gschema.xml.in.in.h:11
msgid "History for command (Alt-F2) dialog" msgid "History for command (Alt-F2) dialog"
@ -316,6 +316,7 @@ msgid "Select an extension to configure using the combobox above."
msgstr "يۇقىرىدىكى combobox دىن سەپلەيدىغان كېڭەيتىلمىنى تاللاڭ." msgstr "يۇقىرىدىكى combobox دىن سەپلەيدىغان كېڭەيتىلمىنى تاللاڭ."
#: ../js/gdm/loginDialog.js:405 #: ../js/gdm/loginDialog.js:405
#| msgid "Session..."
msgid "Session…" msgid "Session…"
msgstr "ئەڭگىمە…" msgstr "ئەڭگىمە…"
@ -330,7 +331,7 @@ msgstr "تىزىمدا يوقمۇ؟"
#: ../js/ui/components/polkitAgent.js:162 ../js/ui/endSessionDialog.js:375 #: ../js/ui/components/polkitAgent.js:162 ../js/ui/endSessionDialog.js:375
#: ../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:415 ../js/ui/unlockDialog.js:126 #: ../js/ui/status/bluetooth.js:415 ../js/ui/unlockDialog.js:126
#: ../js/ui/userMenu.js:934 #: ../js/ui/userMenu.js:932
msgid "Cancel" msgid "Cancel"
msgstr "ئەمەلدىن قالدۇر" msgstr "ئەمەلدىن قالدۇر"
@ -360,8 +361,8 @@ msgstr "كىرىش كۆزنىكى"
msgid "Power" msgid "Power"
msgstr "توك مەنبە" msgstr "توك مەنبە"
#: ../js/gdm/powerMenu.js:93 ../js/ui/userMenu.js:695 ../js/ui/userMenu.js:699 #: ../js/gdm/powerMenu.js:93 ../js/ui/userMenu.js:694 ../js/ui/userMenu.js:698
#: ../js/ui/userMenu.js:815 #: ../js/ui/userMenu.js:814
msgid "Suspend" msgid "Suspend"
msgstr "توڭلات" msgstr "توڭلات"
@ -369,14 +370,14 @@ msgstr "توڭلات"
msgid "Restart" msgid "Restart"
msgstr "قايتا قوزغات" msgstr "قايتا قوزغات"
#: ../js/gdm/powerMenu.js:103 ../js/ui/userMenu.js:697 #: ../js/gdm/powerMenu.js:103 ../js/ui/userMenu.js:696
#: ../js/ui/userMenu.js:699 ../js/ui/userMenu.js:814 ../js/ui/userMenu.js:938 #: ../js/ui/userMenu.js:698 ../js/ui/userMenu.js:813 ../js/ui/userMenu.js:936
msgid "Power Off" msgid "Power Off"
msgstr "توكنى ئۈز" msgstr "توكنى ئۈز"
#: ../js/gdm/util.js:182 #: ../js/gdm/util.js:182
msgid "Authentication error" msgid "Authentication error"
msgstr "كىملىك دەلىللەش خاتالىقى" msgstr "سالاھىيەت دەلىللەش خاتالىقى"
#. 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
@ -434,7 +435,7 @@ msgstr "%s يىغقۇچىڭىزغا قوشۇلدى."
msgid "%s has been removed from your favorites." msgid "%s has been removed from your favorites."
msgstr "%s يىغقۇچىڭىزدىن چىقىرىۋېتىلىدۇ." msgstr "%s يىغقۇچىڭىزدىن چىقىرىۋېتىلىدۇ."
#: ../js/ui/backgroundMenu.js:19 ../js/ui/userMenu.js:788 #: ../js/ui/backgroundMenu.js:19 ../js/ui/userMenu.js:787
msgid "Settings" msgid "Settings"
msgstr "تەڭشەكلەر" msgstr "تەڭشەكلەر"
@ -696,7 +697,7 @@ msgstr "%s كە باغلىنىش ئۈچۈن بىر ئىمغا ئېھتىي
#: ../js/ui/components/polkitAgent.js:55 #: ../js/ui/components/polkitAgent.js:55
msgid "Authentication Required" msgid "Authentication Required"
msgstr "كىملىك دەلىللەش زۆرۈر" msgstr "سالاھىيەت دەلىللەش زۆرۈر"
#: ../js/ui/components/polkitAgent.js:93 #: ../js/ui/components/polkitAgent.js:93
msgid "Administrator" msgid "Administrator"
@ -846,7 +847,7 @@ msgstr "تور خاتالىقى"
#: ../js/ui/components/telepathyClient.js:1300 #: ../js/ui/components/telepathyClient.js:1300
msgid "Authentication failed" msgid "Authentication failed"
msgstr "كىملىك دەلىللەش مەغلۇپ بولدى" msgstr "سالاھىيەت دەلىللەش مەغلۇپ بولدى"
#: ../js/ui/components/telepathyClient.js:1302 #: ../js/ui/components/telepathyClient.js:1302
msgid "Encryption error" msgid "Encryption error"
@ -955,7 +956,7 @@ msgstr "ھېساباتنى كۆرۈش"
msgid "Unknown reason" msgid "Unknown reason"
msgstr "نامەلۇم سەۋەب" msgstr "نامەلۇم سەۋەب"
#: ../js/ui/ctrlAltTab.js:29 ../js/ui/viewSelector.js:96 #: ../js/ui/ctrlAltTab.js:29 ../js/ui/viewSelector.js:97
msgid "Windows" msgid "Windows"
msgstr "كۆزنەكلەر" msgstr "كۆزنەكلەر"
@ -1143,6 +1144,7 @@ msgid "Remove"
msgstr "چىقىرىۋەت" msgstr "چىقىرىۋەت"
#: ../js/ui/messageTray.js:1501 #: ../js/ui/messageTray.js:1501
#| msgid "No Messages"
msgid "Clear Messages" msgid "Clear Messages"
msgstr "ئۇچۇرنى تازىلا" msgstr "ئۇچۇرنى تازىلا"
@ -1186,6 +1188,7 @@ msgstr "قىسقىچە بايان"
#. active; it should not exceed ~30 #. active; it should not exceed ~30
#. characters. #. characters.
#: ../js/ui/overview.js:284 #: ../js/ui/overview.js:284
#| msgid "Type to search..."
msgid "Type to search…" msgid "Type to search…"
msgstr "ئىزدەيدىغاننى كىرگۈزۈڭ.…" msgstr "ئىزدەيدىغاننى كىرگۈزۈڭ.…"
@ -1232,7 +1235,7 @@ msgid "%d new notification"
msgid_plural "%d new notifications" msgid_plural "%d new notifications"
msgstr[0] "%d دانە ئۇقتۇرۇش" msgstr[0] "%d دانە ئۇقتۇرۇش"
#: ../js/ui/screenShield.js:442 ../js/ui/userMenu.js:806 #: ../js/ui/screenShield.js:442 ../js/ui/userMenu.js:805
msgid "Lock" msgid "Lock"
msgstr "قۇلۇپلا" msgstr "قۇلۇپلا"
@ -1248,6 +1251,7 @@ msgstr "گىنوم ئېكراننى قۇلۇپلىشى زۆرۈر"
#. 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:758 ../js/ui/screenShield.js:1169 #: ../js/ui/screenShield.js:758 ../js/ui/screenShield.js:1169
#| msgid "Unable to connect to %s"
msgid "Unable to lock" msgid "Unable to lock"
msgstr "قۇلۇپلىغىلى بولمىدى" msgstr "قۇلۇپلىغىلى بولمىدى"
@ -1256,6 +1260,7 @@ msgid "Lock was blocked by an application"
msgstr "بىر پروگرامما قۇلۇپلاشنى توسۇپ قويغان" msgstr "بىر پروگرامما قۇلۇپلاشنى توسۇپ قويغان"
#: ../js/ui/searchDisplay.js:431 #: ../js/ui/searchDisplay.js:431
#| msgid "Searching..."
msgid "Searching…" msgid "Searching…"
msgstr "ئىزدەۋاتىدۇ…" msgstr "ئىزدەۋاتىدۇ…"
@ -1351,10 +1356,12 @@ msgid "Visibility"
msgstr "كۆرۈشچانلىقى" msgstr "كۆرۈشچانلىقى"
#: ../js/ui/status/bluetooth.js:59 #: ../js/ui/status/bluetooth.js:59
#| msgid "Send Files to Device..."
msgid "Send Files to Device…" msgid "Send Files to Device…"
msgstr "ھۆججەتلەرنى ئۈسكۈنىگە ئەۋەتىش…" msgstr "ھۆججەتلەرنى ئۈسكۈنىگە ئەۋەتىش…"
#: ../js/ui/status/bluetooth.js:60 #: ../js/ui/status/bluetooth.js:60
#| msgid "Set Up a New Device..."
msgid "Set Up a New Device…" msgid "Set Up a New Device…"
msgstr "يېڭى ئۈسكۈنە ئورنىتىش…" msgstr "يېڭى ئۈسكۈنە ئورنىتىش…"
@ -1381,6 +1388,7 @@ msgid "connecting..."
msgstr "باغلىنىۋاتىدۇ…" msgstr "باغلىنىۋاتىدۇ…"
#: ../js/ui/status/bluetooth.js:239 #: ../js/ui/status/bluetooth.js:239
#| msgid "Send Files..."
msgid "Send Files…" msgid "Send Files…"
msgstr "ھۆججەت يوللاش…" msgstr "ھۆججەت يوللاش…"
@ -1486,7 +1494,7 @@ msgstr "باشقۇرۇلمىغان"
#. 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:469 ../js/ui/status/network.js:1549 #: ../js/ui/status/network.js:469 ../js/ui/status/network.js:1549
msgid "authentication required" msgid "authentication required"
msgstr "كىملىك دەلىللەش زۆرۈر" msgstr "سالاھىيەت دەلىللەش زۆرۈر"
#. 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
@ -1593,6 +1601,7 @@ msgstr "توك مەنبە تەڭشىكى"
#. 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
#: ../js/ui/status/power.js:99 #: ../js/ui/status/power.js:99
#| msgid "Estimating..."
msgid "Estimating…" msgid "Estimating…"
msgstr "مۆلچەرلەۋاتىدۇ…" msgstr "مۆلچەرلەۋاتىدۇ…"
@ -1696,79 +1705,79 @@ msgstr "باشقا ئىشلەتكۈچى سۈپىتىدە كىر"
msgid "Unlock Window" msgid "Unlock Window"
msgstr "كۆزنەكنى قۇلۇپلا" msgstr "كۆزنەكنى قۇلۇپلا"
#: ../js/ui/userMenu.js:193 #: ../js/ui/userMenu.js:192
msgid "Available" msgid "Available"
msgstr "ئىشلەتكىلى بولىدۇ" msgstr "ئىشلەتكىلى بولىدۇ"
#: ../js/ui/userMenu.js:196 #: ../js/ui/userMenu.js:195
msgid "Busy" msgid "Busy"
msgstr "ئالدىراش" msgstr "ئالدىراش"
#: ../js/ui/userMenu.js:199 #: ../js/ui/userMenu.js:198
msgid "Invisible" msgid "Invisible"
msgstr "يوشۇرۇن" msgstr "يوشۇرۇن"
#: ../js/ui/userMenu.js:202 #: ../js/ui/userMenu.js:201
msgid "Away" msgid "Away"
msgstr "يوق" msgstr "يوق"
#: ../js/ui/userMenu.js:205 #: ../js/ui/userMenu.js:204
msgid "Idle" msgid "Idle"
msgstr "بىكار" msgstr "بىكار"
#: ../js/ui/userMenu.js:208 #: ../js/ui/userMenu.js:207
msgid "Offline" msgid "Offline"
msgstr "توردا يوق" msgstr "توردا يوق"
#: ../js/ui/userMenu.js:780 #: ../js/ui/userMenu.js:779
msgid "Notifications" msgid "Notifications"
msgstr "ئۇقتۇرۇشلار" msgstr "ئۇقتۇرۇشلار"
#: ../js/ui/userMenu.js:796 #: ../js/ui/userMenu.js:795
msgid "Switch User" msgid "Switch User"
msgstr "ئىشلەتكۈچى ئالماشتۇرۇش" msgstr "ئىشلەتكۈچى ئالماشتۇرۇش"
#: ../js/ui/userMenu.js:801 #: ../js/ui/userMenu.js:800
msgid "Log Out" msgid "Log Out"
msgstr "تىزىمدىن چىقىش" msgstr "تىزىمدىن چىقىش"
#: ../js/ui/userMenu.js:821 #: ../js/ui/userMenu.js:820
msgid "Install Updates & Restart" msgid "Install Updates & Restart"
msgstr "يېڭىلانمىلارنى ئورنىتىپ قايتا قوزغات" msgstr "يېڭىلانمىلارنى ئورنىتىپ قايتا قوزغات"
#: ../js/ui/userMenu.js:839 #: ../js/ui/userMenu.js:838
msgid "Your chat status will be set to busy" msgid "Your chat status will be set to busy"
msgstr "سىزنىڭ سۆھبەت ھالىتىڭىز ئالدىراش قىلىنىدۇ" msgstr "سىزنىڭ سۆھبەت ھالىتىڭىز ئالدىراش قىلىنىدۇ"
#: ../js/ui/userMenu.js:840 #: ../js/ui/userMenu.js:839
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."
msgstr "" msgstr ""
#: ../js/ui/userMenu.js:886 #: ../js/ui/userMenu.js:885
msgid "Other users are logged in." msgid "Other users are logged in."
msgstr "باشقا ئىشلەتكۈچىلەر كىردى:" msgstr "باشقا ئىشلەتكۈچىلەر كىردى:"
#: ../js/ui/userMenu.js:891 #: ../js/ui/userMenu.js:890
msgid "Shutting down might cause them to lose unsaved work." msgid "Shutting down might cause them to lose unsaved work."
msgstr "تاقالسا ساقلانمىغان خىزمەتلەر يوقىلىشى مۇمكىن." msgstr "تاقالسا ساقلانمىغان خىزمەتلەر يوقىلىشى مۇمكىن."
#: ../js/ui/userMenu.js:918 #: ../js/ui/userMenu.js:916
#, c-format #, c-format
msgid "%s (remote)" msgid "%s (remote)"
msgstr "%s (يىراق)" msgstr "%s (يىراق)"
#: ../js/ui/userMenu.js:920 #: ../js/ui/userMenu.js:918
#, c-format #, c-format
msgid "%s (console)" msgid "%s (console)"
msgstr "%s (تىزگىن كۆزنەك)" msgstr "%s (تىزگىن كۆزنەك)"
#: ../js/ui/viewSelector.js:100 #: ../js/ui/viewSelector.js:101
msgid "Applications" msgid "Applications"
msgstr "پروگراممىلار" msgstr "پروگراممىلار"
#: ../js/ui/viewSelector.js:104 #: ../js/ui/viewSelector.js:105
msgid "Search" msgid "Search"
msgstr "ئىزدە" msgstr "ئىزدە"

1123
po/uk.po

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -287,7 +287,7 @@ libgnome_shell_la_LIBADD = \
libgnome_shell_la_CPPFLAGS = $(gnome_shell_cflags) libgnome_shell_la_CPPFLAGS = $(gnome_shell_cflags)
Shell-0.1.gir: libgnome-shell.la St-1.0.gir Shell-0.1.gir: libgnome-shell.la St-1.0.gir
Shell_0_1_gir_INCLUDES = Clutter-1.0 ClutterX11-1.0 Meta-3.0 TelepathyGLib-0.12 Soup-2.4 GMenu-3.0 NetworkManager-1.0 NMClient-1.0 Shell_0_1_gir_INCLUDES = Clutter-1.0 ClutterX11-1.0 Meta-3.0 TelepathyGLib-0.12 TelepathyLogger-0.2 Soup-2.4 GMenu-3.0 NetworkManager-1.0 NMClient-1.0
Shell_0_1_gir_CFLAGS = $(libgnome_shell_la_CPPFLAGS) -I $(srcdir) Shell_0_1_gir_CFLAGS = $(libgnome_shell_la_CPPFLAGS) -I $(srcdir)
Shell_0_1_gir_LIBS = libgnome-shell.la Shell_0_1_gir_LIBS = libgnome-shell.la
Shell_0_1_gir_FILES = $(libgnome_shell_la_gir_sources) Shell_0_1_gir_FILES = $(libgnome_shell_la_gir_sources)

@ -490,17 +490,6 @@ calendar_sources_registry_source_removed_cb (ESourceRegistry *registry,
} }
} }
static void
ensure_appointment_sources (CalendarSources *sources)
{
if (!sources->priv->appointment_sources.loaded)
{
calendar_sources_load_esource_list (sources->priv->registry,
&sources->priv->appointment_sources);
sources->priv->appointment_sources.loaded = TRUE;
}
}
GList * GList *
calendar_sources_get_appointment_clients (CalendarSources *sources) calendar_sources_get_appointment_clients (CalendarSources *sources)
{ {
@ -508,7 +497,12 @@ calendar_sources_get_appointment_clients (CalendarSources *sources)
g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), NULL); g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), NULL);
ensure_appointment_sources (sources); if (!sources->priv->appointment_sources.loaded)
{
calendar_sources_load_esource_list (sources->priv->registry,
&sources->priv->appointment_sources);
sources->priv->appointment_sources.loaded = TRUE;
}
list = g_hash_table_get_values (sources->priv->appointment_sources.clients); list = g_hash_table_get_values (sources->priv->appointment_sources.clients);
@ -518,17 +512,6 @@ calendar_sources_get_appointment_clients (CalendarSources *sources)
return list; return list;
} }
static void
ensure_task_sources (CalendarSources *sources)
{
if (!sources->priv->task_sources.loaded)
{
calendar_sources_load_esource_list (sources->priv->registry,
&sources->priv->task_sources);
sources->priv->task_sources.loaded = TRUE;
}
}
GList * GList *
calendar_sources_get_task_clients (CalendarSources *sources) calendar_sources_get_task_clients (CalendarSources *sources)
{ {
@ -536,7 +519,12 @@ calendar_sources_get_task_clients (CalendarSources *sources)
g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), NULL); g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), NULL);
ensure_task_sources (sources); if (!sources->priv->task_sources.loaded)
{
calendar_sources_load_esource_list (sources->priv->registry,
&sources->priv->task_sources);
sources->priv->task_sources.loaded = TRUE;
}
list = g_hash_table_get_values (sources->priv->task_sources.clients); list = g_hash_table_get_values (sources->priv->task_sources.clients);
@ -545,15 +533,3 @@ calendar_sources_get_task_clients (CalendarSources *sources)
return list; return list;
} }
gboolean
calendar_sources_has_sources (CalendarSources *sources)
{
g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), FALSE);
ensure_appointment_sources (sources);
ensure_task_sources (sources);
return g_hash_table_size (sources->priv->appointment_sources.clients) > 0 ||
g_hash_table_size (sources->priv->task_sources.clients) > 0;
}

@ -61,8 +61,6 @@ CalendarSources *calendar_sources_get (void);
GList *calendar_sources_get_appointment_clients (CalendarSources *sources); GList *calendar_sources_get_appointment_clients (CalendarSources *sources);
GList *calendar_sources_get_task_clients (CalendarSources *sources); GList *calendar_sources_get_task_clients (CalendarSources *sources);
gboolean calendar_sources_has_sources (CalendarSources *sources);
G_END_DECLS G_END_DECLS
#endif /* __CALENDAR_SOURCES_H__ */ #endif /* __CALENDAR_SOURCES_H__ */

@ -57,7 +57,6 @@ static const gchar introspection_xml[] =
" <signal name='Changed'/>" " <signal name='Changed'/>"
" <property name='Since' type='x' access='read'/>" " <property name='Since' type='x' access='read'/>"
" <property name='Until' type='x' access='read'/>" " <property name='Until' type='x' access='read'/>"
" <property name='HasCalendars' type='b' access='read'/>"
" </interface>" " </interface>"
"</node>"; "</node>";
static GDBusNodeInfo *introspection_data = NULL; static GDBusNodeInfo *introspection_data = NULL;
@ -727,12 +726,6 @@ app_load_events (App *app)
app->cache_invalid = FALSE; app->cache_invalid = FALSE;
} }
static gboolean
app_has_calendars (App *app)
{
return calendar_sources_has_sources (app->sources);
}
static void static void
on_appointment_sources_changed (CalendarSources *sources, on_appointment_sources_changed (CalendarSources *sources,
gpointer user_data) gpointer user_data)
@ -741,26 +734,6 @@ on_appointment_sources_changed (CalendarSources *sources,
print_debug ("Sources changed\n"); print_debug ("Sources changed\n");
app_load_events (app); app_load_events (app);
/* Notify the HasCalendars property */
{
GVariantBuilder dict_builder;
g_variant_builder_init (&dict_builder, G_VARIANT_TYPE ("a{sv}"));
g_variant_builder_add (&dict_builder, "{sv}", "HasCalendars",
g_variant_new_boolean (app_has_calendars (app)));
g_dbus_connection_emit_signal (g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL),
NULL,
"/org/gnome/Shell/CalendarServer",
"org.freedesktop.DBus.Properties",
"PropertiesChanged",
g_variant_new ("(sa{sv}as)",
"org.gnome.Shell.CalendarServer",
&dict_builder,
NULL),
NULL);
}
} }
static App * static App *
@ -960,10 +933,6 @@ handle_get_property (GDBusConnection *connection,
{ {
ret = g_variant_new_int64 (app->until); ret = g_variant_new_int64 (app->until);
} }
else if (g_strcmp0 (property_name, "HasCalendars") == 0)
{
ret = g_variant_new_boolean (app_has_calendars (app));
}
else else
{ {
g_assert_not_reached (); g_assert_not_reached ();

@ -8,7 +8,9 @@
#include "shell-embedded-window-private.h" #include "shell-embedded-window-private.h"
/* This type is a subclass of GtkWindow that ties the window to a /* This type is a subclass of GtkWindow that ties the window to a
* ShellGtkEmbed; the resizing logic is bound to the clutter logic. * ShellGtkEmbed; the window is reparented into the stage
* window for the actor and the resizing logic is bound to the clutter
* logic.
* *
* The typical usage we might expect is * The typical usage we might expect is
* *
@ -26,13 +28,16 @@
G_DEFINE_TYPE (ShellEmbeddedWindow, shell_embedded_window, GTK_TYPE_WINDOW); G_DEFINE_TYPE (ShellEmbeddedWindow, shell_embedded_window, GTK_TYPE_WINDOW);
enum { enum {
PROP_0 PROP_0,
PROP_STAGE
}; };
struct _ShellEmbeddedWindowPrivate { struct _ShellEmbeddedWindowPrivate {
ShellGtkEmbed *actor; ShellGtkEmbed *actor;
GdkRectangle position; GdkRectangle position;
Window stage_xwindow;
}; };
/* /*
@ -75,6 +80,27 @@ shell_embedded_window_hide (GtkWidget *widget)
GTK_WIDGET_CLASS (shell_embedded_window_parent_class)->hide (widget); GTK_WIDGET_CLASS (shell_embedded_window_parent_class)->hide (widget);
} }
static void
shell_embedded_window_realize (GtkWidget *widget)
{
ShellEmbeddedWindow *window = SHELL_EMBEDDED_WINDOW (widget);
GTK_WIDGET_CLASS (shell_embedded_window_parent_class)->realize (widget);
/* Using XReparentWindow() is simpler than using gdk_window_reparent(),
* since it avoids maybe having to create a new foreign GDK window for
* the stage. However, GDK will be left thinking that the parent of
* window->window is the root window - it's not immediately clear
* to me whether that is more or less likely to cause problems than
* modifying the GDK hierarchy.
*/
XReparentWindow (GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (widget)),
gdk_x11_window_get_xid (gtk_widget_get_window (widget)),
window->priv->stage_xwindow,
window->priv->position.x, window->priv->position.y);
}
static gboolean static gboolean
shell_embedded_window_configure_event (GtkWidget *widget, shell_embedded_window_configure_event (GtkWidget *widget,
GdkEventConfigure *event) GdkEventConfigure *event)
@ -101,6 +127,27 @@ shell_embedded_window_check_resize (GtkContainer *container)
clutter_actor_queue_relayout (CLUTTER_ACTOR (window->priv->actor)); clutter_actor_queue_relayout (CLUTTER_ACTOR (window->priv->actor));
} }
static void
shell_embedded_window_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
ShellEmbeddedWindow *window = SHELL_EMBEDDED_WINDOW (object);
switch (prop_id)
{
case PROP_STAGE:
window->priv->stage_xwindow =
clutter_x11_get_stage_window (g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static GObject * static GObject *
shell_embedded_window_constructor (GType gtype, shell_embedded_window_constructor (GType gtype,
guint n_properties, guint n_properties,
@ -135,13 +182,23 @@ shell_embedded_window_class_init (ShellEmbeddedWindowClass *klass)
g_type_class_add_private (klass, sizeof (ShellEmbeddedWindowPrivate)); g_type_class_add_private (klass, sizeof (ShellEmbeddedWindowPrivate));
object_class->set_property = shell_embedded_window_set_property;
object_class->constructor = shell_embedded_window_constructor; object_class->constructor = shell_embedded_window_constructor;
widget_class->show = shell_embedded_window_show; widget_class->show = shell_embedded_window_show;
widget_class->hide = shell_embedded_window_hide; widget_class->hide = shell_embedded_window_hide;
widget_class->realize = shell_embedded_window_realize;
widget_class->configure_event = shell_embedded_window_configure_event; widget_class->configure_event = shell_embedded_window_configure_event;
container_class->check_resize = shell_embedded_window_check_resize; container_class->check_resize = shell_embedded_window_check_resize;
g_object_class_install_property (object_class,
PROP_STAGE,
g_param_spec_object ("stage",
"Stage",
"ClutterStage to embed on",
CLUTTER_TYPE_STAGE,
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
} }
static void static void
@ -225,8 +282,9 @@ _shell_embedded_window_unmap (ShellEmbeddedWindow *window)
* Public API * Public API
*/ */
GtkWidget * GtkWidget *
shell_embedded_window_new (void) shell_embedded_window_new (ClutterStage *stage)
{ {
return g_object_new (SHELL_TYPE_EMBEDDED_WINDOW, return g_object_new (SHELL_TYPE_EMBEDDED_WINDOW,
"stage", stage,
NULL); NULL);
} }

@ -30,6 +30,6 @@ struct _ShellEmbeddedWindowClass
}; };
GType shell_embedded_window_get_type (void) G_GNUC_CONST; GType shell_embedded_window_get_type (void) G_GNUC_CONST;
GtkWidget *shell_embedded_window_new (void); GtkWidget *shell_embedded_window_new (ClutterStage *stage);
#endif /* __SHELL_EMBEDDED_WINDOW_H__ */ #endif /* __SHELL_EMBEDDED_WINDOW_H__ */

@ -3,11 +3,8 @@
#include "config.h" #include "config.h"
#include "shell-embedded-window-private.h" #include "shell-embedded-window-private.h"
#include "shell-global.h"
#include <gdk/gdkx.h> #include <gdk/gdkx.h>
#include <meta/display.h>
#include <meta/window.h>
enum { enum {
PROP_0, PROP_0,
@ -18,14 +15,9 @@ enum {
struct _ShellGtkEmbedPrivate struct _ShellGtkEmbedPrivate
{ {
ShellEmbeddedWindow *window; ShellEmbeddedWindow *window;
ClutterActor *window_actor;
guint window_actor_destroyed_handler;
guint window_created_handler;
}; };
G_DEFINE_TYPE (ShellGtkEmbed, shell_gtk_embed, CLUTTER_TYPE_CLONE); G_DEFINE_TYPE (ShellGtkEmbed, shell_gtk_embed, CLUTTER_X11_TYPE_TEXTURE_PIXMAP);
static void shell_gtk_embed_set_window (ShellGtkEmbed *embed, static void shell_gtk_embed_set_window (ShellGtkEmbed *embed,
ShellEmbeddedWindow *window); ShellEmbeddedWindow *window);
@ -38,106 +30,39 @@ shell_gtk_embed_on_window_destroy (GtkWidget *object,
} }
static void static void
shell_gtk_embed_remove_window_actor (ShellGtkEmbed *embed) shell_gtk_embed_on_window_realize (GtkWidget *widget,
{
ShellGtkEmbedPrivate *priv = embed->priv;
if (priv->window_actor)
{
g_signal_handler_disconnect (priv->window_actor,
priv->window_actor_destroyed_handler);
priv->window_actor_destroyed_handler = 0;
g_object_unref (priv->window_actor);
priv->window_actor = NULL;
}
clutter_clone_set_source (CLUTTER_CLONE (embed), NULL);
}
static void
shell_gtk_embed_window_created_cb (MetaDisplay *display,
MetaWindow *window,
ShellGtkEmbed *embed) ShellGtkEmbed *embed)
{ {
ShellGtkEmbedPrivate *priv = embed->priv; /* Here automatic=FALSE means to use CompositeRedirectManual.
Window xwindow = meta_window_get_xwindow (window); * That is, the X server shouldn't draw the window onto the
GdkWindow *gdk_window = gtk_widget_get_window (GTK_WIDGET (priv->window)); * screen.
*/
if (xwindow == gdk_x11_window_get_xid (gdk_window)) clutter_x11_texture_pixmap_set_window (CLUTTER_X11_TEXTURE_PIXMAP (embed),
{ gdk_x11_window_get_xid (gtk_widget_get_window (widget)),
ClutterActor *window_actor = FALSE);
CLUTTER_ACTOR (meta_window_get_compositor_private (window));
MetaDisplay *display = shell_global_get_display (shell_global_get ());
GCallback remove_cb = G_CALLBACK (shell_gtk_embed_remove_window_actor);
cairo_region_t *empty_region;
clutter_clone_set_source (CLUTTER_CLONE (embed), window_actor);
/* We want to explicitly clear the clone source when the window
actor is destroyed because otherwise we might end up keeping
it alive after it has been disposed. Otherwise this can cause
a crash if there is a paint after mutter notices that the top
level window has been destroyed, which causes it to dispose
the window, and before the tray manager notices that the
window is gone which would otherwise reset the window and
unref the clone */
priv->window_actor = g_object_ref (window_actor);
priv->window_actor_destroyed_handler =
g_signal_connect_swapped (window_actor,
"destroy",
remove_cb,
embed);
/* Hide the original actor otherwise it will appear in the scene
as a normal window */
clutter_actor_set_opacity (window_actor, 0);
/* Set an empty input shape on the window so that it can't get
any input. This probably isn't the ideal way to acheive this.
It would probably be better to force the window to go behind
Mutter's guard window, but this is quite difficult to do as
Mutter doesn't manage the stacking for override redirect
windows and the guard window is repeatedly lowered to the
bottom of the stack. */
empty_region = cairo_region_create ();
gdk_window_input_shape_combine_region (gdk_window,
empty_region,
0, 0 /* offset x/y */);
cairo_region_destroy (empty_region);
/* Now that we've found the window we don't need to listen for
new windows anymore */
g_signal_handler_disconnect (display,
priv->window_created_handler);
priv->window_created_handler = 0;
}
} }
static void static void
shell_gtk_embed_set_window (ShellGtkEmbed *embed, shell_gtk_embed_set_window (ShellGtkEmbed *embed,
ShellEmbeddedWindow *window) ShellEmbeddedWindow *window)
{ {
MetaDisplay *display = shell_global_get_display (shell_global_get ());
if (embed->priv->window) if (embed->priv->window)
{ {
if (embed->priv->window_created_handler)
{
g_signal_handler_disconnect (display,
embed->priv->window_created_handler);
embed->priv->window_created_handler = 0;
}
shell_gtk_embed_remove_window_actor (embed);
_shell_embedded_window_set_actor (embed->priv->window, NULL); _shell_embedded_window_set_actor (embed->priv->window, NULL);
g_object_unref (embed->priv->window); g_object_unref (embed->priv->window);
clutter_x11_texture_pixmap_set_window (CLUTTER_X11_TEXTURE_PIXMAP (embed),
None,
FALSE);
g_signal_handlers_disconnect_by_func (embed->priv->window, g_signal_handlers_disconnect_by_func (embed->priv->window,
(gpointer)shell_gtk_embed_on_window_destroy, (gpointer)shell_gtk_embed_on_window_destroy,
embed); embed);
g_signal_handlers_disconnect_by_func (embed->priv->window,
(gpointer)shell_gtk_embed_on_window_realize,
embed);
} }
embed->priv->window = window; embed->priv->window = window;
@ -150,14 +75,11 @@ shell_gtk_embed_set_window (ShellGtkEmbed *embed,
g_signal_connect (embed->priv->window, "destroy", g_signal_connect (embed->priv->window, "destroy",
G_CALLBACK (shell_gtk_embed_on_window_destroy), embed); G_CALLBACK (shell_gtk_embed_on_window_destroy), embed);
g_signal_connect (embed->priv->window, "realize",
G_CALLBACK (shell_gtk_embed_on_window_realize), embed);
/* Listen for new windows so we can detect when Mutter has if (gtk_widget_get_realized (GTK_WIDGET (window)))
created a MutterWindow for this window */ shell_gtk_embed_on_window_realize (GTK_WIDGET (embed->priv->window), embed);
embed->priv->window_created_handler =
g_signal_connect (display,
"window-created",
G_CALLBACK (shell_gtk_embed_window_created_cb),
embed);
} }
clutter_actor_queue_relayout (CLUTTER_ACTOR (embed)); clutter_actor_queue_relayout (CLUTTER_ACTOR (embed));
@ -338,6 +260,11 @@ shell_gtk_embed_init (ShellGtkEmbed *embed)
{ {
embed->priv = G_TYPE_INSTANCE_GET_PRIVATE (embed, SHELL_TYPE_GTK_EMBED, embed->priv = G_TYPE_INSTANCE_GET_PRIVATE (embed, SHELL_TYPE_GTK_EMBED,
ShellGtkEmbedPrivate); ShellGtkEmbedPrivate);
/* automatic here means whether ClutterX11TexturePixmap should
* process damage update and refresh the pixmap itself.
*/
clutter_x11_texture_pixmap_set_automatic (CLUTTER_X11_TEXTURE_PIXMAP (embed), TRUE);
} }
/* /*

@ -2,7 +2,7 @@
#ifndef __SHELL_GTK_EMBED_H__ #ifndef __SHELL_GTK_EMBED_H__
#define __SHELL_GTK_EMBED_H__ #define __SHELL_GTK_EMBED_H__
#include <clutter/clutter.h> #include <clutter/x11/clutter-x11.h>
#include "shell-embedded-window.h" #include "shell-embedded-window.h"
@ -19,14 +19,14 @@ typedef struct _ShellGtkEmbedPrivate ShellGtkEmbedPrivate;
struct _ShellGtkEmbed struct _ShellGtkEmbed
{ {
ClutterClone parent; ClutterX11TexturePixmap parent;
ShellGtkEmbedPrivate *priv; ShellGtkEmbedPrivate *priv;
}; };
struct _ShellGtkEmbedClass struct _ShellGtkEmbedClass
{ {
ClutterCloneClass parent_class; ClutterX11TexturePixmapClass parent_class;
}; };
GType shell_gtk_embed_get_type (void) G_GNUC_CONST; GType shell_gtk_embed_get_type (void) G_GNUC_CONST;

@ -21,12 +21,9 @@
#include "config.h" #include "config.h"
#include <string.h> #include <string.h>
#include <gnome-keyring.h>
#include <dbus/dbus-glib.h> #include <dbus/dbus-glib.h>
/* For use of unstable features in libsecret, until they stabilize */
#define SECRET_API_SUBJECT_TO_CHANGE
#include <libsecret/secret.h>
#include "shell-network-agent.h" #include "shell-network-agent.h"
enum { enum {
@ -38,7 +35,7 @@ enum {
static gint signals[SIGNAL_LAST]; static gint signals[SIGNAL_LAST];
typedef struct { typedef struct {
GCancellable * cancellable; gpointer keyring_op;
ShellNetworkAgent *self; ShellNetworkAgent *self;
gchar *request_id; gchar *request_id;
@ -62,24 +59,14 @@ struct _ShellNetworkAgentPrivate {
G_DEFINE_TYPE (ShellNetworkAgent, shell_network_agent, NM_TYPE_SECRET_AGENT) G_DEFINE_TYPE (ShellNetworkAgent, shell_network_agent, NM_TYPE_SECRET_AGENT)
static const SecretSchema network_agent_schema = {
"org.freedesktop.NetworkManager.Connection",
SECRET_SCHEMA_DONT_MATCH_NAME,
{
{ SHELL_KEYRING_UUID_TAG, SECRET_SCHEMA_ATTRIBUTE_STRING },
{ SHELL_KEYRING_SN_TAG, SECRET_SCHEMA_ATTRIBUTE_STRING },
{ SHELL_KEYRING_SK_TAG, SECRET_SCHEMA_ATTRIBUTE_STRING },
{ NULL, 0 },
}
};
static void static void
shell_agent_request_free (gpointer data) shell_agent_request_free (gpointer data)
{ {
ShellAgentRequest *request = data; ShellAgentRequest *request = data;
g_cancellable_cancel (request->cancellable); if (request->keyring_op)
g_object_unref (request->cancellable); gnome_keyring_cancel_request (request->keyring_op);
g_object_unref (request->self); g_object_unref (request->self);
g_object_unref (request->connection); g_object_unref (request->connection);
g_free (request->setting_name); g_free (request->setting_name);
@ -258,92 +245,87 @@ strv_has (gchar **haystack,
} }
static void static void
get_secrets_keyring_cb (GObject *source, get_secrets_keyring_cb (GnomeKeyringResult result,
GAsyncResult *result, GList *list,
gpointer user_data) gpointer user_data)
{ {
ShellAgentRequest *closure; ShellAgentRequest *closure;
ShellNetworkAgent *self; ShellNetworkAgent *self;
ShellNetworkAgentPrivate *priv; ShellNetworkAgentPrivate *priv;
GError *secret_error = NULL;
GError *error = NULL; GError *error = NULL;
gint n_found = 0; gint n_found = 0;
GList *items; GList *iter;
GList *l;
GHashTable *outer; GHashTable *outer;
items = secret_service_search_finish (NULL, result, &secret_error); if (result == GNOME_KEYRING_RESULT_CANCELLED)
return;
if (g_error_matches (secret_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
{
g_error_free (secret_error);
return;
}
closure = user_data; closure = user_data;
self = closure->self; self = closure->self;
priv = self->priv; priv = self->priv;
if (secret_error != NULL) closure->keyring_op = NULL;
if (result == GNOME_KEYRING_RESULT_DENIED)
{ {
g_set_error (&error, g_set_error (&error,
NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR,
NM_SECRET_AGENT_ERROR_INTERNAL_ERROR, NM_SECRET_AGENT_ERROR_USER_CANCELED,
"Internal error while retrieving secrets from the keyring (%s)", secret_error->message); "Access to the secret storage was denied by the user");
g_error_free (secret_error);
closure->callback (NM_SECRET_AGENT (closure->self), closure->connection, NULL, error, closure->callback_data); closure->callback (NM_SECRET_AGENT (closure->self), closure->connection, NULL, error, closure->callback_data);
goto out; goto out;
} }
for (l = items; l; l = g_list_next (l)) if (result != GNOME_KEYRING_RESULT_OK &&
result != GNOME_KEYRING_RESULT_NO_MATCH)
{ {
SecretItem *item = l->data; g_set_error (&error,
GHashTable *attributes; NM_SECRET_AGENT_ERROR,
GHashTableIter iter; NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
const gchar *name, *attribute; "Internal error while retrieving secrets from the keyring (result %d)", result);
SecretValue *secret = secret_item_get_secret (item);
/* This can happen if the user denied a request to unlock */ closure->callback (NM_SECRET_AGENT (closure->self), closure->connection, NULL, error, closure->callback_data);
if (secret == NULL)
continue;
attributes = secret_item_get_attributes (item); goto out;
g_hash_table_iter_init (&iter, attributes); }
while (g_hash_table_iter_next (&iter, (gpointer *)&name, (gpointer *)&attribute))
for (iter = list; iter; iter = g_list_next (iter))
{
GnomeKeyringFound *item = iter->data;
int i;
for (i = 0; i < item->attributes->len; i++)
{ {
if (g_strcmp0 (name, SHELL_KEYRING_SK_TAG) == 0) GnomeKeyringAttribute *attr = &gnome_keyring_attribute_list_index (item->attributes, i);
if (g_strcmp0 (attr->name, SHELL_KEYRING_SK_TAG) == 0
&& (attr->type == GNOME_KEYRING_ATTRIBUTE_TYPE_STRING))
{ {
gchar *secret_name = g_strdup (attribute); gchar *secret_name = g_strdup (attr->value.string);
if (!closure->is_vpn) if (!closure->is_vpn)
{ {
GValue *secret_value = g_slice_new0 (GValue); GValue *secret_value = g_slice_new0 (GValue);
g_value_init (secret_value, G_TYPE_STRING); g_value_init (secret_value, G_TYPE_STRING);
g_value_set_string (secret_value, secret_value_get (secret, NULL)); g_value_set_string (secret_value, item->secret);
g_hash_table_insert (closure->entries, secret_name, secret_value); g_hash_table_insert (closure->entries, secret_name, secret_value);
} }
else else
g_hash_table_insert (closure->vpn_entries, secret_name, g_strdup (secret_value_get (secret, NULL))); g_hash_table_insert (closure->vpn_entries, secret_name, g_strdup (item->secret));
if (closure->hints) if (closure->hints)
n_found += strv_has (closure->hints, secret_name); n_found += strv_has (closure->hints, secret_name);
else else
n_found += 1; n_found += 1;
g_hash_table_unref (attributes);
secret_value_unref (secret);
break; break;
} }
} }
g_hash_table_unref (attributes);
secret_value_unref (secret);
} }
g_list_free_full (items, g_object_unref);
if (n_found == 0 && if (n_found == 0 &&
(closure->flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION)) (closure->flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION))
{ {
@ -379,7 +361,6 @@ shell_network_agent_get_secrets (NMSecretAgent *agent,
ShellAgentRequest *request; ShellAgentRequest *request;
NMSettingConnection *setting_connection; NMSettingConnection *setting_connection;
const char *connection_type; const char *connection_type;
GHashTable *attributes;
char *request_id; char *request_id;
request_id = g_strdup_printf ("%s/%s", connection_path, setting_name); request_id = g_strdup_printf ("%s/%s", connection_path, setting_name);
@ -397,7 +378,6 @@ shell_network_agent_get_secrets (NMSecretAgent *agent,
request = g_slice_new (ShellAgentRequest); request = g_slice_new (ShellAgentRequest);
request->self = g_object_ref (self); request->self = g_object_ref (self);
request->cancellable = g_cancellable_new ();
request->connection = g_object_ref (connection); request->connection = g_object_ref (connection);
request->setting_name = g_strdup (setting_name); request->setting_name = g_strdup (setting_name);
request->hints = g_strdupv ((gchar **)hints); request->hints = g_strdupv ((gchar **)hints);
@ -405,6 +385,7 @@ shell_network_agent_get_secrets (NMSecretAgent *agent,
request->callback = callback; request->callback = callback;
request->callback_data = callback_data; request->callback_data = callback_data;
request->is_vpn = !strcmp(connection_type, NM_SETTING_VPN_SETTING_NAME); request->is_vpn = !strcmp(connection_type, NM_SETTING_VPN_SETTING_NAME);
request->keyring_op = NULL;
request->entries = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, gvalue_destroy_notify); request->entries = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, gvalue_destroy_notify);
if (request->is_vpn) if (request->is_vpn)
@ -432,16 +413,17 @@ shell_network_agent_get_secrets (NMSecretAgent *agent,
return; return;
} }
attributes = secret_attributes_build (&network_agent_schema, request->keyring_op = gnome_keyring_find_itemsv (GNOME_KEYRING_ITEM_GENERIC_SECRET,
SHELL_KEYRING_UUID_TAG, nm_connection_get_uuid (connection), get_secrets_keyring_cb,
SHELL_KEYRING_SN_TAG, setting_name, request,
NULL); NULL, /* GDestroyNotify */
SHELL_KEYRING_UUID_TAG,
secret_service_search (NULL, &network_agent_schema, attributes, GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
SECRET_SEARCH_ALL | SECRET_SEARCH_UNLOCK | SECRET_SEARCH_LOAD_SECRETS, nm_connection_get_uuid (connection),
request->cancellable, get_secrets_keyring_cb, request); SHELL_KEYRING_SN_TAG,
GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
g_hash_table_unref (attributes); setting_name,
NULL);
} }
void void
@ -559,7 +541,7 @@ shell_network_agent_cancel_get_secrets (NMSecretAgent *agent,
/************************* saving of secrets ****************************************/ /************************* saving of secrets ****************************************/
static GHashTable * static GnomeKeyringAttributeList *
create_keyring_add_attr_list (NMConnection *connection, create_keyring_add_attr_list (NMConnection *connection,
const gchar *connection_uuid, const gchar *connection_uuid,
const gchar *connection_id, const gchar *connection_id,
@ -567,6 +549,7 @@ create_keyring_add_attr_list (NMConnection *connection,
const gchar *setting_key, const gchar *setting_key,
gchar **out_display_name) gchar **out_display_name)
{ {
GnomeKeyringAttributeList *attrs = NULL;
NMSettingConnection *s_con; NMSettingConnection *s_con;
if (connection) if (connection)
@ -590,11 +573,17 @@ create_keyring_add_attr_list (NMConnection *connection,
setting_key); setting_key);
} }
return secret_attributes_build (&network_agent_schema, attrs = gnome_keyring_attribute_list_new ();
SHELL_KEYRING_UUID_TAG, connection_uuid, gnome_keyring_attribute_list_append_string (attrs,
SHELL_KEYRING_SN_TAG, setting_name, SHELL_KEYRING_UUID_TAG,
SHELL_KEYRING_SK_TAG, setting_key, connection_uuid);
NULL); gnome_keyring_attribute_list_append_string (attrs,
SHELL_KEYRING_SN_TAG,
setting_name);
gnome_keyring_attribute_list_append_string (attrs,
SHELL_KEYRING_SK_TAG,
setting_key);
return attrs;
} }
typedef struct typedef struct
@ -618,8 +607,8 @@ keyring_request_free (KeyringRequest *r)
} }
static void static void
save_secret_cb (GObject *source, save_secret_cb (GnomeKeyringResult result,
GAsyncResult *result, guint val,
gpointer user_data) gpointer user_data)
{ {
KeyringRequest *call = user_data; KeyringRequest *call = user_data;
@ -642,7 +631,7 @@ save_one_secret (KeyringRequest *r,
const gchar *secret, const gchar *secret,
const gchar *display_name) const gchar *display_name)
{ {
GHashTable *attrs; GnomeKeyringAttributeList *attrs;
gchar *alt_display_name = NULL; gchar *alt_display_name = NULL;
const gchar *setting_name; const gchar *setting_name;
NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE; NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
@ -661,11 +650,17 @@ save_one_secret (KeyringRequest *r,
display_name ? NULL : &alt_display_name); display_name ? NULL : &alt_display_name);
g_assert (attrs); g_assert (attrs);
r->n_secrets++; r->n_secrets++;
secret_password_storev (&network_agent_schema, attrs, SECRET_COLLECTION_DEFAULT, gnome_keyring_item_create (NULL,
display_name ? display_name : alt_display_name, GNOME_KEYRING_ITEM_GENERIC_SECRET,
secret, NULL, save_secret_cb, r); display_name ? display_name : alt_display_name,
attrs,
secret,
TRUE,
save_secret_cb,
r,
NULL);
g_hash_table_unref (attrs); gnome_keyring_attribute_list_free (attrs);
g_free (alt_display_name); g_free (alt_display_name);
} }
@ -771,28 +766,38 @@ shell_network_agent_save_secrets (NMSecretAgent *agent,
} }
static void static void
delete_items_cb (GObject *source, keyring_delete_cb (GnomeKeyringResult result, gpointer user_data)
GAsyncResult *result, {
gpointer user_data) /* Ignored */
}
static void
delete_find_items_cb (GnomeKeyringResult result, GList *list, gpointer user_data)
{ {
KeyringRequest *r = user_data; KeyringRequest *r = user_data;
GError *secret_error = NULL; GList *iter;
GError *error = NULL; GError *error = NULL;
NMSecretAgentDeleteSecretsFunc callback = r->callback; NMSecretAgentDeleteSecretsFunc callback = r->callback;
secret_password_clear_finish (result, &secret_error); if ((result == GNOME_KEYRING_RESULT_OK) || (result == GNOME_KEYRING_RESULT_NO_MATCH))
if (secret_error != NULL) {
for (iter = list; iter != NULL; iter = g_list_next (iter))
{
GnomeKeyringFound *found = (GnomeKeyringFound *) iter->data;
gnome_keyring_item_delete (found->keyring, found->item_id, keyring_delete_cb, NULL, NULL);
}
}
else
{ {
error = g_error_new (NM_SECRET_AGENT_ERROR, error = g_error_new (NM_SECRET_AGENT_ERROR,
NM_SECRET_AGENT_ERROR_INTERNAL_ERROR, NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
"The request could not be completed. Keyring result: %s", "The request could not be completed. Keyring result: %d",
secret_error->message); result);
g_error_free (secret_error);
} }
callback (r->self, r->connection, error, r->callback_data); callback (r->self, r->connection, error, r->callback_data);
g_clear_error (&error); g_clear_error (&error);
keyring_request_free (r);
} }
static void static void
@ -818,9 +823,14 @@ shell_network_agent_delete_secrets (NMSecretAgent *agent,
uuid = nm_setting_connection_get_uuid (s_con); uuid = nm_setting_connection_get_uuid (s_con);
g_assert (uuid); g_assert (uuid);
secret_password_clear (&network_agent_schema, NULL, delete_items_cb, r, gnome_keyring_find_itemsv (GNOME_KEYRING_ITEM_GENERIC_SECRET,
SHELL_KEYRING_UUID_TAG, uuid, delete_find_items_cb,
NULL); r,
(GDestroyNotify)keyring_request_free,
SHELL_KEYRING_UUID_TAG,
GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
uuid,
NULL);
} }
void void

@ -19,11 +19,6 @@
#include <clutter/x11/clutter-x11.h> #include <clutter/x11/clutter-x11.h>
#include <X11/extensions/Xfixes.h> #include <X11/extensions/Xfixes.h>
#include <X11/extensions/XInput.h>
#include <X11/extensions/XInput2.h>
/* This is also hard-coded in mutter and GDK */
#define VIRTUAL_CORE_POINTER_ID 2
typedef enum { typedef enum {
RECORDER_STATE_CLOSED, RECORDER_STATE_CLOSED,
@ -62,8 +57,6 @@ struct _ShellRecorder {
gboolean have_xfixes; gboolean have_xfixes;
int xfixes_event_base; int xfixes_event_base;
int xinput_opcode;
CoglHandle recording_icon; /* icon shown while playing */ CoglHandle recording_icon; /* icon shown while playing */
cairo_surface_t *cursor_image; cairo_surface_t *cursor_image;
@ -668,15 +661,10 @@ recorder_event_filter (XEvent *xev,
gpointer data) gpointer data)
{ {
ShellRecorder *recorder = data; ShellRecorder *recorder = data;
XIEvent *input_event = NULL;
if (xev->xany.window != clutter_x11_get_stage_window (recorder->stage)) if (xev->xany.window != clutter_x11_get_stage_window (recorder->stage))
return CLUTTER_X11_FILTER_CONTINUE; return CLUTTER_X11_FILTER_CONTINUE;
if (xev->xany.type == GenericEvent &&
xev->xcookie.extension == recorder->xinput_opcode)
input_event = (XIEvent *) xev->xcookie.data;
if (xev->xany.type == recorder->xfixes_event_base + XFixesCursorNotify) if (xev->xany.type == recorder->xfixes_event_base + XFixesCursorNotify)
{ {
XFixesCursorNotifyEvent *notify_event = (XFixesCursorNotifyEvent *)xev; XFixesCursorNotifyEvent *notify_event = (XFixesCursorNotifyEvent *)xev;
@ -692,53 +680,36 @@ recorder_event_filter (XEvent *xev,
recorder_queue_redraw (recorder); recorder_queue_redraw (recorder);
} }
} }
else if (input_event != NULL && else if (xev->xany.type == MotionNotify)
input_event->evtype == XI_Motion)
{ {
XIDeviceEvent *device_event = (XIDeviceEvent *) input_event; recorder->pointer_x = xev->xmotion.x;
if (device_event->deviceid == VIRTUAL_CORE_POINTER_ID) recorder->pointer_y = xev->xmotion.y;
{
recorder->pointer_x = device_event->event_x;
recorder->pointer_y = device_event->event_y;
recorder_queue_redraw (recorder); recorder_queue_redraw (recorder);
}
} }
/* We want to track whether the pointer is over the stage /* We want to track whether the pointer is over the stage
* window itself, and not in a child window. A "virtual" * window itself, and not in a child window. A "virtual"
* crossing is one that goes directly from ancestor to child. * crossing is one that goes directly from ancestor to child.
*/ */
else if (input_event != NULL && else if (xev->xany.type == EnterNotify &&
input_event->evtype == XI_Enter) (xev->xcrossing.detail != NotifyVirtual &&
xev->xcrossing.detail != NotifyNonlinearVirtual))
{ {
XIEnterEvent *enter_event = (XIEnterEvent *) input_event; recorder->have_pointer = TRUE;
recorder->pointer_x = xev->xcrossing.x;
recorder->pointer_y = xev->xcrossing.y;
if (enter_event->deviceid == VIRTUAL_CORE_POINTER_ID && recorder_queue_redraw (recorder);
(enter_event->detail != XINotifyVirtual &&
enter_event->detail != XINotifyNonlinearVirtual))
{
recorder->have_pointer = TRUE;
recorder->pointer_x = enter_event->event_x;
recorder->pointer_y = enter_event->event_y;
recorder_queue_redraw (recorder);
}
} }
else if (input_event != NULL && else if (xev->xany.type == LeaveNotify &&
input_event->evtype == XI_Leave) (xev->xcrossing.detail != NotifyVirtual &&
xev->xcrossing.detail != NotifyNonlinearVirtual))
{ {
XILeaveEvent *leave_event = (XILeaveEvent *) input_event; recorder->have_pointer = FALSE;
recorder->pointer_x = xev->xcrossing.x;
recorder->pointer_y = xev->xcrossing.y;
if (leave_event->deviceid == VIRTUAL_CORE_POINTER_ID && recorder_queue_redraw (recorder);
(leave_event->detail != XINotifyVirtual &&
leave_event->detail != XINotifyNonlinearVirtual))
{
recorder->have_pointer = FALSE;
recorder->pointer_x = leave_event->event_x;
recorder->pointer_y = leave_event->event_y;
recorder_queue_redraw (recorder);
}
} }
return CLUTTER_X11_FILTER_CONTINUE; return CLUTTER_X11_FILTER_CONTINUE;
@ -793,6 +764,14 @@ recorder_get_initial_cursor_position (ShellRecorder *recorder)
XUngrabServer(xdisplay); XUngrabServer(xdisplay);
XFlush(xdisplay); XFlush(xdisplay);
/* While we are at it, add mouse events to the event mask; they will
* be there for the stage windows that Clutter creates by default, but
* maybe this stage was created differently. Since we've already
* retrieved the event mask, it's almost free.
*/
XSelectInput(xdisplay, xwindow,
xwa.your_event_mask | EnterWindowMask | LeaveWindowMask | PointerMotionMask);
} }
/* When the cursor is not over the stage's input area, we query for the /* When the cursor is not over the stage's input area, we query for the
@ -851,48 +830,6 @@ recorder_remove_update_pointer_timeout (ShellRecorder *recorder)
} }
} }
static void
recorder_connect_stage_callbacks (ShellRecorder *recorder)
{
g_signal_connect (recorder->stage, "destroy",
G_CALLBACK (recorder_on_stage_destroy), recorder);
g_signal_connect_after (recorder->stage, "paint",
G_CALLBACK (recorder_on_stage_paint), recorder);
g_signal_connect (recorder->stage, "notify::width",
G_CALLBACK (recorder_on_stage_notify_size), recorder);
g_signal_connect (recorder->stage, "notify::width",
G_CALLBACK (recorder_on_stage_notify_size), recorder);
clutter_x11_add_filter (recorder_event_filter, recorder);
}
static void
recorder_disconnect_stage_callbacks (ShellRecorder *recorder)
{
g_signal_handlers_disconnect_by_func (recorder->stage,
(void *)recorder_on_stage_destroy,
recorder);
g_signal_handlers_disconnect_by_func (recorder->stage,
(void *)recorder_on_stage_paint,
recorder);
g_signal_handlers_disconnect_by_func (recorder->stage,
(void *)recorder_on_stage_notify_size,
recorder);
clutter_x11_remove_filter (recorder_event_filter, recorder);
/* We don't don't deselect for cursor changes in case someone else just
* happened to be selecting for cursor events on the same window; sending
* us the events is close to free in any case.
*/
if (recorder->redraw_idle)
{
g_source_remove (recorder->redraw_idle);
recorder->redraw_idle = 0;
}
}
static void static void
recorder_set_stage (ShellRecorder *recorder, recorder_set_stage (ShellRecorder *recorder,
ClutterStage *stage) ClutterStage *stage)
@ -904,16 +841,48 @@ recorder_set_stage (ShellRecorder *recorder,
shell_recorder_close (recorder); shell_recorder_close (recorder);
if (recorder->stage) if (recorder->stage)
recorder_disconnect_stage_callbacks (recorder); {
g_signal_handlers_disconnect_by_func (recorder->stage,
(void *)recorder_on_stage_destroy,
recorder);
g_signal_handlers_disconnect_by_func (recorder->stage,
(void *)recorder_on_stage_paint,
recorder);
g_signal_handlers_disconnect_by_func (recorder->stage,
(void *)recorder_on_stage_notify_size,
recorder);
clutter_x11_remove_filter (recorder_event_filter, recorder);
/* We don't don't deselect for cursor changes in case someone else just
* happened to be selecting for cursor events on the same window; sending
* us the events is close to free in any case.
*/
if (recorder->redraw_idle)
{
g_source_remove (recorder->redraw_idle);
recorder->redraw_idle = 0;
}
}
recorder->stage = stage; recorder->stage = stage;
if (recorder->stage) if (recorder->stage)
{ {
int error_base, event_base; int error_base;
int major = 2, minor = 3;
recorder->stage = stage; recorder->stage = stage;
g_signal_connect (recorder->stage, "destroy",
G_CALLBACK (recorder_on_stage_destroy), recorder);
g_signal_connect_after (recorder->stage, "paint",
G_CALLBACK (recorder_on_stage_paint), recorder);
g_signal_connect (recorder->stage, "notify::width",
G_CALLBACK (recorder_on_stage_notify_size), recorder);
g_signal_connect (recorder->stage, "notify::width",
G_CALLBACK (recorder_on_stage_notify_size), recorder);
clutter_x11_add_filter (recorder_event_filter, recorder);
recorder_update_size (recorder); recorder_update_size (recorder);
@ -925,28 +894,6 @@ recorder_set_stage (ShellRecorder *recorder,
clutter_x11_get_stage_window (stage), clutter_x11_get_stage_window (stage),
XFixesDisplayCursorNotifyMask); XFixesDisplayCursorNotifyMask);
if (XQueryExtension (clutter_x11_get_default_display (),
"XInputExtension",
&recorder->xinput_opcode,
&error_base,
&event_base))
{
if (XIQueryVersion (clutter_x11_get_default_display (), &major, &minor) == Success)
{
int version = (major * 10) + minor;
if (version < 22)
g_warning("ShellRecorder: xinput version %d.%d is too old", major, minor);
}
else
{
g_warning("ShellRecorder: xinput version could not be queried");
}
}
else
{
g_warning("ShellRecorder: xinput extension unavailable");
}
clutter_stage_ensure_current (stage); clutter_stage_ensure_current (stage);
recorder_get_initial_cursor_position (recorder); recorder_get_initial_cursor_position (recorder);
@ -1470,8 +1417,6 @@ recorder_pipeline_closed (RecorderPipeline *pipeline)
(gpointer) recorder_pipeline_on_memory_used_changed, (gpointer) recorder_pipeline_on_memory_used_changed,
pipeline); pipeline);
recorder_disconnect_stage_callbacks (pipeline->recorder);
gst_element_set_state (pipeline->pipeline, GST_STATE_NULL); gst_element_set_state (pipeline->pipeline, GST_STATE_NULL);
if (pipeline->recorder) if (pipeline->recorder)
@ -1733,8 +1678,6 @@ shell_recorder_record (ShellRecorder *recorder)
if (!recorder_open_pipeline (recorder)) if (!recorder_open_pipeline (recorder))
return FALSE; return FALSE;
recorder_connect_stage_callbacks (recorder);
recorder->start_time = get_wall_time(); recorder->start_time = get_wall_time();
recorder->last_frame_time = 0; recorder->last_frame_time = 0;

@ -7,7 +7,6 @@
#include "shell-window-tracker.h" #include "shell-window-tracker.h"
#include "tray/na-tray-child.h" #include "tray/na-tray-child.h"
#include <gdk/gdkx.h> #include <gdk/gdkx.h>
#include <X11/Xatom.h>
#include "st.h" #include "st.h"
enum { enum {

@ -17,6 +17,7 @@
struct _ShellTrayManagerPrivate { struct _ShellTrayManagerPrivate {
NaTrayManager *na_manager; NaTrayManager *na_manager;
ClutterStage *stage;
ClutterColor bg_color; ClutterColor bg_color;
GHashTable *icons; GHashTable *icons;
@ -134,6 +135,7 @@ shell_tray_manager_finalize (GObject *object)
ShellTrayManager *manager = SHELL_TRAY_MANAGER (object); ShellTrayManager *manager = SHELL_TRAY_MANAGER (object);
g_object_unref (manager->priv->na_manager); g_object_unref (manager->priv->na_manager);
g_object_unref (manager->priv->stage);
g_hash_table_destroy (manager->priv->icons); g_hash_table_destroy (manager->priv->icons);
G_OBJECT_CLASS (shell_tray_manager_parent_class)->finalize (object); G_OBJECT_CLASS (shell_tray_manager_parent_class)->finalize (object);
@ -217,19 +219,42 @@ shell_tray_manager_style_changed (StWidget *theme_widget,
} }
void void
shell_tray_manager_manage_screen (ShellTrayManager *manager, shell_tray_manager_manage_stage (ShellTrayManager *manager,
MetaScreen *screen, ClutterStage *stage,
StWidget *theme_widget) StWidget *theme_widget)
{ {
Window stage_xwindow;
GdkWindow *stage_window;
GdkDisplay *display; GdkDisplay *display;
GdkScreen *gdk_screen; GdkScreen *screen;
int screen_number;
display = gdk_display_get_default (); g_return_if_fail (manager->priv->stage == NULL);
screen_number = meta_screen_get_screen_number (screen);
gdk_screen = gdk_display_get_screen (display, screen_number);
na_tray_manager_manage_screen (manager->priv->na_manager, gdk_screen); manager->priv->stage = g_object_ref (stage);
stage_xwindow = clutter_x11_get_stage_window (stage);
/* This is a pretty ugly way to get the GdkScreen for the stage; it
* will normally go through the foreign_new() case with a
* round-trip to the X server, it might be nicer to pass the screen
* in in some way. (The Clutter/Mutter combo is currently incapable
* of multi-screen operation, so alternatively we could just assume
* that clutter_x11_get_default_screen() gives us the right
* screen.) We assume, in any case, that we are using the default
* GDK display.
*/
display = gdk_display_get_default();
stage_window = gdk_x11_window_lookup_for_display (display, stage_xwindow);
if (stage_window)
g_object_ref (stage_window);
else
stage_window = gdk_x11_window_foreign_new_for_display (display, stage_xwindow);
screen = gdk_window_get_screen (stage_window);
g_object_unref (stage_window);
na_tray_manager_manage_screen (manager->priv->na_manager, screen);
g_signal_connect (theme_widget, "style-changed", g_signal_connect (theme_widget, "style-changed",
G_CALLBACK (shell_tray_manager_style_changed), manager); G_CALLBACK (shell_tray_manager_style_changed), manager);
@ -295,7 +320,7 @@ na_tray_icon_added (NaTrayManager *na_manager, GtkWidget *socket,
*/ */
na_tray_child_set_composited (NA_TRAY_CHILD (socket), FALSE); na_tray_child_set_composited (NA_TRAY_CHILD (socket), FALSE);
win = shell_embedded_window_new (); win = shell_embedded_window_new (manager->priv->stage);
gtk_container_add (GTK_CONTAINER (win), socket); gtk_container_add (GTK_CONTAINER (win), socket);
/* The visual of the socket matches that of its contents; make /* The visual of the socket matches that of its contents; make

@ -41,9 +41,9 @@ struct _ShellTrayManagerClass
GType shell_tray_manager_get_type (void); GType shell_tray_manager_get_type (void);
ShellTrayManager *shell_tray_manager_new (void); ShellTrayManager *shell_tray_manager_new (void);
void shell_tray_manager_manage_screen (ShellTrayManager *manager, void shell_tray_manager_manage_stage (ShellTrayManager *manager,
MetaScreen *screen, ClutterStage *stage,
StWidget *theme_widget); StWidget *theme_widget);
G_END_DECLS G_END_DECLS

@ -534,32 +534,23 @@ void
st_icon_set_icon_name (StIcon *icon, st_icon_set_icon_name (StIcon *icon,
const gchar *icon_name) const gchar *icon_name)
{ {
StIconPrivate *priv = icon->priv; StIconPrivate *priv;
GIcon *gicon = NULL;
g_return_if_fail (ST_IS_ICON (icon)); g_return_if_fail (ST_IS_ICON (icon));
if (icon_name) priv = icon->priv;
gicon = g_themed_icon_new_with_default_fallbacks (icon_name);
if (g_icon_equal (priv->gicon, gicon)) /* do nothing */
{
g_object_unref (gicon);
return;
}
if (priv->gicon) if (priv->gicon)
g_object_unref (priv->gicon); g_object_unref (priv->gicon);
g_object_freeze_notify (G_OBJECT (icon)); if (icon_name)
priv->gicon = g_themed_icon_new_with_default_fallbacks (icon_name);
priv->gicon = gicon; else
priv->gicon = NULL;
g_object_notify (G_OBJECT (icon), "gicon"); g_object_notify (G_OBJECT (icon), "gicon");
g_object_notify (G_OBJECT (icon), "icon-name"); g_object_notify (G_OBJECT (icon), "icon-name");
g_object_thaw_notify (G_OBJECT (icon));
st_icon_update (icon); st_icon_update (icon);
} }
@ -588,7 +579,7 @@ st_icon_set_gicon (StIcon *icon, GIcon *gicon)
g_return_if_fail (ST_IS_ICON (icon)); g_return_if_fail (ST_IS_ICON (icon));
g_return_if_fail (gicon == NULL || G_IS_ICON (gicon)); g_return_if_fail (gicon == NULL || G_IS_ICON (gicon));
if (g_icon_equal (icon->priv->gicon, gicon)) /* do nothing */ if (icon->priv->gicon == gicon) /* do nothing */
return; return;
if (icon->priv->gicon) if (icon->priv->gicon)

@ -36,6 +36,13 @@ uniform float hvalue;
*/ */
uniform mat2 fade_area; uniform mat2 fade_area;
/*
* Scale from [0.0, 1.0] to [1.0, 1.0]. Do this by
* changing it to scaling [1.0, 0.0] to [0.0, 0.0]
* and then transforming the end result.
*/
#define FADE(gradient, factor) (1.0 - (1.0 - gradient) * factor)
void main () void main ()
{ {
cogl_color_out = cogl_color_in * texture2D (tex, vec2 (cogl_tex_coord_in[0].xy)); cogl_color_out = cogl_color_in * texture2D (tex, vec2 (cogl_tex_coord_in[0].xy));
@ -43,38 +50,35 @@ void main ()
float y = height * cogl_tex_coord_in[0].y; float y = height * cogl_tex_coord_in[0].y;
float x = width * cogl_tex_coord_in[0].x; float x = width * cogl_tex_coord_in[0].x;
/* if (x < fade_area[0][0] || x > fade_area[1][0] ||
* We cannot just return here due to a bug in llvmpipe see: y < fade_area[0][1] || y > fade_area[1][1])
* https://bugzilla.freedesktop.org/show_bug.cgi?id=62357 return;
*/
if (x > fade_area[0][0] && x < fade_area[1][0] &&
y > fade_area[0][1] && y < fade_area[1][1]) {
float ratio = 1.0;
float fade_bottom_start = fade_area[1][1] - vfade_offset;
float fade_right_start = fade_area[1][0] - hfade_offset;
bool fade_top = y < vfade_offset && vvalue > 0.0;
bool fade_bottom = y > fade_bottom_start && vvalue < 1.0;
bool fade_left = x < hfade_offset && hvalue > 0.0;
bool fade_right = x > fade_right_start && hvalue < 1.0;
float vfade_scale = height / vfade_offset; float ratio = 1.0;
if (fade_top) { float fade_bottom_start = fade_area[1][1] - vfade_offset;
ratio *= y / vfade_offset; float fade_right_start = fade_area[1][0] - hfade_offset;
} bool fade_top = y < vfade_offset && vvalue > 0.0;
bool fade_bottom = y > fade_bottom_start && vvalue < 1.0;
bool fade_left = x < hfade_offset && hvalue > 0.0;
bool fade_right = x > fade_right_start && hvalue < 1.0;
if (fade_bottom) { float vfade_scale = height / vfade_offset;
ratio *= (fade_area[1][1] - y) / (fade_area[1][1] - fade_bottom_start); if (fade_top) {
} ratio *= FADE((y / vfade_offset), min(sqrt(vvalue) * vfade_scale, 1.0));
float hfade_scale = width / hfade_offset;
if (fade_left) {
ratio *= x / hfade_offset;
}
if (fade_right) {
ratio *= (fade_area[1][0] - x) / (fade_area[1][0] - fade_right_start);
}
cogl_color_out *= ratio;
} }
if (fade_bottom) {
ratio *= FADE((fade_area[1][1] - y)/(fade_area[1][1] - fade_bottom_start), min(sqrt(1.0 - vvalue) * vfade_scale, 1.0));
}
float hfade_scale = width / hfade_offset;
if (fade_left) {
ratio *= FADE(x / hfade_offset, min(sqrt(hvalue) * hfade_scale, 1.0));
}
if (fade_right) {
ratio *= FADE((fade_area[1][0] - x)/(fade_area[1][0] - fade_right_start), min(sqrt(1.0 - hvalue) * hfade_scale, 1.0));
}
cogl_color_out *= ratio;
} }