Compare commits
134 Commits
wip/gcampa
...
3.7.92
Author | SHA1 | Date | |
---|---|---|---|
307004a455 | |||
d097327bd8 | |||
f72f501931 | |||
e9584cfcab | |||
aec0e75d73 | |||
a6b4d49454 | |||
1cc726593e | |||
5001bd8810 | |||
46aa70aa09 | |||
41cf447c45 | |||
b328fd7aed | |||
917a24580b | |||
af36182879 | |||
160c721afc | |||
ee0040ef1c | |||
9e60a55dd7 | |||
57f27572ae | |||
ceae032112 | |||
d3ab367fcd | |||
0ceefb48c8 | |||
a9815ae1e9 | |||
79140fd132 | |||
6c0f48ce25 | |||
da7ab7057d | |||
499ae609dd | |||
e78b3644e4 | |||
ce6b39bd1c | |||
603f528b47 | |||
5fecd07045 | |||
8edd7ad32e | |||
3a27d0b849 | |||
b351536dee | |||
eeea8559b6 | |||
17591117c1 | |||
c4470fd1e6 | |||
45fc7ec01f | |||
dc54472ca5 | |||
4b3bf05aaf | |||
ec014a7ecf | |||
a8c87f3bab | |||
b39e76200a | |||
01deb9ef7d | |||
5c89568cca | |||
6119b44746 | |||
7af4a7bf88 | |||
04e4d69662 | |||
a87e0f028a | |||
31774a7711 | |||
a32f27a2aa | |||
805a409318 | |||
42d45bd14a | |||
5870709fbc | |||
0fdb7430ff | |||
4771f80d6f | |||
91405583fd | |||
f864303aac | |||
e5ebf4a2b2 | |||
725a36e37a | |||
78dacfa865 | |||
16547fb3c2 | |||
9eeabf79f9 | |||
81b71cc143 | |||
8301acd4d6 | |||
f80afb1755 | |||
71c6b60054 | |||
95a1b874d8 | |||
07de96ede9 | |||
209014b083 | |||
127f10e7a8 | |||
67615a0cbc | |||
dde20f0c76 | |||
ddced79475 | |||
e80795b7f0 | |||
080337a4e8 | |||
884af3c986 | |||
edc95b2cb7 | |||
96239e95ec | |||
331ad76675 | |||
29152d3022 | |||
73fa4b1cbd | |||
7766a91e8c | |||
b7bad765aa | |||
5af09a60e9 | |||
ef623cfbbd | |||
ce5faba185 | |||
40d9ed535b | |||
d5675765f3 | |||
65067c24cc | |||
e2463cb501 | |||
de2f2d7ef1 | |||
7cdb75e7ce | |||
1502e308b8 | |||
dd1fe68e5c | |||
85c1dc5a0c | |||
7b3a689aad | |||
2c28a12cca | |||
324021fd64 | |||
1dceae97c6 | |||
0bef925b51 | |||
0ba1f29e40 | |||
3b1e536822 | |||
9a83662a18 | |||
5bcead5b5c | |||
d8f9502ea2 | |||
020128b9ca | |||
1566a4e607 | |||
fbd59631ee | |||
6c36856499 | |||
f4cda49988 | |||
a6b49fe7d6 | |||
26966b2bf3 | |||
36a7429aa0 | |||
7be1fe09f1 | |||
62bf08d323 | |||
260c082c4e | |||
5e0ff7fd56 | |||
aac312ca34 | |||
a90401454a | |||
5679e6b3a9 | |||
194574bb0e | |||
337d2da38a | |||
df848aa084 | |||
0cc1615252 | |||
d4259fa8aa | |||
157d7e2b59 | |||
ee50904147 | |||
ec39aa3890 | |||
beb0fdf4b8 | |||
443fe813c3 | |||
69cdc5a9b9 | |||
dac54a6019 | |||
e5ca53e56c | |||
7a79236bab | |||
cd37e2908f |
74
NEWS
74
NEWS
@ -1,3 +1,77 @@
|
||||
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
|
||||
======
|
||||
* Let GNOME Shell work on EGL and GLES2 [Neil; #693225, #693438, #693339]
|
||||
|
@ -1,5 +1,5 @@
|
||||
AC_PREREQ(2.63)
|
||||
AC_INIT([gnome-shell],[3.7.90],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
|
||||
AC_INIT([gnome-shell],[3.7.92],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
|
||||
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
AC_CONFIG_SRCDIR([src/shell-global.c])
|
||||
@ -63,7 +63,7 @@ AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
|
||||
CLUTTER_MIN_VERSION=1.13.4
|
||||
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
|
||||
GJS_MIN_VERSION=1.35.4
|
||||
MUTTER_MIN_VERSION=3.7.90
|
||||
MUTTER_MIN_VERSION=3.7.92
|
||||
GTK_MIN_VERSION=3.7.9
|
||||
GIO_MIN_VERSION=2.35.0
|
||||
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
|
||||
libnm-glib libnm-util >= $NETWORKMANAGER_MIN_VERSION
|
||||
libnm-gtk >= $NETWORKMANAGER_MIN_VERSION
|
||||
gnome-keyring-1 gcr-3 >= $GCR_MIN_VERSION)
|
||||
libsecret-unstable gcr-3 >= $GCR_MIN_VERSION)
|
||||
|
||||
PKG_CHECK_MODULES(GNOME_SHELL_JS, gio-2.0 gjs-internals-1.0 >= $GJS_MIN_VERSION)
|
||||
PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 >= 0.6.8 x11)
|
||||
|
@ -1,3 +1,6 @@
|
||||
wandadir = $(pkgdatadir)
|
||||
dist_wanda_DATA = wanda.png
|
||||
|
||||
desktopdir=$(datadir)/applications
|
||||
desktop_DATA = gnome-shell.desktop gnome-shell-extension-prefs.desktop
|
||||
|
||||
|
@ -125,7 +125,8 @@ StScrollBar StButton#vhandle:active {
|
||||
|
||||
/* PopupMenu */
|
||||
|
||||
.popup-menu-boxpointer {
|
||||
.popup-menu-boxpointer,
|
||||
.candidate-popup-boxpointer {
|
||||
-arrow-border-radius: 8px;
|
||||
-arrow-background-color: rgba(0,0,0,0.9);
|
||||
-arrow-border-width: 2px;
|
||||
@ -315,10 +316,20 @@ StScrollBar StButton#vhandle:active {
|
||||
.notification-button:focus,
|
||||
.notification-icon-button:focus,
|
||||
.hotplug-notification-item:focus,
|
||||
.modal-dialog-button:focus {
|
||||
.modal-dialog-button:focus,
|
||||
.app-view-control:focus {
|
||||
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:pressed,
|
||||
.notification-button:active,
|
||||
@ -886,8 +897,8 @@ StScrollBar StButton#vhandle:active {
|
||||
.search-display > StBoxLayout,
|
||||
.all-apps > StBoxLayout,
|
||||
.frequent-apps > StBoxLayout {
|
||||
/* horizontal padding to make sure the scrollbar doesn't overlap content */
|
||||
padding: 0px 18px;
|
||||
/* horizontal padding to make sure scrollbars or dash don't overlap content */
|
||||
padding: 0px 88px;
|
||||
}
|
||||
|
||||
.app-folder-icon {
|
||||
@ -963,6 +974,10 @@ StScrollBar StButton#vhandle:active {
|
||||
color:white;
|
||||
}
|
||||
|
||||
.app-display .app-well-app > .overview-icon {
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.list-search-result:hover .list-search-result-description {
|
||||
text-shadow: rgba(0,0,0,0.8) 0px 1px 2px;
|
||||
}
|
||||
@ -1257,15 +1272,15 @@ StScrollBar StButton#vhandle:active {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.calendar-other-month-day {
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.calendar-day-with-events {
|
||||
font-weight: bold;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.calendar-other-month-day {
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.events-header-vbox {
|
||||
spacing: 6pt;
|
||||
padding-right: .5em;
|
||||
@ -2129,23 +2144,12 @@ StScrollBar StButton#vhandle:active {
|
||||
}
|
||||
|
||||
/* 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 {
|
||||
padding: 0.5em;
|
||||
spacing: 0.3em;
|
||||
}
|
||||
|
||||
.candidate-popup-text {
|
||||
font-size: 9pt;
|
||||
}
|
||||
|
||||
.candidate-index {
|
||||
padding: 0 0.5em 0 0;
|
||||
color: #cccccc;
|
||||
@ -2211,6 +2215,9 @@ StScrollBar StButton#vhandle:active {
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
|
||||
padding-bottom: 80px;
|
||||
padding-top: 80px;
|
||||
|
||||
border-radius: 16px;
|
||||
min-height: 150px;
|
||||
max-height: 700px;
|
||||
|
BIN
data/wanda.png
Normal file
BIN
data/wanda.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
@ -66,11 +66,4 @@ its dependencies to build from tarballs.</description>
|
||||
<gnome:userid>fmuellner</gnome:userid>
|
||||
</foaf:Person>
|
||||
</maintainer>
|
||||
<maintainer>
|
||||
<foaf:Person>
|
||||
<foaf:name>Ray Strode</foaf:name>
|
||||
<foaf:mbox rdf:resource="mailto:halfline@gmail.com" />
|
||||
<gnome:userid>halfline</gnome:userid>
|
||||
</foaf:Person>
|
||||
</maintainer>
|
||||
</Project>
|
||||
|
@ -594,6 +594,7 @@ const LoginDialog = new Lang.Class({
|
||||
this._promptEntry = new St.Entry({ style_class: 'login-dialog-prompt-entry',
|
||||
can_focus: true });
|
||||
this._promptEntryTextChangedId = 0;
|
||||
this._promptEntryActivateId = 0;
|
||||
this._promptBox.add(this._promptEntry,
|
||||
{ expand: true,
|
||||
x_fill: true,
|
||||
@ -690,6 +691,8 @@ const LoginDialog = new Lang.Class({
|
||||
},
|
||||
|
||||
_reset: function() {
|
||||
this._userVerifier.clear();
|
||||
|
||||
this._updateSensitivity(true);
|
||||
this._promptMessage.hide();
|
||||
this._user = null;
|
||||
@ -811,6 +814,11 @@ const LoginDialog = new Lang.Class({
|
||||
Lang.bind(this, function() {
|
||||
this._updateSignInButtonSensitivity(this._promptEntry.text.length > 0);
|
||||
}));
|
||||
|
||||
this._promptEntryActivateId =
|
||||
this._promptEntry.clutter_text.connect('activate', function() {
|
||||
hold.release();
|
||||
});
|
||||
},
|
||||
|
||||
_updateSensitivity: function(sensitive) {
|
||||
@ -835,6 +843,11 @@ const LoginDialog = new Lang.Class({
|
||||
this._promptEntryTextChangedId = 0;
|
||||
}
|
||||
|
||||
if (this._promptEntryActivateId > 0) {
|
||||
this._promptEntry.clutter_text.disconnect(this._promptEntryActivateId);
|
||||
this._promptEntryActivateId = 0;
|
||||
}
|
||||
|
||||
this._setWorking(false);
|
||||
this._promptBox.hide();
|
||||
this._promptLoginHint.hide();
|
||||
@ -917,7 +930,7 @@ const LoginDialog = new Lang.Class({
|
||||
return batch.run();
|
||||
},
|
||||
|
||||
_onSessionOpened: function(client, serviceName) {
|
||||
_startSession: function(serviceName) {
|
||||
Tweener.addTween(this.dialogLayout,
|
||||
{ opacity: 0,
|
||||
time: _FADE_ANIMATION_TIME,
|
||||
@ -940,6 +953,18 @@ const LoginDialog = new Lang.Class({
|
||||
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) {
|
||||
let item = this._userList.getItemFromUserName(userName);
|
||||
|
||||
@ -1003,7 +1028,7 @@ const LoginDialog = new Lang.Class({
|
||||
function() {
|
||||
// If we're just starting out, start on the right
|
||||
// item.
|
||||
if (!this.is_loaded) {
|
||||
if (!this._userManager.is_loaded) {
|
||||
this._userList.jumpToItem(this._timedLoginItem);
|
||||
}
|
||||
},
|
||||
@ -1141,15 +1166,6 @@ const LoginDialog = new Lang.Class({
|
||||
Lang.bind(this, function(userManager, 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() {
|
||||
@ -1164,5 +1180,9 @@ const LoginDialog = new Lang.Class({
|
||||
this.parent();
|
||||
|
||||
Main.ctrlAltTabManager.removeGroup(this.dialogLayout);
|
||||
}
|
||||
},
|
||||
|
||||
addCharacter: function(unichar) {
|
||||
this._promptEntry.clutter_text.insert_unichar(unichar);
|
||||
},
|
||||
});
|
||||
|
116
js/gdm/util.js
116
js/gdm/util.js
@ -2,6 +2,7 @@
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Signals = imports.signals;
|
||||
@ -27,6 +28,9 @@ const ALLOWED_FAILURES_KEY = 'allowed-failures';
|
||||
const LOGO_KEY = 'logo';
|
||||
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) {
|
||||
if (actor.opacity == 255 && actor.visible)
|
||||
return null;
|
||||
@ -114,6 +118,9 @@ const ShellUserVerifier = new Lang.Class({
|
||||
|
||||
this._fprintManager = new Fprint.FprintManager();
|
||||
this._realmManager = new Realmd.Manager();
|
||||
this._messageQueue = [];
|
||||
this._messageQueueTimeoutId = 0;
|
||||
this.hasPendingMessages = false;
|
||||
|
||||
this._failCounter = 0;
|
||||
},
|
||||
@ -153,13 +160,73 @@ const ShellUserVerifier = new Lang.Class({
|
||||
this._userVerifier.run_dispose();
|
||||
this._userVerifier = null;
|
||||
}
|
||||
|
||||
this._clearMessageQueue();
|
||||
},
|
||||
|
||||
answerQuery: function(serviceName, answer) {
|
||||
// Clear any previous message
|
||||
this.emit('show-message', null, null);
|
||||
if (!this._userVerifier.hasPendingMessages) {
|
||||
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);
|
||||
}));
|
||||
}
|
||||
},
|
||||
|
||||
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);
|
||||
},
|
||||
|
||||
_checkForFingerprintReader: function() {
|
||||
@ -179,7 +246,7 @@ const ShellUserVerifier = new Lang.Class({
|
||||
logError(error, where);
|
||||
this._hold.release();
|
||||
|
||||
this.emit('show-message', _("Authentication error"), 'login-dialog-message-warning');
|
||||
this._queueMessage(_("Authentication error"), 'login-dialog-message-warning');
|
||||
this._verificationFailed(false);
|
||||
},
|
||||
|
||||
@ -298,7 +365,7 @@ const ShellUserVerifier = new Lang.Class({
|
||||
// to indicate the user can swipe their finger instead
|
||||
this.emit('show-login-hint', _("(or swipe finger)"));
|
||||
} else if (serviceName == PASSWORD_SERVICE_NAME) {
|
||||
this.emit('show-message', info, 'login-dialog-message-info');
|
||||
this._queueMessage(info, 'login-dialog-message-info');
|
||||
}
|
||||
},
|
||||
|
||||
@ -307,7 +374,7 @@ const ShellUserVerifier = new Lang.Class({
|
||||
// users who haven't enrolled their fingerprint.
|
||||
if (serviceName != PASSWORD_SERVICE_NAME)
|
||||
return;
|
||||
this.emit('show-message', problem, 'login-dialog-message-warning');
|
||||
this._queueMessage(problem, 'login-dialog-message-warning');
|
||||
},
|
||||
|
||||
_showRealmLoginHint: function() {
|
||||
@ -346,8 +413,6 @@ const ShellUserVerifier = new Lang.Class({
|
||||
},
|
||||
|
||||
_onReset: function() {
|
||||
this.clear();
|
||||
|
||||
// Clear previous attempts to authenticate
|
||||
this._failCounter = 0;
|
||||
|
||||
@ -358,6 +423,15 @@ const ShellUserVerifier = new Lang.Class({
|
||||
this.emit('verification-complete');
|
||||
},
|
||||
|
||||
_cancelAndReset: function() {
|
||||
this.cancel();
|
||||
this._onReset();
|
||||
},
|
||||
|
||||
_retry: function() {
|
||||
this.begin(this._userName, new Batch.Hold());
|
||||
},
|
||||
|
||||
_verificationFailed: function(retry) {
|
||||
// For Not Listed / enterprise logins, immediately reset
|
||||
// the dialog
|
||||
@ -369,15 +443,25 @@ const ShellUserVerifier = new Lang.Class({
|
||||
this._failCounter < this._settings.get_int(ALLOWED_FAILURES_KEY);
|
||||
|
||||
if (canRetry) {
|
||||
this.clear();
|
||||
this.begin(this._userName, new Batch.Hold());
|
||||
if (!this._userVerifier.hasPendingMessages) {
|
||||
this._retry();
|
||||
} else {
|
||||
let signalId = this._userVerifier.connect('no-more-messages',
|
||||
Lang.bind(this, function() {
|
||||
this._userVerifier.disconnect(signalId);
|
||||
this._retry();
|
||||
}));
|
||||
}
|
||||
} else {
|
||||
// Allow some time to see the message, then reset everything
|
||||
Mainloop.timeout_add(3000, Lang.bind(this, function() {
|
||||
this.cancel();
|
||||
|
||||
this._onReset();
|
||||
}));
|
||||
if (!this._userVerifier.hasPendingMessages) {
|
||||
this._cancelAndReset();
|
||||
} else {
|
||||
let signalId = this._userVerifier.connect('no-more-messages',
|
||||
Lang.bind(this, function() {
|
||||
this._userVerifier.disconnect(signalId);
|
||||
this._cancelAndReset();
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
this.emit('verification-failed');
|
||||
|
@ -75,6 +75,35 @@ function haveSystemd() {
|
||||
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;
|
||||
|
||||
/**
|
||||
|
@ -5,6 +5,9 @@ const GLib = imports.gi.GLib;
|
||||
const St = imports.gi.St;
|
||||
|
||||
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
|
||||
const _balancedParens = '\\((?:[^\\s()<>]+|(?:\\(?:[^\\s()<>]+\\)))*\\)';
|
||||
@ -234,3 +237,39 @@ function makeCloseButton() {
|
||||
|
||||
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,7 +29,6 @@ const Util = imports.misc.util;
|
||||
|
||||
const MAX_APPLICATION_WORK_MILLIS = 75;
|
||||
const MENU_POPUP_TIMEOUT = 600;
|
||||
const SCROLL_TIME = 0.1;
|
||||
const MAX_COLUMNS = 6;
|
||||
|
||||
const INACTIVE_GRID_OPACITY = 77;
|
||||
@ -157,6 +156,30 @@ 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({
|
||||
Name: 'AllView',
|
||||
Extends: AlphabeticalView,
|
||||
@ -165,7 +188,7 @@ const AllView = new Lang.Class({
|
||||
this.parent();
|
||||
|
||||
let box = new St.BoxLayout({ vertical: true });
|
||||
this._stack = new St.Widget({ layout_manager: new Clutter.BinLayout() });
|
||||
this._stack = new St.Widget({ layout_manager: new AllViewLayout() });
|
||||
this._stack.add_actor(this._grid.actor);
|
||||
this._eventBlocker = new St.Widget({ x_expand: true, y_expand: true });
|
||||
this._stack.add_actor(this._eventBlocker);
|
||||
@ -259,29 +282,7 @@ const AllView = new Lang.Class({
|
||||
},
|
||||
|
||||
_ensureIconVisible: function(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' });
|
||||
Util.ensureActorVisibleInScrollView(this.actor, icon);
|
||||
},
|
||||
|
||||
_updateIconOpacities: function(folderOpen) {
|
||||
|
@ -267,9 +267,9 @@ const Background = new Lang.Class({
|
||||
_destroy: function() {
|
||||
this._cancellable.cancel();
|
||||
|
||||
if (this._animationUpdateTimeoutId) {
|
||||
GLib.source_remove (this._animationUpdateTimeoutId);
|
||||
this._animationUpdateTimeoutId = 0
|
||||
if (this._updateAnimationTimeoutId) {
|
||||
GLib.source_remove (this._updateAnimationTimeoutId);
|
||||
this._updateAnimationTimeoutId = 0;
|
||||
}
|
||||
|
||||
let i;
|
||||
@ -356,7 +356,10 @@ const Background = new Lang.Class({
|
||||
|
||||
let actor = new Meta.BackgroundActor();
|
||||
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._watchCacheFile(filename);
|
||||
@ -372,22 +375,21 @@ const Background = new Lang.Class({
|
||||
},
|
||||
|
||||
_updateAnimationProgress: function() {
|
||||
if (this._images[1]) {
|
||||
this._images[1].raise_top();
|
||||
if (this._images[1])
|
||||
this._images[1].opacity = this._animation.transitionProgress * 255;
|
||||
}
|
||||
|
||||
this._queueAnimationUpdate();
|
||||
this._queueUpdateAnimation();
|
||||
},
|
||||
|
||||
_updateAnimation: function() {
|
||||
this._animationUpdateTimeoutId = 0;
|
||||
this._updateAnimationTimeoutId = 0;
|
||||
|
||||
let files = this._animation.getKeyFrameFiles(this._layoutManager.monitors[this._monitorIndex]);
|
||||
this._animation.update(this._layoutManager.monitors[this._monitorIndex]);
|
||||
let files = this._animation.keyFrameFiles;
|
||||
|
||||
if (!files) {
|
||||
if (files.length == 0) {
|
||||
this._setLoaded();
|
||||
this._queueAnimationUpdate();
|
||||
this._queueUpdateAnimation();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -406,7 +408,7 @@ const Background = new Lang.Class({
|
||||
style: this._style,
|
||||
filename: files[i],
|
||||
cancellable: this._cancellable,
|
||||
onFinished: Lang.bind(this, function(content) {
|
||||
onFinished: Lang.bind(this, function(content, i) {
|
||||
numPendingImages--;
|
||||
|
||||
if (!content) {
|
||||
@ -426,27 +428,30 @@ const Background = new Lang.Class({
|
||||
this._setLoaded();
|
||||
this._updateAnimationProgress();
|
||||
}
|
||||
})
|
||||
}, i)
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_queueAnimationUpdate: function() {
|
||||
if (this._animationUpdateTimeoutId != 0)
|
||||
_queueUpdateAnimation: function() {
|
||||
if (this._updateAnimationTimeoutId != 0)
|
||||
return;
|
||||
|
||||
if (!this._cancellable || this._cancellable.is_cancelled())
|
||||
return;
|
||||
|
||||
if (!this._animation.duration)
|
||||
if (!this._animation.transitionDuration)
|
||||
return;
|
||||
|
||||
let nSteps = 255 / ANIMATION_OPACITY_STEP_INCREMENT;
|
||||
let timePerStep = (this._animation.transitionDuration * 1000) / nSteps;
|
||||
|
||||
let interval = Math.max(ANIMATION_MIN_WAKEUP_INTERVAL * 1000,
|
||||
ANIMATION_OPACITY_STEP_INCREMENT / this._animation.duration);
|
||||
this._animationUpdateTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
|
||||
timePerStep);
|
||||
this._updateAnimationTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
|
||||
interval,
|
||||
Lang.bind(this, function() {
|
||||
this._animationUpdateTimeoutId = 0;
|
||||
this._updateAnimationTimeoutId = 0;
|
||||
this._updateAnimation();
|
||||
return false;
|
||||
}));
|
||||
@ -585,9 +590,9 @@ const Animation = new Lang.Class({
|
||||
params = Params.parse(params, { filename: null });
|
||||
|
||||
this.filename = params.filename;
|
||||
this._keyFrames = [];
|
||||
this.duration = 0.0;
|
||||
this.keyFrameFiles = [];
|
||||
this.transitionProgress = 0.0;
|
||||
this.transitionDuration = 0.0;
|
||||
this.loaded = false;
|
||||
},
|
||||
|
||||
@ -599,33 +604,31 @@ const Animation = new Lang.Class({
|
||||
this._show.load_async(null,
|
||||
Lang.bind(this,
|
||||
function(object, result) {
|
||||
this.duration = this._show.get_total_duration();
|
||||
this.loaded = true;
|
||||
if (callback)
|
||||
callback();
|
||||
}));
|
||||
},
|
||||
|
||||
getKeyFrameFiles: function(monitor) {
|
||||
update: function(monitor) {
|
||||
this.keyFrameFiles = [];
|
||||
|
||||
if (!this._show)
|
||||
return null;
|
||||
return;
|
||||
|
||||
if (this._show.get_num_slides() < 1)
|
||||
return null;
|
||||
return;
|
||||
|
||||
let [progress, duration, isFixed, file1, file2] = this._show.get_current_slide(monitor.width, monitor.height);
|
||||
|
||||
this.transitionDuration = duration;
|
||||
this.transitionProgress = progress;
|
||||
|
||||
let files = [];
|
||||
|
||||
if (file1)
|
||||
files.push(file1);
|
||||
this.keyFrameFiles.push(file1);
|
||||
|
||||
if (file2)
|
||||
files.push(file2);
|
||||
|
||||
return files;
|
||||
this.keyFrameFiles.push(file2);
|
||||
},
|
||||
});
|
||||
Signals.addSignalMethods(Animation.prototype);
|
||||
|
@ -168,6 +168,12 @@ const EmptyEventSource = new Lang.Class({
|
||||
Name: 'EmptyEventSource',
|
||||
|
||||
_init: function() {
|
||||
this.isLoading = false;
|
||||
this.isDummy = true;
|
||||
this.hasCalendars = false;
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
},
|
||||
|
||||
requestRange: function(begin, end) {
|
||||
@ -191,6 +197,7 @@ const CalendarServerIface = <interface name="org.gnome.Shell.CalendarServer">
|
||||
<arg type="b" direction="in" />
|
||||
<arg type="a(sssbxxa{sv})" direction="out" />
|
||||
</method>
|
||||
<property name="HasCalendars" type="b" access="read" />
|
||||
<signal name="Changed" />
|
||||
</interface>;
|
||||
|
||||
@ -201,8 +208,7 @@ function CalendarServer() {
|
||||
g_interface_name: CalendarServerInfo.name,
|
||||
g_interface_info: CalendarServerInfo,
|
||||
g_name: 'org.gnome.Shell.CalendarServer',
|
||||
g_object_path: '/org/gnome/Shell/CalendarServer',
|
||||
g_flags: Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES });
|
||||
g_object_path: '/org/gnome/Shell/CalendarServer' });
|
||||
}
|
||||
|
||||
function _datesEqual(a, b) {
|
||||
@ -229,6 +235,8 @@ const DBusEventSource = new Lang.Class({
|
||||
|
||||
_init: function() {
|
||||
this._resetCache();
|
||||
this.isLoading = false;
|
||||
this.isDummy = false;
|
||||
|
||||
this._initialized = false;
|
||||
this._dbusProxy = new CalendarServer();
|
||||
@ -249,11 +257,27 @@ const DBusEventSource = new Lang.Class({
|
||||
this._onNameVanished();
|
||||
}));
|
||||
|
||||
this._dbusProxy.connect('g-properties-changed', Lang.bind(this, function() {
|
||||
this.emit('notify::has-calendars');
|
||||
}));
|
||||
|
||||
this._initialized = true;
|
||||
this.emit('notify::has-calendars');
|
||||
this._onNameAppeared();
|
||||
}));
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this._dbusProxy.run_dispose();
|
||||
},
|
||||
|
||||
get hasCalendars() {
|
||||
if (this._initialized)
|
||||
return this._dbusProxy.HasCalendars;
|
||||
else
|
||||
return false;
|
||||
},
|
||||
|
||||
_resetCache: function() {
|
||||
this._events = [];
|
||||
this._lastRequestBegin = null;
|
||||
@ -293,6 +317,7 @@ const DBusEventSource = new Lang.Class({
|
||||
}
|
||||
|
||||
this._events = newEvents;
|
||||
this.isLoading = false;
|
||||
this.emit('changed');
|
||||
},
|
||||
|
||||
@ -315,6 +340,7 @@ const DBusEventSource = new Lang.Class({
|
||||
|
||||
requestRange: function(begin, end, forceReload) {
|
||||
if (forceReload || !(_datesEqual(begin, this._lastRequestBegin) && _datesEqual(end, this._lastRequestEnd))) {
|
||||
this.isLoading = true;
|
||||
this._lastRequestBegin = begin;
|
||||
this._lastRequestEnd = end;
|
||||
this._curRequestBegin = begin;
|
||||
@ -389,19 +415,11 @@ const Calendar = new Lang.Class({
|
||||
// @eventSource: is an object implementing the EventSource API, e.g. the
|
||||
// requestRange(), getEvents(), hasEvents() methods and the ::changed signal.
|
||||
setEventSource: function(eventSource) {
|
||||
if (this._eventSource) {
|
||||
this._eventSource.disconnect(this._eventSourceChangedId);
|
||||
this._eventSource = null;
|
||||
}
|
||||
|
||||
this._eventSource = eventSource;
|
||||
|
||||
if (this._eventSource) {
|
||||
this._eventSourceChangedId = this._eventSource.connect('changed', Lang.bind(this, function() {
|
||||
this._update(false);
|
||||
}));
|
||||
this._update(true);
|
||||
}
|
||||
this._eventSource.connect('changed', Lang.bind(this, function() {
|
||||
this._update(false);
|
||||
}));
|
||||
this._update(true);
|
||||
},
|
||||
|
||||
// Sets the calendar to show a specific date
|
||||
@ -538,20 +556,44 @@ const Calendar = new Lang.Class({
|
||||
children[i].destroy();
|
||||
|
||||
// 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);
|
||||
beginDate.setDate(1);
|
||||
beginDate.setSeconds(0);
|
||||
beginDate.setHours(12);
|
||||
let year = beginDate.getYear();
|
||||
|
||||
let daysToWeekStart = (7 + beginDate.getDay() - this._weekStart) % 7;
|
||||
beginDate.setTime(beginDate.getTime() - daysToWeekStart * MSECS_IN_DAY);
|
||||
let startsOnWeekStart = daysToWeekStart == 0;
|
||||
let weekPadding = startsOnWeekStart ? 7 : 0;
|
||||
|
||||
beginDate.setTime(beginDate.getTime() - (weekPadding + daysToWeekStart) * MSECS_IN_DAY);
|
||||
|
||||
let iter = new Date(beginDate);
|
||||
let row = 2;
|
||||
while (true) {
|
||||
// nRows here means 6 weeks + one header + one navbar
|
||||
let nRows = 8;
|
||||
while (row < 8) {
|
||||
let button = new St.Button({ label: iter.getDate().toString() });
|
||||
let rtl = button.get_text_direction() == Clutter.TextDirection.RTL;
|
||||
|
||||
if (!this._eventSource)
|
||||
if (this._eventSource.isDummy)
|
||||
button.reactive = false;
|
||||
|
||||
let iterStr = iter.toUTCString();
|
||||
@ -560,8 +602,9 @@ const Calendar = new Lang.Class({
|
||||
this.setDate(newlySelectedDate, false);
|
||||
}));
|
||||
|
||||
let hasEvents = this._eventSource && this._eventSource.hasEvents(iter);
|
||||
let hasEvents = this._eventSource.hasEvents(iter);
|
||||
let styleClass = 'calendar-day-base calendar-day';
|
||||
|
||||
if (_isWorkDay(iter))
|
||||
styleClass += ' calendar-work-day'
|
||||
else
|
||||
@ -601,17 +644,13 @@ const Calendar = new Lang.Class({
|
||||
}
|
||||
|
||||
iter.setTime(iter.getTime() + MSECS_IN_DAY);
|
||||
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;
|
||||
|
||||
if (iter.getDay() == this._weekStart)
|
||||
row++;
|
||||
}
|
||||
}
|
||||
// Signal to the event source that we are interested in events
|
||||
// only from this date range
|
||||
if (this._eventSource)
|
||||
this._eventSource.requestRange(beginDate, iter, forceReload);
|
||||
this._eventSource.requestRange(beginDate, iter, forceReload);
|
||||
}
|
||||
});
|
||||
|
||||
@ -629,16 +668,8 @@ const EventsList = new Lang.Class({
|
||||
},
|
||||
|
||||
setEventSource: function(eventSource) {
|
||||
if (this._eventSource) {
|
||||
this._eventSource.disconnect(this._eventSourceChangedId);
|
||||
this._eventSource = null;
|
||||
}
|
||||
|
||||
this._eventSource = eventSource;
|
||||
|
||||
if (this._eventSource) {
|
||||
this._eventSourceChangedId = this._eventSource.connect('changed', Lang.bind(this, this._update));
|
||||
}
|
||||
this._eventSource.connect('changed', Lang.bind(this, this._update));
|
||||
},
|
||||
|
||||
_addEvent: function(dayNameBox, timeBox, eventTitleBox, includeDayName, day, time, desc) {
|
||||
@ -655,9 +686,6 @@ const EventsList = new Lang.Class({
|
||||
},
|
||||
|
||||
_addPeriod: function(header, begin, end, includeDayName, showNothingScheduled) {
|
||||
if (!this._eventSource)
|
||||
return;
|
||||
|
||||
let events = this._eventSource.getEvents(begin, end);
|
||||
|
||||
let clockFormat = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);;
|
||||
@ -754,6 +782,9 @@ const EventsList = new Lang.Class({
|
||||
},
|
||||
|
||||
_update: function() {
|
||||
if (this._eventSource.isLoading)
|
||||
return;
|
||||
|
||||
let today = new Date();
|
||||
if (_sameDay (this._date, today)) {
|
||||
this._showToday();
|
||||
|
@ -222,7 +222,7 @@ const KeyringPrompter = new Lang.Class({
|
||||
enable: function() {
|
||||
this._prompter.register(Gio.DBus.session);
|
||||
this._dbusId = Gio.DBus.session.own_name('org.gnome.keyring.SystemPrompter',
|
||||
Gio.BusNameOwnerFlags.REPLACE, null, null);
|
||||
Gio.BusNameOwnerFlags.ALLOW_REPLACEMENT, null, null);
|
||||
},
|
||||
|
||||
disable: function() {
|
||||
|
@ -103,6 +103,11 @@ const CtrlAltTabManager = new Lang.Class({
|
||||
else
|
||||
icon = textureCache.bind_pixbuf_property(windows[i], 'icon');
|
||||
items.push({ name: windows[i].title,
|
||||
proxy: windows[i].get_compositor_private(),
|
||||
focusCallback: Lang.bind(windows[i],
|
||||
function(timestamp) {
|
||||
Main.activateWindow(this, timestamp);
|
||||
}),
|
||||
iconActor: icon,
|
||||
sortGroup: SortGroup.MIDDLE });
|
||||
}
|
||||
|
@ -161,8 +161,10 @@ const DateMenuButton = new Lang.Class({
|
||||
this._openClocksItem.actor.visible = app !== null;
|
||||
},
|
||||
|
||||
_setEventsVisibility: function(visible) {
|
||||
_updateEventsVisibility: function() {
|
||||
let visible = this._eventSource.hasCalendars;
|
||||
this._openCalendarItem.actor.visible = visible;
|
||||
this._openClocksItem.actor.visible = visible;
|
||||
this._separator.visible = visible;
|
||||
if (visible) {
|
||||
let alignment = 0.25;
|
||||
@ -177,8 +179,16 @@ const DateMenuButton = new Lang.Class({
|
||||
},
|
||||
|
||||
_setEventSource: function(eventSource) {
|
||||
if (this._eventSource)
|
||||
this._eventSource.destroy();
|
||||
|
||||
this._calendar.setEventSource(eventSource);
|
||||
this._eventList.setEventSource(eventSource);
|
||||
|
||||
this._eventSource = eventSource;
|
||||
this._eventSource.connect('notify::has-calendars', Lang.bind(this, function() {
|
||||
this._updateEventsVisibility();
|
||||
}));
|
||||
},
|
||||
|
||||
_sessionUpdated: function() {
|
||||
@ -187,10 +197,10 @@ const DateMenuButton = new Lang.Class({
|
||||
if (showEvents) {
|
||||
eventSource = new Calendar.DBusEventSource();
|
||||
} else {
|
||||
eventSource = null;
|
||||
eventSource = new Calendar.EmptyEventSource();
|
||||
}
|
||||
this._setEventSource(eventSource);
|
||||
this._setEventsVisibility(showEvents);
|
||||
this._updateEventsVisibility();
|
||||
|
||||
// This needs to be handled manually, as the code to
|
||||
// autohide separators doesn't work across the vbox
|
||||
|
12
js/ui/dnd.js
12
js/ui/dnd.js
@ -85,11 +85,13 @@ const _Draggable = new Lang.Class({
|
||||
|
||||
this.actor.connect('destroy', Lang.bind(this, function() {
|
||||
this._actorDestroyed = true;
|
||||
|
||||
// If the drag actor is destroyed and we were going to fix
|
||||
// up its hover state, fix up the parent hover state instead
|
||||
if (this.actor == this._firstLeaveActor)
|
||||
this._firstLeaveActor = this._dragOrigParent;
|
||||
if (this._dragInProgress)
|
||||
|
||||
if (this._dragInProgress && this._dragCancellable)
|
||||
this._cancelDrag(global.get_current_time());
|
||||
this.disconnectAll();
|
||||
}));
|
||||
@ -102,6 +104,7 @@ const _Draggable = new Lang.Class({
|
||||
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._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.
|
||||
// But we remember the actors that we first left/last entered so we can
|
||||
@ -439,6 +442,11 @@ 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) {
|
||||
if (target._delegate && target._delegate.acceptDrop) {
|
||||
let [r, targX, targY] = target.transform_stage_point(dropX, dropY);
|
||||
@ -447,8 +455,6 @@ const _Draggable = new Lang.Class({
|
||||
targX,
|
||||
targY,
|
||||
event.get_time())) {
|
||||
if (this._actorDestroyed)
|
||||
return true;
|
||||
// If it accepted the drop without taking the actor,
|
||||
// handle it ourselves.
|
||||
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.screen.focus_default_window(global.get_current_time());
|
||||
global.screen.focus_default_window(global.display.get_current_time_roundtrip());
|
||||
},
|
||||
|
||||
// ignoreRelease:
|
||||
@ -308,6 +308,8 @@ const GrabHelper = new Lang.Class({
|
||||
if (!this.grabbed && this._capturedEventId > 0) {
|
||||
global.stage.disconnect(this._capturedEventId);
|
||||
this._capturedEventId = 0;
|
||||
|
||||
this._ignoreRelease = false;
|
||||
}
|
||||
|
||||
if (hadFocus) {
|
||||
|
514
js/ui/layout.js
514
js/ui/layout.js
@ -18,7 +18,6 @@ const Main = imports.ui.main;
|
||||
const Params = imports.misc.params;
|
||||
const Tweener = imports.ui.tweener;
|
||||
|
||||
const HOT_CORNER_ACTIVATION_TIMEOUT = 0.5;
|
||||
const STARTUP_ANIMATION_TIME = 0.5;
|
||||
const KEYBOARD_ANIMATION_TIME = 0.15;
|
||||
const BACKGROUND_FADE_ANIMATION_TIME = 1.0;
|
||||
@ -29,6 +28,9 @@ const DEFAULT_BACKGROUND_COLOR = Clutter.Color.from_pixel(0x2e3436ff);
|
||||
const MESSAGE_TRAY_PRESSURE_THRESHOLD = 250; // pixels
|
||||
const MESSAGE_TRAY_PRESSURE_TIMEOUT = 1000; // ms
|
||||
|
||||
const HOT_CORNER_PRESSURE_THRESHOLD = 100; // pixels
|
||||
const HOT_CORNER_PRESSURE_TIMEOUT = 1000; // ms
|
||||
|
||||
function isPopupMetaWindow(actor) {
|
||||
switch(actor.meta_window.get_window_type()) {
|
||||
case Meta.WindowType.DROPDOWN_MENU:
|
||||
@ -130,9 +132,9 @@ const LayoutManager = new Lang.Class({
|
||||
this.monitors = [];
|
||||
this.primaryMonitor = null;
|
||||
this.primaryIndex = -1;
|
||||
this.hotCorners = [];
|
||||
|
||||
this._keyboardIndex = -1;
|
||||
this._hotCorners = [];
|
||||
this._leftPanelBarrier = null;
|
||||
this._rightPanelBarrier = null;
|
||||
this._trayBarrier = null;
|
||||
|
||||
@ -140,6 +142,7 @@ const LayoutManager = new Lang.Class({
|
||||
this._updateRegionIdle = 0;
|
||||
|
||||
this._trackedActors = [];
|
||||
this._topActors = [];
|
||||
this._isPopupWindowVisible = false;
|
||||
this._startingUp = true;
|
||||
|
||||
@ -191,6 +194,7 @@ const LayoutManager = new Lang.Class({
|
||||
this.trayBox = new St.Widget({ name: 'trayBox',
|
||||
layout_manager: new Clutter.BinLayout() });
|
||||
this.addChrome(this.trayBox);
|
||||
this._setupTrayPressure();
|
||||
|
||||
this.keyboardBox = new St.BoxLayout({ name: 'keyboardBox',
|
||||
reactive: true,
|
||||
@ -218,6 +222,8 @@ const LayoutManager = new Lang.Class({
|
||||
Lang.bind(this, this._windowsRestacked));
|
||||
global.screen.connect('monitors-changed',
|
||||
Lang.bind(this, this._monitorsChanged));
|
||||
global.screen.connect('in-fullscreen-changed',
|
||||
Lang.bind(this, this._updateFullscreen));
|
||||
this._monitorsChanged();
|
||||
},
|
||||
|
||||
@ -228,6 +234,8 @@ const LayoutManager = new Lang.Class({
|
||||
Main.overview.connect('showing', Lang.bind(this, this._overviewShowing));
|
||||
Main.overview.connect('hidden', Lang.bind(this, this._overviewHidden));
|
||||
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
|
||||
|
||||
this._prepareStartupAnimation();
|
||||
},
|
||||
|
||||
_overviewShowing: function() {
|
||||
@ -275,56 +283,58 @@ const LayoutManager = new Lang.Class({
|
||||
|
||||
_updateHotCorners: function() {
|
||||
// destroy old hot corners
|
||||
for (let i = 0; i < this._hotCorners.length; i++)
|
||||
this._hotCorners[i].destroy();
|
||||
this._hotCorners = [];
|
||||
for (let i = 0; i < this.hotCorners.length; i++)
|
||||
this.hotCorners[i].destroy();
|
||||
this.hotCorners = [];
|
||||
|
||||
let size = this.panelBox.height;
|
||||
|
||||
// build new hot corners
|
||||
for (let i = 0; i < this.monitors.length; i++) {
|
||||
if (i == this.primaryIndex)
|
||||
continue;
|
||||
|
||||
let monitor = this.monitors[i];
|
||||
let cornerX = this._rtl ? monitor.x + monitor.width : monitor.x;
|
||||
let cornerY = monitor.y;
|
||||
|
||||
let haveTopLeftCorner = true;
|
||||
if (i != this.primaryIndex) {
|
||||
let haveTopLeftCorner = true;
|
||||
|
||||
// Check if we have a top left (right for RTL) corner.
|
||||
// I.e. if there is no monitor directly above or to the left(right)
|
||||
let besideX = this._rtl ? monitor.x + 1 : cornerX - 1;
|
||||
let besideY = cornerY;
|
||||
let aboveX = cornerX;
|
||||
let aboveY = cornerY - 1;
|
||||
// Check if we have a top left (right for RTL) corner.
|
||||
// I.e. if there is no monitor directly above or to the left(right)
|
||||
let besideX = this._rtl ? monitor.x + 1 : cornerX - 1;
|
||||
let besideY = cornerY;
|
||||
let aboveX = cornerX;
|
||||
let aboveY = cornerY - 1;
|
||||
|
||||
for (let j = 0; j < this.monitors.length; j++) {
|
||||
if (i == j)
|
||||
for (let j = 0; j < this.monitors.length; j++) {
|
||||
if (i == j)
|
||||
continue;
|
||||
let otherMonitor = this.monitors[j];
|
||||
if (besideX >= otherMonitor.x &&
|
||||
besideX < otherMonitor.x + otherMonitor.width &&
|
||||
besideY >= otherMonitor.y &&
|
||||
besideY < otherMonitor.y + otherMonitor.height) {
|
||||
haveTopLeftCorner = false;
|
||||
break;
|
||||
}
|
||||
if (aboveX >= otherMonitor.x &&
|
||||
aboveX < otherMonitor.x + otherMonitor.width &&
|
||||
aboveY >= otherMonitor.y &&
|
||||
aboveY < otherMonitor.y + otherMonitor.height) {
|
||||
haveTopLeftCorner = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!haveTopLeftCorner)
|
||||
continue;
|
||||
let otherMonitor = this.monitors[j];
|
||||
if (besideX >= otherMonitor.x &&
|
||||
besideX < otherMonitor.x + otherMonitor.width &&
|
||||
besideY >= otherMonitor.y &&
|
||||
besideY < otherMonitor.y + otherMonitor.height) {
|
||||
haveTopLeftCorner = false;
|
||||
break;
|
||||
}
|
||||
if (aboveX >= otherMonitor.x &&
|
||||
aboveX < otherMonitor.x + otherMonitor.width &&
|
||||
aboveY >= otherMonitor.y &&
|
||||
aboveY < otherMonitor.y + otherMonitor.height) {
|
||||
haveTopLeftCorner = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!haveTopLeftCorner)
|
||||
continue;
|
||||
|
||||
let corner = new HotCorner(this);
|
||||
this._hotCorners.push(corner);
|
||||
corner.actor.set_position(cornerX, cornerY);
|
||||
this.addChrome(corner.actor);
|
||||
let corner = new HotCorner(this, monitor, cornerX, cornerY);
|
||||
corner.setBarrierSize(size);
|
||||
this.hotCorners.push(corner);
|
||||
}
|
||||
|
||||
this.emit('hot-corners-changed');
|
||||
},
|
||||
|
||||
_createBackground: function(monitorIndex) {
|
||||
@ -394,16 +404,15 @@ const LayoutManager = new Lang.Class({
|
||||
},
|
||||
|
||||
_panelBoxChanged: function() {
|
||||
this.emit('panel-box-changed');
|
||||
this._updatePanelBarriers();
|
||||
this._updatePanelBarrier();
|
||||
|
||||
let size = this.panelBox.height;
|
||||
this.hotCorners.forEach(function(corner) {
|
||||
corner.setBarrierSize(size);
|
||||
});
|
||||
},
|
||||
|
||||
_updatePanelBarriers: function() {
|
||||
if (this._leftPanelBarrier) {
|
||||
this._leftPanelBarrier.destroy();
|
||||
this._leftPanelBarrier = null;
|
||||
}
|
||||
|
||||
_updatePanelBarrier: function() {
|
||||
if (this._rightPanelBarrier) {
|
||||
this._rightPanelBarrier.destroy();
|
||||
this._rightPanelBarrier = null;
|
||||
@ -412,10 +421,6 @@ const LayoutManager = new Lang.Class({
|
||||
if (this.panelBox.height) {
|
||||
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,
|
||||
x1: primary.x + primary.width, y1: primary.y,
|
||||
x2: primary.x + primary.width, y2: primary.y + this.panelBox.height,
|
||||
@ -423,28 +428,44 @@ 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() {
|
||||
let monitor = this.bottomMonitor;
|
||||
|
||||
if (this._trayBarrier) {
|
||||
this._trayPressure.removeBarrier(this._trayBarrier);
|
||||
this._trayBarrier.destroy();
|
||||
this._trayBarrier = null;
|
||||
}
|
||||
|
||||
if (this._trayPressure) {
|
||||
this._trayPressure.destroy();
|
||||
this._trayPressure = null;
|
||||
}
|
||||
|
||||
this._trayBarrier = new Meta.Barrier({ display: global.display,
|
||||
x1: monitor.x, x2: monitor.x + monitor.width,
|
||||
y1: monitor.y + monitor.height, y2: monitor.y + monitor.height,
|
||||
directions: Meta.BarrierDirection.NEGATIVE_Y });
|
||||
this._trayPressure.addBarrier(this._trayBarrier);
|
||||
},
|
||||
|
||||
this._trayPressure = new PressureBarrier(this._trayBarrier, MESSAGE_TRAY_PRESSURE_THRESHOLD, MESSAGE_TRAY_PRESSURE_TIMEOUT);
|
||||
this._trayPressure.connect('trigger', function(barrier) {
|
||||
Main.messageTray.openTray();
|
||||
});
|
||||
_trayBarrierEventFilter: function(event) {
|
||||
// Throw out all events where the pointer was grabbed by another
|
||||
// client, as the client that grabbed the pointer expects to have
|
||||
// complete control over it
|
||||
if (event.grabbed && Main.modalCount == 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
_monitorsChanged: function() {
|
||||
@ -534,7 +555,7 @@ const LayoutManager = new Lang.Class({
|
||||
// MetaBackgroundActor inside global.window_group covers the entirety of the
|
||||
// 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
|
||||
// so events don't get delivered to X11 windows (which are distorted by the animation)
|
||||
global.stage_input_mode = Shell.StageInputMode.FULLSCREEN;
|
||||
@ -572,25 +593,25 @@ const LayoutManager = new Lang.Class({
|
||||
Lang.bind(this, function() {
|
||||
this._systemBackground.disconnect(signalId);
|
||||
this._systemBackground.actor.show();
|
||||
global.stage.show();
|
||||
|
||||
this.emit('startup-prepared');
|
||||
|
||||
// We're mostly prepared for the startup animation
|
||||
// now, but since a lot is going on asynchronously
|
||||
// during startup, let's defer emission of the
|
||||
// startup-prepared signal until the event loop is
|
||||
// uncontended and idle. This helps to prevent us
|
||||
// from running the animation when the system is
|
||||
// bogged down
|
||||
// during startup, let's defer the startup animation
|
||||
// until the event loop is uncontended and idle.
|
||||
// This helps to prevent us from running the animation
|
||||
// when the system is bogged down
|
||||
GLib.idle_add(GLib.PRIORITY_LOW,
|
||||
Lang.bind(this, function() {
|
||||
this.emit('startup-prepared');
|
||||
return false;
|
||||
}));
|
||||
|
||||
this._startupAnimation();
|
||||
return false;
|
||||
}));
|
||||
}));
|
||||
},
|
||||
|
||||
startupAnimation: function() {
|
||||
global.stage.show();
|
||||
_startupAnimation: function() {
|
||||
if (Main.sessionMode.isGreeter)
|
||||
this._startupAnimationGreeter();
|
||||
else
|
||||
@ -636,9 +657,9 @@ const LayoutManager = new Lang.Class({
|
||||
if (!Main.sessionMode.isGreeter)
|
||||
this._createSecondaryBackgrounds();
|
||||
|
||||
this.emit('panel-box-changed');
|
||||
|
||||
this._queueUpdateRegions();
|
||||
|
||||
this.emit('startup-complete');
|
||||
},
|
||||
|
||||
showKeyboard: function () {
|
||||
@ -814,6 +835,11 @@ const LayoutManager = new Lang.Class({
|
||||
},
|
||||
|
||||
_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++) {
|
||||
let actorData = this._trackedActors[i], visible;
|
||||
if (!actorData.trackFullscreen)
|
||||
@ -821,7 +847,7 @@ const LayoutManager = new Lang.Class({
|
||||
if (!actorData.isToplevel)
|
||||
continue;
|
||||
|
||||
if (this._inOverview || !Main.sessionMode.hasWindows)
|
||||
if (!windowsVisible)
|
||||
visible = true;
|
||||
else if (this.findMonitorForActor(actorData.actor).inFullscreen)
|
||||
visible = false;
|
||||
@ -871,95 +897,25 @@ const LayoutManager = new Lang.Class({
|
||||
},
|
||||
|
||||
_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++)
|
||||
this.monitors[i].inFullscreen = false;
|
||||
this.monitors[i].inFullscreen = global.screen.get_monitor_in_fullscreen (i);
|
||||
|
||||
// Ordinary chrome should be visible unless there is a window
|
||||
// with layer FULLSCREEN, or a window with layer
|
||||
// OVERRIDE_REDIRECT that covers the whole screen.
|
||||
// ('override_redirect' is not actually a layer above all
|
||||
// other windows, but this seems to be how mutter treats it
|
||||
// currently...) If we wanted to be extra clever, we could
|
||||
// figure out when an OVERRIDE_REDIRECT window was trying to
|
||||
// partially overlap us, and then adjust the input region and
|
||||
// our clip region accordingly...
|
||||
this._updateVisibility();
|
||||
this._queueUpdateRegions();
|
||||
|
||||
// @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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.emit('fullscreen-changed');
|
||||
},
|
||||
|
||||
_windowsRestacked: function() {
|
||||
let wasInFullscreen = [];
|
||||
for (let i = 0; i < this.monitors.length; i++)
|
||||
wasInFullscreen[i] = this.monitors[i].inFullscreen;
|
||||
|
||||
let primaryWasInFullscreen = this.primaryMonitor.inFullscreen;
|
||||
|
||||
this._updateFullscreen();
|
||||
|
||||
let changed = false;
|
||||
for (let i = 0; i < wasInFullscreen.length; i++) {
|
||||
if (wasInFullscreen[i] != this.monitors[i].inFullscreen) {
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!changed && (this._isPopupWindowVisible != global.top_window_group.get_children().some(isPopupMetaWindow)))
|
||||
if (this._isPopupWindowVisible != global.top_window_group.get_children().some(isPopupMetaWindow))
|
||||
changed = true;
|
||||
|
||||
if (changed) {
|
||||
this._updateVisibility();
|
||||
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() {
|
||||
@ -1089,54 +1045,25 @@ Signals.addSignalMethods(LayoutManager.prototype);
|
||||
const HotCorner = new Lang.Class({
|
||||
Name: 'HotCorner',
|
||||
|
||||
_init : function(layoutManager) {
|
||||
_init : function(layoutManager, monitor, x, y) {
|
||||
// We use this flag to mark the case where the user has entered the
|
||||
// hot corner and has not left both the hot corner and a surrounding
|
||||
// guard area (the "environs"). This avoids triggering the hot corner
|
||||
// multiple times due to an accidental jitter.
|
||||
this._entered = false;
|
||||
|
||||
this.actor = new Clutter.Actor({ name: 'hot-corner-environs',
|
||||
width: 3,
|
||||
height: 3,
|
||||
reactive: true });
|
||||
this._monitor = monitor;
|
||||
|
||||
this._corner = new Clutter.Rectangle({ name: 'hot-corner',
|
||||
width: 1,
|
||||
height: 1,
|
||||
opacity: 0,
|
||||
reactive: true });
|
||||
this._corner._delegate = this;
|
||||
this._x = x;
|
||||
this._y = y;
|
||||
|
||||
this.actor.add_child(this._corner);
|
||||
this._setupFallbackCornerIfNeeded(layoutManager);
|
||||
|
||||
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._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));
|
||||
this._pressureBarrier = new PressureBarrier(HOT_CORNER_PRESSURE_THRESHOLD,
|
||||
HOT_CORNER_PRESSURE_TIMEOUT,
|
||||
Shell.KeyBindingMode.NORMAL |
|
||||
Shell.KeyBindingMode.OVERVIEW);
|
||||
this._pressureBarrier.connect('trigger', Lang.bind(this, this._toggleOverview));
|
||||
|
||||
// Cache the three ripples instead of dynamically creating and destroying them.
|
||||
this._ripple1 = new St.BoxLayout({ style_class: 'ripple-box', opacity: 0, visible: false });
|
||||
@ -1148,8 +1075,74 @@ const HotCorner = new Lang.Class({
|
||||
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() {
|
||||
this.actor.destroy();
|
||||
this.setBarrierSize(0);
|
||||
this._pressureBarrier.destroy();
|
||||
this._pressureBarrier = null;
|
||||
|
||||
if (this.actor)
|
||||
this.actor.destroy();
|
||||
},
|
||||
|
||||
_animRipple : function(ripple, delay, time, startScale, startOpacity, finalScale) {
|
||||
@ -1169,9 +1162,8 @@ const HotCorner = new Lang.Class({
|
||||
ripple.opacity = 255 * Math.sqrt(startOpacity);
|
||||
ripple.scale_x = ripple.scale_y = startScale;
|
||||
|
||||
let [x, y] = this._corner.get_transformed_position();
|
||||
ripple.x = x;
|
||||
ripple.y = y;
|
||||
ripple.x = this._x;
|
||||
ripple.y = this._y;
|
||||
|
||||
Tweener.addTween(ripple, { _opacity: 0,
|
||||
scale_x: finalScale,
|
||||
@ -1183,7 +1175,7 @@ const HotCorner = new Lang.Class({
|
||||
onComplete: function() { ripple.visible = false; } });
|
||||
},
|
||||
|
||||
rippleAnimation: function() {
|
||||
_rippleAnimation: function() {
|
||||
// Show three concentric ripples expanding outwards; the exact
|
||||
// parameters were found by trial and error, so don't look
|
||||
// for them to make perfect sense mathematically
|
||||
@ -1194,37 +1186,31 @@ const HotCorner = new Lang.Class({
|
||||
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) {
|
||||
if (source != Main.xdndHandler)
|
||||
return DND.DragMotionResult.CONTINUE;
|
||||
|
||||
if (!Main.overview.visible && !Main.overview.animationInProgress) {
|
||||
this.rippleAnimation();
|
||||
Main.overview.showTemporarily();
|
||||
}
|
||||
|
||||
return DND.DragMotionResult.CONTINUE;
|
||||
},
|
||||
|
||||
_onCornerEntered : function() {
|
||||
if (!this._entered) {
|
||||
this._entered = true;
|
||||
if (!Main.overview.animationInProgress) {
|
||||
this._activationTime = Date.now() / 1000;
|
||||
|
||||
this.rippleAnimation();
|
||||
Main.overview.toggle();
|
||||
}
|
||||
this._toggleOverview();
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
_onCornerClicked : function() {
|
||||
if (this.shouldToggleOverviewOnClick())
|
||||
Main.overview.toggle();
|
||||
return true;
|
||||
},
|
||||
|
||||
_onCornerLeft : function(actor, event) {
|
||||
if (event.get_related() != this.actor)
|
||||
this._entered = false;
|
||||
@ -1236,42 +1222,47 @@ const HotCorner = new Lang.Class({
|
||||
if (event.get_related() != this._corner)
|
||||
this._entered = false;
|
||||
return false;
|
||||
},
|
||||
|
||||
// Checks if the Activities button is currently sensitive to
|
||||
// clicks. The first call to this function within the
|
||||
// HOT_CORNER_ACTIVATION_TIMEOUT time of the hot corner being
|
||||
// triggered will return false. This avoids opening and closing
|
||||
// the overview if the user both triggered the hot corner and
|
||||
// clicked the Activities button.
|
||||
shouldToggleOverviewOnClick: function() {
|
||||
if (Main.overview.animationInProgress)
|
||||
return false;
|
||||
if (this._activationTime == 0 || Date.now() / 1000 - this._activationTime > HOT_CORNER_ACTIVATION_TIMEOUT)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
const PressureBarrier = new Lang.Class({
|
||||
Name: 'PressureBarrier',
|
||||
|
||||
_init: function(barrier, threshold, timeout) {
|
||||
this._barrier = barrier;
|
||||
_init: function(threshold, timeout, keybindingMode) {
|
||||
this._threshold = threshold;
|
||||
this._timeout = timeout;
|
||||
this._orientation = (barrier.y1 == barrier.y2) ? Clutter.Orientation.HORIZONTAL : Clutter.Orientation.VERTICAL;
|
||||
this._keybindingMode = keybindingMode;
|
||||
this._barriers = [];
|
||||
this._eventFilter = null;
|
||||
|
||||
this._isTriggered = false;
|
||||
this._reset();
|
||||
},
|
||||
|
||||
this._barrierHitId = this._barrier.connect('hit', Lang.bind(this, this._onBarrierHit));
|
||||
this._barrierLeftId = this._barrier.connect('left', Lang.bind(this, this._onBarrierLeft));
|
||||
addBarrier: function(barrier) {
|
||||
barrier._pressureHitId = barrier.connect('hit', Lang.bind(this, this._onBarrierHit));
|
||||
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() {
|
||||
this._barrier.disconnect(this._barrierHitId);
|
||||
this._barrier.disconnect(this._barrierLeftId);
|
||||
this._barrier = null;
|
||||
this._barriers.forEach(Lang.bind(this, this._disconnectBarrier));
|
||||
this._barriers = [];
|
||||
},
|
||||
|
||||
setEventFilter: function(filter) {
|
||||
this._eventFilter = filter;
|
||||
},
|
||||
|
||||
_reset: function() {
|
||||
@ -1280,33 +1271,34 @@ const PressureBarrier = new Lang.Class({
|
||||
this._lastTime = 0;
|
||||
},
|
||||
|
||||
_getDistanceAcrossBarrier: function(event) {
|
||||
if (this._orientation == Clutter.Orientation.HORIZONTAL)
|
||||
_isHorizontal: function(barrier) {
|
||||
return barrier.y1 == barrier.y2;
|
||||
},
|
||||
|
||||
_getDistanceAcrossBarrier: function(barrier, event) {
|
||||
if (this._isHorizontal(barrier))
|
||||
return Math.abs(event.dy);
|
||||
else
|
||||
return Math.abs(event.dx);
|
||||
},
|
||||
|
||||
_getDistanceAlongBarrier: function(event) {
|
||||
if (this._orientation == Clutter.Orientation.HORIZONTAL)
|
||||
_getDistanceAlongBarrier: function(barrier, event) {
|
||||
if (this._isHorizontal(barrier))
|
||||
return Math.abs(event.dx);
|
||||
else
|
||||
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() {
|
||||
// Events are guaranteed to be sorted in time order from
|
||||
// oldest to newest, so just look for the first old event,
|
||||
// and then chop events after that off.
|
||||
let i = 0;
|
||||
let threshold = this._lastTime - this._timeout;
|
||||
|
||||
while (i < this._barrierEvents.length) {
|
||||
if (!this._isBarrierEventTooOld(this._barrierEvents[i]))
|
||||
let [time, distance] = this._barrierEvents[i];
|
||||
if (time >= threshold)
|
||||
break;
|
||||
i++;
|
||||
}
|
||||
@ -1314,7 +1306,8 @@ const PressureBarrier = new Lang.Class({
|
||||
let firstNewEvent = i;
|
||||
|
||||
for (i = 0; i < firstNewEvent; i++) {
|
||||
this._currentPressure -= this._getDistanceAcrossBarrier(this._barrierEvents[i]);
|
||||
let [time, distance] = this._barrierEvents[i];
|
||||
this._currentPressure -= distance;
|
||||
}
|
||||
|
||||
this._barrierEvents = this._barrierEvents.slice(firstNewEvent);
|
||||
@ -1322,29 +1315,30 @@ const PressureBarrier = new Lang.Class({
|
||||
|
||||
_onBarrierLeft: function(barrier, event) {
|
||||
this._reset();
|
||||
this._isTriggered = false;
|
||||
},
|
||||
|
||||
_trigger: function() {
|
||||
this._isTriggered = true;
|
||||
this.emit('trigger');
|
||||
this._reset();
|
||||
},
|
||||
|
||||
_onBarrierHit: function(barrier, event) {
|
||||
// Throw out all events where the pointer was grabbed by another
|
||||
// client, as the client that grabbed the pointer expects to have
|
||||
// complete control over it
|
||||
if (event.grabbed && Main.modalCount == 0)
|
||||
// If we've triggered the barrier, wait until the pointer has the
|
||||
// left the barrier hitbox until we trigger it again.
|
||||
if (this._isTriggered)
|
||||
return;
|
||||
|
||||
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)
|
||||
if (this._eventFilter && this._eventFilter(event))
|
||||
return;
|
||||
|
||||
let slide = this._getDistanceAlongBarrier(event);
|
||||
let distance = this._getDistanceAcrossBarrier(event);
|
||||
// Throw out all events not in the proper keybinding mode
|
||||
if (!(this._keybindingMode & Main.keybindingMode))
|
||||
return;
|
||||
|
||||
let slide = this._getDistanceAlongBarrier(barrier, event);
|
||||
let distance = this._getDistanceAcrossBarrier(barrier, event);
|
||||
|
||||
if (distance >= this._threshold) {
|
||||
this._trigger();
|
||||
@ -1360,8 +1354,10 @@ const PressureBarrier = new Lang.Class({
|
||||
this._lastTime = event.time;
|
||||
|
||||
this._trimBarrierEvents();
|
||||
this._barrierEvents.push(event);
|
||||
this._currentPressure += Math.min(15, distance);
|
||||
distance = Math.min(15, distance);
|
||||
|
||||
this._barrierEvents.push([event.time, distance]);
|
||||
this._currentPressure += distance;
|
||||
|
||||
if (this._currentPressure >= this._threshold)
|
||||
this._trigger();
|
||||
|
@ -24,6 +24,7 @@ const Panel = imports.ui.panel;
|
||||
const Params = imports.misc.params;
|
||||
const RunDialog = imports.ui.runDialog;
|
||||
const Layout = imports.ui.layout;
|
||||
const LoginManager = imports.misc.loginManager;
|
||||
const LookingGlass = imports.ui.lookingGlass;
|
||||
const NotificationDaemon = imports.ui.notificationDaemon;
|
||||
const WindowAttentionHandler = imports.ui.windowAttentionHandler;
|
||||
@ -32,7 +33,6 @@ const Scripting = imports.ui.scripting;
|
||||
const SessionMode = imports.ui.sessionMode;
|
||||
const ShellDBus = imports.ui.shellDBus;
|
||||
const ShellMountOperation = imports.ui.shellMountOperation;
|
||||
const UnlockDialog = imports.ui.unlockDialog;
|
||||
const WindowManager = imports.ui.windowManager;
|
||||
const Magnifier = imports.ui.magnifier;
|
||||
const XdndHandler = imports.ui.xdndHandler;
|
||||
@ -58,7 +58,7 @@ let shellDBusService = null;
|
||||
let shellMountOpDBusService = null;
|
||||
let screenSaverDBus = null;
|
||||
let modalCount = 0;
|
||||
let keybindingMode = Shell.KeyBindingMode.NORMAL;
|
||||
let keybindingMode = Shell.KeyBindingMode.NONE;
|
||||
let modalActorFocusStack = [];
|
||||
let uiGroup = null;
|
||||
let magnifier = null;
|
||||
@ -71,6 +71,8 @@ let _cssStylesheet = null;
|
||||
let _overridesSettings = null;
|
||||
|
||||
function _sessionUpdated() {
|
||||
_loadDefaultStylesheet();
|
||||
|
||||
wm.setCustomKeybindingHandler('panel-main-menu',
|
||||
Shell.KeyBindingMode.NORMAL |
|
||||
Shell.KeyBindingMode.OVERVIEW,
|
||||
@ -82,8 +84,9 @@ function _sessionUpdated() {
|
||||
Shell.KeyBindingMode.NORMAL |
|
||||
Shell.KeyBindingMode.OVERVIEW,
|
||||
sessionMode.hasRunDialog ? openRunDialog : null);
|
||||
if (sessionMode.isGreeter)
|
||||
screenShield.showDialog();
|
||||
|
||||
if (!sessionMode.hasRunDialog && lookingGlass)
|
||||
lookingGlass.close();
|
||||
}
|
||||
|
||||
function start() {
|
||||
@ -91,29 +94,27 @@ function start() {
|
||||
global.logError = 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
|
||||
global.connect('notify-error', function (global, msg, detail) { notifyError(msg, detail); });
|
||||
|
||||
Gio.DesktopAppInfo.set_desktop_env('GNOME');
|
||||
|
||||
sessionMode = new SessionMode.SessionMode();
|
||||
|
||||
// start session after we know what mode we're running in
|
||||
let signalId = sessionMode.connect('updated', function() {
|
||||
sessionMode.disconnect(signalId);
|
||||
startSession();
|
||||
});
|
||||
sessionMode.connect('sessions-loaded', _sessionsLoaded);
|
||||
sessionMode.init();
|
||||
}
|
||||
|
||||
function startSession() {
|
||||
sessionMode.connect('updated', _loadDefaultStylesheet);
|
||||
function _sessionsLoaded() {
|
||||
sessionMode.connect('updated', _sessionUpdated);
|
||||
_initializeUI();
|
||||
|
||||
shellDBusService = new ShellDBus.GnomeShell();
|
||||
shellMountOpDBusService = new ShellMountOperation.GnomeShellMountOpHandler();
|
||||
|
||||
_sessionUpdated();
|
||||
}
|
||||
|
||||
function _initializeUI() {
|
||||
// Ensure ShellWindowTracker and ShellAppUsage are initialized; this will
|
||||
// also initialize ShellAppSystem first. ShellAppSystem
|
||||
// needs to load all the .desktop files, and ShellWindowTracker
|
||||
@ -143,13 +144,9 @@ function startSession() {
|
||||
overview = new Overview.Overview();
|
||||
wm = new WindowManager.WindowManager();
|
||||
magnifier = new Magnifier.Magnifier();
|
||||
if (UnlockDialog.isSupported())
|
||||
if (LoginManager.canLock())
|
||||
screenShield = new ScreenShield.ScreenShield();
|
||||
else
|
||||
screenShield = new ScreenShield.ScreenShieldFallback();
|
||||
|
||||
// The message tray relies on being constructed
|
||||
// after the panel.
|
||||
panel = new Panel.Panel();
|
||||
messageTray = new MessageTray.MessageTray();
|
||||
keyboard = new Keyboard.Keyboard();
|
||||
@ -158,14 +155,11 @@ function startSession() {
|
||||
componentManager = new Components.ComponentManager();
|
||||
|
||||
layoutManager.init();
|
||||
layoutManager.prepareStartupAnimation();
|
||||
overview.init();
|
||||
|
||||
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT,
|
||||
false, -1, 1);
|
||||
global.display.connect('overlay-key', Lang.bind(overview, overview.toggle));
|
||||
sessionMode.connect('updated', _sessionUpdated);
|
||||
_sessionUpdated();
|
||||
|
||||
// Provide the bus object for gnome-session to
|
||||
// initiate logouts.
|
||||
@ -199,8 +193,16 @@ function startSession() {
|
||||
ExtensionDownloader.init();
|
||||
ExtensionSystem.init();
|
||||
|
||||
layoutManager.connect('startup-prepared', function() {
|
||||
layoutManager.startupAnimation();
|
||||
if (sessionMode.isGreeter && screenShield) {
|
||||
layoutManager.connect('startup-prepared', function() {
|
||||
screenShield.showDialog();
|
||||
});
|
||||
}
|
||||
|
||||
layoutManager.connect('startup-complete', function() {
|
||||
if (keybindingMode == Shell.KeyBindingMode.NONE) {
|
||||
keybindingMode = Shell.KeyBindingMode.NORMAL;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1591,17 +1591,13 @@ const MessageTray = new Lang.Class({
|
||||
global.focus_manager.add_group(this.actor);
|
||||
this._summary = new St.BoxLayout({ style_class: 'message-tray-summary',
|
||||
reactive: true,
|
||||
track_hover: true,
|
||||
x_align: Clutter.ActorAlign.END,
|
||||
x_expand: true,
|
||||
y_align: Clutter.ActorAlign.CENTER,
|
||||
y_expand: true });
|
||||
this._summary.connect('notify::hover', Lang.bind(this, this._onSummaryHoverChanged));
|
||||
this.actor.add_actor(this._summary);
|
||||
this._summary.opacity = 0;
|
||||
|
||||
this._summaryMotionId = 0;
|
||||
this._trayMotionId = 0;
|
||||
|
||||
this._summaryBoxPointer = new BoxPointer.BoxPointer(St.Side.BOTTOM,
|
||||
{ reactive: true,
|
||||
@ -1636,15 +1632,22 @@ const MessageTray = new Lang.Class({
|
||||
Main.layoutManager.connect('keyboard-visible-changed', Lang.bind(this, this._onKeyboardVisibleChanged));
|
||||
|
||||
this._trayState = State.HIDDEN;
|
||||
this._locked = false;
|
||||
this._traySummoned = false;
|
||||
this._useLongerTrayLeftTimeout = false;
|
||||
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._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._summaryState = State.HIDDEN;
|
||||
this._pointerInSummary = false;
|
||||
this._notificationClosed = false;
|
||||
this._notificationState = State.HIDDEN;
|
||||
this._notificationTimeoutId = 0;
|
||||
@ -1666,20 +1669,18 @@ const MessageTray = new Lang.Class({
|
||||
});
|
||||
|
||||
Main.layoutManager.trayBox.add_actor(this.actor);
|
||||
this.actor.y = 0;
|
||||
Main.layoutManager.trackChrome(this.actor);
|
||||
Main.layoutManager.trackChrome(this._notificationWidget);
|
||||
Main.layoutManager.trackChrome(this._closeButton);
|
||||
|
||||
Main.layoutManager.connect('primary-fullscreen-changed', Lang.bind(this, this._onFullscreenChanged));
|
||||
Main.layoutManager.connect('fullscreen-changed', Lang.bind(this, this._updateState));
|
||||
Main.layoutManager.connect('hot-corners-changed', Lang.bind(this, this._hotCornersChanged));
|
||||
|
||||
// If the overview shows or hides while we're in
|
||||
// the message tray, revert back to normal mode.
|
||||
Main.overview.connect('showing', 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.wm.addKeybinding('toggle-message-tray',
|
||||
@ -1703,6 +1704,7 @@ const MessageTray = new Lang.Class({
|
||||
this._trayDwellTimeoutId = 0;
|
||||
this._setupTrayDwellIfNeeded();
|
||||
this._sessionUpdated();
|
||||
this._hotCornersChanged();
|
||||
|
||||
this._noMessages = new St.Label({ text: _("No Messages"),
|
||||
style_class: 'no-messages-label',
|
||||
@ -1743,13 +1745,11 @@ const MessageTray = new Lang.Class({
|
||||
|
||||
_openContextMenu: function () {
|
||||
let [x, y, mask] = global.get_pointer();
|
||||
this._lock();
|
||||
this._contextMenu.setPosition(Math.round(x), Math.round(y));
|
||||
this._grabHelper.grab({ actor: this._contextMenu.actor,
|
||||
grabFocus: true,
|
||||
onUngrab: Lang.bind(this, function () {
|
||||
this._contextMenu.close(BoxPointer.PopupAnimation.FULL);
|
||||
this._unlock();
|
||||
})
|
||||
});
|
||||
this._contextMenu.open(BoxPointer.PopupAnimation.FULL);
|
||||
@ -1775,11 +1775,6 @@ const MessageTray = new Lang.Class({
|
||||
},
|
||||
|
||||
_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) {
|
||||
Main.ctrlAltTabManager.removeGroup(this._summary);
|
||||
this._inCtrlAltTab = false;
|
||||
@ -1828,6 +1823,9 @@ const MessageTray = new Lang.Class({
|
||||
_trayDwellTimeout: function() {
|
||||
this._trayDwellTimeoutId = 0;
|
||||
|
||||
if (Main.layoutManager.bottomMonitor.inFullscreen)
|
||||
return false;
|
||||
|
||||
// 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
|
||||
// overview we have to take the overview's modal push into account
|
||||
@ -1999,18 +1997,6 @@ const MessageTray = new Lang.Class({
|
||||
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() {
|
||||
if (Main.overview.animationInProgress)
|
||||
return;
|
||||
@ -2036,7 +2022,6 @@ const MessageTray = new Lang.Class({
|
||||
hide: function() {
|
||||
this._traySummoned = false;
|
||||
this.actor.set_hover(false);
|
||||
this._summary.set_hover(false);
|
||||
this._updateState();
|
||||
},
|
||||
|
||||
@ -2096,13 +2081,19 @@ const MessageTray = new Lang.Class({
|
||||
this._updateState();
|
||||
},
|
||||
|
||||
_onSummaryHoverChanged: function() {
|
||||
this._pointerInSummary = this._summary.hover;
|
||||
this._updateState();
|
||||
_hotCornersChanged: function() {
|
||||
let primary = Main.layoutManager.primaryIndex;
|
||||
let corner = Main.layoutManager.hotCorners[primary];
|
||||
if (corner && corner.actor)
|
||||
this._grabHelper.addActor(corner.actor);
|
||||
},
|
||||
|
||||
_onTrayHoverChanged: function() {
|
||||
if (this.actor.hover) {
|
||||
if (this.actor.hover == this._trayHovered)
|
||||
return;
|
||||
|
||||
this._trayHovered = this.actor.hover;
|
||||
if (this._trayHovered) {
|
||||
// No dwell inside notifications at the bottom of the screen
|
||||
this._cancelTrayDwell();
|
||||
|
||||
@ -2110,13 +2101,6 @@ const MessageTray = new Lang.Class({
|
||||
if (this._trayState == State.HIDDEN && this._notificationState == State.HIDDEN)
|
||||
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;
|
||||
if (this._trayLeftTimeoutId) {
|
||||
Mainloop.source_remove(this._trayLeftTimeoutId);
|
||||
@ -2165,11 +2149,6 @@ const MessageTray = new Lang.Class({
|
||||
this._updateState();
|
||||
},
|
||||
|
||||
_onFullscreenChanged: function(obj, state) {
|
||||
this._inFullscreen = state;
|
||||
this._updateState();
|
||||
},
|
||||
|
||||
_onStatusChanged: function(status) {
|
||||
if (status == GnomeSession.PresenceStatus.BUSY) {
|
||||
// remove notification and allow the summary to be closed now
|
||||
@ -2201,7 +2180,6 @@ const MessageTray = new Lang.Class({
|
||||
this._trayLeftTimeoutId = 0;
|
||||
this._useLongerTrayLeftTimeout = false;
|
||||
this._pointerInTray = false;
|
||||
this._pointerInSummary = false;
|
||||
this._updateNotificationTimeout(0);
|
||||
this._updateState();
|
||||
}
|
||||
@ -2209,9 +2187,7 @@ const MessageTray = new Lang.Class({
|
||||
},
|
||||
|
||||
_escapeTray: function() {
|
||||
this._unlock();
|
||||
this._pointerInTray = false;
|
||||
this._pointerInSummary = false;
|
||||
this._traySummoned = false;
|
||||
this._setClickedSummaryItem(null);
|
||||
this._updateNotificationTimeout(0);
|
||||
@ -2220,7 +2196,7 @@ const MessageTray = new Lang.Class({
|
||||
|
||||
// All of the logic for what happens when occurs here; the various
|
||||
// event handlers merely update variables such as
|
||||
// 'this._pointerInTray', 'this._summaryState', etc, and
|
||||
// 'this._pointerInTray', 'this._traySummoned', etc, and
|
||||
// _updateState() figures out what (if anything) needs to be done
|
||||
// at the present time.
|
||||
_updateState: function() {
|
||||
@ -2228,25 +2204,22 @@ const MessageTray = new Lang.Class({
|
||||
let notificationQueue = this._notificationQueue;
|
||||
let notificationUrgent = notificationQueue.length > 0 && notificationQueue[0].urgency == Urgency.CRITICAL;
|
||||
let notificationForFeedback = notificationQueue.length > 0 && notificationQueue[0].forFeedback;
|
||||
let notificationsLimited = this._busy || this._inFullscreen;
|
||||
let notificationsLimited = this._busy || Main.layoutManager.bottomMonitor.inFullscreen;
|
||||
let notificationsPending = notificationQueue.length > 0 && (!notificationsLimited || notificationUrgent || notificationForFeedback) && Main.sessionMode.hasNotifications;
|
||||
let nextNotification = notificationQueue.length > 0 ? notificationQueue[0] : null;
|
||||
let notificationPinned = this._pointerInTray && !this._pointerInSummary && !this._notificationRemoved;
|
||||
let notificationPinned = this._pointerInTray && !this._notificationRemoved;
|
||||
let notificationExpanded = this._notification && this._notification.expanded;
|
||||
let notificationExpired = this._notificationTimeoutId == 0 &&
|
||||
!(this._notification && this._notification.urgency == Urgency.CRITICAL) &&
|
||||
!(this._notification && this._notification.focused) &&
|
||||
!this._pointerInTray &&
|
||||
!this._locked &&
|
||||
!(this._pointerInKeyboard && notificationExpanded);
|
||||
!this._pointerInTray;
|
||||
let notificationLockedOut = !Main.sessionMode.hasNotifications && this._notification;
|
||||
let notificationMustClose = this._notificationRemoved || notificationLockedOut || (notificationExpired && this._userActiveWhileNotificationShown) || this._notificationClosed;
|
||||
let canShowNotification = notificationsPending && this._summaryState == State.HIDDEN;
|
||||
let canShowNotification = notificationsPending && this._trayState == State.HIDDEN;
|
||||
|
||||
if (this._notificationState == State.HIDDEN) {
|
||||
if (canShowNotification) {
|
||||
if (canShowNotification)
|
||||
this._showNotification();
|
||||
}
|
||||
} else if (this._notificationState == State.SHOWN) {
|
||||
if (notificationMustClose)
|
||||
this._hideNotification();
|
||||
@ -2256,27 +2229,17 @@ const MessageTray = new Lang.Class({
|
||||
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 notificationsDone = !notificationsVisible && !notificationsPending;
|
||||
|
||||
let mustHideSummary = ((notificationsPending && notificationUrgent)
|
||||
|| notificationsVisible || !Main.sessionMode.hasNotifications);
|
||||
|
||||
if (this._summaryState == State.HIDDEN && !mustHideSummary && summarySummoned)
|
||||
this._showSummary();
|
||||
else if (this._summaryState == State.SHOWN && (!summaryPinned || mustHideSummary))
|
||||
this._hideSummary();
|
||||
let mustHideTray = ((notificationsPending && notificationUrgent)
|
||||
|| notificationsVisible || !Main.sessionMode.hasNotifications);
|
||||
|
||||
// Summary notification
|
||||
let haveClickedSummaryItem = this._clickedSummaryItem != null;
|
||||
let summarySourceIsMainNotificationSource = (haveClickedSummaryItem && this._notification &&
|
||||
this._clickedSummaryItem.source == this._notification.source);
|
||||
let canShowSummaryBoxPointer = this._summaryState == State.SHOWN;
|
||||
let canShowSummaryBoxPointer = this._trayState == State.SHOWN;
|
||||
// We only have sources with empty notification stacks for legacy tray icons. Currently, we never attempt
|
||||
// to show notifications for legacy tray icons, but this would be necessary if we did.
|
||||
let requestedNotificationStackIsEmpty = (this._clickedSummaryItemMouseButton == 1 && this._clickedSummaryItem.source.notifications.length == 0);
|
||||
@ -2291,7 +2254,7 @@ const MessageTray = new Lang.Class({
|
||||
if (haveClickedSummaryItem && !summarySourceIsMainNotificationSource && canShowSummaryBoxPointer && !requestedNotificationStackIsEmpty)
|
||||
this._showSummaryBoxPointer();
|
||||
} else if (this._summaryBoxPointerState == State.SHOWN) {
|
||||
if (!haveClickedSummaryItem || !canShowSummaryBoxPointer || wrongSummaryBoxPointer || mustHideSummary) {
|
||||
if (!haveClickedSummaryItem || !canShowSummaryBoxPointer || wrongSummaryBoxPointer || mustHideTray) {
|
||||
this._hideSummaryBoxPointer();
|
||||
if (wrongSummaryBoxPointer)
|
||||
this._showSummaryBoxPointer();
|
||||
@ -2301,9 +2264,7 @@ const MessageTray = new Lang.Class({
|
||||
// Tray itself
|
||||
let trayIsVisible = (this._trayState == State.SHOWING ||
|
||||
this._trayState == State.SHOWN);
|
||||
let trayShouldBeVisible = (this._summaryState == State.SHOWING ||
|
||||
this._summaryState == State.SHOWN) &&
|
||||
!this._keyboardVisible;
|
||||
let trayShouldBeVisible = this._traySummoned && !this._keyboardVisible && !mustHideTray;
|
||||
if (!trayIsVisible && trayShouldBeVisible)
|
||||
trayShouldBeVisible = this._showTray();
|
||||
else if (trayIsVisible && !trayShouldBeVisible)
|
||||
@ -2441,7 +2402,7 @@ const MessageTray = new Lang.Class({
|
||||
_showNotification: function() {
|
||||
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 the user isn't active, set up a watch to let us know
|
||||
// when the user becomes active.
|
||||
@ -2576,6 +2537,14 @@ const MessageTray = new Lang.Class({
|
||||
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) {
|
||||
Tweener.removeTweens(this._notificationWidget);
|
||||
this._notificationWidget.y = this.actor.height;
|
||||
@ -2606,7 +2575,11 @@ const MessageTray = new Lang.Class({
|
||||
this._notificationRemoved = false;
|
||||
this._closeButton.hide();
|
||||
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._notificationWidget.hide();
|
||||
},
|
||||
@ -2619,10 +2592,9 @@ const MessageTray = new Lang.Class({
|
||||
},
|
||||
|
||||
_expandNotification: function(autoExpanding) {
|
||||
// Don't grab focus in notifications that are auto-expanded.
|
||||
// Don't focus notifications that are auto-expanding.
|
||||
if (!autoExpanding)
|
||||
this._grabHelper.grab({ actor: this._notification.actor,
|
||||
grabFocus: true });
|
||||
this._ensureNotificationFocused();
|
||||
|
||||
if (!this._notificationExpandedId)
|
||||
this._notificationExpandedId =
|
||||
@ -2652,30 +2624,11 @@ 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() {
|
||||
this._grabHelper.grab({ actor: this._notification.actor,
|
||||
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) {
|
||||
if (closeTray) {
|
||||
this._escapeTray();
|
||||
@ -2719,7 +2672,6 @@ const MessageTray = new Lang.Class({
|
||||
this._grabHelper.grab({ actor: this._summaryBoxPointer.bin.child,
|
||||
grabFocus: true,
|
||||
onUngrab: Lang.bind(this, this._onSummaryBoxPointerUngrabbed) });
|
||||
this._lock();
|
||||
|
||||
this._summaryBoxPointer.actor.opacity = 0;
|
||||
this._summaryBoxPointer.actor.show();
|
||||
@ -2754,10 +2706,8 @@ const MessageTray = new Lang.Class({
|
||||
this._clickedSummaryItem.actor.remove_style_pseudo_class('selected');
|
||||
this._clickedSummaryItem.actor.disconnect(this._clickedSummaryItemAllocationChangedId);
|
||||
this._summary.disconnect(this._summaryMotionId);
|
||||
Main.layoutManager.trayBox.disconnect(this._trayMotionId);
|
||||
this._clickedSummaryItemAllocationChangedId = 0;
|
||||
this._summaryMotionId = 0;
|
||||
this._trayMotionId = 0;
|
||||
}
|
||||
|
||||
this._clickedSummaryItem = item;
|
||||
@ -2772,8 +2722,6 @@ const MessageTray = new Lang.Class({
|
||||
// _clickedSummaryItem.actor can change absolute position without changing allocation
|
||||
this._summaryMotionId = this._summary.connect('allocation-changed',
|
||||
Lang.bind(this, this._adjustSummaryBoxPointerPosition));
|
||||
this._trayMotionId = Main.layoutManager.trayBox.connect('notify::anchor-y',
|
||||
Lang.bind(this, this._adjustSummaryBoxPointerPosition));
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -2810,8 +2758,6 @@ const MessageTray = new Lang.Class({
|
||||
this._sourceDoneDisplayingId = 0;
|
||||
}
|
||||
|
||||
this._unlock();
|
||||
|
||||
if (this._summaryBoxPointerItem.source.notifications.length == 0) {
|
||||
this._summaryBoxPointer.actor.hide();
|
||||
this._hideSummaryBoxPointerCompleted();
|
||||
|
@ -244,7 +244,7 @@ const NotificationDaemon = new Lang.Class({
|
||||
Main.overview.connect('hidden',
|
||||
Lang.bind(this, this._onFocusAppChanged));
|
||||
|
||||
this._trayManager.manage_stage(global.stage, Main.messageTray.actor);
|
||||
this._trayManager.manage_screen(global.screen, Main.messageTray.actor);
|
||||
},
|
||||
|
||||
_imageForNotificationData: function(hints) {
|
||||
|
@ -164,12 +164,12 @@ const OsdWindow = new Lang.Class({
|
||||
},
|
||||
|
||||
_monitorsChanged: function() {
|
||||
/* assume 130x130 on a 640x480 display and scale from there */
|
||||
/* assume 110x110 on a 640x480 display and scale from there */
|
||||
let monitor = Main.layoutManager.primaryMonitor;
|
||||
let scalew = monitor.width / 640.0;
|
||||
let scaleh = monitor.height / 480.0;
|
||||
let scale = Math.min(scalew, scaleh);
|
||||
let size = 130 * Math.max(1, scale);
|
||||
let size = 110 * Math.max(1, scale);
|
||||
|
||||
this._box.set_size(size, size);
|
||||
this._box.translation_y = monitor.height / 4;
|
||||
|
@ -33,6 +33,8 @@ const SHADE_ANIMATION_TIME = .20;
|
||||
|
||||
const DND_WINDOW_SWITCH_TIMEOUT = 1250;
|
||||
|
||||
const OVERVIEW_ACTIVATION_TIMEOUT = 0.5;
|
||||
|
||||
const ShellInfo = new Lang.Class({
|
||||
Name: 'ShellInfo',
|
||||
|
||||
@ -93,9 +95,7 @@ const Overview = new Lang.Class({
|
||||
_init: function() {
|
||||
this._overviewCreated = 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));
|
||||
this._sessionUpdated();
|
||||
},
|
||||
@ -146,9 +146,10 @@ const Overview = new Lang.Class({
|
||||
this._backgroundGroup.hide();
|
||||
this._bgManagers = [];
|
||||
|
||||
this._activationTime = 0;
|
||||
|
||||
this.visible = false; // animating to overview, in overview, animating out
|
||||
this._shown = false; // show() and not hide()
|
||||
this._shownTemporarily = false; // showTemporarily() and not hideTemporarily()
|
||||
this._modal = false; // have a modal grab
|
||||
this.animationInProgress = false;
|
||||
this.visibleTarget = false;
|
||||
@ -238,20 +239,6 @@ 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() {
|
||||
this.isDummy = !Main.sessionMode.hasOverview;
|
||||
this._createOverview();
|
||||
@ -346,18 +333,22 @@ const Overview = new Lang.Class({
|
||||
},
|
||||
|
||||
_onDragBegin: function() {
|
||||
this._inXdndDrag = true;
|
||||
|
||||
DND.addDragMonitor(this._dragMonitor);
|
||||
// Remember the workspace we started from
|
||||
this._lastActiveWorkspaceIndex = global.screen.get_active_workspace_index();
|
||||
},
|
||||
|
||||
_onDragEnd: function(time) {
|
||||
this._inXdndDrag = false;
|
||||
|
||||
// In case the drag was canceled while in the overview
|
||||
// we have to go back to where we started and hide
|
||||
// the overview
|
||||
if (this._shownTemporarily) {
|
||||
if (this._shown) {
|
||||
global.screen.get_workspace_by_index(this._lastActiveWorkspaceIndex).activate(time);
|
||||
this.hideTemporarily();
|
||||
this.hide();
|
||||
}
|
||||
this._resetWindowSwitchTimeout();
|
||||
this._lastHoveredWindow = null;
|
||||
@ -405,7 +396,7 @@ const Overview = new Lang.Class({
|
||||
this._needsFakePointerEvent = true;
|
||||
Main.activateWindow(dragEvent.targetActor._delegate.metaWindow,
|
||||
this._windowSwitchTimestamp);
|
||||
this.hideTemporarily();
|
||||
this.hide();
|
||||
this._lastHoveredWindow = null;
|
||||
}));
|
||||
}
|
||||
@ -501,9 +492,10 @@ const Overview = new Lang.Class({
|
||||
if (this._shown)
|
||||
return;
|
||||
this._shown = true;
|
||||
this._syncInputMode();
|
||||
if (!this._modal)
|
||||
|
||||
if (!this._syncInputMode())
|
||||
return;
|
||||
|
||||
this._animateVisible();
|
||||
},
|
||||
|
||||
@ -536,6 +528,7 @@ const Overview = new Lang.Class({
|
||||
this.visible = true;
|
||||
this.animationInProgress = true;
|
||||
this.visibleTarget = true;
|
||||
this._activationTime = Date.now() / 1000;
|
||||
|
||||
// All the the actors in the window group are completely obscured,
|
||||
// hiding the group holding them while the Overview is displayed greatly
|
||||
@ -547,8 +540,6 @@ const Overview = new Lang.Class({
|
||||
//
|
||||
// Disable unredirection while in the overview
|
||||
Meta.disable_unredirect_for_screen(global.screen);
|
||||
global.window_group.hide();
|
||||
global.top_window_group.hide();
|
||||
this._stack.show();
|
||||
this._backgroundGroup.show();
|
||||
this._viewSelector.show();
|
||||
@ -568,24 +559,6 @@ const Overview = new Lang.Class({
|
||||
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:
|
||||
//
|
||||
// Reverses the effect of show()
|
||||
@ -596,33 +569,22 @@ const Overview = new Lang.Class({
|
||||
if (!this._shown)
|
||||
return;
|
||||
|
||||
if (this._controlPressed)
|
||||
return;
|
||||
let event = Clutter.get_current_event();
|
||||
if (event) {
|
||||
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;
|
||||
}
|
||||
|
||||
if (!this._shownTemporarily)
|
||||
this._animateNotVisible();
|
||||
this._animateNotVisible();
|
||||
|
||||
this._shown = false;
|
||||
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() {
|
||||
if (this.isDummy)
|
||||
return;
|
||||
@ -633,6 +595,20 @@ const Overview = new Lang.Class({
|
||||
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 ////
|
||||
|
||||
_syncInputMode: function() {
|
||||
@ -640,22 +616,23 @@ const Overview = new Lang.Class({
|
||||
// overview we don't have a problem with the release of a press/release
|
||||
// going to an application.
|
||||
if (this.animationInProgress)
|
||||
return;
|
||||
return true;
|
||||
|
||||
if (this._shown) {
|
||||
if (!this._modal) {
|
||||
if (Main.pushModal(this._overview,
|
||||
{ keybindingMode: Shell.KeyBindingMode.OVERVIEW }))
|
||||
this._modal = true;
|
||||
else
|
||||
this.hide();
|
||||
let shouldBeModal = !this._inXdndDrag;
|
||||
if (shouldBeModal) {
|
||||
if (!this._modal) {
|
||||
if (Main.pushModal(this._overview,
|
||||
{ keybindingMode: Shell.KeyBindingMode.OVERVIEW })) {
|
||||
this._modal = true;
|
||||
} 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 {
|
||||
if (this._modal) {
|
||||
Main.popModal(this._overview);
|
||||
@ -664,6 +641,7 @@ const Overview = new Lang.Class({
|
||||
else if (global.stage_input_mode == Shell.StageInputMode.FULLSCREEN)
|
||||
global.stage_input_mode = Shell.StageInputMode.NORMAL;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
_animateNotVisible: function() {
|
||||
@ -697,7 +675,7 @@ const Overview = new Lang.Class({
|
||||
|
||||
this.emit('shown');
|
||||
// Handle any calls to hide* while we were showing
|
||||
if (!this._shown && !this._shownTemporarily)
|
||||
if (!this._shown)
|
||||
this._animateNotVisible();
|
||||
|
||||
this._syncInputMode();
|
||||
@ -708,9 +686,6 @@ const Overview = new Lang.Class({
|
||||
// Re-enable unredirection
|
||||
Meta.enable_unredirect_for_screen(global.screen);
|
||||
|
||||
global.window_group.show();
|
||||
global.top_window_group.show();
|
||||
|
||||
this._viewSelector.hide();
|
||||
this._desktopFade.hide();
|
||||
this._backgroundGroup.hide();
|
||||
@ -723,7 +698,7 @@ const Overview = new Lang.Class({
|
||||
|
||||
this.emit('hidden');
|
||||
// Handle any calls to show* while we were hiding
|
||||
if (this._shown || this._shownTemporarily)
|
||||
if (this._shown)
|
||||
this._animateVisible();
|
||||
|
||||
this._syncInputMode();
|
||||
|
@ -452,9 +452,11 @@ const MessagesIndicator = new Lang.Class({
|
||||
|
||||
_updateCount: function() {
|
||||
let count = 0;
|
||||
let hasChats = false;
|
||||
this._sources.forEach(Lang.bind(this,
|
||||
function(source) {
|
||||
count += source.indicatorCount;
|
||||
hasChats |= source.isChat;
|
||||
}));
|
||||
|
||||
this._count = count;
|
||||
@ -462,6 +464,7 @@ const MessagesIndicator = new Lang.Class({
|
||||
"%d new messages",
|
||||
count).format(count);
|
||||
|
||||
this._icon.visible = hasChats;
|
||||
this._updateVisibility();
|
||||
},
|
||||
|
||||
|
@ -18,7 +18,6 @@ const Atk = imports.gi.Atk;
|
||||
const Config = imports.misc.config;
|
||||
const CtrlAltTab = imports.ui.ctrlAltTab;
|
||||
const DND = imports.ui.dnd;
|
||||
const Layout = imports.ui.layout;
|
||||
const Overview = imports.ui.overview;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
@ -630,23 +629,15 @@ const ActivitiesButton = new Lang.Class({
|
||||
this.parent(0.0, null, true);
|
||||
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';
|
||||
|
||||
/* Translators: If there is no suitable word for "Activities"
|
||||
in your language, you can use the word for "Overview". */
|
||||
this._label = new St.Label({ text: _("Activities") });
|
||||
container.add_actor(this._label);
|
||||
this.actor.add_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_after('button-release-event', Lang.bind(this, this._onButtonRelease));
|
||||
this.actor.connect_after('key-release-event', Lang.bind(this, this._onKeyRelease));
|
||||
@ -661,44 +652,6 @@ const ActivitiesButton = new Lang.Class({
|
||||
}));
|
||||
|
||||
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) {
|
||||
@ -708,14 +661,14 @@ const ActivitiesButton = new Lang.Class({
|
||||
if (this._xdndTimeOut != 0)
|
||||
Mainloop.source_remove(this._xdndTimeOut);
|
||||
this._xdndTimeOut = Mainloop.timeout_add(BUTTON_DND_ACTIVATION_TIMEOUT,
|
||||
Lang.bind(this, this._xdndShowOverview, actor));
|
||||
Lang.bind(this, this._xdndToggleOverview, actor));
|
||||
|
||||
return DND.DragMotionResult.CONTINUE;
|
||||
},
|
||||
|
||||
_onCapturedEvent: function(actor, event) {
|
||||
if (event.type() == Clutter.EventType.BUTTON_PRESS) {
|
||||
if (!this.hotCorner.shouldToggleOverviewOnClick())
|
||||
if (!Main.overview.shouldToggleByCornerOrButton())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -732,15 +685,12 @@ const ActivitiesButton = new Lang.Class({
|
||||
}
|
||||
},
|
||||
|
||||
_xdndShowOverview: function(actor) {
|
||||
_xdndToggleOverview: function(actor) {
|
||||
let [x, y, mask] = global.get_pointer();
|
||||
let pickedActor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y);
|
||||
|
||||
if (pickedActor == this.actor) {
|
||||
if (!Main.overview.visible && !Main.overview.animationInProgress) {
|
||||
Main.overview.showTemporarily();
|
||||
}
|
||||
}
|
||||
if (pickedActor == this.actor && Main.overview.shouldToggleByCornerOrButton())
|
||||
Main.overview.toggle();
|
||||
|
||||
Mainloop.source_remove(this._xdndTimeOut);
|
||||
this._xdndTimeOut = 0;
|
||||
|
@ -30,138 +30,6 @@ const EXEC_ARG_KEY = 'exec-arg';
|
||||
|
||||
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({
|
||||
Name: 'RunDialog',
|
||||
Extends: ModalDialog.ModalDialog,
|
||||
@ -242,8 +110,6 @@ const RunDialog = new Lang.Class({
|
||||
key: Clutter.Escape }]);
|
||||
|
||||
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,
|
||||
entry: this._entryText });
|
||||
@ -259,18 +125,6 @@ const RunDialog = new Lang.Class({
|
||||
|
||||
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) {
|
||||
let text = o.get_text();
|
||||
let prefix;
|
||||
@ -282,8 +136,6 @@ const RunDialog = new Lang.Class({
|
||||
if (postfix != null && postfix.length > 0) {
|
||||
o.insert_text(postfix, -1);
|
||||
o.set_cursor_position(text.length + postfix.length);
|
||||
if (postfix[postfix.length - 1] == '/')
|
||||
this._getCompletion(text + postfix + 'a');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -291,11 +143,53 @@ 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) {
|
||||
if (text.indexOf('/') != -1) {
|
||||
return this._pathCompleter.get_completion_suffix(text);
|
||||
} else {
|
||||
return this._commandCompleter.getCompletion(text);
|
||||
return this._getCommandCompletion(text);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -38,11 +38,7 @@ const ARROW_DRAG_THRESHOLD = 0.1;
|
||||
const N_ARROWS = 3;
|
||||
const ARROW_ANIMATION_TIME = 0.6;
|
||||
const ARROW_ANIMATION_PEAK_OPACITY = 0.4;
|
||||
|
||||
// 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 ARROW_IDLE_TIME = 30000; // ms
|
||||
|
||||
const SUMMARY_ICON_SIZE = 48;
|
||||
|
||||
@ -435,8 +431,8 @@ const ScreenShield = new Lang.Class({
|
||||
name: 'lockScreenGroup',
|
||||
visible: false,
|
||||
});
|
||||
this._lockScreenGroup.connect('key-release-event',
|
||||
Lang.bind(this, this._onLockScreenKeyRelease));
|
||||
this._lockScreenGroup.connect('key-press-event',
|
||||
Lang.bind(this, this._onLockScreenKeyPress));
|
||||
this._lockScreenGroup.connect('scroll-event',
|
||||
Lang.bind(this, this._onLockScreenScroll));
|
||||
Main.ctrlAltTabManager.addGroup(this._lockScreenGroup, _("Lock"), 'changes-prevent-symbolic');
|
||||
@ -456,6 +452,9 @@ const ScreenShield = new Lang.Class({
|
||||
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',
|
||||
vertical: true,
|
||||
x_align: Clutter.ActorAlign.CENTER,
|
||||
@ -507,6 +506,7 @@ const ScreenShield = new Lang.Class({
|
||||
this._screenSaverDBus = new ShellDBus.ScreenSaverDBus(this);
|
||||
|
||||
this._inhibitor = null;
|
||||
this._aboutToSuspend = false;
|
||||
this._loginManager = LoginManager.getLoginManager();
|
||||
this._loginManager.connect('prepare-for-sleep',
|
||||
Lang.bind(this, this._prepareForSleep));
|
||||
@ -590,28 +590,28 @@ const ScreenShield = new Lang.Class({
|
||||
return this._isModal;
|
||||
},
|
||||
|
||||
_onLockScreenKeyRelease: function(actor, event) {
|
||||
_onLockScreenKeyPress: function(actor, event) {
|
||||
let symbol = event.get_key_symbol();
|
||||
let unichar = event.get_key_unicode();
|
||||
|
||||
// Do nothing if the lock screen is not fully shown.
|
||||
// This avoids reusing the previous (and stale) unlock
|
||||
// dialog if esc is pressed while the curtain is going
|
||||
// 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)
|
||||
return false;
|
||||
|
||||
if (symbol == Clutter.KEY_Escape ||
|
||||
symbol == Clutter.KEY_Return ||
|
||||
symbol == Clutter.KEY_KP_Enter) {
|
||||
this._liftShield(false, 0);
|
||||
return true;
|
||||
}
|
||||
let isEnter = (symbol == Clutter.KEY_Return || symbol == Clutter.KEY_KP_Enter);
|
||||
if (!isEnter && !(GLib.unichar_isprint(unichar) || symbol == Clutter.KEY_Escape))
|
||||
return false;
|
||||
|
||||
this._bumpLockScreen();
|
||||
this._ensureUnlockDialog(true, true);
|
||||
|
||||
if (GLib.unichar_isgraph(unichar))
|
||||
this._dialog.addCharacter(unichar);
|
||||
|
||||
this._liftShield(true, 0);
|
||||
return true;
|
||||
},
|
||||
|
||||
@ -649,6 +649,8 @@ const ScreenShield = new Lang.Class({
|
||||
},
|
||||
|
||||
_prepareForSleep: function(loginManager, aboutToSuspend) {
|
||||
this._aboutToSuspend = aboutToSuspend;
|
||||
|
||||
if (aboutToSuspend) {
|
||||
if (!this._settings.get_boolean(LOCK_ENABLED_KEY)) {
|
||||
this._uninhibitSuspend();
|
||||
@ -849,21 +851,6 @@ const ScreenShield = new Lang.Class({
|
||||
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() {
|
||||
if (Main.sessionMode.currentMode == 'lock-screen')
|
||||
Main.sessionMode.popMode('lock-screen');
|
||||
@ -917,14 +904,12 @@ const ScreenShield = new Lang.Class({
|
||||
|
||||
|
||||
let time = global.get_current_time();
|
||||
this._dialog.connect('loaded', Lang.bind(this, function() {
|
||||
if (!this._dialog.open(time, onPrimary)) {
|
||||
// This is kind of an impossible error: we're already modal
|
||||
// by the time we reach this...
|
||||
log('Could not open login dialog: failed to acquire grab');
|
||||
this.deactivate(true);
|
||||
}
|
||||
}));
|
||||
if (!this._dialog.open(time, onPrimary)) {
|
||||
// This is kind of an impossible error: we're already modal
|
||||
// by the time we reach this...
|
||||
log('Could not open login dialog: failed to acquire grab');
|
||||
this.deactivate(true);
|
||||
}
|
||||
|
||||
this._dialog.connect('failed', Lang.bind(this, this._onUnlockFailed));
|
||||
this._dialog.connect('unlocked', Lang.bind(this, this._onUnlockSucceded));
|
||||
@ -990,16 +975,60 @@ const ScreenShield = new Lang.Class({
|
||||
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() {
|
||||
if (this._dialog && !this._isGreeter) {
|
||||
this._dialog.destroy();
|
||||
this._dialog = null;
|
||||
}
|
||||
|
||||
if (this._arrowAnimationId)
|
||||
Mainloop.source_remove(this._arrowAnimationId);
|
||||
this._arrowAnimationId = Mainloop.timeout_add(6000, Lang.bind(this, this._animateArrows));
|
||||
this._animateArrows();
|
||||
this._checkArrowAnimation();
|
||||
|
||||
let motionId = global.stage.connect('captured-event', function(stage, event) {
|
||||
if (event.type() == Clutter.EventType.MOTION) {
|
||||
@ -1021,7 +1050,8 @@ const ScreenShield = new Lang.Class({
|
||||
if (prevIsActive != this._isActive)
|
||||
this.emit('active-changed');
|
||||
|
||||
this._uninhibitSuspend();
|
||||
if (this._aboutToSuspend)
|
||||
this._uninhibitSuspend();
|
||||
|
||||
this.emit('lock-screen-shown');
|
||||
},
|
||||
@ -1061,12 +1091,9 @@ const ScreenShield = new Lang.Class({
|
||||
this._notificationsBox = null;
|
||||
}
|
||||
|
||||
this._lockScreenContentsBox.destroy();
|
||||
this._stopArrowAnimation();
|
||||
|
||||
if (this._arrowAnimationId) {
|
||||
Mainloop.source_remove(this._arrowAnimationId);
|
||||
this._arrowAnimationId = 0;
|
||||
}
|
||||
this._lockScreenContentsBox.destroy();
|
||||
|
||||
this._hasLockScreen = false;
|
||||
},
|
||||
@ -1178,62 +1205,3 @@ const ScreenShield = new Lang.Class({
|
||||
},
|
||||
});
|
||||
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,6 +4,7 @@ const Clutter = imports.gi.Clutter;
|
||||
const Lang = imports.lang;
|
||||
const Gtk = imports.gi.Gtk;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Signals = imports.signals;
|
||||
const St = imports.gi.St;
|
||||
const Atk = imports.gi.Atk;
|
||||
|
||||
@ -13,6 +14,7 @@ const Main = imports.ui.main;
|
||||
const Overview = imports.ui.overview;
|
||||
const Separator = imports.ui.separator;
|
||||
const Search = imports.ui.search;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const MAX_LIST_SEARCH_RESULTS_ROWS = 3;
|
||||
const MAX_GRID_SEARCH_RESULTS_ROWS = 1;
|
||||
@ -102,6 +104,7 @@ const ListSearchResult = new Lang.Class({
|
||||
y_fill: false,
|
||||
x_align: St.Align.START,
|
||||
y_align: St.Align.START });
|
||||
this.actor.label_actor = title;
|
||||
|
||||
if (this.metaInfo['description']) {
|
||||
let description = new St.Label({ style_class: 'list-search-result-description' });
|
||||
@ -139,6 +142,7 @@ const GridSearchResult = new Lang.Class({
|
||||
}
|
||||
|
||||
this.actor.set_child(content);
|
||||
this.actor.label_actor = content.label_actor;
|
||||
|
||||
let draggable = DND.makeDraggable(this.actor);
|
||||
draggable.connect('drag-begin',
|
||||
@ -227,9 +231,14 @@ const ListSearchResults = new Lang.Class({
|
||||
this._pendingClear = true;
|
||||
},
|
||||
|
||||
_keyFocusIn: function(icon) {
|
||||
this.emit('key-focus-in', icon);
|
||||
},
|
||||
|
||||
renderResults: function(metas) {
|
||||
for (let i = 0; i < metas.length; i++) {
|
||||
let display = new ListSearchResult(this.provider, metas[i], this._terms);
|
||||
display.actor.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
|
||||
this._content.add_actor(display.actor);
|
||||
}
|
||||
},
|
||||
@ -246,6 +255,7 @@ const ListSearchResults = new Lang.Class({
|
||||
return null;
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(ListSearchResults.prototype);
|
||||
|
||||
const GridSearchResults = new Lang.Class({
|
||||
Name: 'GridSearchResults',
|
||||
@ -288,9 +298,14 @@ const GridSearchResults = new Lang.Class({
|
||||
this._pendingClear = true;
|
||||
},
|
||||
|
||||
_keyFocusIn: function(icon) {
|
||||
this.emit('key-focus-in', icon);
|
||||
},
|
||||
|
||||
renderResults: function(metas) {
|
||||
for (let i = 0; i < metas.length; i++) {
|
||||
let display = new GridSearchResult(this.provider, metas[i], this._terms);
|
||||
display.actor.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
|
||||
this._grid.addItem(display.actor);
|
||||
}
|
||||
},
|
||||
@ -307,6 +322,7 @@ const GridSearchResults = new Lang.Class({
|
||||
return null;
|
||||
}
|
||||
});
|
||||
Signals.addSignalMethods(GridSearchResults.prototype);
|
||||
|
||||
const SearchResults = new Lang.Class({
|
||||
Name: 'SearchResults',
|
||||
@ -366,6 +382,10 @@ const SearchResults = new Lang.Class({
|
||||
return false;
|
||||
},
|
||||
|
||||
_keyFocusIn: function(provider, icon) {
|
||||
Util.ensureActorVisibleInScrollView(this._scrollView, icon);
|
||||
},
|
||||
|
||||
createProviderMeta: function(provider) {
|
||||
let providerBox = new St.BoxLayout({ style_class: 'search-section',
|
||||
vertical: true });
|
||||
@ -379,6 +399,8 @@ const SearchResults = new Lang.Class({
|
||||
resultDisplay = new GridSearchResults(provider);
|
||||
}
|
||||
|
||||
resultDisplay.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
|
||||
|
||||
let resultDisplayBin = new St.Bin({ child: resultDisplay.actor,
|
||||
x_fill: true,
|
||||
y_fill: true });
|
||||
@ -557,6 +579,7 @@ const ProviderIcon = new Lang.Class({
|
||||
this.parent({ style_class: 'search-provider-icon',
|
||||
reactive: true,
|
||||
can_focus: true,
|
||||
accessible_name: provider.appInfo.get_name(),
|
||||
track_hover: true });
|
||||
|
||||
this._content = new St.Widget({ layout_manager: new Clutter.BinLayout() });
|
||||
|
@ -158,12 +158,7 @@ function listModes() {
|
||||
const SessionMode = new Lang.Class({
|
||||
Name: 'SessionMode',
|
||||
|
||||
_init: function() {
|
||||
global.connect('notify::session-mode', Lang.bind(this, this._sync));
|
||||
this._modes = _modes;
|
||||
this._modeStack = [DEFAULT_MODE];
|
||||
this._sync();
|
||||
|
||||
init: function() {
|
||||
_getModes(Lang.bind(this, function(modes) {
|
||||
this._modes = modes;
|
||||
let primary = modes[global.session_mode] &&
|
||||
@ -171,6 +166,8 @@ const SessionMode = new Lang.Class({
|
||||
let mode = primary ? global.session_mode : 'user';
|
||||
this._modeStack = [mode];
|
||||
this._sync();
|
||||
|
||||
this.emit('sessions-loaded');
|
||||
}));
|
||||
},
|
||||
|
||||
|
@ -337,14 +337,14 @@ const InputSourceIndicator = new Lang.Class({
|
||||
Main.wm.addKeybinding('switch-input-source',
|
||||
new Gio.Settings({ schema: "org.gnome.desktop.wm.keybindings" }),
|
||||
Meta.KeyBindingFlags.REVERSES,
|
||||
Shell.KeyBindingMode.ALL,
|
||||
Shell.KeyBindingMode.ALL & ~Shell.KeyBindingMode.MESSAGE_TRAY,
|
||||
Lang.bind(this, this._switchInputSource));
|
||||
this._keybindingActionBackward =
|
||||
Main.wm.addKeybinding('switch-input-source-backward',
|
||||
new Gio.Settings({ schema: "org.gnome.desktop.wm.keybindings" }),
|
||||
Meta.KeyBindingFlags.REVERSES |
|
||||
Meta.KeyBindingFlags.REVERSED,
|
||||
Shell.KeyBindingMode.ALL,
|
||||
Shell.KeyBindingMode.ALL & ~Shell.KeyBindingMode.MESSAGE_TRAY,
|
||||
Lang.bind(this, this._switchInputSource));
|
||||
this._settings = new Gio.Settings({ schema: DESKTOP_INPUT_SOURCES_SCHEMA });
|
||||
this._settings.connect('changed::' + KEY_CURRENT_INPUT_SOURCE, Lang.bind(this, this._currentInputSourceChanged));
|
||||
@ -416,6 +416,11 @@ const InputSourceIndicator = new Lang.Class({
|
||||
let oldSource;
|
||||
[oldSource, this._currentSource] = [this._currentSource, newSource];
|
||||
|
||||
if (oldSource) {
|
||||
oldSource.menuItem.setShowDot(false);
|
||||
oldSource.indicatorLabel.hide();
|
||||
}
|
||||
|
||||
if (!newSource || (nVisibleSources < 2 && !newSource.properties)) {
|
||||
// This source index might be invalid if we weren't able
|
||||
// to build a menu item for it, so we hide ourselves since
|
||||
@ -430,11 +435,6 @@ const InputSourceIndicator = new Lang.Class({
|
||||
|
||||
this.actor.show();
|
||||
|
||||
if (oldSource) {
|
||||
oldSource.menuItem.setShowDot(false);
|
||||
oldSource.indicatorLabel.hide();
|
||||
}
|
||||
|
||||
newSource.menuItem.setShowDot(true);
|
||||
newSource.indicatorLabel.show();
|
||||
|
||||
@ -487,7 +487,7 @@ const InputSourceIndicator = new Lang.Class({
|
||||
let is = new InputSource(type, id, displayName, shortName, i);
|
||||
|
||||
is.connect('activate', Lang.bind(this, function() {
|
||||
if (this._currentSource.index == is.index)
|
||||
if (this._currentSource && this._currentSource.index == is.index)
|
||||
return;
|
||||
this._settings.set_value(KEY_CURRENT_INPUT_SOURCE,
|
||||
GLib.Variant.new_uint32(is.index));
|
||||
@ -703,7 +703,7 @@ const InputSourceIndicator = new Lang.Class({
|
||||
item.prop = prop;
|
||||
item.connect('activate', Lang.bind(this, function() {
|
||||
this._ibusManager.activateProperty(item.prop.get_key(),
|
||||
IBus.PropState.CHECKED);
|
||||
item.prop.get_state());
|
||||
}));
|
||||
break;
|
||||
|
||||
|
@ -27,35 +27,6 @@ const LoginDialog = imports.gdm.loginDialog;
|
||||
// The timeout before going back automatically to the lock screen (in seconds)
|
||||
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({
|
||||
Name: 'UnlockDialog',
|
||||
Extends: ModalDialog.ModalDialog,
|
||||
@ -76,6 +47,7 @@ const UnlockDialog = new Lang.Class({
|
||||
|
||||
this._greeterClient = new Gdm.Client();
|
||||
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('show-message', Lang.bind(this, this._showMessage));
|
||||
@ -98,6 +70,7 @@ const UnlockDialog = new Lang.Class({
|
||||
|
||||
this._promptEntry = new St.Entry({ style_class: 'login-dialog-prompt-entry',
|
||||
can_focus: true });
|
||||
this._promptEntry.clutter_text.connect('activate', Lang.bind(this, this._doUnlock));
|
||||
this._promptEntry.clutter_text.set_password_char('\u25cf');
|
||||
ShellEntry.addContextMenu(this._promptEntry, { isPassword: true });
|
||||
this.setInitialKeyFocus(this._promptEntry);
|
||||
@ -169,11 +142,6 @@ const UnlockDialog = new Lang.Class({
|
||||
let batch = new Batch.Hold();
|
||||
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');
|
||||
|
||||
this._idleMonitor = new GnomeDesktop.IdleMonitor();
|
||||
@ -282,18 +250,35 @@ const UnlockDialog = new Lang.Class({
|
||||
this._userVerifier.answerQuery(query, this._promptEntry.text);
|
||||
},
|
||||
|
||||
_onVerificationComplete: function() {
|
||||
_finishUnlock: function() {
|
||||
this._userVerifier.clear();
|
||||
this.emit('unlocked');
|
||||
},
|
||||
|
||||
_onVerificationComplete: function() {
|
||||
this._userVerified = true;
|
||||
if (!this._userVerifier.hasPendingMessages) {
|
||||
this._finishUnlock();
|
||||
} else {
|
||||
let signalId = this._userVerifier.connect('no-more-messages',
|
||||
Lang.bind(this, function() {
|
||||
this._userVerifier.disconnect(signalId);
|
||||
this._finishUnlock();
|
||||
}));
|
||||
}
|
||||
},
|
||||
|
||||
_onReset: function() {
|
||||
this.emit('failed');
|
||||
if (!this._userVerified) {
|
||||
this._userVerifier.clear();
|
||||
this.emit('failed');
|
||||
}
|
||||
},
|
||||
|
||||
_onVerificationFailed: function() {
|
||||
this._currentQuery = null;
|
||||
this._firstQuestion = true;
|
||||
this._userVerified = false;
|
||||
|
||||
this._promptEntry.text = '';
|
||||
this._promptEntry.clutter_text.set_password_char('\u25cf');
|
||||
@ -333,4 +318,8 @@ const UnlockDialog = new Lang.Class({
|
||||
|
||||
this.destroy();
|
||||
},
|
||||
|
||||
addCharacter: function(unichar) {
|
||||
this._promptEntry.clutter_text.insert_unichar(unichar);
|
||||
},
|
||||
});
|
||||
|
@ -600,7 +600,8 @@ const UserMenuButton = new Lang.Class({
|
||||
Lang.bind(this, this._updateHaveShutdown));
|
||||
|
||||
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
|
||||
Main.screenShield.connect('locked-changed', Lang.bind(this, this._updatePresenceIcon));
|
||||
if (Main.screenShield)
|
||||
Main.screenShield.connect('locked-changed', Lang.bind(this, this._updatePresenceIcon));
|
||||
this._sessionUpdated();
|
||||
},
|
||||
|
||||
@ -656,7 +657,7 @@ const UserMenuButton = new Lang.Class({
|
||||
|
||||
_updateLockScreen: function() {
|
||||
let allowLockScreen = !this._lockdownSettings.get_boolean(DISABLE_LOCK_SCREEN_KEY);
|
||||
this._lockScreenItem.actor.visible = allowLockScreen;
|
||||
this._lockScreenItem.actor.visible = allowLockScreen && LoginManager.canLock();
|
||||
},
|
||||
|
||||
_updateInstallUpdates: function() {
|
||||
@ -864,7 +865,8 @@ const UserMenuButton = new Lang.Class({
|
||||
_onLoginScreenActivate: function() {
|
||||
this.menu.close(BoxPointer.PopupAnimation.NONE);
|
||||
Main.overview.hide();
|
||||
Main.screenShield.lock(false);
|
||||
if (Main.screenShield)
|
||||
Main.screenShield.lock(false);
|
||||
Gdm.goto_login_session_sync(null);
|
||||
},
|
||||
|
||||
@ -915,8 +917,10 @@ const UserMenuButton = new Lang.Class({
|
||||
session.user.get_real_name() : session.username;
|
||||
|
||||
if (session.info.remote)
|
||||
/* Translators: Remote here refers to a remote session, like a ssh login */
|
||||
userLabelText = _("%s (remote)").format(userName);
|
||||
else if (session.info.type == "tty")
|
||||
/* Translators: Console here refers to a tty like a VT console */
|
||||
userLabelText = _("%s (console)").format(userName);
|
||||
else
|
||||
userLabelText = userName;
|
||||
|
@ -11,12 +11,14 @@ const Layout = imports.ui.layout;
|
||||
const Main = imports.ui.main;
|
||||
const Panel = imports.ui.panel;
|
||||
|
||||
// we could make these gsettings
|
||||
const FISH_NAME = 'wanda';
|
||||
const FISH_FILENAME = 'wanda.png';
|
||||
const FISH_SPEED = 300;
|
||||
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 MAGIC_FISH_KEY = 'free the fish';
|
||||
@ -26,33 +28,16 @@ const WandaIcon = new Lang.Class({
|
||||
Extends: IconGrid.BaseIcon,
|
||||
|
||||
_init : function(fish, label, params) {
|
||||
this._fish = fish;
|
||||
let file = GLib.build_filenamev([global.datadir, GNOME_PANEL_PIXMAPDIR, fish + '.fish']);
|
||||
|
||||
if (GLib.file_test(file, GLib.FileTest.EXISTS)) {
|
||||
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);
|
||||
|
||||
this._fish = fish;
|
||||
this._imageFile = GLib.build_filenamev([global.datadir, fish + '.png']);
|
||||
|
||||
this._imgHeight = FISH_HEIGHT;
|
||||
this._imgWidth = FISH_WIDTH;
|
||||
},
|
||||
|
||||
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.play();
|
||||
return this._animations.actor;
|
||||
|
@ -121,15 +121,33 @@ const WindowClone = new Lang.Class({
|
||||
return this._slot;
|
||||
},
|
||||
|
||||
// Find the actor just below us, respecting reparenting done
|
||||
// by DND code
|
||||
getActualStackAbove: function() {
|
||||
if (this._stackAbove == null)
|
||||
return null;
|
||||
|
||||
if (this.inDrag || this._zooming) {
|
||||
if (this._stackAbove._delegate)
|
||||
return this._stackAbove._delegate.getActualStackAbove();
|
||||
else
|
||||
return null;
|
||||
} else {
|
||||
return this._stackAbove;
|
||||
}
|
||||
},
|
||||
|
||||
setStackAbove: function (actor) {
|
||||
this._stackAbove = actor;
|
||||
if (this.inDrag)
|
||||
// We'll fix up the stack after the drag
|
||||
return;
|
||||
if (this._stackAbove == null)
|
||||
|
||||
let actualAbove = this.getActualStackAbove();
|
||||
if (actualAbove == null)
|
||||
this.actor.lower_bottom();
|
||||
else
|
||||
this.actor.raise(this._stackAbove);
|
||||
this.actor.raise(actualAbove);
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
@ -908,7 +926,7 @@ const Workspace = new Lang.Class({
|
||||
this._windowOverlays = [];
|
||||
for (let i = 0; i < windows.length; i++) {
|
||||
if (this._isOverviewWindow(windows[i])) {
|
||||
this._addWindowClone(windows[i]);
|
||||
this._addWindowClone(windows[i], true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1033,11 +1051,23 @@ const Workspace = new Lang.Class({
|
||||
if (clone.inDrag)
|
||||
continue;
|
||||
|
||||
clone.slot = [x, y, clone.actor.width * scale, clone.actor.height * scale];
|
||||
let cloneWidth = clone.actor.width * scale;
|
||||
let cloneHeight = clone.actor.height * scale;
|
||||
clone.slot = [x, y, cloneWidth, cloneHeight];
|
||||
|
||||
if (overlay && initialPositioning)
|
||||
if (overlay && (initialPositioning || !clone.positioned))
|
||||
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 (!metaWindow.showing_on_its_workspace()) {
|
||||
/* Hidden windows should fade in and grow
|
||||
@ -1223,7 +1253,7 @@ const Workspace = new Lang.Class({
|
||||
if (!this._isMyWindow(win) || !this._isOverviewWindow(win))
|
||||
return;
|
||||
|
||||
let [clone, overlay] = this._addWindowClone(win);
|
||||
let [clone, overlay] = this._addWindowClone(win, false);
|
||||
|
||||
if (win._overviewHint) {
|
||||
let x = win._overviewHint.x - this.actor.x;
|
||||
@ -1232,16 +1262,10 @@ const Workspace = new Lang.Class({
|
||||
delete win._overviewHint;
|
||||
|
||||
clone.slot = [x, y, clone.actor.width * scale, clone.actor.height * scale];
|
||||
clone.positioned = true;
|
||||
clone.actor.set_position (x, y);
|
||||
clone.actor.set_scale (scale, scale);
|
||||
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;
|
||||
@ -1388,10 +1412,11 @@ const Workspace = new Lang.Class({
|
||||
},
|
||||
|
||||
// Create a clone of a (non-desktop) window and add it to the window list
|
||||
_addWindowClone : function(win) {
|
||||
_addWindowClone : function(win, positioned) {
|
||||
let clone = new WindowClone(win, this);
|
||||
let overlay = new WindowOverlay(clone, this._windowOverlaysGroup);
|
||||
clone.overlay = overlay;
|
||||
clone.positioned = positioned;
|
||||
|
||||
clone.connect('selected',
|
||||
Lang.bind(this, this._onCloneSelected));
|
||||
|
@ -62,12 +62,35 @@ const WindowClone = new Lang.Class({
|
||||
this.inDrag = false;
|
||||
},
|
||||
|
||||
// Find the actor just below us, respecting reparenting done
|
||||
// by DND code
|
||||
getActualStackAbove: function() {
|
||||
if (this._stackAbove == null)
|
||||
return null;
|
||||
|
||||
if (this.inDrag) {
|
||||
if (this._stackAbove._delegate)
|
||||
return this._stackAbove._delegate.getActualStackAbove();
|
||||
else
|
||||
return null;
|
||||
} else {
|
||||
return this._stackAbove;
|
||||
}
|
||||
},
|
||||
|
||||
setStackAbove: function (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();
|
||||
else
|
||||
this.actor.raise(this._stackAbove);
|
||||
this.actor.raise(actualAbove);
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
@ -535,6 +558,7 @@ const ThumbnailsBox = new Lang.Class({
|
||||
this._dropPlaceholderPos = -1;
|
||||
this._dropPlaceholder = new St.Bin({ style_class: 'placeholder' });
|
||||
this.actor.add_actor(this._dropPlaceholder);
|
||||
this._spliceIndex = -1;
|
||||
|
||||
this._targetScale = 0;
|
||||
this._scale = 0;
|
||||
@ -717,6 +741,8 @@ const ThumbnailsBox = new Lang.Class({
|
||||
return win.get_workspace() >= newWorkspaceIndex;
|
||||
});
|
||||
|
||||
this._spliceIndex = newWorkspaceIndex;
|
||||
|
||||
// ... move them down one.
|
||||
windows.forEach(function(win) {
|
||||
win.meta_window.change_workspace_by_index(win.get_workspace() + 1,
|
||||
@ -738,6 +764,14 @@ const ThumbnailsBox = new Lang.Class({
|
||||
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;
|
||||
} else {
|
||||
return false;
|
||||
@ -825,7 +859,8 @@ const ThumbnailsBox = new Lang.Class({
|
||||
this._thumbnails.push(thumbnail);
|
||||
this.actor.add_actor(thumbnail.actor);
|
||||
|
||||
if (start > 0) { // not the initial fill
|
||||
if (start > 0 && this._spliceIndex == -1) {
|
||||
// not the initial fill, and not splicing via DND
|
||||
thumbnail.state = ThumbnailState.NEW;
|
||||
thumbnail.slidePosition = 1; // start slid out
|
||||
this._haveNewThumbnails = true;
|
||||
@ -840,6 +875,9 @@ const ThumbnailsBox = new Lang.Class({
|
||||
|
||||
// The thumbnails indicator actually needs to be on top of the thumbnails
|
||||
this._indicator.raise_top();
|
||||
|
||||
// Clear the splice index, we got the message
|
||||
this._spliceIndex = -1;
|
||||
},
|
||||
|
||||
removeThumbnails: function(start, count) {
|
||||
|
@ -386,6 +386,15 @@ const WorkspacesView = new Lang.Class({
|
||||
let current = Math.round(adj.value);
|
||||
|
||||
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;
|
||||
metaWorkspace.activate(global.get_current_time());
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
275
po/et.po
275
po/et.po
@ -13,8 +13,8 @@ msgstr ""
|
||||
"Project-Id-Version: gnome-shell MASTER\n"
|
||||
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
|
||||
"shell&keywords=I18N+L10N&component=general\n"
|
||||
"POT-Creation-Date: 2012-12-09 13:03+0000\n"
|
||||
"PO-Revision-Date: 2012-12-19 16:47+0300\n"
|
||||
"POT-Creation-Date: 2013-03-11 22:02+0000\n"
|
||||
"PO-Revision-Date: 2013-03-12 16:28+0300\n"
|
||||
"Last-Translator: Mattias Põldaru <mahfiaz@gmail.com>\n"
|
||||
"Language-Team: Estonian <>\n"
|
||||
"Language: et\n"
|
||||
@ -104,6 +104,16 @@ msgid ""
|
||||
"favorites area."
|
||||
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"
|
||||
msgstr "Käsudialoogi (Alt-F2) ajalugu"
|
||||
|
||||
@ -135,12 +145,6 @@ msgstr ""
|
||||
"See võti keelab automaatse 'Logi välja' menüükirje peitmise, kui arvutis on "
|
||||
"üks kasutaja ning avatud üks seanss."
|
||||
|
||||
msgid "Show full name in the user menu"
|
||||
msgstr "Kasutajamenüüs näidatakse kasutaja tervet nime"
|
||||
|
||||
msgid "Whether the users full name is shown in the user menu or not."
|
||||
msgstr "Kas kasutajamenüüs näidatakse kasutaja kogu nime või mitte."
|
||||
|
||||
msgid ""
|
||||
"Whether to remember password for mounting encrypted or remote filesystems"
|
||||
msgstr ""
|
||||
@ -299,12 +303,8 @@ msgstr "Laiendus"
|
||||
msgid "Select an extension to configure using the combobox above."
|
||||
msgstr "Vali seadistatav laiendus kasutades ülemist valikukasti."
|
||||
|
||||
msgid "Session..."
|
||||
msgstr "Seanss..."
|
||||
|
||||
msgctxt "title"
|
||||
msgid "Sign In"
|
||||
msgstr "Sisselogimine"
|
||||
msgid "Session…"
|
||||
msgstr "Seanss…"
|
||||
|
||||
#. translators: this message is shown below the user list on the
|
||||
#. login screen. It can be activated to reveal an entry for
|
||||
@ -368,16 +368,12 @@ msgstr "Käsku pole võimalik analüüsida:"
|
||||
msgid "Execution of '%s' failed:"
|
||||
msgstr "'%s' käivitamine nurjus:"
|
||||
|
||||
#. Translators: Filter to display all applications
|
||||
msgid "Frequent"
|
||||
msgstr "Sage"
|
||||
|
||||
msgid "All"
|
||||
msgstr "Kõik"
|
||||
|
||||
msgid "APPLICATIONS"
|
||||
msgstr "RAKENDUSED"
|
||||
|
||||
msgid "SETTINGS"
|
||||
msgstr "SEADED"
|
||||
|
||||
msgid "New Window"
|
||||
msgstr "Uus aken"
|
||||
|
||||
@ -395,6 +391,12 @@ msgstr "%s lisati lemmikutesse."
|
||||
msgid "%s has been removed from your favorites."
|
||||
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
|
||||
#. * Keep it short, best if you can use less then 10 characters
|
||||
#.
|
||||
@ -402,15 +404,18 @@ msgctxt "event list time"
|
||||
msgid "All Day"
|
||||
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"
|
||||
msgid "%H:%M"
|
||||
msgstr "%H:%M"
|
||||
msgid "%H\\u2236%M"
|
||||
msgstr "%H\\u2236%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"
|
||||
msgid "%l:%M %p"
|
||||
msgstr "%l:%M %p"
|
||||
msgid "%l\\u2236%M\\u2009%p"
|
||||
msgstr "%l\\u2236%M\\u2009%p"
|
||||
|
||||
#. Translators: Calendar grid abbreviation for Sunday.
|
||||
#. *
|
||||
@ -517,6 +522,12 @@ msgstr "Käesolev nädal"
|
||||
msgid "Next week"
|
||||
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"
|
||||
msgstr "Eemaldatavad seadmed"
|
||||
|
||||
@ -801,12 +812,20 @@ msgstr "Aknad"
|
||||
msgid "Show Applications"
|
||||
msgstr "Rakenduste kuvamine"
|
||||
|
||||
msgid "Date and Time Settings"
|
||||
msgstr "Kuupäeva ja kellaaja sätted"
|
||||
#. Translators: this is the name of the dock/favorites area on
|
||||
#. the left of the overview
|
||||
msgid "Dash"
|
||||
msgstr "Dokk"
|
||||
|
||||
msgid "Open Calendar"
|
||||
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
|
||||
#. * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
|
||||
#.
|
||||
@ -897,9 +916,6 @@ msgstr "Paigalda"
|
||||
msgid "Download and install '%s' from extensions.gnome.org?"
|
||||
msgstr "Kas laadida alla ja paigaldada '%s' aadressilt extensions.gnome.org?"
|
||||
|
||||
msgid "tray"
|
||||
msgstr "salv"
|
||||
|
||||
msgid "Keyboard"
|
||||
msgstr "Klaviatuur"
|
||||
|
||||
@ -920,6 +936,8 @@ msgstr "Näita vigu"
|
||||
msgid "Enabled"
|
||||
msgstr "Lubatud"
|
||||
|
||||
#. translators:
|
||||
#. * The device has been disabled
|
||||
msgid "Disabled"
|
||||
msgstr "Keelatud"
|
||||
|
||||
@ -944,6 +962,12 @@ msgstr "Ava"
|
||||
msgid "Remove"
|
||||
msgstr "Eemalda"
|
||||
|
||||
msgid "Clear Messages"
|
||||
msgstr "Kustuta teated"
|
||||
|
||||
msgid "Notification Settings"
|
||||
msgstr "Märguannete sätted"
|
||||
|
||||
msgid "No Messages"
|
||||
msgstr "Teateid pole"
|
||||
|
||||
@ -957,6 +981,12 @@ msgctxt "program"
|
||||
msgid "Unknown"
|
||||
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"
|
||||
msgstr "Võta tagasi"
|
||||
|
||||
@ -967,13 +997,8 @@ msgstr "Ülevaade"
|
||||
#. in the search entry when no search is
|
||||
#. active; it should not exceed ~30
|
||||
#. characters.
|
||||
msgid "Type to search..."
|
||||
msgstr "Otsing..."
|
||||
|
||||
#. Translators: this is the name of the dock/favorites area on
|
||||
#. the left of the overview
|
||||
msgid "Dash"
|
||||
msgstr "Dokk"
|
||||
msgid "Type to search…"
|
||||
msgstr "Otsing…"
|
||||
|
||||
msgid "Quit"
|
||||
msgstr "Sulge"
|
||||
@ -1005,12 +1030,6 @@ msgstr "Sulge"
|
||||
msgid "%A, %B %d"
|
||||
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
|
||||
msgid "%d new notification"
|
||||
msgid_plural "%d new notifications"
|
||||
@ -1020,8 +1039,24 @@ msgstr[1] "%d uut märguannet"
|
||||
msgid "Lock"
|
||||
msgstr "Lukusta"
|
||||
|
||||
msgid "Searching..."
|
||||
msgstr "Otsimine..."
|
||||
msgid "GNOME needs to lock the screen"
|
||||
msgstr "GNOME peab ekraani lukustama"
|
||||
|
||||
#. 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."
|
||||
msgstr "Tulemused puuduvad."
|
||||
@ -1089,11 +1124,11 @@ msgstr "Bluetooth"
|
||||
msgid "Visibility"
|
||||
msgstr "Nähtavus"
|
||||
|
||||
msgid "Send Files to Device..."
|
||||
msgstr "Failide saatmine seadmesse..."
|
||||
msgid "Send Files to Device…"
|
||||
msgstr "Failide saatmine seadmesse…"
|
||||
|
||||
msgid "Set up a New Device..."
|
||||
msgstr "Uue seadme häälestamine..."
|
||||
msgid "Set Up a New Device…"
|
||||
msgstr "Uue seadme häälestamine…"
|
||||
|
||||
msgid "Bluetooth Settings"
|
||||
msgstr "Bluetoothi sätted"
|
||||
@ -1111,8 +1146,8 @@ msgstr "ühenduse katkestamine..."
|
||||
msgid "connecting..."
|
||||
msgstr "ühendumine..."
|
||||
|
||||
msgid "Send Files..."
|
||||
msgstr "Failide saatmine..."
|
||||
msgid "Send Files…"
|
||||
msgstr "Failide saatmine…"
|
||||
|
||||
msgid "Keyboard Settings"
|
||||
msgstr "Klaviatuurisätted"
|
||||
@ -1173,7 +1208,7 @@ msgstr "Olgu"
|
||||
msgid "Show Keyboard Layout"
|
||||
msgstr "Klaviatuuripaigutuse kuvamine"
|
||||
|
||||
msgid "Region and Language Settings"
|
||||
msgid "Region & Language Settings"
|
||||
msgstr "Asukoha ja keele sätted"
|
||||
|
||||
msgid "Volume, network, battery"
|
||||
@ -1212,17 +1247,23 @@ msgstr "pole saadaval"
|
||||
msgid "connection failed"
|
||||
msgstr "ühendumine nurjus"
|
||||
|
||||
msgid "More..."
|
||||
msgstr "Veel..."
|
||||
msgid "More…"
|
||||
msgstr "Veel…"
|
||||
|
||||
#. TRANSLATORS: this is the indication that a connection for another logged in user is active,
|
||||
#. and we cannot access its settings (including the name)
|
||||
msgid "Connected (private)"
|
||||
msgstr "Ühendatud (privaatne)"
|
||||
|
||||
msgid "Wired"
|
||||
msgstr "Juhtmega"
|
||||
|
||||
msgid "Auto Ethernet"
|
||||
msgstr "Automaatne ethernet"
|
||||
|
||||
msgid "Mobile broadband"
|
||||
msgstr "Mobiiliühendus"
|
||||
|
||||
msgid "Auto broadband"
|
||||
msgstr "Automaatne lairibaühendus"
|
||||
|
||||
@ -1269,8 +1310,8 @@ msgstr "Toitesätted..."
|
||||
|
||||
#. 0 is reported when UPower does not have enough data
|
||||
#. to estimate battery life
|
||||
msgid "Estimating..."
|
||||
msgstr "Andmete kogumine..."
|
||||
msgid "Estimating…"
|
||||
msgstr "Andmete kogumine…"
|
||||
|
||||
#, c-format
|
||||
msgid "%d hour remaining"
|
||||
@ -1304,10 +1345,10 @@ msgctxt "percent of battery remaining"
|
||||
msgid "%d%%"
|
||||
msgstr "%d%%"
|
||||
|
||||
msgid "AC adapter"
|
||||
msgstr "Võrgutoite adapter"
|
||||
msgid "AC Adapter"
|
||||
msgstr "Laadija"
|
||||
|
||||
msgid "Laptop battery"
|
||||
msgid "Laptop Battery"
|
||||
msgstr "Sülearvuti aku"
|
||||
|
||||
msgid "UPS"
|
||||
@ -1322,10 +1363,10 @@ msgstr "Hiir"
|
||||
msgid "PDA"
|
||||
msgstr "Elektronmärkmik"
|
||||
|
||||
msgid "Cell phone"
|
||||
msgid "Cell Phone"
|
||||
msgstr "Mobiiltelefon"
|
||||
|
||||
msgid "Media player"
|
||||
msgid "Media Player"
|
||||
msgstr "Meediaesitaja"
|
||||
|
||||
msgid "Tablet"
|
||||
@ -1338,6 +1379,9 @@ msgctxt "device"
|
||||
msgid "Unknown"
|
||||
msgstr "Tundmatu"
|
||||
|
||||
msgid "Volume changed"
|
||||
msgstr "Helivaljus muutus"
|
||||
|
||||
#. Translators: This is the label for audio volume
|
||||
msgid "Volume"
|
||||
msgstr "Helivaljus"
|
||||
@ -1372,9 +1416,6 @@ msgstr "Ühendamata"
|
||||
msgid "Notifications"
|
||||
msgstr "Märguanded"
|
||||
|
||||
msgid "Settings"
|
||||
msgstr "Sätted"
|
||||
|
||||
msgid "Switch User"
|
||||
msgstr "Vaheta kasutajat"
|
||||
|
||||
@ -1394,6 +1435,22 @@ msgstr ""
|
||||
"Märguanded on nüüd keelatud, sealhulgas vestlusteated. Sinu netiolekut "
|
||||
"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"
|
||||
msgstr "Rakendused"
|
||||
|
||||
@ -1412,9 +1469,6 @@ msgstr ""
|
||||
msgid "%s the Oracle says"
|
||||
msgstr "Oraakel %s ütleb"
|
||||
|
||||
msgid "Your favorite Easter Egg"
|
||||
msgstr "Sinu lemmiküllatusmuna"
|
||||
|
||||
#, c-format
|
||||
msgid "'%s' is ready"
|
||||
msgstr "'%s' on valmis"
|
||||
@ -1422,6 +1476,25 @@ msgstr "'%s' on valmis"
|
||||
msgid "Evolution Calendar"
|
||||
msgstr "Evolutioni kalender"
|
||||
|
||||
#. translators:
|
||||
#. * The number of sound outputs on a particular device
|
||||
#, c-format
|
||||
msgid "%u Output"
|
||||
msgid_plural "%u Outputs"
|
||||
msgstr[0] "%u väljund"
|
||||
msgstr[1] "%u väljundit"
|
||||
|
||||
#. translators:
|
||||
#. * The number of sound inputs on a particular device
|
||||
#, c-format
|
||||
msgid "%u Input"
|
||||
msgid_plural "%u Inputs"
|
||||
msgstr[0] "%u sisend"
|
||||
msgstr[1] "%u sisendit"
|
||||
|
||||
msgid "System Sounds"
|
||||
msgstr "Süsteemi helid"
|
||||
|
||||
msgid "Print version"
|
||||
msgstr "Printimise versioon"
|
||||
|
||||
@ -1444,15 +1517,48 @@ msgstr "Paroolid ei kattu."
|
||||
msgid "Password cannot be blank"
|
||||
msgstr "Parool ei saa olla tühi"
|
||||
|
||||
msgid "United Kingdom"
|
||||
msgstr "Suurbritannia"
|
||||
|
||||
msgid "Default"
|
||||
msgstr "Vaikimisi"
|
||||
|
||||
msgid "Authentication dialog was dismissed by the user"
|
||||
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"
|
||||
#~ msgstr "Tellimuse päring"
|
||||
|
||||
@ -1471,28 +1577,9 @@ msgstr "Kasutaja katkestas autentimisdialoogi"
|
||||
#~ msgid "Reconnect"
|
||||
#~ msgstr "Ühendu uuesti"
|
||||
|
||||
#~ msgid "Wired"
|
||||
#~ msgstr "Juhtmega"
|
||||
|
||||
#~ msgid "Wireless"
|
||||
#~ msgstr "Juhtmeta"
|
||||
|
||||
#~ msgid "Mobile broadband"
|
||||
#~ msgstr "Mobiiliühendus"
|
||||
|
||||
#~ msgid "%u Output"
|
||||
#~ msgid_plural "%u Outputs"
|
||||
#~ msgstr[0] "%u väljund"
|
||||
#~ msgstr[1] "%u väljundit"
|
||||
|
||||
#~ msgid "%u Input"
|
||||
#~ msgid_plural "%u Inputs"
|
||||
#~ msgstr[0] "%u sisend"
|
||||
#~ msgstr[1] "%u sisendit"
|
||||
|
||||
#~ msgid "System Sounds"
|
||||
#~ msgstr "Süsteemi helid"
|
||||
|
||||
#~ msgid "disabled OpenSearch providers"
|
||||
#~ msgstr "keelatud OpenSearch pakkujad"
|
||||
|
||||
|
4
po/ru.po
4
po/ru.po
@ -16,7 +16,7 @@ msgstr ""
|
||||
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
|
||||
"shell&keywords=I18N+L10N&component=general\n"
|
||||
"POT-Creation-Date: 2013-03-03 12:52+0000\n"
|
||||
"PO-Revision-Date: 2013-03-03 23:21+0400\n"
|
||||
"PO-Revision-Date: 2013-03-07 09:35+0400\n"
|
||||
"Last-Translator: Yuri Myasoedov <omerta13@yandex.ru>\n"
|
||||
"Language-Team: русский <gnome-cyr@gnome.org>\n"
|
||||
"Language: ru\n"
|
||||
@ -400,7 +400,7 @@ msgstr "Войти"
|
||||
|
||||
#: ../js/gdm/loginDialog.js:799
|
||||
msgid "Next"
|
||||
msgstr "Следующий"
|
||||
msgstr "Далее"
|
||||
|
||||
#. TTLS and PEAP are actually much more complicated, but this complication
|
||||
#. is not visible here since we only care about phase2 authentication
|
||||
|
592
po/sr@latin.po
592
po/sr@latin.po
File diff suppressed because it is too large
Load Diff
77
po/ug.po
77
po/ug.po
@ -11,8 +11,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"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"
|
||||
"POT-Creation-Date: 2013-03-02 23:02+0000\n"
|
||||
"PO-Revision-Date: 2013-03-03 15:57+0900\n"
|
||||
"POT-Creation-Date: 2013-03-04 08:38+0000\n"
|
||||
"PO-Revision-Date: 2013-03-07 21:01+0900\n"
|
||||
"Last-Translator: Gheyret Kenji <gheyret@gmail.com>\n"
|
||||
"Language-Team: Uyghur Computer Science Association <UKIJ@yahoogroups.com>\n"
|
||||
"Language: \n"
|
||||
@ -112,13 +112,13 @@ msgstr "مۇناسىپ پروگرامما بەلگىسى يىغقۇچ رايون
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:9
|
||||
msgid "List of categories that should be displayed as folders"
|
||||
msgstr ""
|
||||
msgstr "قىسقۇچ سۈپىتىدە كۆرسىتىلىدىغان كاتېگورىيەلەرنىڭ تىزىمى"
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:10
|
||||
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 ""
|
||||
msgstr "پروگرامما كۆرۈنۈشىدە ھەر بىر كاتېگورىيە ئاتى قىسقۇچ سۈپىتىدە ئىپادىلىنىدۇ، ئاساسىي كۆرۈنۈشتە كۆرسىتىلمەيدۇ."
|
||||
|
||||
#: ../data/org.gnome.shell.gschema.xml.in.in.h:11
|
||||
msgid "History for command (Alt-F2) dialog"
|
||||
@ -316,7 +316,6 @@ msgid "Select an extension to configure using the combobox above."
|
||||
msgstr "يۇقىرىدىكى combobox دىن سەپلەيدىغان كېڭەيتىلمىنى تاللاڭ."
|
||||
|
||||
#: ../js/gdm/loginDialog.js:405
|
||||
#| msgid "Session..."
|
||||
msgid "Session…"
|
||||
msgstr "ئەڭگىمە…"
|
||||
|
||||
@ -331,7 +330,7 @@ msgstr "تىزىمدا يوقمۇ؟"
|
||||
#: ../js/ui/components/polkitAgent.js:162 ../js/ui/endSessionDialog.js:375
|
||||
#: ../js/ui/extensionDownloader.js:195 ../js/ui/shellMountOperation.js:399
|
||||
#: ../js/ui/status/bluetooth.js:415 ../js/ui/unlockDialog.js:126
|
||||
#: ../js/ui/userMenu.js:932
|
||||
#: ../js/ui/userMenu.js:934
|
||||
msgid "Cancel"
|
||||
msgstr "ئەمەلدىن قالدۇر"
|
||||
|
||||
@ -361,8 +360,8 @@ msgstr "كىرىش كۆزنىكى"
|
||||
msgid "Power"
|
||||
msgstr "توك مەنبە"
|
||||
|
||||
#: ../js/gdm/powerMenu.js:93 ../js/ui/userMenu.js:694 ../js/ui/userMenu.js:698
|
||||
#: ../js/ui/userMenu.js:814
|
||||
#: ../js/gdm/powerMenu.js:93 ../js/ui/userMenu.js:695 ../js/ui/userMenu.js:699
|
||||
#: ../js/ui/userMenu.js:815
|
||||
msgid "Suspend"
|
||||
msgstr "توڭلات"
|
||||
|
||||
@ -370,14 +369,14 @@ msgstr "توڭلات"
|
||||
msgid "Restart"
|
||||
msgstr "قايتا قوزغات"
|
||||
|
||||
#: ../js/gdm/powerMenu.js:103 ../js/ui/userMenu.js:696
|
||||
#: ../js/ui/userMenu.js:698 ../js/ui/userMenu.js:813 ../js/ui/userMenu.js:936
|
||||
#: ../js/gdm/powerMenu.js:103 ../js/ui/userMenu.js:697
|
||||
#: ../js/ui/userMenu.js:699 ../js/ui/userMenu.js:814 ../js/ui/userMenu.js:938
|
||||
msgid "Power Off"
|
||||
msgstr "توكنى ئۈز"
|
||||
|
||||
#: ../js/gdm/util.js:182
|
||||
msgid "Authentication error"
|
||||
msgstr "سالاھىيەت دەلىللەش خاتالىقى"
|
||||
msgstr "كىملىك دەلىللەش خاتالىقى"
|
||||
|
||||
#. Translators: this message is shown below the password entry field
|
||||
#. to indicate the user can swipe their finger instead
|
||||
@ -435,7 +434,7 @@ msgstr "%s يىغقۇچىڭىزغا قوشۇلدى."
|
||||
msgid "%s has been removed from your favorites."
|
||||
msgstr "%s يىغقۇچىڭىزدىن چىقىرىۋېتىلىدۇ."
|
||||
|
||||
#: ../js/ui/backgroundMenu.js:19 ../js/ui/userMenu.js:787
|
||||
#: ../js/ui/backgroundMenu.js:19 ../js/ui/userMenu.js:788
|
||||
msgid "Settings"
|
||||
msgstr "تەڭشەكلەر"
|
||||
|
||||
@ -697,7 +696,7 @@ msgstr "‹%s› كە باغلىنىش ئۈچۈن بىر ئىمغا ئېھتىي
|
||||
|
||||
#: ../js/ui/components/polkitAgent.js:55
|
||||
msgid "Authentication Required"
|
||||
msgstr "سالاھىيەت دەلىللەش زۆرۈر"
|
||||
msgstr "كىملىك دەلىللەش زۆرۈر"
|
||||
|
||||
#: ../js/ui/components/polkitAgent.js:93
|
||||
msgid "Administrator"
|
||||
@ -847,7 +846,7 @@ msgstr "تور خاتالىقى"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1300
|
||||
msgid "Authentication failed"
|
||||
msgstr "سالاھىيەت دەلىللەش مەغلۇپ بولدى"
|
||||
msgstr "كىملىك دەلىللەش مەغلۇپ بولدى"
|
||||
|
||||
#: ../js/ui/components/telepathyClient.js:1302
|
||||
msgid "Encryption error"
|
||||
@ -956,7 +955,7 @@ msgstr "ھېساباتنى كۆرۈش"
|
||||
msgid "Unknown reason"
|
||||
msgstr "نامەلۇم سەۋەب"
|
||||
|
||||
#: ../js/ui/ctrlAltTab.js:29 ../js/ui/viewSelector.js:97
|
||||
#: ../js/ui/ctrlAltTab.js:29 ../js/ui/viewSelector.js:96
|
||||
msgid "Windows"
|
||||
msgstr "كۆزنەكلەر"
|
||||
|
||||
@ -1144,7 +1143,6 @@ msgid "Remove"
|
||||
msgstr "چىقىرىۋەت"
|
||||
|
||||
#: ../js/ui/messageTray.js:1501
|
||||
#| msgid "No Messages"
|
||||
msgid "Clear Messages"
|
||||
msgstr "ئۇچۇرنى تازىلا"
|
||||
|
||||
@ -1188,7 +1186,6 @@ msgstr "قىسقىچە بايان"
|
||||
#. active; it should not exceed ~30
|
||||
#. characters.
|
||||
#: ../js/ui/overview.js:284
|
||||
#| msgid "Type to search..."
|
||||
msgid "Type to search…"
|
||||
msgstr "ئىزدەيدىغاننى كىرگۈزۈڭ.…"
|
||||
|
||||
@ -1235,7 +1232,7 @@ msgid "%d new notification"
|
||||
msgid_plural "%d new notifications"
|
||||
msgstr[0] "%d دانە ئۇقتۇرۇش"
|
||||
|
||||
#: ../js/ui/screenShield.js:442 ../js/ui/userMenu.js:805
|
||||
#: ../js/ui/screenShield.js:442 ../js/ui/userMenu.js:806
|
||||
msgid "Lock"
|
||||
msgstr "قۇلۇپلا"
|
||||
|
||||
@ -1251,7 +1248,6 @@ msgstr "گىنوم ئېكراننى قۇلۇپلىشى زۆرۈر"
|
||||
#. XXX: another option is to kick the user into the gdm login
|
||||
#. screen, where we're not affected by grabs
|
||||
#: ../js/ui/screenShield.js:758 ../js/ui/screenShield.js:1169
|
||||
#| msgid "Unable to connect to %s"
|
||||
msgid "Unable to lock"
|
||||
msgstr "قۇلۇپلىغىلى بولمىدى"
|
||||
|
||||
@ -1260,7 +1256,6 @@ msgid "Lock was blocked by an application"
|
||||
msgstr "بىر پروگرامما قۇلۇپلاشنى توسۇپ قويغان"
|
||||
|
||||
#: ../js/ui/searchDisplay.js:431
|
||||
#| msgid "Searching..."
|
||||
msgid "Searching…"
|
||||
msgstr "ئىزدەۋاتىدۇ…"
|
||||
|
||||
@ -1356,12 +1351,10 @@ msgid "Visibility"
|
||||
msgstr "كۆرۈشچانلىقى"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:59
|
||||
#| msgid "Send Files to Device..."
|
||||
msgid "Send Files to Device…"
|
||||
msgstr "ھۆججەتلەرنى ئۈسكۈنىگە ئەۋەتىش…"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:60
|
||||
#| msgid "Set Up a New Device..."
|
||||
msgid "Set Up a New Device…"
|
||||
msgstr "يېڭى ئۈسكۈنە ئورنىتىش…"
|
||||
|
||||
@ -1388,7 +1381,6 @@ msgid "connecting..."
|
||||
msgstr "باغلىنىۋاتىدۇ…"
|
||||
|
||||
#: ../js/ui/status/bluetooth.js:239
|
||||
#| msgid "Send Files..."
|
||||
msgid "Send Files…"
|
||||
msgstr "ھۆججەت يوللاش…"
|
||||
|
||||
@ -1494,7 +1486,7 @@ msgstr "باشقۇرۇلمىغان"
|
||||
#. 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
|
||||
msgid "authentication required"
|
||||
msgstr "سالاھىيەت دەلىللەش زۆرۈر"
|
||||
msgstr "كىملىك دەلىللەش زۆرۈر"
|
||||
|
||||
#. Translators: this is for devices that require some kind of firmware or kernel
|
||||
#. module, which is missing
|
||||
@ -1601,7 +1593,6 @@ msgstr "توك مەنبە تەڭشىكى"
|
||||
#. 0 is reported when UPower does not have enough data
|
||||
#. to estimate battery life
|
||||
#: ../js/ui/status/power.js:99
|
||||
#| msgid "Estimating..."
|
||||
msgid "Estimating…"
|
||||
msgstr "مۆلچەرلەۋاتىدۇ…"
|
||||
|
||||
@ -1705,79 +1696,79 @@ msgstr "باشقا ئىشلەتكۈچى سۈپىتىدە كىر"
|
||||
msgid "Unlock Window"
|
||||
msgstr "كۆزنەكنى قۇلۇپلا"
|
||||
|
||||
#: ../js/ui/userMenu.js:192
|
||||
#: ../js/ui/userMenu.js:193
|
||||
msgid "Available"
|
||||
msgstr "ئىشلەتكىلى بولىدۇ"
|
||||
|
||||
#: ../js/ui/userMenu.js:195
|
||||
#: ../js/ui/userMenu.js:196
|
||||
msgid "Busy"
|
||||
msgstr "ئالدىراش"
|
||||
|
||||
#: ../js/ui/userMenu.js:198
|
||||
#: ../js/ui/userMenu.js:199
|
||||
msgid "Invisible"
|
||||
msgstr "يوشۇرۇن"
|
||||
|
||||
#: ../js/ui/userMenu.js:201
|
||||
#: ../js/ui/userMenu.js:202
|
||||
msgid "Away"
|
||||
msgstr "يوق"
|
||||
|
||||
#: ../js/ui/userMenu.js:204
|
||||
#: ../js/ui/userMenu.js:205
|
||||
msgid "Idle"
|
||||
msgstr "بىكار"
|
||||
|
||||
#: ../js/ui/userMenu.js:207
|
||||
#: ../js/ui/userMenu.js:208
|
||||
msgid "Offline"
|
||||
msgstr "توردا يوق"
|
||||
|
||||
#: ../js/ui/userMenu.js:779
|
||||
#: ../js/ui/userMenu.js:780
|
||||
msgid "Notifications"
|
||||
msgstr "ئۇقتۇرۇشلار"
|
||||
|
||||
#: ../js/ui/userMenu.js:795
|
||||
#: ../js/ui/userMenu.js:796
|
||||
msgid "Switch User"
|
||||
msgstr "ئىشلەتكۈچى ئالماشتۇرۇش"
|
||||
|
||||
#: ../js/ui/userMenu.js:800
|
||||
#: ../js/ui/userMenu.js:801
|
||||
msgid "Log Out"
|
||||
msgstr "تىزىمدىن چىقىش"
|
||||
|
||||
#: ../js/ui/userMenu.js:820
|
||||
#: ../js/ui/userMenu.js:821
|
||||
msgid "Install Updates & Restart"
|
||||
msgstr "يېڭىلانمىلارنى ئورنىتىپ قايتا قوزغات"
|
||||
|
||||
#: ../js/ui/userMenu.js:838
|
||||
#: ../js/ui/userMenu.js:839
|
||||
msgid "Your chat status will be set to busy"
|
||||
msgstr "سىزنىڭ سۆھبەت ھالىتىڭىز ئالدىراش قىلىنىدۇ"
|
||||
|
||||
#: ../js/ui/userMenu.js:839
|
||||
#: ../js/ui/userMenu.js:840
|
||||
msgid ""
|
||||
"Notifications are now disabled, including chat messages. Your online status "
|
||||
"has been adjusted to let others know that you might not see their messages."
|
||||
msgstr ""
|
||||
|
||||
#: ../js/ui/userMenu.js:885
|
||||
#: ../js/ui/userMenu.js:886
|
||||
msgid "Other users are logged in."
|
||||
msgstr "باشقا ئىشلەتكۈچىلەر كىردى:"
|
||||
|
||||
#: ../js/ui/userMenu.js:890
|
||||
#: ../js/ui/userMenu.js:891
|
||||
msgid "Shutting down might cause them to lose unsaved work."
|
||||
msgstr "تاقالسا ساقلانمىغان خىزمەتلەر يوقىلىشى مۇمكىن."
|
||||
|
||||
#: ../js/ui/userMenu.js:916
|
||||
#: ../js/ui/userMenu.js:918
|
||||
#, c-format
|
||||
msgid "%s (remote)"
|
||||
msgstr "%s (يىراق)"
|
||||
|
||||
#: ../js/ui/userMenu.js:918
|
||||
#: ../js/ui/userMenu.js:920
|
||||
#, c-format
|
||||
msgid "%s (console)"
|
||||
msgstr "%s (تىزگىن كۆزنەك)"
|
||||
|
||||
#: ../js/ui/viewSelector.js:101
|
||||
#: ../js/ui/viewSelector.js:100
|
||||
msgid "Applications"
|
||||
msgstr "پروگراممىلار"
|
||||
|
||||
#: ../js/ui/viewSelector.js:105
|
||||
#: ../js/ui/viewSelector.js:104
|
||||
msgid "Search"
|
||||
msgstr "ئىزدە"
|
||||
|
||||
|
585
po/zh_HK.po
585
po/zh_HK.po
File diff suppressed because it is too large
Load Diff
587
po/zh_TW.po
587
po/zh_TW.po
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)
|
||||
|
||||
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 TelepathyLogger-0.2 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 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_LIBS = libgnome-shell.la
|
||||
Shell_0_1_gir_FILES = $(libgnome_shell_la_gir_sources)
|
||||
|
@ -490,6 +490,17 @@ 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 *
|
||||
calendar_sources_get_appointment_clients (CalendarSources *sources)
|
||||
{
|
||||
@ -497,12 +508,7 @@ calendar_sources_get_appointment_clients (CalendarSources *sources)
|
||||
|
||||
g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), NULL);
|
||||
|
||||
if (!sources->priv->appointment_sources.loaded)
|
||||
{
|
||||
calendar_sources_load_esource_list (sources->priv->registry,
|
||||
&sources->priv->appointment_sources);
|
||||
sources->priv->appointment_sources.loaded = TRUE;
|
||||
}
|
||||
ensure_appointment_sources (sources);
|
||||
|
||||
list = g_hash_table_get_values (sources->priv->appointment_sources.clients);
|
||||
|
||||
@ -512,6 +518,17 @@ calendar_sources_get_appointment_clients (CalendarSources *sources)
|
||||
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 *
|
||||
calendar_sources_get_task_clients (CalendarSources *sources)
|
||||
{
|
||||
@ -519,12 +536,7 @@ calendar_sources_get_task_clients (CalendarSources *sources)
|
||||
|
||||
g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), NULL);
|
||||
|
||||
if (!sources->priv->task_sources.loaded)
|
||||
{
|
||||
calendar_sources_load_esource_list (sources->priv->registry,
|
||||
&sources->priv->task_sources);
|
||||
sources->priv->task_sources.loaded = TRUE;
|
||||
}
|
||||
ensure_task_sources (sources);
|
||||
|
||||
list = g_hash_table_get_values (sources->priv->task_sources.clients);
|
||||
|
||||
@ -533,3 +545,15 @@ calendar_sources_get_task_clients (CalendarSources *sources)
|
||||
|
||||
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,6 +61,8 @@ CalendarSources *calendar_sources_get (void);
|
||||
GList *calendar_sources_get_appointment_clients (CalendarSources *sources);
|
||||
GList *calendar_sources_get_task_clients (CalendarSources *sources);
|
||||
|
||||
gboolean calendar_sources_has_sources (CalendarSources *sources);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __CALENDAR_SOURCES_H__ */
|
||||
|
@ -57,6 +57,7 @@ static const gchar introspection_xml[] =
|
||||
" <signal name='Changed'/>"
|
||||
" <property name='Since' type='x' access='read'/>"
|
||||
" <property name='Until' type='x' access='read'/>"
|
||||
" <property name='HasCalendars' type='b' access='read'/>"
|
||||
" </interface>"
|
||||
"</node>";
|
||||
static GDBusNodeInfo *introspection_data = NULL;
|
||||
@ -726,6 +727,12 @@ app_load_events (App *app)
|
||||
app->cache_invalid = FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
app_has_calendars (App *app)
|
||||
{
|
||||
return calendar_sources_has_sources (app->sources);
|
||||
}
|
||||
|
||||
static void
|
||||
on_appointment_sources_changed (CalendarSources *sources,
|
||||
gpointer user_data)
|
||||
@ -734,6 +741,26 @@ on_appointment_sources_changed (CalendarSources *sources,
|
||||
|
||||
print_debug ("Sources changed\n");
|
||||
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 *
|
||||
@ -933,6 +960,10 @@ handle_get_property (GDBusConnection *connection,
|
||||
{
|
||||
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
|
||||
{
|
||||
g_assert_not_reached ();
|
||||
|
@ -8,9 +8,7 @@
|
||||
#include "shell-embedded-window-private.h"
|
||||
|
||||
/* This type is a subclass of GtkWindow that ties the window to a
|
||||
* ShellGtkEmbed; the window is reparented into the stage
|
||||
* window for the actor and the resizing logic is bound to the clutter
|
||||
* logic.
|
||||
* ShellGtkEmbed; the resizing logic is bound to the clutter logic.
|
||||
*
|
||||
* The typical usage we might expect is
|
||||
*
|
||||
@ -28,16 +26,13 @@
|
||||
G_DEFINE_TYPE (ShellEmbeddedWindow, shell_embedded_window, GTK_TYPE_WINDOW);
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
|
||||
PROP_STAGE
|
||||
PROP_0
|
||||
};
|
||||
|
||||
struct _ShellEmbeddedWindowPrivate {
|
||||
ShellGtkEmbed *actor;
|
||||
|
||||
GdkRectangle position;
|
||||
Window stage_xwindow;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -80,27 +75,6 @@ shell_embedded_window_hide (GtkWidget *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
|
||||
shell_embedded_window_configure_event (GtkWidget *widget,
|
||||
GdkEventConfigure *event)
|
||||
@ -127,27 +101,6 @@ shell_embedded_window_check_resize (GtkContainer *container)
|
||||
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 *
|
||||
shell_embedded_window_constructor (GType gtype,
|
||||
guint n_properties,
|
||||
@ -182,23 +135,13 @@ shell_embedded_window_class_init (ShellEmbeddedWindowClass *klass)
|
||||
|
||||
g_type_class_add_private (klass, sizeof (ShellEmbeddedWindowPrivate));
|
||||
|
||||
object_class->set_property = shell_embedded_window_set_property;
|
||||
object_class->constructor = shell_embedded_window_constructor;
|
||||
|
||||
widget_class->show = shell_embedded_window_show;
|
||||
widget_class->hide = shell_embedded_window_hide;
|
||||
widget_class->realize = shell_embedded_window_realize;
|
||||
widget_class->configure_event = shell_embedded_window_configure_event;
|
||||
|
||||
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
|
||||
@ -282,9 +225,8 @@ _shell_embedded_window_unmap (ShellEmbeddedWindow *window)
|
||||
* Public API
|
||||
*/
|
||||
GtkWidget *
|
||||
shell_embedded_window_new (ClutterStage *stage)
|
||||
shell_embedded_window_new (void)
|
||||
{
|
||||
return g_object_new (SHELL_TYPE_EMBEDDED_WINDOW,
|
||||
"stage", stage,
|
||||
NULL);
|
||||
}
|
||||
|
@ -30,6 +30,6 @@ struct _ShellEmbeddedWindowClass
|
||||
};
|
||||
|
||||
GType shell_embedded_window_get_type (void) G_GNUC_CONST;
|
||||
GtkWidget *shell_embedded_window_new (ClutterStage *stage);
|
||||
GtkWidget *shell_embedded_window_new (void);
|
||||
|
||||
#endif /* __SHELL_EMBEDDED_WINDOW_H__ */
|
||||
|
@ -3,8 +3,11 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "shell-embedded-window-private.h"
|
||||
#include "shell-global.h"
|
||||
|
||||
#include <gdk/gdkx.h>
|
||||
#include <meta/display.h>
|
||||
#include <meta/window.h>
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
@ -15,9 +18,14 @@ enum {
|
||||
struct _ShellGtkEmbedPrivate
|
||||
{
|
||||
ShellEmbeddedWindow *window;
|
||||
|
||||
ClutterActor *window_actor;
|
||||
guint window_actor_destroyed_handler;
|
||||
|
||||
guint window_created_handler;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (ShellGtkEmbed, shell_gtk_embed, CLUTTER_X11_TYPE_TEXTURE_PIXMAP);
|
||||
G_DEFINE_TYPE (ShellGtkEmbed, shell_gtk_embed, CLUTTER_TYPE_CLONE);
|
||||
|
||||
static void shell_gtk_embed_set_window (ShellGtkEmbed *embed,
|
||||
ShellEmbeddedWindow *window);
|
||||
@ -30,39 +38,106 @@ shell_gtk_embed_on_window_destroy (GtkWidget *object,
|
||||
}
|
||||
|
||||
static void
|
||||
shell_gtk_embed_on_window_realize (GtkWidget *widget,
|
||||
shell_gtk_embed_remove_window_actor (ShellGtkEmbed *embed)
|
||||
{
|
||||
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)
|
||||
{
|
||||
/* Here automatic=FALSE means to use CompositeRedirectManual.
|
||||
* That is, the X server shouldn't draw the window onto the
|
||||
* screen.
|
||||
*/
|
||||
clutter_x11_texture_pixmap_set_window (CLUTTER_X11_TEXTURE_PIXMAP (embed),
|
||||
gdk_x11_window_get_xid (gtk_widget_get_window (widget)),
|
||||
FALSE);
|
||||
ShellGtkEmbedPrivate *priv = embed->priv;
|
||||
Window xwindow = meta_window_get_xwindow (window);
|
||||
GdkWindow *gdk_window = gtk_widget_get_window (GTK_WIDGET (priv->window));
|
||||
|
||||
if (xwindow == gdk_x11_window_get_xid (gdk_window))
|
||||
{
|
||||
ClutterActor *window_actor =
|
||||
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
|
||||
shell_gtk_embed_set_window (ShellGtkEmbed *embed,
|
||||
ShellEmbeddedWindow *window)
|
||||
{
|
||||
MetaDisplay *display = shell_global_get_display (shell_global_get ());
|
||||
|
||||
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);
|
||||
|
||||
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,
|
||||
(gpointer)shell_gtk_embed_on_window_destroy,
|
||||
embed);
|
||||
g_signal_handlers_disconnect_by_func (embed->priv->window,
|
||||
(gpointer)shell_gtk_embed_on_window_realize,
|
||||
embed);
|
||||
}
|
||||
|
||||
embed->priv->window = window;
|
||||
@ -75,11 +150,14 @@ shell_gtk_embed_set_window (ShellGtkEmbed *embed,
|
||||
|
||||
g_signal_connect (embed->priv->window, "destroy",
|
||||
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);
|
||||
|
||||
if (gtk_widget_get_realized (GTK_WIDGET (window)))
|
||||
shell_gtk_embed_on_window_realize (GTK_WIDGET (embed->priv->window), embed);
|
||||
/* Listen for new windows so we can detect when Mutter has
|
||||
created a MutterWindow for this window */
|
||||
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));
|
||||
@ -260,11 +338,6 @@ shell_gtk_embed_init (ShellGtkEmbed *embed)
|
||||
{
|
||||
embed->priv = G_TYPE_INSTANCE_GET_PRIVATE (embed, SHELL_TYPE_GTK_EMBED,
|
||||
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__
|
||||
#define __SHELL_GTK_EMBED_H__
|
||||
|
||||
#include <clutter/x11/clutter-x11.h>
|
||||
#include <clutter/clutter.h>
|
||||
|
||||
#include "shell-embedded-window.h"
|
||||
|
||||
@ -19,14 +19,14 @@ typedef struct _ShellGtkEmbedPrivate ShellGtkEmbedPrivate;
|
||||
|
||||
struct _ShellGtkEmbed
|
||||
{
|
||||
ClutterX11TexturePixmap parent;
|
||||
ClutterClone parent;
|
||||
|
||||
ShellGtkEmbedPrivate *priv;
|
||||
};
|
||||
|
||||
struct _ShellGtkEmbedClass
|
||||
{
|
||||
ClutterX11TexturePixmapClass parent_class;
|
||||
ClutterCloneClass parent_class;
|
||||
};
|
||||
|
||||
GType shell_gtk_embed_get_type (void) G_GNUC_CONST;
|
||||
|
@ -21,9 +21,12 @@
|
||||
|
||||
#include "config.h"
|
||||
#include <string.h>
|
||||
#include <gnome-keyring.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"
|
||||
|
||||
enum {
|
||||
@ -35,7 +38,7 @@ enum {
|
||||
static gint signals[SIGNAL_LAST];
|
||||
|
||||
typedef struct {
|
||||
gpointer keyring_op;
|
||||
GCancellable * cancellable;
|
||||
ShellNetworkAgent *self;
|
||||
|
||||
gchar *request_id;
|
||||
@ -59,14 +62,24 @@ struct _ShellNetworkAgentPrivate {
|
||||
|
||||
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
|
||||
shell_agent_request_free (gpointer data)
|
||||
{
|
||||
ShellAgentRequest *request = data;
|
||||
|
||||
if (request->keyring_op)
|
||||
gnome_keyring_cancel_request (request->keyring_op);
|
||||
|
||||
g_cancellable_cancel (request->cancellable);
|
||||
g_object_unref (request->cancellable);
|
||||
g_object_unref (request->self);
|
||||
g_object_unref (request->connection);
|
||||
g_free (request->setting_name);
|
||||
@ -245,87 +258,92 @@ strv_has (gchar **haystack,
|
||||
}
|
||||
|
||||
static void
|
||||
get_secrets_keyring_cb (GnomeKeyringResult result,
|
||||
GList *list,
|
||||
gpointer user_data)
|
||||
get_secrets_keyring_cb (GObject *source,
|
||||
GAsyncResult *result,
|
||||
gpointer user_data)
|
||||
{
|
||||
ShellAgentRequest *closure;
|
||||
ShellNetworkAgent *self;
|
||||
ShellNetworkAgentPrivate *priv;
|
||||
GError *secret_error = NULL;
|
||||
GError *error = NULL;
|
||||
gint n_found = 0;
|
||||
GList *iter;
|
||||
GList *items;
|
||||
GList *l;
|
||||
GHashTable *outer;
|
||||
|
||||
if (result == GNOME_KEYRING_RESULT_CANCELLED)
|
||||
return;
|
||||
items = secret_service_search_finish (NULL, result, &secret_error);
|
||||
|
||||
if (g_error_matches (secret_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
||||
{
|
||||
g_error_free (secret_error);
|
||||
return;
|
||||
}
|
||||
|
||||
closure = user_data;
|
||||
self = closure->self;
|
||||
priv = self->priv;
|
||||
|
||||
closure->keyring_op = NULL;
|
||||
|
||||
if (result == GNOME_KEYRING_RESULT_DENIED)
|
||||
if (secret_error != NULL)
|
||||
{
|
||||
g_set_error (&error,
|
||||
NM_SECRET_AGENT_ERROR,
|
||||
NM_SECRET_AGENT_ERROR_USER_CANCELED,
|
||||
"Access to the secret storage was denied by the user");
|
||||
|
||||
NM_SECRET_AGENT_ERROR,
|
||||
NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
|
||||
"Internal error while retrieving secrets from the keyring (%s)", secret_error->message);
|
||||
g_error_free (secret_error);
|
||||
closure->callback (NM_SECRET_AGENT (closure->self), closure->connection, NULL, error, closure->callback_data);
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (result != GNOME_KEYRING_RESULT_OK &&
|
||||
result != GNOME_KEYRING_RESULT_NO_MATCH)
|
||||
for (l = items; l; l = g_list_next (l))
|
||||
{
|
||||
g_set_error (&error,
|
||||
NM_SECRET_AGENT_ERROR,
|
||||
NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
|
||||
"Internal error while retrieving secrets from the keyring (result %d)", result);
|
||||
SecretItem *item = l->data;
|
||||
GHashTable *attributes;
|
||||
GHashTableIter iter;
|
||||
const gchar *name, *attribute;
|
||||
SecretValue *secret = secret_item_get_secret (item);
|
||||
|
||||
closure->callback (NM_SECRET_AGENT (closure->self), closure->connection, NULL, error, closure->callback_data);
|
||||
/* This can happen if the user denied a request to unlock */
|
||||
if (secret == NULL)
|
||||
continue;
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (iter = list; iter; iter = g_list_next (iter))
|
||||
{
|
||||
GnomeKeyringFound *item = iter->data;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < item->attributes->len; i++)
|
||||
attributes = secret_item_get_attributes (item);
|
||||
g_hash_table_iter_init (&iter, attributes);
|
||||
while (g_hash_table_iter_next (&iter, (gpointer *)&name, (gpointer *)&attribute))
|
||||
{
|
||||
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))
|
||||
if (g_strcmp0 (name, SHELL_KEYRING_SK_TAG) == 0)
|
||||
{
|
||||
gchar *secret_name = g_strdup (attr->value.string);
|
||||
gchar *secret_name = g_strdup (attribute);
|
||||
|
||||
if (!closure->is_vpn)
|
||||
{
|
||||
GValue *secret_value = g_slice_new0 (GValue);
|
||||
g_value_init (secret_value, G_TYPE_STRING);
|
||||
g_value_set_string (secret_value, item->secret);
|
||||
g_value_set_string (secret_value, secret_value_get (secret, NULL));
|
||||
|
||||
g_hash_table_insert (closure->entries, secret_name, secret_value);
|
||||
}
|
||||
else
|
||||
g_hash_table_insert (closure->vpn_entries, secret_name, g_strdup (item->secret));
|
||||
g_hash_table_insert (closure->vpn_entries, secret_name, g_strdup (secret_value_get (secret, NULL)));
|
||||
|
||||
if (closure->hints)
|
||||
n_found += strv_has (closure->hints, secret_name);
|
||||
else
|
||||
n_found += 1;
|
||||
|
||||
g_hash_table_unref (attributes);
|
||||
secret_value_unref (secret);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
g_hash_table_unref (attributes);
|
||||
secret_value_unref (secret);
|
||||
}
|
||||
|
||||
g_list_free_full (items, g_object_unref);
|
||||
|
||||
if (n_found == 0 &&
|
||||
(closure->flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION))
|
||||
{
|
||||
@ -361,6 +379,7 @@ shell_network_agent_get_secrets (NMSecretAgent *agent,
|
||||
ShellAgentRequest *request;
|
||||
NMSettingConnection *setting_connection;
|
||||
const char *connection_type;
|
||||
GHashTable *attributes;
|
||||
char *request_id;
|
||||
|
||||
request_id = g_strdup_printf ("%s/%s", connection_path, setting_name);
|
||||
@ -378,6 +397,7 @@ shell_network_agent_get_secrets (NMSecretAgent *agent,
|
||||
|
||||
request = g_slice_new (ShellAgentRequest);
|
||||
request->self = g_object_ref (self);
|
||||
request->cancellable = g_cancellable_new ();
|
||||
request->connection = g_object_ref (connection);
|
||||
request->setting_name = g_strdup (setting_name);
|
||||
request->hints = g_strdupv ((gchar **)hints);
|
||||
@ -385,7 +405,6 @@ shell_network_agent_get_secrets (NMSecretAgent *agent,
|
||||
request->callback = callback;
|
||||
request->callback_data = callback_data;
|
||||
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);
|
||||
|
||||
if (request->is_vpn)
|
||||
@ -413,17 +432,16 @@ shell_network_agent_get_secrets (NMSecretAgent *agent,
|
||||
return;
|
||||
}
|
||||
|
||||
request->keyring_op = gnome_keyring_find_itemsv (GNOME_KEYRING_ITEM_GENERIC_SECRET,
|
||||
get_secrets_keyring_cb,
|
||||
request,
|
||||
NULL, /* GDestroyNotify */
|
||||
SHELL_KEYRING_UUID_TAG,
|
||||
GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
|
||||
nm_connection_get_uuid (connection),
|
||||
SHELL_KEYRING_SN_TAG,
|
||||
GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
|
||||
setting_name,
|
||||
NULL);
|
||||
attributes = secret_attributes_build (&network_agent_schema,
|
||||
SHELL_KEYRING_UUID_TAG, nm_connection_get_uuid (connection),
|
||||
SHELL_KEYRING_SN_TAG, setting_name,
|
||||
NULL);
|
||||
|
||||
secret_service_search (NULL, &network_agent_schema, attributes,
|
||||
SECRET_SEARCH_ALL | SECRET_SEARCH_UNLOCK | SECRET_SEARCH_LOAD_SECRETS,
|
||||
request->cancellable, get_secrets_keyring_cb, request);
|
||||
|
||||
g_hash_table_unref (attributes);
|
||||
}
|
||||
|
||||
void
|
||||
@ -541,7 +559,7 @@ shell_network_agent_cancel_get_secrets (NMSecretAgent *agent,
|
||||
|
||||
/************************* saving of secrets ****************************************/
|
||||
|
||||
static GnomeKeyringAttributeList *
|
||||
static GHashTable *
|
||||
create_keyring_add_attr_list (NMConnection *connection,
|
||||
const gchar *connection_uuid,
|
||||
const gchar *connection_id,
|
||||
@ -549,7 +567,6 @@ create_keyring_add_attr_list (NMConnection *connection,
|
||||
const gchar *setting_key,
|
||||
gchar **out_display_name)
|
||||
{
|
||||
GnomeKeyringAttributeList *attrs = NULL;
|
||||
NMSettingConnection *s_con;
|
||||
|
||||
if (connection)
|
||||
@ -573,17 +590,11 @@ create_keyring_add_attr_list (NMConnection *connection,
|
||||
setting_key);
|
||||
}
|
||||
|
||||
attrs = gnome_keyring_attribute_list_new ();
|
||||
gnome_keyring_attribute_list_append_string (attrs,
|
||||
SHELL_KEYRING_UUID_TAG,
|
||||
connection_uuid);
|
||||
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;
|
||||
return secret_attributes_build (&network_agent_schema,
|
||||
SHELL_KEYRING_UUID_TAG, connection_uuid,
|
||||
SHELL_KEYRING_SN_TAG, setting_name,
|
||||
SHELL_KEYRING_SK_TAG, setting_key,
|
||||
NULL);
|
||||
}
|
||||
|
||||
typedef struct
|
||||
@ -607,8 +618,8 @@ keyring_request_free (KeyringRequest *r)
|
||||
}
|
||||
|
||||
static void
|
||||
save_secret_cb (GnomeKeyringResult result,
|
||||
guint val,
|
||||
save_secret_cb (GObject *source,
|
||||
GAsyncResult *result,
|
||||
gpointer user_data)
|
||||
{
|
||||
KeyringRequest *call = user_data;
|
||||
@ -631,7 +642,7 @@ save_one_secret (KeyringRequest *r,
|
||||
const gchar *secret,
|
||||
const gchar *display_name)
|
||||
{
|
||||
GnomeKeyringAttributeList *attrs;
|
||||
GHashTable *attrs;
|
||||
gchar *alt_display_name = NULL;
|
||||
const gchar *setting_name;
|
||||
NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
|
||||
@ -650,17 +661,11 @@ save_one_secret (KeyringRequest *r,
|
||||
display_name ? NULL : &alt_display_name);
|
||||
g_assert (attrs);
|
||||
r->n_secrets++;
|
||||
gnome_keyring_item_create (NULL,
|
||||
GNOME_KEYRING_ITEM_GENERIC_SECRET,
|
||||
display_name ? display_name : alt_display_name,
|
||||
attrs,
|
||||
secret,
|
||||
TRUE,
|
||||
save_secret_cb,
|
||||
r,
|
||||
NULL);
|
||||
secret_password_storev (&network_agent_schema, attrs, SECRET_COLLECTION_DEFAULT,
|
||||
display_name ? display_name : alt_display_name,
|
||||
secret, NULL, save_secret_cb, r);
|
||||
|
||||
gnome_keyring_attribute_list_free (attrs);
|
||||
g_hash_table_unref (attrs);
|
||||
g_free (alt_display_name);
|
||||
}
|
||||
|
||||
@ -766,38 +771,28 @@ shell_network_agent_save_secrets (NMSecretAgent *agent,
|
||||
}
|
||||
|
||||
static void
|
||||
keyring_delete_cb (GnomeKeyringResult result, gpointer user_data)
|
||||
{
|
||||
/* Ignored */
|
||||
}
|
||||
|
||||
static void
|
||||
delete_find_items_cb (GnomeKeyringResult result, GList *list, gpointer user_data)
|
||||
delete_items_cb (GObject *source,
|
||||
GAsyncResult *result,
|
||||
gpointer user_data)
|
||||
{
|
||||
KeyringRequest *r = user_data;
|
||||
GList *iter;
|
||||
GError *secret_error = NULL;
|
||||
GError *error = NULL;
|
||||
NMSecretAgentDeleteSecretsFunc callback = r->callback;
|
||||
|
||||
if ((result == GNOME_KEYRING_RESULT_OK) || (result == GNOME_KEYRING_RESULT_NO_MATCH))
|
||||
{
|
||||
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
|
||||
secret_password_clear_finish (result, &secret_error);
|
||||
if (secret_error != NULL)
|
||||
{
|
||||
error = g_error_new (NM_SECRET_AGENT_ERROR,
|
||||
NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
|
||||
"The request could not be completed. Keyring result: %d",
|
||||
result);
|
||||
"The request could not be completed. Keyring result: %s",
|
||||
secret_error->message);
|
||||
g_error_free (secret_error);
|
||||
}
|
||||
|
||||
callback (r->self, r->connection, error, r->callback_data);
|
||||
g_clear_error (&error);
|
||||
keyring_request_free (r);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -823,14 +818,9 @@ shell_network_agent_delete_secrets (NMSecretAgent *agent,
|
||||
uuid = nm_setting_connection_get_uuid (s_con);
|
||||
g_assert (uuid);
|
||||
|
||||
gnome_keyring_find_itemsv (GNOME_KEYRING_ITEM_GENERIC_SECRET,
|
||||
delete_find_items_cb,
|
||||
r,
|
||||
(GDestroyNotify)keyring_request_free,
|
||||
SHELL_KEYRING_UUID_TAG,
|
||||
GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
|
||||
uuid,
|
||||
NULL);
|
||||
secret_password_clear (&network_agent_schema, NULL, delete_items_cb, r,
|
||||
SHELL_KEYRING_UUID_TAG, uuid,
|
||||
NULL);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -19,6 +19,11 @@
|
||||
|
||||
#include <clutter/x11/clutter-x11.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 {
|
||||
RECORDER_STATE_CLOSED,
|
||||
@ -57,6 +62,8 @@ struct _ShellRecorder {
|
||||
gboolean have_xfixes;
|
||||
int xfixes_event_base;
|
||||
|
||||
int xinput_opcode;
|
||||
|
||||
CoglHandle recording_icon; /* icon shown while playing */
|
||||
|
||||
cairo_surface_t *cursor_image;
|
||||
@ -661,10 +668,15 @@ recorder_event_filter (XEvent *xev,
|
||||
gpointer data)
|
||||
{
|
||||
ShellRecorder *recorder = data;
|
||||
XIEvent *input_event = NULL;
|
||||
|
||||
if (xev->xany.window != clutter_x11_get_stage_window (recorder->stage))
|
||||
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)
|
||||
{
|
||||
XFixesCursorNotifyEvent *notify_event = (XFixesCursorNotifyEvent *)xev;
|
||||
@ -680,36 +692,53 @@ recorder_event_filter (XEvent *xev,
|
||||
recorder_queue_redraw (recorder);
|
||||
}
|
||||
}
|
||||
else if (xev->xany.type == MotionNotify)
|
||||
else if (input_event != NULL &&
|
||||
input_event->evtype == XI_Motion)
|
||||
{
|
||||
recorder->pointer_x = xev->xmotion.x;
|
||||
recorder->pointer_y = xev->xmotion.y;
|
||||
XIDeviceEvent *device_event = (XIDeviceEvent *) input_event;
|
||||
if (device_event->deviceid == VIRTUAL_CORE_POINTER_ID)
|
||||
{
|
||||
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
|
||||
* window itself, and not in a child window. A "virtual"
|
||||
* crossing is one that goes directly from ancestor to child.
|
||||
*/
|
||||
else if (xev->xany.type == EnterNotify &&
|
||||
(xev->xcrossing.detail != NotifyVirtual &&
|
||||
xev->xcrossing.detail != NotifyNonlinearVirtual))
|
||||
else if (input_event != NULL &&
|
||||
input_event->evtype == XI_Enter)
|
||||
{
|
||||
recorder->have_pointer = TRUE;
|
||||
recorder->pointer_x = xev->xcrossing.x;
|
||||
recorder->pointer_y = xev->xcrossing.y;
|
||||
XIEnterEvent *enter_event = (XIEnterEvent *) input_event;
|
||||
|
||||
recorder_queue_redraw (recorder);
|
||||
if (enter_event->deviceid == VIRTUAL_CORE_POINTER_ID &&
|
||||
(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 (xev->xany.type == LeaveNotify &&
|
||||
(xev->xcrossing.detail != NotifyVirtual &&
|
||||
xev->xcrossing.detail != NotifyNonlinearVirtual))
|
||||
else if (input_event != NULL &&
|
||||
input_event->evtype == XI_Leave)
|
||||
{
|
||||
recorder->have_pointer = FALSE;
|
||||
recorder->pointer_x = xev->xcrossing.x;
|
||||
recorder->pointer_y = xev->xcrossing.y;
|
||||
XILeaveEvent *leave_event = (XILeaveEvent *) input_event;
|
||||
|
||||
recorder_queue_redraw (recorder);
|
||||
if (leave_event->deviceid == VIRTUAL_CORE_POINTER_ID &&
|
||||
(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;
|
||||
@ -764,14 +793,6 @@ recorder_get_initial_cursor_position (ShellRecorder *recorder)
|
||||
|
||||
XUngrabServer(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
|
||||
@ -830,6 +851,48 @@ 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
|
||||
recorder_set_stage (ShellRecorder *recorder,
|
||||
ClutterStage *stage)
|
||||
@ -841,48 +904,16 @@ recorder_set_stage (ShellRecorder *recorder,
|
||||
shell_recorder_close (recorder);
|
||||
|
||||
if (recorder->stage)
|
||||
{
|
||||
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_disconnect_stage_callbacks (recorder);
|
||||
|
||||
recorder->stage = stage;
|
||||
|
||||
if (recorder->stage)
|
||||
{
|
||||
int error_base;
|
||||
int error_base, event_base;
|
||||
int major = 2, minor = 3;
|
||||
|
||||
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);
|
||||
|
||||
@ -894,6 +925,28 @@ recorder_set_stage (ShellRecorder *recorder,
|
||||
clutter_x11_get_stage_window (stage),
|
||||
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);
|
||||
|
||||
recorder_get_initial_cursor_position (recorder);
|
||||
@ -1417,6 +1470,8 @@ recorder_pipeline_closed (RecorderPipeline *pipeline)
|
||||
(gpointer) recorder_pipeline_on_memory_used_changed,
|
||||
pipeline);
|
||||
|
||||
recorder_disconnect_stage_callbacks (pipeline->recorder);
|
||||
|
||||
gst_element_set_state (pipeline->pipeline, GST_STATE_NULL);
|
||||
|
||||
if (pipeline->recorder)
|
||||
@ -1678,6 +1733,8 @@ shell_recorder_record (ShellRecorder *recorder)
|
||||
if (!recorder_open_pipeline (recorder))
|
||||
return FALSE;
|
||||
|
||||
recorder_connect_stage_callbacks (recorder);
|
||||
|
||||
recorder->start_time = get_wall_time();
|
||||
recorder->last_frame_time = 0;
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "shell-window-tracker.h"
|
||||
#include "tray/na-tray-child.h"
|
||||
#include <gdk/gdkx.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include "st.h"
|
||||
|
||||
enum {
|
||||
|
@ -17,7 +17,6 @@
|
||||
|
||||
struct _ShellTrayManagerPrivate {
|
||||
NaTrayManager *na_manager;
|
||||
ClutterStage *stage;
|
||||
ClutterColor bg_color;
|
||||
|
||||
GHashTable *icons;
|
||||
@ -135,7 +134,6 @@ shell_tray_manager_finalize (GObject *object)
|
||||
ShellTrayManager *manager = SHELL_TRAY_MANAGER (object);
|
||||
|
||||
g_object_unref (manager->priv->na_manager);
|
||||
g_object_unref (manager->priv->stage);
|
||||
g_hash_table_destroy (manager->priv->icons);
|
||||
|
||||
G_OBJECT_CLASS (shell_tray_manager_parent_class)->finalize (object);
|
||||
@ -219,42 +217,19 @@ shell_tray_manager_style_changed (StWidget *theme_widget,
|
||||
}
|
||||
|
||||
void
|
||||
shell_tray_manager_manage_stage (ShellTrayManager *manager,
|
||||
ClutterStage *stage,
|
||||
StWidget *theme_widget)
|
||||
shell_tray_manager_manage_screen (ShellTrayManager *manager,
|
||||
MetaScreen *screen,
|
||||
StWidget *theme_widget)
|
||||
{
|
||||
Window stage_xwindow;
|
||||
GdkWindow *stage_window;
|
||||
GdkDisplay *display;
|
||||
GdkScreen *screen;
|
||||
GdkScreen *gdk_screen;
|
||||
int screen_number;
|
||||
|
||||
g_return_if_fail (manager->priv->stage == NULL);
|
||||
display = gdk_display_get_default ();
|
||||
screen_number = meta_screen_get_screen_number (screen);
|
||||
gdk_screen = gdk_display_get_screen (display, screen_number);
|
||||
|
||||
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);
|
||||
na_tray_manager_manage_screen (manager->priv->na_manager, gdk_screen);
|
||||
|
||||
g_signal_connect (theme_widget, "style-changed",
|
||||
G_CALLBACK (shell_tray_manager_style_changed), manager);
|
||||
@ -320,7 +295,7 @@ na_tray_icon_added (NaTrayManager *na_manager, GtkWidget *socket,
|
||||
*/
|
||||
na_tray_child_set_composited (NA_TRAY_CHILD (socket), FALSE);
|
||||
|
||||
win = shell_embedded_window_new (manager->priv->stage);
|
||||
win = shell_embedded_window_new ();
|
||||
gtk_container_add (GTK_CONTAINER (win), socket);
|
||||
|
||||
/* The visual of the socket matches that of its contents; make
|
||||
|
@ -41,9 +41,9 @@ struct _ShellTrayManagerClass
|
||||
GType shell_tray_manager_get_type (void);
|
||||
|
||||
ShellTrayManager *shell_tray_manager_new (void);
|
||||
void shell_tray_manager_manage_stage (ShellTrayManager *manager,
|
||||
ClutterStage *stage,
|
||||
StWidget *theme_widget);
|
||||
void shell_tray_manager_manage_screen (ShellTrayManager *manager,
|
||||
MetaScreen *screen,
|
||||
StWidget *theme_widget);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
@ -534,23 +534,32 @@ void
|
||||
st_icon_set_icon_name (StIcon *icon,
|
||||
const gchar *icon_name)
|
||||
{
|
||||
StIconPrivate *priv;
|
||||
StIconPrivate *priv = icon->priv;
|
||||
GIcon *gicon = NULL;
|
||||
|
||||
g_return_if_fail (ST_IS_ICON (icon));
|
||||
|
||||
priv = icon->priv;
|
||||
if (icon_name)
|
||||
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)
|
||||
g_object_unref (priv->gicon);
|
||||
|
||||
if (icon_name)
|
||||
priv->gicon = g_themed_icon_new_with_default_fallbacks (icon_name);
|
||||
else
|
||||
priv->gicon = NULL;
|
||||
g_object_freeze_notify (G_OBJECT (icon));
|
||||
|
||||
priv->gicon = gicon;
|
||||
|
||||
g_object_notify (G_OBJECT (icon), "gicon");
|
||||
g_object_notify (G_OBJECT (icon), "icon-name");
|
||||
|
||||
g_object_thaw_notify (G_OBJECT (icon));
|
||||
|
||||
st_icon_update (icon);
|
||||
}
|
||||
|
||||
@ -579,7 +588,7 @@ st_icon_set_gicon (StIcon *icon, GIcon *gicon)
|
||||
g_return_if_fail (ST_IS_ICON (icon));
|
||||
g_return_if_fail (gicon == NULL || G_IS_ICON (gicon));
|
||||
|
||||
if (icon->priv->gicon == gicon) /* do nothing */
|
||||
if (g_icon_equal (icon->priv->gicon, gicon)) /* do nothing */
|
||||
return;
|
||||
|
||||
if (icon->priv->gicon)
|
||||
|
@ -50,35 +50,38 @@ void main ()
|
||||
float y = height * cogl_tex_coord_in[0].y;
|
||||
float x = width * cogl_tex_coord_in[0].x;
|
||||
|
||||
if (x < fade_area[0][0] || x > fade_area[1][0] ||
|
||||
y < fade_area[0][1] || y > fade_area[1][1])
|
||||
return;
|
||||
/*
|
||||
* We cannot just return here due to a bug in llvmpipe see:
|
||||
* https://bugzilla.freedesktop.org/show_bug.cgi?id=62357
|
||||
*/
|
||||
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 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;
|
||||
if (fade_top) {
|
||||
ratio *= FADE((y / vfade_offset), min(sqrt(vvalue) * vfade_scale, 1.0));
|
||||
}
|
||||
|
||||
float vfade_scale = height / vfade_offset;
|
||||
if (fade_top) {
|
||||
ratio *= FADE((y / vfade_offset), min(sqrt(vvalue) * vfade_scale, 1.0));
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user