Compare commits

..

2 Commits

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

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

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

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

43
NEWS
View File

@@ -1,46 +1,3 @@
3.7.91
======
* overview: Fade out controls during DND that are not targets [Cosimo; #686984]
* overview: Keep open when a Control key is held [Florian; #686984]
* Improve login screen => session transition [Ray; #694321]
* Center application grid horizontally [Florian; #694261]
* Fix hiding panel when fullscreen windows span multiple monitors [Adel; 646861]
* Tweak thresholds of pressure barrier [Jasper; #694467]
* Tweak window picker layout [Jasper; #694902]
* Expose key grab DBus API to gnome-settings-daemon [Florian; #643111]
* Don't always show message tray in overview, add message indicator
[Cosimo; #687787]
* Tweak startup animation [Ray; #694326]
* Add OSD popups and expose them to gnome-settings-daemon [Florian; #613543]
* Move loupe icon to the start of the search entry [Jasper; #695069]
* Don't show the input switcher with less than 2 items [Rui; 695000]
* Fix auto-completion of system modals immediately upon display [Stef; #692937]
* Ignore workspaces in alt-tab [Florian; #661156]
* Disable copying text from password entries [Florian; #695104]
* Use standard styling for ibus candidate popups [Allan; #694796]
* Fix calendar changing height on month changes [Giovanni; #641383]
* Port the hot corner to use pressure barriers [Jasper; #663661]
* Misc bug fixes and cleanups: [Hashem, Giovanni, Alban, Jasper, Cosimo,
Florian, Adel, Daniel, Matthias, Ray, Rui, Guillaume, Stef; #685849, #690643,
#694292, #693814, #694234, #694365, #694287, #694336, #694256, #694261,
#663601, #694441, #694284, #694463, #694475, #687248, #694394, #694320,
#694701, #694784, #694858, #694906, #694327, #694876, #694905, #694969,
#694970, #694988, #695006, #695001, #694998, #695023, #695002, #695073,
#695126, #687748, #694837, #693907, #679851, #694988]
Contributors:
Giovanni Campagna, Cosimo Cecchi, Matthias Clasen, Alban Crequy, Allan Day,
Guillaume Desmottes, Adel Gadllah, Rui Matos, Daniel Mustieles,
Hashem Nasarat, Jasper St. Pierre, Ray Strode, Stef Walter
Translations:
Yuri Myasoedov [ru], Adam Matoušek [cs], Piotr Drąg [pl], Matej Urbančič [sl],
Sweta Kothari [gu], Kjartan Maraas [nb], Nguyễn Thái Ngọc Duy [vi],
Chao-Hsiung Liao [zh_HK, zh_TW], Dimitris Spingos [el],
Inaki Larranaga Murgoitio [eu], Luca Ferretti [it], A S Alam [pa],
Gheyret Kenji [ug], Stas Solovey [ru], Enrico Nicoletto [pt_BR],
Fran Diéguez [gl], Daniel Mustieles [es], Aurimas Černius [lt]
3.7.90 3.7.90
====== ======
* Let GNOME Shell work on EGL and GLES2 [Neil; #693225, #693438, #693339] * Let GNOME Shell work on EGL and GLES2 [Neil; #693225, #693438, #693339]

View File

@@ -1,5 +1,5 @@
AC_PREREQ(2.63) AC_PREREQ(2.63)
AC_INIT([gnome-shell],[3.7.91],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell]) AC_INIT([gnome-shell],[3.7.90],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
AC_CONFIG_HEADERS([config.h]) AC_CONFIG_HEADERS([config.h])
AC_CONFIG_SRCDIR([src/shell-global.c]) AC_CONFIG_SRCDIR([src/shell-global.c])
@@ -63,7 +63,7 @@ AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
CLUTTER_MIN_VERSION=1.13.4 CLUTTER_MIN_VERSION=1.13.4
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1 GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
GJS_MIN_VERSION=1.35.4 GJS_MIN_VERSION=1.35.4
MUTTER_MIN_VERSION=3.7.91 MUTTER_MIN_VERSION=3.7.90
GTK_MIN_VERSION=3.7.9 GTK_MIN_VERSION=3.7.9
GIO_MIN_VERSION=2.35.0 GIO_MIN_VERSION=2.35.0
LIBECAL_MIN_VERSION=3.5.3 LIBECAL_MIN_VERSION=3.5.3
@@ -96,7 +96,7 @@ PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes
libnm-glib libnm-util >= $NETWORKMANAGER_MIN_VERSION libnm-glib libnm-util >= $NETWORKMANAGER_MIN_VERSION
libnm-gtk >= $NETWORKMANAGER_MIN_VERSION libnm-gtk >= $NETWORKMANAGER_MIN_VERSION
libsecret-unstable gcr-3 >= $GCR_MIN_VERSION) gnome-keyring-1 gcr-3 >= $GCR_MIN_VERSION)
PKG_CHECK_MODULES(GNOME_SHELL_JS, gio-2.0 gjs-internals-1.0 >= $GJS_MIN_VERSION) PKG_CHECK_MODULES(GNOME_SHELL_JS, gio-2.0 gjs-internals-1.0 >= $GJS_MIN_VERSION)
PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 >= 0.6.8 x11) PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 >= 0.6.8 x11)

View File

@@ -125,8 +125,7 @@ StScrollBar StButton#vhandle:active {
/* PopupMenu */ /* PopupMenu */
.popup-menu-boxpointer, .popup-menu-boxpointer {
.candidate-popup-boxpointer {
-arrow-border-radius: 8px; -arrow-border-radius: 8px;
-arrow-background-color: rgba(0,0,0,0.9); -arrow-background-color: rgba(0,0,0,0.9);
-arrow-border-width: 2px; -arrow-border-width: 2px;
@@ -1258,15 +1257,15 @@ StScrollBar StButton#vhandle:active {
font-weight: bold; font-weight: bold;
} }
.calendar-other-month-day {
color: #333333;
}
.calendar-day-with-events { .calendar-day-with-events {
font-weight: bold; font-weight: bold;
color: white; color: white;
} }
.calendar-other-month-day {
color: #333333;
}
.events-header-vbox { .events-header-vbox {
spacing: 6pt; spacing: 6pt;
padding-right: .5em; padding-right: .5em;
@@ -2130,12 +2129,23 @@ StScrollBar StButton#vhandle:active {
} }
/* IBus Candidate Popup */ /* IBus Candidate Popup */
.candidate-popup-boxpointer {
-arrow-border-radius: 8px;
-arrow-background-color: #707070;
-arrow-border-width: 0px;
-arrow-base: 24px;
-arrow-rise: 11px;
}
.candidate-popup-content { .candidate-popup-content {
padding: 0.5em; padding: 0.5em;
spacing: 0.3em; spacing: 0.3em;
} }
.candidate-popup-text {
font-size: 9pt;
}
.candidate-index { .candidate-index {
padding: 0 0.5em 0 0; padding: 0 0.5em 0 0;
color: #cccccc; color: #cccccc;

View File

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

View File

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

View File

@@ -18,6 +18,7 @@ const Main = imports.ui.main;
const Params = imports.misc.params; const Params = imports.misc.params;
const Tweener = imports.ui.tweener; const Tweener = imports.ui.tweener;
const HOT_CORNER_ACTIVATION_TIMEOUT = 0.5;
const STARTUP_ANIMATION_TIME = 0.5; const STARTUP_ANIMATION_TIME = 0.5;
const KEYBOARD_ANIMATION_TIME = 0.15; const KEYBOARD_ANIMATION_TIME = 0.15;
const BACKGROUND_FADE_ANIMATION_TIME = 1.0; const BACKGROUND_FADE_ANIMATION_TIME = 1.0;
@@ -28,9 +29,6 @@ const DEFAULT_BACKGROUND_COLOR = Clutter.Color.from_pixel(0x2e3436ff);
const MESSAGE_TRAY_PRESSURE_THRESHOLD = 250; // pixels const MESSAGE_TRAY_PRESSURE_THRESHOLD = 250; // pixels
const MESSAGE_TRAY_PRESSURE_TIMEOUT = 1000; // ms const MESSAGE_TRAY_PRESSURE_TIMEOUT = 1000; // ms
const HOT_CORNER_PRESSURE_THRESHOLD = 100; // pixels
const HOT_CORNER_PRESSURE_TIMEOUT = 1000; // ms
function isPopupMetaWindow(actor) { function isPopupMetaWindow(actor) {
switch(actor.meta_window.get_window_type()) { switch(actor.meta_window.get_window_type()) {
case Meta.WindowType.DROPDOWN_MENU: case Meta.WindowType.DROPDOWN_MENU:
@@ -132,9 +130,9 @@ const LayoutManager = new Lang.Class({
this.monitors = []; this.monitors = [];
this.primaryMonitor = null; this.primaryMonitor = null;
this.primaryIndex = -1; this.primaryIndex = -1;
this.hotCorners = [];
this._keyboardIndex = -1; this._keyboardIndex = -1;
this._hotCorners = [];
this._leftPanelBarrier = null;
this._rightPanelBarrier = null; this._rightPanelBarrier = null;
this._trayBarrier = null; this._trayBarrier = null;
@@ -193,7 +191,6 @@ const LayoutManager = new Lang.Class({
this.trayBox = new St.Widget({ name: 'trayBox', this.trayBox = new St.Widget({ name: 'trayBox',
layout_manager: new Clutter.BinLayout() }); layout_manager: new Clutter.BinLayout() });
this.addChrome(this.trayBox); this.addChrome(this.trayBox);
this._setupTrayPressure();
this.keyboardBox = new St.BoxLayout({ name: 'keyboardBox', this.keyboardBox = new St.BoxLayout({ name: 'keyboardBox',
reactive: true, reactive: true,
@@ -278,58 +275,56 @@ const LayoutManager = new Lang.Class({
_updateHotCorners: function() { _updateHotCorners: function() {
// destroy old hot corners // destroy old hot corners
for (let i = 0; i < this.hotCorners.length; i++) for (let i = 0; i < this._hotCorners.length; i++)
this.hotCorners[i].destroy(); this._hotCorners[i].destroy();
this.hotCorners = []; this._hotCorners = [];
let size = this.panelBox.height;
// build new hot corners // build new hot corners
for (let i = 0; i < this.monitors.length; i++) { for (let i = 0; i < this.monitors.length; i++) {
if (i == this.primaryIndex)
continue;
let monitor = this.monitors[i]; let monitor = this.monitors[i];
let cornerX = this._rtl ? monitor.x + monitor.width : monitor.x; let cornerX = this._rtl ? monitor.x + monitor.width : monitor.x;
let cornerY = monitor.y; let cornerY = monitor.y;
if (i != this.primaryIndex) { let haveTopLeftCorner = true;
let haveTopLeftCorner = true;
// Check if we have a top left (right for RTL) corner. // Check if we have a top left (right for RTL) corner.
// I.e. if there is no monitor directly above or to the left(right) // I.e. if there is no monitor directly above or to the left(right)
let besideX = this._rtl ? monitor.x + 1 : cornerX - 1; let besideX = this._rtl ? monitor.x + 1 : cornerX - 1;
let besideY = cornerY; let besideY = cornerY;
let aboveX = cornerX; let aboveX = cornerX;
let aboveY = cornerY - 1; let aboveY = cornerY - 1;
for (let j = 0; j < this.monitors.length; j++) { for (let j = 0; j < this.monitors.length; j++) {
if (i == j) if (i == j)
continue;
let otherMonitor = this.monitors[j];
if (besideX >= otherMonitor.x &&
besideX < otherMonitor.x + otherMonitor.width &&
besideY >= otherMonitor.y &&
besideY < otherMonitor.y + otherMonitor.height) {
haveTopLeftCorner = false;
break;
}
if (aboveX >= otherMonitor.x &&
aboveX < otherMonitor.x + otherMonitor.width &&
aboveY >= otherMonitor.y &&
aboveY < otherMonitor.y + otherMonitor.height) {
haveTopLeftCorner = false;
break;
}
}
if (!haveTopLeftCorner)
continue; continue;
let otherMonitor = this.monitors[j];
if (besideX >= otherMonitor.x &&
besideX < otherMonitor.x + otherMonitor.width &&
besideY >= otherMonitor.y &&
besideY < otherMonitor.y + otherMonitor.height) {
haveTopLeftCorner = false;
break;
}
if (aboveX >= otherMonitor.x &&
aboveX < otherMonitor.x + otherMonitor.width &&
aboveY >= otherMonitor.y &&
aboveY < otherMonitor.y + otherMonitor.height) {
haveTopLeftCorner = false;
break;
}
} }
let corner = new HotCorner(this, cornerX, cornerY); if (!haveTopLeftCorner)
corner.setBarrierSize(size); continue;
this.hotCorners.push(corner);
}
this.emit('hot-corners-changed'); let corner = new HotCorner(this);
this._hotCorners.push(corner);
corner.actor.set_position(cornerX, cornerY);
this.addChrome(corner.actor);
}
}, },
_createBackground: function(monitorIndex) { _createBackground: function(monitorIndex) {
@@ -399,15 +394,16 @@ const LayoutManager = new Lang.Class({
}, },
_panelBoxChanged: function() { _panelBoxChanged: function() {
this._updatePanelBarrier(); this.emit('panel-box-changed');
this._updatePanelBarriers();
let size = this.panelBox.height;
this.hotCorners.forEach(function(corner) {
corner.setBarrierSize(size);
});
}, },
_updatePanelBarrier: function() { _updatePanelBarriers: function() {
if (this._leftPanelBarrier) {
this._leftPanelBarrier.destroy();
this._leftPanelBarrier = null;
}
if (this._rightPanelBarrier) { if (this._rightPanelBarrier) {
this._rightPanelBarrier.destroy(); this._rightPanelBarrier.destroy();
this._rightPanelBarrier = null; this._rightPanelBarrier = null;
@@ -416,6 +412,10 @@ const LayoutManager = new Lang.Class({
if (this.panelBox.height) { if (this.panelBox.height) {
let primary = this.primaryMonitor; let primary = this.primaryMonitor;
this._leftPanelBarrier = new Meta.Barrier({ display: global.display,
x1: primary.x, y1: primary.y,
x2: primary.x, y2: primary.y + this.panelBox.height,
directions: Meta.BarrierDirection.POSITIVE_X });
this._rightPanelBarrier = new Meta.Barrier({ display: global.display, this._rightPanelBarrier = new Meta.Barrier({ display: global.display,
x1: primary.x + primary.width, y1: primary.y, x1: primary.x + primary.width, y1: primary.y,
x2: primary.x + primary.width, y2: primary.y + this.panelBox.height, x2: primary.x + primary.width, y2: primary.y + this.panelBox.height,
@@ -423,41 +423,28 @@ const LayoutManager = new Lang.Class({
} }
}, },
_setupTrayPressure: function() {
this._trayPressure = new PressureBarrier(MESSAGE_TRAY_PRESSURE_THRESHOLD,
MESSAGE_TRAY_PRESSURE_TIMEOUT,
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW);
this._trayPressure.setEventFilter(this._trayBarrierEventFilter);
this._trayPressure.connect('trigger', function(barrier) {
Main.messageTray.openTray();
});
},
_updateTrayBarrier: function() { _updateTrayBarrier: function() {
let monitor = this.bottomMonitor; let monitor = this.bottomMonitor;
if (this._trayBarrier) { if (this._trayBarrier) {
this._trayPressure.removeBarrier(this._trayBarrier);
this._trayBarrier.destroy(); this._trayBarrier.destroy();
this._trayBarrier = null; this._trayBarrier = null;
} }
if (this._trayPressure) {
this._trayPressure.destroy();
this._trayPressure = null;
}
this._trayBarrier = new Meta.Barrier({ display: global.display, this._trayBarrier = new Meta.Barrier({ display: global.display,
x1: monitor.x, x2: monitor.x + monitor.width, x1: monitor.x, x2: monitor.x + monitor.width,
y1: monitor.y + monitor.height, y2: monitor.y + monitor.height, y1: monitor.y + monitor.height, y2: monitor.y + monitor.height,
directions: Meta.BarrierDirection.NEGATIVE_Y }); directions: Meta.BarrierDirection.NEGATIVE_Y });
this._trayPressure.addBarrier(this._trayBarrier);
},
_trayBarrierEventFilter: function(event) { this._trayPressure = new PressureBarrier(this._trayBarrier, MESSAGE_TRAY_PRESSURE_THRESHOLD, MESSAGE_TRAY_PRESSURE_TIMEOUT);
// Throw out all events where the pointer was grabbed by another this._trayPressure.connect('trigger', function(barrier) {
// client, as the client that grabbed the pointer expects to have Main.messageTray.openTray();
// complete control over it });
if (event.grabbed && Main.modalCount == 0)
return true;
return false;
}, },
_monitorsChanged: function() { _monitorsChanged: function() {
@@ -649,9 +636,9 @@ const LayoutManager = new Lang.Class({
if (!Main.sessionMode.isGreeter) if (!Main.sessionMode.isGreeter)
this._createSecondaryBackgrounds(); this._createSecondaryBackgrounds();
this._queueUpdateRegions(); this.emit('panel-box-changed');
this.emit('startup-complete'); this._queueUpdateRegions();
}, },
showKeyboard: function () { showKeyboard: function () {
@@ -1102,23 +1089,54 @@ Signals.addSignalMethods(LayoutManager.prototype);
const HotCorner = new Lang.Class({ const HotCorner = new Lang.Class({
Name: 'HotCorner', Name: 'HotCorner',
_init : function(layoutManager, x, y) { _init : function(layoutManager) {
// We use this flag to mark the case where the user has entered the // We use this flag to mark the case where the user has entered the
// hot corner and has not left both the hot corner and a surrounding // hot corner and has not left both the hot corner and a surrounding
// guard area (the "environs"). This avoids triggering the hot corner // guard area (the "environs"). This avoids triggering the hot corner
// multiple times due to an accidental jitter. // multiple times due to an accidental jitter.
this._entered = false; this._entered = false;
this._x = x; this.actor = new Clutter.Actor({ name: 'hot-corner-environs',
this._y = y; width: 3,
height: 3,
reactive: true });
this._setupFallbackCornerIfNeeded(); this._corner = new Clutter.Rectangle({ name: 'hot-corner',
width: 1,
height: 1,
opacity: 0,
reactive: true });
this._corner._delegate = this;
this._pressureBarrier = new PressureBarrier(HOT_CORNER_PRESSURE_THRESHOLD, this.actor.add_child(this._corner);
HOT_CORNER_PRESSURE_TIMEOUT,
Shell.KeyBindingMode.NORMAL | if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) {
Shell.KeyBindingMode.OVERVIEW); this._corner.set_position(this.actor.width - this._corner.width, 0);
this._pressureBarrier.connect('trigger', Lang.bind(this, this._toggleOverview)); 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));
// Cache the three ripples instead of dynamically creating and destroying them. // Cache the three ripples instead of dynamically creating and destroying them.
this._ripple1 = new St.BoxLayout({ style_class: 'ripple-box', opacity: 0, visible: false }); this._ripple1 = new St.BoxLayout({ style_class: 'ripple-box', opacity: 0, visible: false });
@@ -1130,74 +1148,8 @@ const HotCorner = new Lang.Class({
layoutManager.uiGroup.add_actor(this._ripple3); layoutManager.uiGroup.add_actor(this._ripple3);
}, },
setBarrierSize: function(size) {
if (this._verticalBarrier) {
this._pressureBarrier.removeBarrier(this._verticalBarrier);
this._verticalBarrier.destroy();
this._verticalBarrier = null;
}
if (this._horizontalBarrier) {
this._pressureBarrier.removeBarrier(this._horizontalBarrier);
this._horizontalBarrier.destroy();
this._horizontalBarrier = null;
}
if (size > 0) {
this._verticalBarrier = new Meta.Barrier({ display: global.display,
x1: this._x, x2: this._x, y1: this._y, y2: this._y + size,
directions: Meta.BarrierDirection.POSITIVE_X });
this._horizontalBarrier = new Meta.Barrier({ display: global.display,
x1: this._x, x2: this._x + size, y1: this._y, y2: this._y,
directions: Meta.BarrierDirection.POSITIVE_Y });
this._pressureBarrier.addBarrier(this._verticalBarrier);
this._pressureBarrier.addBarrier(this._horizontalBarrier);
}
},
_setupFallbackCornerIfNeeded: function() {
if (!global.display.supports_extended_barriers()) {
this.actor = new Clutter.Actor({ name: 'hot-corner-environs',
x: x, y: y,
width: 3,
height: 3,
reactive: true });
this._corner = new Clutter.Rectangle({ name: 'hot-corner',
width: 1,
height: 1,
opacity: 0,
reactive: true });
this._corner._delegate = this;
this.actor.add_child(this._corner);
layoutManager.addChrome(this.actor);
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) {
this._corner.set_position(this.actor.width - this._corner.width, 0);
this.actor.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
} else {
this._corner.set_position(0, 0);
}
this.actor.connect('leave-event',
Lang.bind(this, this._onEnvironsLeft));
this._corner.connect('enter-event',
Lang.bind(this, this._onCornerEntered));
this._corner.connect('leave-event',
Lang.bind(this, this._onCornerLeft));
}
},
destroy: function() { destroy: function() {
this.setBarrierSize(0); this.actor.destroy();
this._pressureBarrier.destroy();
this._pressureBarrier = null;
if (this.actor)
this.actor.destroy();
}, },
_animRipple : function(ripple, delay, time, startScale, startOpacity, finalScale) { _animRipple : function(ripple, delay, time, startScale, startOpacity, finalScale) {
@@ -1217,8 +1169,9 @@ const HotCorner = new Lang.Class({
ripple.opacity = 255 * Math.sqrt(startOpacity); ripple.opacity = 255 * Math.sqrt(startOpacity);
ripple.scale_x = ripple.scale_y = startScale; ripple.scale_x = ripple.scale_y = startScale;
ripple.x = this._x; let [x, y] = this._corner.get_transformed_position();
ripple.y = this._y; ripple.x = x;
ripple.y = y;
Tweener.addTween(ripple, { _opacity: 0, Tweener.addTween(ripple, { _opacity: 0,
scale_x: finalScale, scale_x: finalScale,
@@ -1230,7 +1183,7 @@ const HotCorner = new Lang.Class({
onComplete: function() { ripple.visible = false; } }); onComplete: function() { ripple.visible = false; } });
}, },
_rippleAnimation: function() { rippleAnimation: function() {
// Show three concentric ripples expanding outwards; the exact // Show three concentric ripples expanding outwards; the exact
// parameters were found by trial and error, so don't look // parameters were found by trial and error, so don't look
// for them to make perfect sense mathematically // for them to make perfect sense mathematically
@@ -1241,28 +1194,37 @@ const HotCorner = new Lang.Class({
this._animRipple(this._ripple3, 0.35, 1.0, 0.0, 0.3, 1); this._animRipple(this._ripple3, 0.35, 1.0, 0.0, 0.3, 1);
}, },
_toggleOverview: function() {
if (Main.overview.shouldToggleByCornerOrButton()) {
this._rippleAnimation();
Main.overview.toggle();
}
},
handleDragOver: function(source, actor, x, y, time) { handleDragOver: function(source, actor, x, y, time) {
if (source != Main.xdndHandler) if (source != Main.xdndHandler)
return DND.DragMotionResult.CONTINUE; return DND.DragMotionResult.CONTINUE;
if (!Main.overview.visible && !Main.overview.animationInProgress) {
this.rippleAnimation();
Main.overview.showTemporarily();
}
return DND.DragMotionResult.CONTINUE; return DND.DragMotionResult.CONTINUE;
}, },
_onCornerEntered : function() { _onCornerEntered : function() {
if (!this._entered) { if (!this._entered) {
this._entered = true; this._entered = true;
this._toggleOverview(); if (!Main.overview.animationInProgress) {
this._activationTime = Date.now() / 1000;
this.rippleAnimation();
Main.overview.toggle();
}
} }
return false; return false;
}, },
_onCornerClicked : function() {
if (this.shouldToggleOverviewOnClick())
Main.overview.toggle();
return true;
},
_onCornerLeft : function(actor, event) { _onCornerLeft : function(actor, event) {
if (event.get_related() != this.actor) if (event.get_related() != this.actor)
this._entered = false; this._entered = false;
@@ -1274,47 +1236,42 @@ const HotCorner = new Lang.Class({
if (event.get_related() != this._corner) if (event.get_related() != this._corner)
this._entered = false; this._entered = false;
return false; return false;
},
// Checks if the Activities button is currently sensitive to
// clicks. The first call to this function within the
// HOT_CORNER_ACTIVATION_TIMEOUT time of the hot corner being
// triggered will return false. This avoids opening and closing
// the overview if the user both triggered the hot corner and
// clicked the Activities button.
shouldToggleOverviewOnClick: function() {
if (Main.overview.animationInProgress)
return false;
if (this._activationTime == 0 || Date.now() / 1000 - this._activationTime > HOT_CORNER_ACTIVATION_TIMEOUT)
return true;
return false;
} }
}); });
const PressureBarrier = new Lang.Class({ const PressureBarrier = new Lang.Class({
Name: 'PressureBarrier', Name: 'PressureBarrier',
_init: function(threshold, timeout, keybindingMode) { _init: function(barrier, threshold, timeout) {
this._barrier = barrier;
this._threshold = threshold; this._threshold = threshold;
this._timeout = timeout; this._timeout = timeout;
this._keybindingMode = keybindingMode; this._orientation = (barrier.y1 == barrier.y2) ? Clutter.Orientation.HORIZONTAL : Clutter.Orientation.VERTICAL;
this._barriers = [];
this._eventFilter = null;
this._isTriggered = false;
this._reset(); this._reset();
},
addBarrier: function(barrier) { this._barrierHitId = this._barrier.connect('hit', Lang.bind(this, this._onBarrierHit));
barrier._pressureHitId = barrier.connect('hit', Lang.bind(this, this._onBarrierHit)); this._barrierLeftId = this._barrier.connect('left', Lang.bind(this, this._onBarrierLeft));
barrier._pressureLeftId = barrier.connect('left', Lang.bind(this, this._onBarrierLeft));
this._barriers.push(barrier);
},
_disconnectBarrier: function(barrier) {
barrier.disconnect(barrier._pressureHitId);
barrier.disconnect(barrier._pressureLeftId);
},
removeBarrier: function(barrier) {
this._disconnectBarrier(barrier);
this._barriers.splice(this._barriers.indexOf(barrier), 1);
}, },
destroy: function() { destroy: function() {
this._barriers.forEach(Lang.bind(this, this._disconnectBarrier)); this._barrier.disconnect(this._barrierHitId);
this._barriers = []; this._barrier.disconnect(this._barrierLeftId);
}, this._barrier = null;
setEventFilter: function(filter) {
this._eventFilter = filter;
}, },
_reset: function() { _reset: function() {
@@ -1323,34 +1280,33 @@ const PressureBarrier = new Lang.Class({
this._lastTime = 0; this._lastTime = 0;
}, },
_isHorizontal: function(barrier) { _getDistanceAcrossBarrier: function(event) {
return barrier.y1 == barrier.y2; if (this._orientation == Clutter.Orientation.HORIZONTAL)
},
_getDistanceAcrossBarrier: function(barrier, event) {
if (this._isHorizontal(barrier))
return Math.abs(event.dy); return Math.abs(event.dy);
else else
return Math.abs(event.dx); return Math.abs(event.dx);
}, },
_getDistanceAlongBarrier: function(barrier, event) { _getDistanceAlongBarrier: function(event) {
if (this._isHorizontal(barrier)) if (this._orientation == Clutter.Orientation.HORIZONTAL)
return Math.abs(event.dx); return Math.abs(event.dx);
else else
return Math.abs(event.dy); return Math.abs(event.dy);
}, },
_isBarrierEventTooOld: function(event) {
// Ignore all events older than this time
let threshold = this._lastTime - this._timeout;
return event.time < threshold;
},
_trimBarrierEvents: function() { _trimBarrierEvents: function() {
// Events are guaranteed to be sorted in time order from // Events are guaranteed to be sorted in time order from
// oldest to newest, so just look for the first old event, // oldest to newest, so just look for the first old event,
// and then chop events after that off. // and then chop events after that off.
let i = 0; let i = 0;
let threshold = this._lastTime - this._timeout;
while (i < this._barrierEvents.length) { while (i < this._barrierEvents.length) {
let [time, distance] = this._barrierEvents[i]; if (!this._isBarrierEventTooOld(this._barrierEvents[i]))
if (time >= threshold)
break; break;
i++; i++;
} }
@@ -1358,8 +1314,7 @@ const PressureBarrier = new Lang.Class({
let firstNewEvent = i; let firstNewEvent = i;
for (i = 0; i < firstNewEvent; i++) { for (i = 0; i < firstNewEvent; i++) {
let [time, distance] = this._barrierEvents[i]; this._currentPressure -= this._getDistanceAcrossBarrier(this._barrierEvents[i]);
this._currentPressure -= distance;
} }
this._barrierEvents = this._barrierEvents.slice(firstNewEvent); this._barrierEvents = this._barrierEvents.slice(firstNewEvent);
@@ -1367,30 +1322,29 @@ const PressureBarrier = new Lang.Class({
_onBarrierLeft: function(barrier, event) { _onBarrierLeft: function(barrier, event) {
this._reset(); this._reset();
this._isTriggered = false;
}, },
_trigger: function() { _trigger: function() {
this._isTriggered = true;
this.emit('trigger'); this.emit('trigger');
this._reset(); this._reset();
}, },
_onBarrierHit: function(barrier, event) { _onBarrierHit: function(barrier, event) {
// If we've triggered the barrier, wait until the pointer has the // Throw out all events where the pointer was grabbed by another
// left the barrier hitbox until we trigger it again. // client, as the client that grabbed the pointer expects to have
if (this._isTriggered) // complete control over it
if (event.grabbed && Main.modalCount == 0)
return; return;
if (this._eventFilter && this._eventFilter(event)) let isOverview = ((Main.keybindingMode & (Shell.KeyBindingMode.OVERVIEW)) != 0);
// Throw out events where the grab is taken by something that's
// not the overview (modal dialogs, etc.)
if (event.grabbed && !isOverview)
return; return;
// Throw out all events not in the proper keybinding mode let slide = this._getDistanceAlongBarrier(event);
if (!(this._keybindingMode & Main.keybindingMode)) let distance = this._getDistanceAcrossBarrier(event);
return;
let slide = this._getDistanceAlongBarrier(barrier, event);
let distance = this._getDistanceAcrossBarrier(barrier, event);
if (distance >= this._threshold) { if (distance >= this._threshold) {
this._trigger(); this._trigger();
@@ -1406,10 +1360,8 @@ const PressureBarrier = new Lang.Class({
this._lastTime = event.time; this._lastTime = event.time;
this._trimBarrierEvents(); this._trimBarrierEvents();
distance = Math.min(15, distance); this._barrierEvents.push(event);
this._currentPressure += Math.min(15, distance);
this._barrierEvents.push([event.time, distance]);
this._currentPressure += distance;
if (this._currentPressure >= this._threshold) if (this._currentPressure >= this._threshold)
this._trigger(); this._trigger();

View File

@@ -58,7 +58,7 @@ let shellDBusService = null;
let shellMountOpDBusService = null; let shellMountOpDBusService = null;
let screenSaverDBus = null; let screenSaverDBus = null;
let modalCount = 0; let modalCount = 0;
let keybindingMode = Shell.KeyBindingMode.NONE; let keybindingMode = Shell.KeyBindingMode.NORMAL;
let modalActorFocusStack = []; let modalActorFocusStack = [];
let uiGroup = null; let uiGroup = null;
let magnifier = null; let magnifier = null;
@@ -91,6 +91,9 @@ function start() {
global.logError = window.log; global.logError = window.log;
global.log = window.log; global.log = window.log;
// Hide the stage until we're ready for it
global.stage.hide();
// Chain up async errors reported from C // Chain up async errors reported from C
global.connect('notify-error', function (global, msg, detail) { notifyError(msg, detail); }); global.connect('notify-error', function (global, msg, detail) { notifyError(msg, detail); });
@@ -145,6 +148,8 @@ function startSession() {
else else
screenShield = new ScreenShield.ScreenShieldFallback(); screenShield = new ScreenShield.ScreenShieldFallback();
// The message tray relies on being constructed
// after the panel.
panel = new Panel.Panel(); panel = new Panel.Panel();
messageTray = new MessageTray.MessageTray(); messageTray = new MessageTray.MessageTray();
keyboard = new Keyboard.Keyboard(); keyboard = new Keyboard.Keyboard();
@@ -159,6 +164,8 @@ function startSession() {
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT, global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT,
false, -1, 1); false, -1, 1);
global.display.connect('overlay-key', Lang.bind(overview, overview.toggle)); global.display.connect('overlay-key', Lang.bind(overview, overview.toggle));
sessionMode.connect('updated', _sessionUpdated);
_sessionUpdated();
// Provide the bus object for gnome-session to // Provide the bus object for gnome-session to
// initiate logouts. // initiate logouts.
@@ -195,14 +202,6 @@ function startSession() {
layoutManager.connect('startup-prepared', function() { layoutManager.connect('startup-prepared', function() {
layoutManager.startupAnimation(); layoutManager.startupAnimation();
}); });
layoutManager.connect('startup-complete', function() {
if (keybindingMode == Shell.KeyBindingMode.NONE) {
keybindingMode = Shell.KeyBindingMode.NORMAL;
}
sessionMode.connect('updated', _sessionUpdated);
_sessionUpdated();
});
} }
let _workspaces = []; let _workspaces = [];

View File

@@ -1672,13 +1672,14 @@ const MessageTray = new Lang.Class({
Main.layoutManager.trackChrome(this._closeButton); Main.layoutManager.trackChrome(this._closeButton);
Main.layoutManager.connect('primary-fullscreen-changed', Lang.bind(this, this._onFullscreenChanged)); Main.layoutManager.connect('primary-fullscreen-changed', Lang.bind(this, this._onFullscreenChanged));
Main.layoutManager.connect('hot-corners-changed', Lang.bind(this, this._hotCornersChanged));
// If the overview shows or hides while we're in // If the overview shows or hides while we're in
// the message tray, revert back to normal mode. // the message tray, revert back to normal mode.
Main.overview.connect('showing', Lang.bind(this, this._escapeTray)); Main.overview.connect('showing', Lang.bind(this, this._escapeTray));
Main.overview.connect('hiding', Lang.bind(this, this._escapeTray)); Main.overview.connect('hiding', Lang.bind(this, this._escapeTray));
// Track if we've added the activities button
this._activitiesButtonAdded = false;
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated)); Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
Main.wm.addKeybinding('toggle-message-tray', Main.wm.addKeybinding('toggle-message-tray',
@@ -1702,7 +1703,6 @@ const MessageTray = new Lang.Class({
this._trayDwellTimeoutId = 0; this._trayDwellTimeoutId = 0;
this._setupTrayDwellIfNeeded(); this._setupTrayDwellIfNeeded();
this._sessionUpdated(); this._sessionUpdated();
this._hotCornersChanged();
this._noMessages = new St.Label({ text: _("No Messages"), this._noMessages = new St.Label({ text: _("No Messages"),
style_class: 'no-messages-label', style_class: 'no-messages-label',
@@ -1775,6 +1775,11 @@ const MessageTray = new Lang.Class({
}, },
_sessionUpdated: function() { _sessionUpdated: function() {
if (!this._activitiesButtonAdded && Main.panel.statusArea.activities) {
this._activitiesButtonAdded = true;
this._grabHelper.addActor(Main.panel.statusArea.activities.hotCorner.actor);
}
if ((Main.sessionMode.isLocked || Main.sessionMode.isGreeter) && this._inCtrlAltTab) { if ((Main.sessionMode.isLocked || Main.sessionMode.isGreeter) && this._inCtrlAltTab) {
Main.ctrlAltTabManager.removeGroup(this._summary); Main.ctrlAltTabManager.removeGroup(this._summary);
this._inCtrlAltTab = false; this._inCtrlAltTab = false;
@@ -2096,13 +2101,6 @@ const MessageTray = new Lang.Class({
this._updateState(); 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() { _onTrayHoverChanged: function() {
if (this.actor.hover) { if (this.actor.hover) {
// No dwell inside notifications at the bottom of the screen // No dwell inside notifications at the bottom of the screen

View File

@@ -33,8 +33,6 @@ const SHADE_ANIMATION_TIME = .20;
const DND_WINDOW_SWITCH_TIMEOUT = 1250; const DND_WINDOW_SWITCH_TIMEOUT = 1250;
const OVERVIEW_ACTIVATION_TIMEOUT = 0.5;
const ShellInfo = new Lang.Class({ const ShellInfo = new Lang.Class({
Name: 'ShellInfo', Name: 'ShellInfo',
@@ -148,10 +146,9 @@ const Overview = new Lang.Class({
this._backgroundGroup.hide(); this._backgroundGroup.hide();
this._bgManagers = []; this._bgManagers = [];
this._activationTime = 0;
this.visible = false; // animating to overview, in overview, animating out this.visible = false; // animating to overview, in overview, animating out
this._shown = false; // show() and not hide() this._shown = false; // show() and not hide()
this._shownTemporarily = false; // showTemporarily() and not hideTemporarily()
this._modal = false; // have a modal grab this._modal = false; // have a modal grab
this.animationInProgress = false; this.animationInProgress = false;
this.visibleTarget = false; this.visibleTarget = false;
@@ -349,22 +346,18 @@ const Overview = new Lang.Class({
}, },
_onDragBegin: function() { _onDragBegin: function() {
this._inXdndDrag = true;
DND.addDragMonitor(this._dragMonitor); DND.addDragMonitor(this._dragMonitor);
// Remember the workspace we started from // Remember the workspace we started from
this._lastActiveWorkspaceIndex = global.screen.get_active_workspace_index(); this._lastActiveWorkspaceIndex = global.screen.get_active_workspace_index();
}, },
_onDragEnd: function(time) { _onDragEnd: function(time) {
this._inXdndDrag = false;
// In case the drag was canceled while in the overview // In case the drag was canceled while in the overview
// we have to go back to where we started and hide // we have to go back to where we started and hide
// the overview // the overview
if (this._shown) { if (this._shownTemporarily) {
global.screen.get_workspace_by_index(this._lastActiveWorkspaceIndex).activate(time); global.screen.get_workspace_by_index(this._lastActiveWorkspaceIndex).activate(time);
this.hide(); this.hideTemporarily();
} }
this._resetWindowSwitchTimeout(); this._resetWindowSwitchTimeout();
this._lastHoveredWindow = null; this._lastHoveredWindow = null;
@@ -412,7 +405,7 @@ const Overview = new Lang.Class({
this._needsFakePointerEvent = true; this._needsFakePointerEvent = true;
Main.activateWindow(dragEvent.targetActor._delegate.metaWindow, Main.activateWindow(dragEvent.targetActor._delegate.metaWindow,
this._windowSwitchTimestamp); this._windowSwitchTimestamp);
this.hide(); this.hideTemporarily();
this._lastHoveredWindow = null; this._lastHoveredWindow = null;
})); }));
} }
@@ -508,10 +501,9 @@ const Overview = new Lang.Class({
if (this._shown) if (this._shown)
return; return;
this._shown = true; this._shown = true;
this._syncInputMode();
if (!this._syncInputMode()) if (!this._modal)
return; return;
this._animateVisible(); this._animateVisible();
}, },
@@ -544,7 +536,6 @@ const Overview = new Lang.Class({
this.visible = true; this.visible = true;
this.animationInProgress = true; this.animationInProgress = true;
this.visibleTarget = true; this.visibleTarget = true;
this._activationTime = Date.now() / 1000;
// All the the actors in the window group are completely obscured, // All the the actors in the window group are completely obscured,
// hiding the group holding them while the Overview is displayed greatly // hiding the group holding them while the Overview is displayed greatly
@@ -577,6 +568,24 @@ const Overview = new Lang.Class({
this.emit('showing'); this.emit('showing');
}, },
// showTemporarily:
//
// Animates the overview visible without grabbing mouse and keyboard input;
// if show() has already been called, this has no immediate effect, but
// will result in the overview not being hidden until hideTemporarily() is
// called.
showTemporarily: function() {
if (this.isDummy)
return;
if (this._shownTemporarily)
return;
this._syncInputMode();
this._animateVisible();
this._shownTemporarily = true;
},
// hide: // hide:
// //
// Reverses the effect of show() // Reverses the effect of show()
@@ -590,12 +599,30 @@ const Overview = new Lang.Class({
if (this._controlPressed) if (this._controlPressed)
return; return;
this._animateNotVisible(); if (!this._shownTemporarily)
this._animateNotVisible();
this._shown = false; this._shown = false;
this._syncInputMode(); this._syncInputMode();
}, },
// hideTemporarily:
//
// Reverses the effect of showTemporarily()
hideTemporarily: function() {
if (this.isDummy)
return;
if (!this._shownTemporarily)
return;
if (!this._shown)
this._animateNotVisible();
this._shownTemporarily = false;
this._syncInputMode();
},
toggle: function() { toggle: function() {
if (this.isDummy) if (this.isDummy)
return; return;
@@ -606,20 +633,6 @@ const Overview = new Lang.Class({
this.show(); this.show();
}, },
// Checks if the Activities button is currently sensitive to
// clicks. The first call to this function within the
// OVERVIEW_ACTIVATION_TIMEOUT time of the hot corner being
// triggered will return false. This avoids opening and closing
// the overview if the user both triggered the hot corner and
// clicked the Activities button.
shouldToggleByCornerOrButton: function() {
if (this.animationInProgress)
return false;
if (this._activationTime == 0 || Date.now() / 1000 - this._activationTime > OVERVIEW_ACTIVATION_TIMEOUT)
return true;
return false;
},
//// Private methods //// //// Private methods ////
_syncInputMode: function() { _syncInputMode: function() {
@@ -627,23 +640,22 @@ const Overview = new Lang.Class({
// overview we don't have a problem with the release of a press/release // overview we don't have a problem with the release of a press/release
// going to an application. // going to an application.
if (this.animationInProgress) if (this.animationInProgress)
return true; return;
if (this._shown) { if (this._shown) {
let shouldBeModal = !this._inXdndDrag; if (!this._modal) {
if (shouldBeModal) { if (Main.pushModal(this._overview,
if (!this._modal) { { keybindingMode: Shell.KeyBindingMode.OVERVIEW }))
if (Main.pushModal(this._overview, this._modal = true;
{ keybindingMode: Shell.KeyBindingMode.OVERVIEW })) { else
this._modal = true; this.hide();
} else {
this.hide();
return false;
}
}
} else {
global.stage_input_mode = Shell.StageInputMode.FULLSCREEN;
} }
} else if (this._shownTemporarily) {
if (this._modal) {
Main.popModal(this._overview);
this._modal = false;
}
global.stage_input_mode = Shell.StageInputMode.FULLSCREEN;
} else { } else {
if (this._modal) { if (this._modal) {
Main.popModal(this._overview); Main.popModal(this._overview);
@@ -652,7 +664,6 @@ const Overview = new Lang.Class({
else if (global.stage_input_mode == Shell.StageInputMode.FULLSCREEN) else if (global.stage_input_mode == Shell.StageInputMode.FULLSCREEN)
global.stage_input_mode = Shell.StageInputMode.NORMAL; global.stage_input_mode = Shell.StageInputMode.NORMAL;
} }
return true;
}, },
_animateNotVisible: function() { _animateNotVisible: function() {
@@ -686,7 +697,7 @@ const Overview = new Lang.Class({
this.emit('shown'); this.emit('shown');
// Handle any calls to hide* while we were showing // Handle any calls to hide* while we were showing
if (!this._shown) if (!this._shown && !this._shownTemporarily)
this._animateNotVisible(); this._animateNotVisible();
this._syncInputMode(); this._syncInputMode();
@@ -712,7 +723,7 @@ const Overview = new Lang.Class({
this.emit('hidden'); this.emit('hidden');
// Handle any calls to show* while we were hiding // Handle any calls to show* while we were hiding
if (this._shown) if (this._shown || this._shownTemporarily)
this._animateVisible(); this._animateVisible();
this._syncInputMode(); this._syncInputMode();

View File

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

View File

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

View File

@@ -337,14 +337,14 @@ const InputSourceIndicator = new Lang.Class({
Main.wm.addKeybinding('switch-input-source', Main.wm.addKeybinding('switch-input-source',
new Gio.Settings({ schema: "org.gnome.desktop.wm.keybindings" }), new Gio.Settings({ schema: "org.gnome.desktop.wm.keybindings" }),
Meta.KeyBindingFlags.REVERSES, Meta.KeyBindingFlags.REVERSES,
Shell.KeyBindingMode.ALL & ~Shell.KeyBindingMode.MESSAGE_TRAY, Shell.KeyBindingMode.ALL,
Lang.bind(this, this._switchInputSource)); Lang.bind(this, this._switchInputSource));
this._keybindingActionBackward = this._keybindingActionBackward =
Main.wm.addKeybinding('switch-input-source-backward', Main.wm.addKeybinding('switch-input-source-backward',
new Gio.Settings({ schema: "org.gnome.desktop.wm.keybindings" }), new Gio.Settings({ schema: "org.gnome.desktop.wm.keybindings" }),
Meta.KeyBindingFlags.REVERSES | Meta.KeyBindingFlags.REVERSES |
Meta.KeyBindingFlags.REVERSED, Meta.KeyBindingFlags.REVERSED,
Shell.KeyBindingMode.ALL & ~Shell.KeyBindingMode.MESSAGE_TRAY, Shell.KeyBindingMode.ALL,
Lang.bind(this, this._switchInputSource)); Lang.bind(this, this._switchInputSource));
this._settings = new Gio.Settings({ schema: DESKTOP_INPUT_SOURCES_SCHEMA }); this._settings = new Gio.Settings({ schema: DESKTOP_INPUT_SOURCES_SCHEMA });
this._settings.connect('changed::' + KEY_CURRENT_INPUT_SOURCE, Lang.bind(this, this._currentInputSourceChanged)); this._settings.connect('changed::' + KEY_CURRENT_INPUT_SOURCE, Lang.bind(this, this._currentInputSourceChanged));

View File

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

View File

@@ -15,6 +15,7 @@ const Main = imports.ui.main;
const Overview = imports.ui.overview; const Overview = imports.ui.overview;
const Panel = imports.ui.panel; const Panel = imports.ui.panel;
const Tweener = imports.ui.tweener; const Tweener = imports.ui.tweener;
const WindowManager = imports.ui.windowManager;
const FOCUS_ANIMATION_TIME = 0.15; const FOCUS_ANIMATION_TIME = 0.15;
@@ -41,6 +42,68 @@ function _interpolate(start, end, step) {
return start + (end - start) * step; return start + (end - start) * step;
} }
const WindowCloneLayout = new Lang.Class({
Name: 'WindowCloneLayout',
Extends: Clutter.LayoutManager,
_init: function(boundingBox) {
this.parent();
this._boundingBox = boundingBox;
},
get boundingBox() {
return this._boundingBox;
},
set boundingBox(b) {
this._boundingBox = b;
this.layout_changed();
},
_makeBoxForWindow: function(window) {
// We need to adjust the position of the actor because of the
// consequences of invisible borders -- in reality, the texture
// has an extra set of "padding" around it that we need to trim
// down.
// The outer rect (from which we compute the bounding box)
// paradoxically is the smaller rectangle, containing the positions
// of the visible frame. The input rect contains everything,
// including the invisible border padding.
let inputRect = window.get_input_rect();
let box = new Clutter.ActorBox();
box.set_origin(inputRect.x - this._boundingBox.x,
inputRect.y - this._boundingBox.y);
box.set_size(inputRect.width, inputRect.height);
return box;
},
vfunc_get_preferred_height: function(container, forWidth) {
return [this._boundingBox.height, this._boundingBox.height];
},
vfunc_get_preferred_width: function(container, forHeight) {
return [this._boundingBox.width, this._boundingBox.width];
},
vfunc_allocate: function(container, box, flags) {
let clone = container.get_children().forEach(function (child) {
let realWindow;
if (child == container._delegate._windowClone)
realWindow = container._delegate.realWindow;
else
realWindow = child.source;
child.allocate(this._makeBoxForWindow(realWindow.meta_window),
flags);
}, this);
},
});
const WindowClone = new Lang.Class({ const WindowClone = new Lang.Class({
Name: 'WindowClone', Name: 'WindowClone',
@@ -50,10 +113,7 @@ const WindowClone = new Lang.Class({
this.metaWindow._delegate = this; this.metaWindow._delegate = this;
this._workspace = workspace; this._workspace = workspace;
let [borderX, borderY] = this._getInvisibleBorderPadding(); this._windowClone = new Clutter.Clone({ source: realWindow.get_texture() });
this._windowClone = new Clutter.Clone({ source: realWindow.get_texture(),
x: -borderX,
y: -borderY });
// We expect this.actor to be used for all interaction rather than // We expect this.actor to be used for all interaction rather than
// this._windowClone; as the former is reactive and the latter // this._windowClone; as the former is reactive and the latter
// is not, this just works for most cases. However, for DND all // is not, this just works for most cases. However, for DND all
@@ -61,20 +121,15 @@ const WindowClone = new Lang.Class({
// To avoid this, we hide it from pick. // To avoid this, we hide it from pick.
Shell.util_set_hidden_from_pick(this._windowClone, true); Shell.util_set_hidden_from_pick(this._windowClone, true);
this.origX = realWindow.x + borderX;
this.origY = realWindow.y + borderY;
let outerRect = realWindow.meta_window.get_outer_rect();
// The MetaShapedTexture that we clone has a size that includes // The MetaShapedTexture that we clone has a size that includes
// the invisible border; this is inconvenient; rather than trying // the invisible border; this is inconvenient; rather than trying
// to compensate all over the place we insert a ClutterActor into // to compensate all over the place we insert a custom container into
// the hierarchy that is sized to only the visible portion. // the hierarchy that is sized to only the visible portion.
// As usual, we cannot use a ShellGenericContainer or StWidget here,
// because Workspace plays dirty tricks with reparenting to do DNDs
// and scroll-to-zoom.
this.actor = new Clutter.Actor({ reactive: true, this.actor = new Clutter.Actor({ reactive: true,
x: this.origX, layout_manager: new WindowCloneLayout() });
y: this.origY,
width: outerRect.width,
height: outerRect.height });
this.actor.add_child(this._windowClone); this.actor.add_child(this._windowClone);
@@ -84,10 +139,19 @@ const WindowClone = new Lang.Class({
this._dragSlot = [0, 0, 0, 0]; this._dragSlot = [0, 0, 0, 0];
this._stackAbove = null; this._stackAbove = null;
this._sizeChangedId = this.realWindow.connect('size-changed', this._windowClone._updateId = this.realWindow.connect('size-changed',
Lang.bind(this, this._onRealWindowSizeChanged)); Lang.bind(this, this._onRealWindowSizeChanged));
this._realWindowDestroyId = this.realWindow.connect('destroy', this._windowClone._destroyId = this.realWindow.connect('destroy', Lang.bind(this, function() {
Lang.bind(this, this._disconnectRealWindowSignals)); // First destroy the clone and then destroy everything
// This will ensure that we never see it in the _disconnectSignals loop
this._windowClone.destroy();
this.destroy();
}));
this._updateAttachedDialogs();
this._computeBoundingBox();
this.actor.x = this._boundingBox.x;
this.actor.y = this._boundingBox.y;
let clickAction = new Clutter.ClickAction(); let clickAction = new Clutter.ClickAction();
clickAction.connect('clicked', Lang.bind(this, this._onClicked)); clickAction.connect('clicked', Lang.bind(this, this._onClicked));
@@ -121,6 +185,97 @@ const WindowClone = new Lang.Class({
return this._slot; return this._slot;
}, },
deleteAll: function() {
// Delete all windows, starting from the bottom-most (most-modal) one
let windows = this.actor.get_children();
for (let i = windows.length - 1; i >= 1; i--) {
let realWindow = windows[i].source;
let metaWindow = realWindow.meta_window;
metaWindow.delete(global.get_current_time());
}
this.metaWindow.delete(global.get_current_time());
},
addAttachedDialog: function(win) {
this._doAddAttachedDialog(win, win.get_compositor_private());
this._computeBoundingBox();
this._updateDimmer();
this.emit('size-changed');
},
_doAddAttachedDialog: function(metaWin, realWin) {
let clone = new Clutter.Clone({ source: realWin });
clone._updateId = realWin.connect('size-changed', Lang.bind(this, function() {
this._computeBoundingBox();
this.emit('size-changed');
}));
clone._destroyId = realWin.connect('destroy', Lang.bind(this, function() {
clone.destroy();
this._computeBoundingBox();
this._updateDimmer();
this.emit('size-changed');
}));
this.actor.add_child(clone);
},
_updateAttachedDialogs: function() {
let iter = Lang.bind(this, function(win) {
let actor = win.get_compositor_private();
if (!actor)
return false;
if (!win.is_attached_dialog())
return false;
this._doAddAttachedDialog(win, actor);
win.foreach_transient(iter);
return true;
});
this.metaWindow.foreach_transient(iter);
this._dimmer = new WindowManager.WindowDimmer(this._windowClone);
this._updateDimmer();
},
_updateDimmer: function() {
if (this.actor.get_n_children() > 1) {
this._dimmer.setEnabled(true);
this._dimmer.dimFactor = 1.0;
} else {
this._dimmer.setEnabled(false);
}
},
get boundingBox() {
return this._boundingBox;
},
getOriginalPosition: function() {
return [this._boundingBox.x, this._boundingBox.y];
},
_computeBoundingBox: function() {
let rect = this.metaWindow.get_outer_rect();
this.actor.get_children().forEach(function (child) {
let realWindow;
if (child == this._windowClone)
realWindow = this.realWindow;
else
realWindow = child.source;
let metaWindow = realWindow.meta_window;
rect = rect.union(metaWindow.get_outer_rect());
}, this);
this._boundingBox = rect;
this.actor.layout_manager.boundingBox = rect;
},
setStackAbove: function (actor) { setStackAbove: function (actor) {
this._stackAbove = actor; this._stackAbove = actor;
if (this.inDrag) if (this.inDrag)
@@ -136,44 +291,26 @@ const WindowClone = new Lang.Class({
this.actor.destroy(); this.actor.destroy();
}, },
_disconnectRealWindowSignals: function() { _disconnectSignals: function() {
if (this._sizeChangedId > 0) this.actor.get_children().forEach(Lang.bind(this, function (child) {
this.realWindow.disconnect(this._sizeChangedId); let realWindow;
this._sizeChangedId = 0; if (child == this._windowClone)
realWindow = this.realWindow;
else
realWindow = child.source;
if (this._realWindowDestroyId > 0) realWindow.disconnect(child._updateId);
this.realWindow.disconnect(this._realWindowDestroyId); realWindow.disconnect(child._destroyId);
this._realWindowDestroyId = 0; }));
},
_getInvisibleBorderPadding: function() {
// We need to adjust the position of the actor because of the
// consequences of invisible borders -- in reality, the texture
// has an extra set of "padding" around it that we need to trim
// down.
// The outer rect paradoxically is the smaller rectangle,
// containing the positions of the visible frame. The input
// rect contains everything, including the invisible border
// padding.
let outerRect = this.metaWindow.get_outer_rect();
let inputRect = this.metaWindow.get_input_rect();
let [borderX, borderY] = [outerRect.x - inputRect.x,
outerRect.y - inputRect.y];
return [borderX, borderY];
}, },
_onRealWindowSizeChanged: function() { _onRealWindowSizeChanged: function() {
let [borderX, borderY] = this._getInvisibleBorderPadding(); this._computeBoundingBox();
let outerRect = this.metaWindow.get_outer_rect();
this.actor.set_size(outerRect.width, outerRect.height);
this._windowClone.set_position(-borderX, -borderY);
this.emit('size-changed'); this.emit('size-changed');
}, },
_onDestroy: function() { _onDestroy: function() {
this._disconnectRealWindowSignals(); this._disconnectSignals();
this.metaWindow._delegate = null; this.metaWindow._delegate = null;
this.actor._delegate = null; this.actor._delegate = null;
@@ -432,7 +569,7 @@ const WindowOverlay = new Lang.Class({
Lang.bind(this, Lang.bind(this,
this._onWindowAdded)); this._onWindowAdded));
metaWindow.delete(global.get_current_time()); this._windowClone.deleteAll();
}, },
_onWindowAdded: function(workspace, win) { _onWindowAdded: function(workspace, win) {
@@ -1220,9 +1357,29 @@ const Workspace = new Lang.Class({
if (this._lookupIndex (metaWin) != -1) if (this._lookupIndex (metaWin) != -1)
return; return;
if (!this._isMyWindow(win) || !this._isOverviewWindow(win)) if (!this._isMyWindow(win))
return; return;
if (!this._isOverviewWindow(win)) {
if (metaWin.is_attached_dialog()) {
let parent = metaWin.get_transient_for();
while (parent.is_attached_dialog())
parent = metaWin.get_transient_for();
let idx = this._lookupIndex (parent);
if (idx < 0) {
// parent was not created yet, it will take care
// of the dialog when created
return;
}
let clone = this._windows[idx];
clone.addAttachedDialog(metaWin);
}
return;
}
let [clone, overlay] = this._addWindowClone(win); let [clone, overlay] = this._addWindowClone(win);
if (win._overviewHint) { if (win._overviewHint) {
@@ -1321,9 +1478,11 @@ const Workspace = new Lang.Class({
overlay.hide(); overlay.hide();
if (clone.metaWindow.showing_on_its_workspace()) { if (clone.metaWindow.showing_on_its_workspace()) {
let [origX, origY] = clone.getOriginalPosition();
Tweener.addTween(clone.actor, Tweener.addTween(clone.actor,
{ x: clone.origX, { x: origX,
y: clone.origY, y: origY,
scale_x: 1.0, scale_x: 1.0,
scale_y: 1.0, scale_y: 1.0,
time: Overview.ANIMATION_TIME, time: Overview.ANIMATION_TIME,

View File

@@ -13,6 +13,7 @@ const Background = imports.ui.background;
const DND = imports.ui.dnd; const DND = imports.ui.dnd;
const Main = imports.ui.main; const Main = imports.ui.main;
const Tweener = imports.ui.tweener; const Tweener = imports.ui.tweener;
const WindowManager = imports.ui.windowManager;
const Workspace = imports.ui.workspace; const Workspace = imports.ui.workspace;
const WorkspacesView = imports.ui.workspacesView; const WorkspacesView = imports.ui.workspacesView;
@@ -31,20 +32,49 @@ const WORKSPACE_KEEP_ALIVE_TIME = 100;
const OVERRIDE_SCHEMA = 'org.gnome.shell.overrides'; const OVERRIDE_SCHEMA = 'org.gnome.shell.overrides';
/* A layout manager that requests size only for primary_actor, but then allocates
all using a fixed layout */
const PrimaryActorLayout = new Lang.Class({
Name: 'PrimaryActorLayout',
Extends: Clutter.FixedLayout,
_init: function(primaryActor) {
this.parent();
this.primaryActor = primaryActor;
},
vfunc_get_preferred_width: function(forHeight) {
return this.primaryActor.get_preferred_width(forHeight);
},
vfunc_get_preferred_height: function(forWidth) {
return this.primaryActor.get_preferred_height(forWidth);
},
});
const WindowClone = new Lang.Class({ const WindowClone = new Lang.Class({
Name: 'WindowClone', Name: 'WindowClone',
_init : function(realWindow) { _init : function(realWindow) {
this.actor = new Clutter.Clone({ source: realWindow.get_texture(), this.clone = new Clutter.Clone({ source: realWindow });
/* Can't use a Shell.GenericContainer because of DND and reparenting... */
this.actor = new Clutter.Actor({ layout_manager: new PrimaryActorLayout(this.clone),
reactive: true }); reactive: true });
this.actor._delegate = this; this.actor._delegate = this;
this.actor.add_child(this.clone);
this.realWindow = realWindow; this.realWindow = realWindow;
this.metaWindow = realWindow.meta_window; this.metaWindow = realWindow.meta_window;
this._positionChangedId = this.realWindow.connect('position-changed', this.clone._updateId = this.realWindow.connect('position-changed',
Lang.bind(this, this._onPositionChanged)); Lang.bind(this, this._onPositionChanged));
this._realWindowDestroyedId = this.realWindow.connect('destroy', this.clone._destroyId = this.realWindow.connect('destroy', Lang.bind(this, function() {
Lang.bind(this, this._disconnectRealWindowSignals)); // First destroy the clone and then destroy everything
// This will ensure that we never see it in the _disconnectSignals loop
this.clone.destroy();
this.destroy();
}));
this._onPositionChanged(); this._onPositionChanged();
this.actor.connect('button-release-event', this.actor.connect('button-release-event',
@@ -60,6 +90,24 @@ const WindowClone = new Lang.Class({
this._draggable.connect('drag-cancelled', Lang.bind(this, this._onDragCancelled)); this._draggable.connect('drag-cancelled', Lang.bind(this, this._onDragCancelled));
this._draggable.connect('drag-end', Lang.bind(this, this._onDragEnd)); this._draggable.connect('drag-end', Lang.bind(this, this._onDragEnd));
this.inDrag = false; this.inDrag = false;
let iter = Lang.bind(this, function(win) {
let actor = win.get_compositor_private();
if (!actor)
return false;
if (!win.is_attached_dialog())
return false;
this._doAddAttachedDialog(win, actor);
win.foreach_transient(iter);
return true;
});
this.metaWindow.foreach_transient(iter);
this._dimmer = new WindowManager.WindowDimmer(this.clone);
this._updateDimmer();
}, },
setStackAbove: function (actor) { setStackAbove: function (actor) {
@@ -74,25 +122,57 @@ const WindowClone = new Lang.Class({
this.actor.destroy(); this.actor.destroy();
}, },
addAttachedDialog: function(win) {
this._doAddAttachedDialog(win, win.get_compositor_private());
this._updateDimmer();
},
_doAddAttachedDialog: function(metaDialog, realDialog) {
let clone = new Clutter.Clone({ source: realDialog });
this._updateDialogPosition(realDialog, clone);
clone._updateId = realDialog.connect('position-changed',
Lang.bind(this, this._updateDialogPosition, clone));
clone._destroyId = realDialog.connect('destroy', Lang.bind(this, function() {
clone.destroy();
this._updateDimmer();
}));
this.actor.add_child(clone);
},
_updateDimmer: function() {
if (this.actor.get_n_children() > 1) {
this._dimmer.setEnabled(true);
this._dimmer.dimFactor = 1.0;
} else {
this._dimmer.setEnabled(false);
}
},
_updateDialogPosition: function(realDialog, cloneDialog) {
let metaDialog = realDialog.meta_window;
let dialogRect = metaDialog.get_outer_rect();
let rect = this.metaWindow.get_outer_rect();
cloneDialog.set_position(dialogRect.x - rect.x, dialogRect.y - rect.y);
},
_onPositionChanged: function() { _onPositionChanged: function() {
let rect = this.metaWindow.get_outer_rect(); let rect = this.metaWindow.get_outer_rect();
this.actor.set_position(this.realWindow.x, this.realWindow.y); this.actor.set_position(this.realWindow.x, this.realWindow.y);
}, },
_disconnectRealWindowSignals: function() { _disconnectSignals: function() {
if (this._positionChangedId != 0) { this.actor.get_children().forEach(function(child) {
this.realWindow.disconnect(this._positionChangedId); let realWindow = child.source;
this._positionChangedId = 0;
}
if (this._realWindowDestroyedId != 0) { realWindow.disconnect(child._updateId);
this.realWindow.disconnect(this._realWindowDestroyedId); realWindow.disconnect(child._destroyId);
this._realWindowDestroyedId = 0; });
}
}, },
_onDestroy: function() { _onDestroy: function() {
this._disconnectRealWindowSignals(); this._disconnectSignals();
this.actor._delegate = null; this.actor._delegate = null;
@@ -320,10 +400,26 @@ const WorkspaceThumbnail = new Lang.Class({
if (this._lookupIndex (metaWin) != -1) if (this._lookupIndex (metaWin) != -1)
return; return;
if (!this._isMyWindow(win) || !this._isOverviewWindow(win)) if (!this._isMyWindow(win))
return; return;
let clone = this._addWindowClone(win); if (this._isOverviewWindow(win)) {
this._addWindowClone(win);
} else if (metaWin.is_attached_dialog()) {
let parent = metaWin.get_transient_for();
while (parent.is_attached_dialog())
parent = metaWin.get_transient_for();
let idx = this._lookupIndex (parent);
if (idx < 0) {
// parent was not created yet, it will take care
// of the dialog when created
return;
}
let clone = this._windows[idx];
clone.addAttachedDialog(metaWin);
}
}, },
_windowAdded : function(metaWorkspace, metaWin) { _windowAdded : function(metaWorkspace, metaWin) {

584
po/lt.po

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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